]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
Add support for GitHub authentication
[rails.git] / vendor / assets / iD / iD.js
1 (function(exports) {
2
3   var bootstrap = (typeof exports.bootstrap === "object") ?
4     exports.bootstrap :
5     (exports.bootstrap = {});
6
7   bootstrap.tooltip = function() {
8
9     var tooltip = function(selection) {
10         selection.each(setup);
11       },
12       animation = d3.functor(false),
13       html = d3.functor(false),
14       title = function() {
15         var title = this.getAttribute("data-original-title");
16         if (title) {
17           return title;
18         } else {
19           title = this.getAttribute("title");
20           this.removeAttribute("title");
21           this.setAttribute("data-original-title", title);
22         }
23         return title;
24       },
25       over = "mouseenter.tooltip",
26       out = "mouseleave.tooltip",
27       placements = "top left bottom right".split(" "),
28       placement = d3.functor("top");
29
30     tooltip.title = function(_) {
31       if (arguments.length) {
32         title = d3.functor(_);
33         return tooltip;
34       } else {
35         return title;
36       }
37     };
38
39     tooltip.html = function(_) {
40       if (arguments.length) {
41         html = d3.functor(_);
42         return tooltip;
43       } else {
44         return html;
45       }
46     };
47
48     tooltip.placement = function(_) {
49       if (arguments.length) {
50         placement = d3.functor(_);
51         return tooltip;
52       } else {
53         return placement;
54       }
55     };
56
57     tooltip.show = function(selection) {
58       selection.each(show);
59     };
60
61     tooltip.hide = function(selection) {
62       selection.each(hide);
63     };
64
65     tooltip.toggle = function(selection) {
66       selection.each(toggle);
67     };
68
69     tooltip.destroy = function(selection) {
70       selection
71         .on(over, null)
72         .on(out, null)
73         .attr("title", function() {
74           return this.getAttribute("data-original-title") || this.getAttribute("title");
75         })
76         .attr("data-original-title", null)
77         .select(".tooltip")
78         .remove();
79     };
80
81     function setup() {
82       var root = d3.select(this),
83           animate = animation.apply(this, arguments),
84           tip = root.append("div")
85             .attr("class", "tooltip");
86
87       if (animate) {
88         tip.classed("fade", true);
89       }
90
91       // TODO "inside" checks?
92
93       tip.append("div")
94         .attr("class", "tooltip-arrow");
95       tip.append("div")
96         .attr("class", "tooltip-inner");
97
98       var place = placement.apply(this, arguments);
99       tip.classed(place, true);
100
101       root.on(over, show);
102       root.on(out, hide);
103     }
104
105     function show() {
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()),
115           pos;
116
117       switch (place) {
118         case "top":
119           pos = {x: outer.x + (outer.w - inner.w) / 2, y: outer.y - inner.h};
120           break;
121         case "right":
122           pos = {x: outer.x + outer.w, y: outer.y + (outer.h - inner.h) / 2};
123           break;
124         case "left":
125           pos = {x: outer.x - inner.w, y: outer.y + (outer.h - inner.h) / 2};
126           break;
127         case "bottom":
128           pos = {x: Math.max(0, outer.x + (outer.w - inner.w) / 2), y: outer.y + outer.h};
129           break;
130       }
131
132       tip.style(pos ?
133         {left: ~~pos.x + "px", top: ~~pos.y + "px"} :
134         {left: null, top: null});
135
136       this.tooltipVisible = true;
137     }
138
139     function hide() {
140       d3.select(this).select(".tooltip")
141         .classed("in", false);
142
143       this.tooltipVisible = false;
144     }
145
146     function toggle() {
147       if (this.tooltipVisible) {
148         hide.apply(this, arguments);
149       } else {
150         show.apply(this, arguments);
151       }
152     }
153
154     return tooltip;
155   };
156
157   function getPosition(node) {
158     var mode = d3.select(node).style('position');
159     if (mode === 'absolute' || mode === 'static') {
160       return {
161         x: node.offsetLeft,
162         y: node.offsetTop,
163         w: node.offsetWidth,
164         h: node.offsetHeight
165       };
166     } else {
167       return {
168         x: 0,
169         y: 0,
170         w: node.offsetWidth,
171         h: node.offsetHeight
172       };
173     }
174   }
175
176 })(this);
177 !function(){
178   var d3 = {version: "3.5.5"}; // semver
179 d3.ascending = d3_ascending;
180
181 function d3_ascending(a, b) {
182   return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
183 }
184 d3.descending = function(a, b) {
185   return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
186 };
187 d3.min = function(array, f) {
188   var i = -1,
189       n = array.length,
190       a,
191       b;
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;
195   } else {
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;
198   }
199   return a;
200 };
201 d3.max = function(array, f) {
202   var i = -1,
203       n = array.length,
204       a,
205       b;
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;
209   } else {
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;
212   }
213   return a;
214 };
215 d3.extent = function(array, f) {
216   var i = -1,
217       n = array.length,
218       a,
219       b,
220       c;
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) {
224       if (a > b) a = b;
225       if (c < b) c = b;
226     }
227   } else {
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) {
230       if (a > b) a = b;
231       if (c < b) c = b;
232     }
233   }
234   return [a, c];
235 };
236 function d3_number(x) {
237   return x === null ? NaN : +x;
238 }
239
240 function d3_numeric(x) {
241   return !isNaN(x);
242 }
243
244 d3.sum = function(array, f) {
245   var s = 0,
246       n = array.length,
247       a,
248       i = -1;
249   if (arguments.length === 1) {
250     while (++i < n) if (d3_numeric(a = +array[i])) s += a; // zero and null are equivalent
251   } else {
252     while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a;
253   }
254   return s;
255 };
256
257 d3.mean = function(array, f) {
258   var s = 0,
259       n = array.length,
260       a,
261       i = -1,
262       j = n;
263   if (arguments.length === 1) {
264     while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j;
265   } else {
266     while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j;
267   }
268   if (j) return s / j;
269 };
270 // R-7 per <http://en.wikipedia.org/wiki/Quantile>
271 d3.quantile = function(values, p) {
272   var H = (values.length - 1) * p + 1,
273       h = Math.floor(H),
274       v = +values[h - 1],
275       e = H - h;
276   return e ? v + e * (values[h] - v) : v;
277 };
278
279 d3.median = function(array, f) {
280   var numbers = [],
281       n = array.length,
282       a,
283       i = -1;
284   if (arguments.length === 1) {
285     while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a);
286   } else {
287     while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a);
288   }
289   if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5);
290 };
291
292 d3.variance = function(array, f) {
293   var n = array.length,
294       m = 0,
295       a,
296       d,
297       s = 0,
298       i = -1,
299       j = 0;
300   if (arguments.length === 1) {
301     while (++i < n) {
302       if (d3_numeric(a = d3_number(array[i]))) {
303         d = a - m;
304         m += d / ++j;
305         s += d * (a - m);
306       }
307     }
308   } else {
309     while (++i < n) {
310       if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) {
311         d = a - m;
312         m += d / ++j;
313         s += d * (a - m);
314       }
315     }
316   }
317   if (j > 1) return s / (j - 1);
318 };
319
320 d3.deviation = function() {
321   var v = d3.variance.apply(this, arguments);
322   return v ? Math.sqrt(v) : v;
323 };
324
325 function d3_bisector(compare) {
326   return {
327     left: function(a, x, lo, hi) {
328       if (arguments.length < 3) lo = 0;
329       if (arguments.length < 4) hi = a.length;
330       while (lo < hi) {
331         var mid = lo + hi >>> 1;
332         if (compare(a[mid], x) < 0) lo = mid + 1;
333         else hi = mid;
334       }
335       return lo;
336     },
337     right: function(a, x, lo, hi) {
338       if (arguments.length < 3) lo = 0;
339       if (arguments.length < 4) hi = a.length;
340       while (lo < hi) {
341         var mid = lo + hi >>> 1;
342         if (compare(a[mid], x) > 0) hi = mid;
343         else lo = mid + 1;
344       }
345       return lo;
346     }
347   };
348 }
349
350 var d3_bisect = d3_bisector(d3_ascending);
351 d3.bisectLeft = d3_bisect.left;
352 d3.bisect = d3.bisectRight = d3_bisect.right;
353
354 d3.bisector = function(f) {
355   return d3_bisector(f.length === 1
356       ? function(d, x) { return d3_ascending(f(d), x); }
357       : f);
358 };
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;
362   while (m) {
363     i = Math.random() * m-- | 0;
364     t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t;
365   }
366   return array;
367 };
368 d3.permute = function(array, indexes) {
369   var i = indexes.length, permutes = new Array(i);
370   while (i--) permutes[i] = array[indexes[i]];
371   return permutes;
372 };
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]];
376   return pairs;
377 };
378
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];
384     }
385   }
386   return zips;
387 };
388
389 function d3_zipLength(d) {
390   return d.length;
391 }
392
393 d3.transpose = function(matrix) {
394   return d3.zip.apply(d3, matrix);
395 };
396 d3.keys = function(map) {
397   var keys = [];
398   for (var key in map) keys.push(key);
399   return keys;
400 };
401 d3.values = function(map) {
402   var values = [];
403   for (var key in map) values.push(map[key]);
404   return values;
405 };
406 d3.entries = function(map) {
407   var entries = [];
408   for (var key in map) entries.push({key: key, value: map[key]});
409   return entries;
410 };
411 d3.merge = function(arrays) {
412   var n = arrays.length,
413       m,
414       i = -1,
415       j = 0,
416       merged,
417       array;
418
419   while (++i < n) j += arrays[i].length;
420   merged = new Array(j);
421
422   while (--n >= 0) {
423     array = arrays[n];
424     m = array.length;
425     while (--m >= 0) {
426       merged[--j] = array[m];
427     }
428   }
429
430   return merged;
431 };
432 var abs = Math.abs;
433
434 d3.range = function(start, stop, step) {
435   if (arguments.length < 3) {
436     step = 1;
437     if (arguments.length < 2) {
438       stop = start;
439       start = 0;
440     }
441   }
442   if ((stop - start) / step === Infinity) throw new Error("infinite range");
443   var range = [],
444        k = d3_range_integerScale(abs(step)),
445        i = -1,
446        j;
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);
450   return range;
451 };
452
453 function d3_range_integerScale(x) {
454   var k = 1;
455   while (x * k % 1) k *= 10;
456   return k;
457 }
458 function d3_class(ctor, properties) {
459   for (var key in properties) {
460     Object.defineProperty(ctor.prototype, key, {
461       value: properties[key],
462       enumerable: false
463     });
464   }
465 }
466
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)) {
472     var i = -1,
473         n = object.length,
474         o;
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);
477   } else {
478     for (var key in object) map.set(key, object[key]);
479   }
480   return map;
481 };
482
483 function d3_Map() {
484   this._ = Object.create(null);
485 }
486
487 var d3_map_proto = "__proto__",
488     d3_map_zero = "\0";
489
490 d3_class(d3_Map, {
491   has: d3_map_has,
492   get: function(key) {
493     return this._[d3_map_escape(key)];
494   },
495   set: function(key, value) {
496     return this._[d3_map_escape(key)] = value;
497   },
498   remove: d3_map_remove,
499   keys: d3_map_keys,
500   values: function() {
501     var values = [];
502     for (var key in this._) values.push(this._[key]);
503     return values;
504   },
505   entries: function() {
506     var entries = [];
507     for (var key in this._) entries.push({key: d3_map_unescape(key), value: this._[key]});
508     return entries;
509   },
510   size: d3_map_size,
511   empty: d3_map_empty,
512   forEach: function(f) {
513     for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]);
514   }
515 });
516
517 function d3_map_escape(key) {
518   return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key;
519 }
520
521 function d3_map_unescape(key) {
522   return (key += "")[0] === d3_map_zero ? key.slice(1) : key;
523 }
524
525 function d3_map_has(key) {
526   return d3_map_escape(key) in this._;
527 }
528
529 function d3_map_remove(key) {
530   return (key = d3_map_escape(key)) in this._ && delete this._[key];
531 }
532
533 function d3_map_keys() {
534   var keys = [];
535   for (var key in this._) keys.push(d3_map_unescape(key));
536   return keys;
537 }
538
539 function d3_map_size() {
540   var size = 0;
541   for (var key in this._) ++size;
542   return size;
543 }
544
545 function d3_map_empty() {
546   for (var key in this._) return false;
547   return true;
548 }
549
550 d3.nest = function() {
551   var nest = {},
552       keys = [],
553       sortKeys = [],
554       sortValues,
555       rollup;
556
557   function map(mapType, array, depth) {
558     if (depth >= keys.length) return rollup
559         ? rollup.call(nest, array) : (sortValues
560         ? array.sort(sortValues)
561         : array);
562
563     var i = -1,
564         n = array.length,
565         key = keys[depth++],
566         keyValue,
567         object,
568         setter,
569         valuesByKey = new d3_Map,
570         values;
571
572     while (++i < n) {
573       if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
574         values.push(object);
575       } else {
576         valuesByKey.set(keyValue, [object]);
577       }
578     }
579
580     if (mapType) {
581       object = mapType();
582       setter = function(keyValue, values) {
583         object.set(keyValue, map(mapType, values, depth));
584       };
585     } else {
586       object = {};
587       setter = function(keyValue, values) {
588         object[keyValue] = map(mapType, values, depth);
589       };
590     }
591
592     valuesByKey.forEach(setter);
593     return object;
594   }
595
596   function entries(map, depth) {
597     if (depth >= keys.length) return map;
598
599     var array = [],
600         sortKey = sortKeys[depth++];
601
602     map.forEach(function(key, keyMap) {
603       array.push({key: key, values: entries(keyMap, depth)});
604     });
605
606     return sortKey
607         ? array.sort(function(a, b) { return sortKey(a.key, b.key); })
608         : array;
609   }
610
611   nest.map = function(array, mapType) {
612     return map(mapType, array, 0);
613   };
614
615   nest.entries = function(array) {
616     return entries(map(d3.map, array, 0), 0);
617   };
618
619   nest.key = function(d) {
620     keys.push(d);
621     return nest;
622   };
623
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;
628     return nest;
629   };
630
631   // Specifies the order for leaf values.
632   // Applies to both maps and entries array.
633   nest.sortValues = function(order) {
634     sortValues = order;
635     return nest;
636   };
637
638   nest.rollup = function(f) {
639     rollup = f;
640     return nest;
641   };
642
643   return nest;
644 };
645
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]);
649   return set;
650 };
651
652 function d3_Set() {
653   this._ = Object.create(null);
654 }
655
656 d3_class(d3_Set, {
657   has: d3_map_has,
658   add: function(key) {
659     this._[d3_map_escape(key += "")] = true;
660     return key;
661   },
662   remove: d3_map_remove,
663   values: d3_map_keys,
664   size: d3_map_size,
665   empty: d3_map_empty,
666   forEach: function(f) {
667     for (var key in this._) f.call(this, d3_map_unescape(key));
668   }
669 });
670 d3.behavior = {};
671 var d3_document = this.document;
672
673 function d3_documentElement(node) {
674   return node
675       && (node.ownerDocument // node is a Node
676       || node.document // node is a Window
677       || node).documentElement; // node is a Document
678 }
679
680 function d3_window(node) {
681   return 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
685 }
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]);
690   return target;
691 };
692
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) {
697   return function() {
698     var value = method.apply(source, arguments);
699     return value === source ? target : value;
700   };
701 }
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;
708   }
709 }
710
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() {}
715
716 d3.dispatch = function() {
717   var dispatch = new d3_dispatch,
718       i = -1,
719       n = arguments.length;
720   while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
721   return dispatch;
722 };
723
724 function d3_dispatch() {}
725
726 d3_dispatch.prototype.on = function(type, listener) {
727   var i = type.indexOf("."),
728       name = "";
729
730   // Extract optional namespace, e.g., "click.foo"
731   if (i >= 0) {
732     name = type.slice(i + 1);
733     type = type.slice(0, i);
734   }
735
736   if (type) return arguments.length < 2
737       ? this[type].on(name)
738       : this[type].on(name, listener);
739
740   if (arguments.length === 2) {
741     if (listener == null) for (type in this) {
742       if (this.hasOwnProperty(type)) this[type].on(name, null);
743     }
744     return this;
745   }
746 };
747
748 function d3_dispatch_event(dispatch) {
749   var listeners = [],
750       listenerByName = new d3_Map;
751
752   function event() {
753     var z = listeners, // defensive reference
754         i = -1,
755         n = z.length,
756         l;
757     while (++i < n) if (l = z[i].on) l.apply(this, arguments);
758     return dispatch;
759   }
760
761   event.on = function(name, listener) {
762     var l = listenerByName.get(name),
763         i;
764
765     // return the current listener, if any
766     if (arguments.length < 2) return l && l.on;
767
768     // remove the old listener, if any (with copy-on-write)
769     if (l) {
770       l.on = null;
771       listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
772       listenerByName.remove(name);
773     }
774
775     // add the new listener, if any
776     if (listener) listeners.push(listenerByName.set(name, {on: listener}));
777
778     return dispatch;
779   };
780
781   return event;
782 }
783
784 d3.event = null;
785
786 function d3_eventPreventDefault() {
787   d3.event.preventDefault();
788 }
789
790 function d3_eventCancel() {
791   d3.event.preventDefault();
792   d3.event.stopPropagation();
793 }
794
795 function d3_eventSource() {
796   var e = d3.event, s;
797   while (s = e.sourceEvent) e = s;
798   return e;
799 }
800
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,
807       i = 0,
808       n = arguments.length;
809
810   while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
811
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) {
823       try {
824         var e0 =
825         e1.sourceEvent = d3.event;
826         e1.target = target;
827         d3.event = e1;
828         dispatch[e1.type].apply(thiz, argumentz);
829       } finally {
830         d3.event = e0;
831       }
832     };
833   };
834
835   return dispatch;
836 }
837 d3.requote = function(s) {
838   return s.replace(d3_requote_re, "\\$&");
839 };
840
841 var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
842 var d3_subclass = {}.__proto__?
843
844 // Until ECMAScript supports array subclassing, prototype injection works well.
845 function(object, prototype) {
846   object.__proto__ = prototype;
847 }:
848
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];
852 };
853
854 function d3_selection(groups) {
855   d3_subclass(groups, d3_selectionPrototype);
856   return groups;
857 }
858
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);
865       };
866       return d3_selectMatches(n, s);
867     };
868
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;
874 }
875
876 d3.selection = function() {
877   return d3.select(d3_document.documentElement);
878 };
879
880 var d3_selectionPrototype = d3.selection.prototype = [];
881
882
883 d3_selectionPrototype.select = function(selector) {
884   var subgroups = [],
885       subgroup,
886       subnode,
887       group,
888       node;
889
890   selector = d3_selection_selector(selector);
891
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__;
899       } else {
900         subgroup.push(null);
901       }
902     }
903   }
904
905   return d3_selection(subgroups);
906 };
907
908 function d3_selection_selector(selector) {
909   return typeof selector === "function" ? selector : function() {
910     return d3_select(selector, this);
911   };
912 }
913
914 d3_selectionPrototype.selectAll = function(selector) {
915   var subgroups = [],
916       subgroup,
917       node;
918
919   selector = d3_selection_selectorAll(selector);
920
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;
926       }
927     }
928   }
929
930   return d3_selection(subgroups);
931 };
932
933 function d3_selection_selectorAll(selector) {
934   return typeof selector === "function" ? selector : function() {
935     return d3_selectAll(selector, this);
936   };
937 }
938 var d3_nsPrefix = {
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/"
944 };
945
946 d3.ns = {
947   prefix: d3_nsPrefix,
948   qualify: function(name) {
949     var i = name.indexOf(":"),
950         prefix = name;
951     if (i >= 0) {
952       prefix = name.slice(0, i);
953       name = name.slice(i + 1);
954     }
955     return d3_nsPrefix.hasOwnProperty(prefix)
956         ? {space: d3_nsPrefix[prefix], local: name}
957         : name;
958   }
959 };
960
961 d3_selectionPrototype.attr = function(name, value) {
962   if (arguments.length < 2) {
963
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);
968       return name.local
969           ? node.getAttributeNS(name.space, name.local)
970           : node.getAttribute(name);
971     }
972
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]));
977     return this;
978   }
979
980   return this.each(d3_selection_attr(name, value));
981 };
982
983 function d3_selection_attr(name, value) {
984   name = d3.ns.qualify(name);
985
986   // For attr(string, null), remove the attribute with the specified name.
987   function attrNull() {
988     this.removeAttribute(name);
989   }
990   function attrNullNS() {
991     this.removeAttributeNS(name.space, name.local);
992   }
993
994   // For attr(string, string), set the attribute with the specified name.
995   function attrConstant() {
996     this.setAttribute(name, value);
997   }
998   function attrConstantNS() {
999     this.setAttributeNS(name.space, name.local, value);
1000   }
1001
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);
1008   }
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);
1013   }
1014
1015   return value == null
1016       ? (name.local ? attrNullNS : attrNull) : (typeof value === "function"
1017       ? (name.local ? attrFunctionNS : attrFunction)
1018       : (name.local ? attrConstantNS : attrConstant));
1019 }
1020 function d3_collapse(s) {
1021   return s.trim().replace(/\s+/g, " ");
1022 }
1023
1024 d3_selectionPrototype.classed = function(name, value) {
1025   if (arguments.length < 2) {
1026
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,
1033           i = -1;
1034       if (value = node.classList) {
1035         while (++i < n) if (!value.contains(name[i])) return false;
1036       } else {
1037         value = node.getAttribute("class");
1038         while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
1039       }
1040       return true;
1041     }
1042
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]));
1046     return this;
1047   }
1048
1049   // Otherwise, both a name and a value are specified, and are handled as below.
1050   return this.each(d3_selection_classed(name, value));
1051 };
1052
1053 function d3_selection_classedRe(name) {
1054   return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
1055 }
1056
1057 function d3_selection_classes(name) {
1058   return (name + "").trim().split(/^|\s+/);
1059 }
1060
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;
1065
1066   function classedConstant() {
1067     var i = -1;
1068     while (++i < n) name[i](this, value);
1069   }
1070
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);
1076   }
1077
1078   return typeof value === "function"
1079       ? classedFunction
1080       : classedConstant;
1081 }
1082
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") || "";
1088     if (value) {
1089       re.lastIndex = 0;
1090       if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
1091     } else {
1092       node.setAttribute("class", d3_collapse(c.replace(re, " ")));
1093     }
1094   };
1095 }
1096
1097 d3_selectionPrototype.style = function(name, value, priority) {
1098   var n = arguments.length;
1099   if (n < 3) {
1100
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));
1108       return this;
1109     }
1110
1111     // For style(string), return the computed style value for the first node.
1112     if (n < 2) {
1113       var node = this.node();
1114       return d3_window(node).getComputedStyle(node, null).getPropertyValue(name);
1115     }
1116
1117     // For style(string, string) or style(string, function), use the default
1118     // priority. The priority is ignored for style(string, null).
1119     priority = "";
1120   }
1121
1122   // Otherwise, a name, value and priority are specified, and handled as below.
1123   return this.each(d3_selection_style(name, value, priority));
1124 };
1125
1126 function d3_selection_style(name, value, priority) {
1127
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);
1132   }
1133
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);
1138   }
1139
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);
1147   }
1148
1149   return value == null
1150       ? styleNull : (typeof value === "function"
1151       ? styleFunction : styleConstant);
1152 }
1153
1154 d3_selectionPrototype.property = function(name, value) {
1155   if (arguments.length < 2) {
1156
1157     // For property(string), return the property value for the first node.
1158     if (typeof name === "string") return this.node()[name];
1159
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]));
1164     return this;
1165   }
1166
1167   // Otherwise, both a name and a value are specified, and are handled as below.
1168   return this.each(d3_selection_property(name, value));
1169 };
1170
1171 function d3_selection_property(name, value) {
1172
1173   // For property(name, null), remove the property with the specified name.
1174   function propertyNull() {
1175     delete this[name];
1176   }
1177
1178   // For property(name, string), set the property with the specified name.
1179   function propertyConstant() {
1180     this[name] = value;
1181   }
1182
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;
1189   }
1190
1191   return value == null
1192       ? propertyNull : (typeof value === "function"
1193       ? propertyFunction : propertyConstant);
1194 }
1195
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;
1203 };
1204
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;
1212 };
1213
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));
1218   });
1219 };
1220
1221 function d3_selection_creator(name) {
1222
1223   function create() {
1224     var document = this.ownerDocument,
1225         namespace = this.namespaceURI;
1226     return namespace
1227         ? document.createElementNS(namespace, name)
1228         : document.createElement(name);
1229   }
1230
1231   function createNS() {
1232     return this.ownerDocument.createElementNS(name.space, name.local);
1233   }
1234
1235   return typeof name === "function" ? name
1236       : (name = d3.ns.qualify(name)).local ? createNS
1237       : create;
1238 }
1239
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);
1245   });
1246 };
1247
1248 // TODO remove(selector)?
1249 // TODO remove(node)?
1250 // TODO remove(function)?
1251 d3_selectionPrototype.remove = function() {
1252   return this.each(d3_selectionRemove);
1253 };
1254
1255 function d3_selectionRemove() {
1256   var parent = this.parentNode;
1257   if (parent) parent.removeChild(this);
1258 }
1259
1260 d3_selectionPrototype.data = function(value, key) {
1261   var i = -1,
1262       n = this.length,
1263       group,
1264       node;
1265
1266   // If no value is specified, return the first value.
1267   if (!arguments.length) {
1268     value = new Array(n = (group = this[0]).length);
1269     while (++i < n) {
1270       if (node = group[i]) {
1271         value[i] = node.__data__;
1272       }
1273     }
1274     return value;
1275   }
1276
1277   function bind(group, groupData) {
1278     var i,
1279         n = group.length,
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),
1285         node,
1286         nodeData;
1287
1288     if (key) {
1289       var nodeByKeyValue = new d3_Map,
1290           keyValues = new Array(n),
1291           keyValue;
1292
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
1296         } else {
1297           nodeByKeyValue.set(keyValue, node);
1298         }
1299         keyValues[i] = keyValue;
1300       }
1301
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;
1308         }
1309         nodeByKeyValue.set(keyValue, true);
1310       }
1311
1312       for (i = -1; ++i < n;) {
1313         if (nodeByKeyValue.get(keyValues[i]) !== true) {
1314           exitNodes[i] = group[i];
1315         }
1316       }
1317     } else {
1318       for (i = -1; ++i < n0;) {
1319         node = group[i];
1320         nodeData = groupData[i];
1321         if (node) {
1322           node.__data__ = nodeData;
1323           updateNodes[i] = node;
1324         } else {
1325           enterNodes[i] = d3_selection_dataNode(nodeData);
1326         }
1327       }
1328       for (; i < m; ++i) {
1329         enterNodes[i] = d3_selection_dataNode(groupData[i]);
1330       }
1331       for (; i < n; ++i) {
1332         exitNodes[i] = group[i];
1333       }
1334     }
1335
1336     enterNodes.update
1337         = updateNodes;
1338
1339     enterNodes.parentNode
1340         = updateNodes.parentNode
1341         = exitNodes.parentNode
1342         = group.parentNode;
1343
1344     enter.push(enterNodes);
1345     update.push(updateNodes);
1346     exit.push(exitNodes);
1347   }
1348
1349   var enter = d3_selection_enter([]),
1350       update = d3_selection([]),
1351       exit = d3_selection([]);
1352
1353   if (typeof value === "function") {
1354     while (++i < n) {
1355       bind(group = this[i], value.call(group, group.parentNode.__data__, i));
1356     }
1357   } else {
1358     while (++i < n) {
1359       bind(group = this[i], value);
1360     }
1361   }
1362
1363   update.enter = function() { return enter; };
1364   update.exit = function() { return exit; };
1365   return update;
1366 };
1367
1368 function d3_selection_dataNode(data) {
1369   return {__data__: data};
1370 }
1371
1372 d3_selectionPrototype.datum = function(value) {
1373   return arguments.length
1374       ? this.property("__data__", value)
1375       : this.property("__data__");
1376 };
1377
1378 d3_selectionPrototype.filter = function(filter) {
1379   var subgroups = [],
1380       subgroup,
1381       group,
1382       node;
1383
1384   if (typeof filter !== "function") filter = d3_selection_filter(filter);
1385
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);
1392       }
1393     }
1394   }
1395
1396   return d3_selection(subgroups);
1397 };
1398
1399 function d3_selection_filter(selector) {
1400   return function() {
1401     return d3_selectMatches(this, selector);
1402   };
1403 }
1404
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);
1410         next = node;
1411       }
1412     }
1413   }
1414   return this;
1415 };
1416
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();
1421 };
1422
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;
1427   };
1428 }
1429
1430 d3_selectionPrototype.each = function(callback) {
1431   return d3_selection_each(this, function(node, i, j) {
1432     callback.call(node, node.__data__, i, j);
1433   });
1434 };
1435
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);
1440     }
1441   }
1442   return groups;
1443 }
1444
1445 d3_selectionPrototype.call = function(callback) {
1446   var args = d3_array(arguments);
1447   callback.apply(args[0] = this, args);
1448   return this;
1449 };
1450
1451 d3_selectionPrototype.empty = function() {
1452   return !this.node();
1453 };
1454
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;
1460     }
1461   }
1462   return null;
1463 };
1464
1465 d3_selectionPrototype.size = function() {
1466   var n = 0;
1467   d3_selection_each(this, function() { ++n; });
1468   return n;
1469 };
1470
1471 function d3_selection_enter(selection) {
1472   d3_subclass(selection, d3_selection_enterPrototype);
1473   return selection;
1474 }
1475
1476 var d3_selection_enterPrototype = [];
1477
1478 d3.selection.enter = d3_selection_enter;
1479 d3.selection.enter.prototype = d3_selection_enterPrototype;
1480
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;
1486
1487
1488 d3_selection_enterPrototype.select = function(selector) {
1489   var subgroups = [],
1490       subgroup,
1491       subnode,
1492       upgroup,
1493       group,
1494       node;
1495
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__;
1504       } else {
1505         subgroup.push(null);
1506       }
1507     }
1508   }
1509
1510   return d3_selection(subgroups);
1511 };
1512
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);
1516 };
1517
1518 function d3_selection_enterInsertBefore(enter) {
1519   var i0, j0;
1520   return function(d, i, j) {
1521     var group = enter[j].update,
1522         n = group.length,
1523         node;
1524     if (j != j0) j0 = j, i0 = 0;
1525     if (i >= i0) i0 = i + 1;
1526     while (!(node = group[i0]) && ++i0 < n);
1527     return node;
1528   };
1529 }
1530
1531 // TODO fast singleton implementation?
1532 d3.select = function(node) {
1533   var group;
1534   if (typeof node === "string") {
1535     group = [d3_select(node, d3_document)];
1536     group.parentNode = d3_document.documentElement;
1537   } else {
1538     group = [node];
1539     group.parentNode = d3_documentElement(node);
1540   }
1541   return d3_selection([group]);
1542 };
1543
1544 d3.selectAll = function(nodes) {
1545   var group;
1546   if (typeof nodes === "string") {
1547     group = d3_array(d3_selectAll(nodes, d3_document));
1548     group.parentNode = d3_document.documentElement;
1549   } else {
1550     group = nodes;
1551     group.parentNode = null;
1552   }
1553   return d3_selection([group]);
1554 };
1555
1556 d3_selectionPrototype.on = function(type, listener, capture) {
1557   var n = arguments.length;
1558   if (n < 3) {
1559
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));
1566       return this;
1567     }
1568
1569     // For on(string), return the listener for the first node.
1570     if (n < 2) return (n = this.node()["__on" + type]) && n._;
1571
1572     // For on(string, function), use the default capture.
1573     capture = false;
1574   }
1575
1576   // Otherwise, a type, listener and capture are specified, and handled as below.
1577   return this.each(d3_selection_on(type, listener, capture));
1578 };
1579
1580 function d3_selection_on(type, listener, capture) {
1581   var name = "__on" + type,
1582       i = type.indexOf("."),
1583       wrap = d3_selection_onListener;
1584
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;
1588
1589   function onRemove() {
1590     var l = this[name];
1591     if (l) {
1592       this.removeEventListener(type, l, l.$);
1593       delete this[name];
1594     }
1595   }
1596
1597   function onAdd() {
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);
1602     l._ = listener;
1603   }
1604
1605   function removeAll() {
1606     var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"),
1607         match;
1608     for (var name in this) {
1609       if (match = name.match(re)) {
1610         var l = this[name];
1611         this.removeEventListener(match[1], l, l.$);
1612         delete this[name];
1613       }
1614     }
1615   }
1616
1617   return i
1618       ? listener ? onAdd : onRemove
1619       : listener ? d3_noop : removeAll;
1620 }
1621
1622 var d3_selection_onFilters = d3.map({
1623   mouseenter: "mouseover",
1624   mouseleave: "mouseout"
1625 });
1626
1627 if (d3_document) {
1628   d3_selection_onFilters.forEach(function(k) {
1629     if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
1630   });
1631 }
1632
1633 function d3_selection_onListener(listener, argumentz) {
1634   return function(e) {
1635     var o = d3.event; // Events can be reentrant (e.g., focus).
1636     d3.event = e;
1637     argumentz[0] = this.__data__;
1638     try {
1639       listener.apply(this, argumentz);
1640     } finally {
1641       d3.event = o;
1642     }
1643   };
1644 }
1645
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))) {
1651       l.call(target, e);
1652     }
1653   };
1654 }
1655
1656 var d3_event_dragSelect,
1657     d3_event_dragId = 0;
1658
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);
1666
1667   if (d3_event_dragSelect == null) {
1668     d3_event_dragSelect = "onselectstart" in node ? false
1669         : d3_vendorSymbol(node.style, "userSelect");
1670   }
1671
1672   if (d3_event_dragSelect) {
1673     var style = d3_documentElement(node).style,
1674         select = style[d3_event_dragSelect];
1675     style[d3_event_dragSelect] = "none";
1676   }
1677
1678   return function(suppressClick) {
1679     w.on(name, null);
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);
1684       setTimeout(off, 0);
1685     }
1686   };
1687 }
1688
1689 d3.mouse = function(container) {
1690   return d3_mousePoint(container, d3_eventSource());
1691 };
1692
1693 // https://bugs.webkit.org/show_bug.cgi?id=44083
1694 var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0;
1695
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",
1706           top: 0,
1707           left: 0,
1708           margin: 0,
1709           padding: 0,
1710           border: "none"
1711         }, "important");
1712         var ctm = svg[0][0].getScreenCTM();
1713         d3_mouse_bug44083 = !(ctm.f || ctm.e);
1714         svg.remove();
1715       }
1716     }
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];
1721   }
1722   var rect = container.getBoundingClientRect();
1723   return [e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop];
1724 };
1725
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;
1731     return point;
1732   }) : [];
1733 };
1734 var ε = 1e-6,
1735     ε2 = ε * ε,
1736     π = Math.PI,
1737     τ = 2 * π,
1738     τε = τ - ε,
1739     halfπ = π / 2,
1740     d3_radians = π / 180,
1741     d3_degrees = 180 / π;
1742
1743 function d3_sgn(x) {
1744   return x > 0 ? 1 : x < 0 ? -1 : 0;
1745 }
1746
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]);
1753 }
1754
1755 function d3_acos(x) {
1756   return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
1757 }
1758
1759 function d3_asin(x) {
1760   return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
1761 }
1762
1763 function d3_sinh(x) {
1764   return ((x = Math.exp(x)) - 1 / x) / 2;
1765 }
1766
1767 function d3_cosh(x) {
1768   return ((x = Math.exp(x)) + 1 / x) / 2;
1769 }
1770
1771 function d3_tanh(x) {
1772   return ((x = Math.exp(2 * x)) - 1) / (x + 1);
1773 }
1774
1775 function d3_haversin(x) {
1776   return (x = Math.sin(x / 2)) * x;
1777 }
1778
1779 var ρ = Math.SQRT2,
1780     ρ2 = 2,
1781     ρ4 = 4;
1782
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];
1788
1789   var dx = ux1 - ux0,
1790       dy = uy1 - uy0,
1791       d2 = dx * dx + dy * dy,
1792       d1 = Math.sqrt(d2),
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),
1797       dr = r1 - r0,
1798       S = (dr || Math.log(w1 / w0)) / ρ;
1799
1800   function interpolate(t) {
1801     var s = t * S;
1802     if (dr) {
1803       // General case.
1804       var coshr0 = d3_cosh(r0),
1805           u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0));
1806       return [
1807         ux0 + u * dx,
1808         uy0 + u * dy,
1809         w0 * coshr0 / d3_cosh(ρ * s + r0)
1810       ];
1811     }
1812     // Special case for u0 ~= u1.
1813     return [
1814       ux0 + t * dx,
1815       uy0 + t * dy,
1816       w0 * Math.exp(ρ * s)
1817     ];
1818   }
1819
1820   interpolate.duration = S * 1000;
1821
1822   return interpolate;
1823 };
1824
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,
1832       duration = 250,
1833       zooming = 0,
1834       mousedown = "mousedown.zoom",
1835       mousemove = "mousemove.zoom",
1836       mouseup = "mouseup.zoom",
1837       mousewheelTimer,
1838       touchstart = "touchstart.zoom",
1839       touchtime, // time of last touchstart (to detect double-tap)
1840       event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"),
1841       x0,
1842       x1,
1843       y0,
1844       y1;
1845
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");
1852   }
1853
1854   function zoom(g) {
1855     g   .on(mousedown, mousedowned)
1856         .on(d3_behavior_zoomWheel + ".zoom", mousewheeled)
1857         .on("dblclick.zoom", dblclicked)
1858         .on(touchstart, touchstarted);
1859   }
1860
1861   zoom.event = function(g) {
1862     g.each(function() {
1863       var dispatch = event.of(this, arguments),
1864           view1 = view;
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);
1870             })
1871             .tween("zoom:zoom", function() {
1872               var dx = size[0],
1873                   dy = size[1],
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]
1879                   );
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};
1883                 zoomed(dispatch);
1884               };
1885             })
1886             .each("interrupt.zoom", function() {
1887               zoomended(dispatch);
1888             })
1889             .each("end.zoom", function() {
1890               zoomended(dispatch);
1891             });
1892       } else {
1893         this.__chart__ = view;
1894         zoomstarted(dispatch);
1895         zoomed(dispatch);
1896         zoomended(dispatch);
1897       }
1898     });
1899   }
1900
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
1904     rescale();
1905     return zoom;
1906   };
1907
1908   zoom.scale = function(_) {
1909     if (!arguments.length) return view.k;
1910     view = {x: view.x, y: view.y, k: +_}; // copy-on-write
1911     rescale();
1912     return zoom;
1913   };
1914
1915   zoom.scaleExtent = function(_) {
1916     if (!arguments.length) return scaleExtent;
1917     scaleExtent = _ == null ? d3_behavior_zoomInfinity : [+_[0], +_[1]];
1918     return zoom;
1919   };
1920
1921   zoom.center = function(_) {
1922     if (!arguments.length) return center;
1923     center = _ && [+_[0], +_[1]];
1924     return zoom;
1925   };
1926
1927   zoom.size = function(_) {
1928     if (!arguments.length) return size;
1929     size = _ && [+_[0], +_[1]];
1930     return zoom;
1931   };
1932
1933   zoom.duration = function(_) {
1934     if (!arguments.length) return duration;
1935     duration = +_; // TODO function based on interpolateZoom distance?
1936     return zoom;
1937   };
1938
1939   zoom.x = function(z) {
1940     if (!arguments.length) return x1;
1941     x1 = z;
1942     x0 = z.copy();
1943     view = {x: 0, y: 0, k: 1}; // copy-on-write
1944     return zoom;
1945   };
1946
1947   zoom.y = function(z) {
1948     if (!arguments.length) return y1;
1949     y1 = z;
1950     y0 = z.copy();
1951     view = {x: 0, y: 0, k: 1}; // copy-on-write
1952     return zoom;
1953   };
1954
1955   function location(p) {
1956     return [(p[0] - view.x) / view.k, (p[1] - view.y) / view.k];
1957   }
1958
1959   function point(l) {
1960     return [l[0] * view.k + view.x, l[1] * view.k + view.y];
1961   }
1962
1963   function scaleTo(s) {
1964     view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
1965   }
1966
1967   function translateTo(p, l) {
1968     l = point(l);
1969     view.x += p[0] - l[0];
1970     view.y += p[1] - l[1];
1971   }
1972
1973   function zoomTo(that, p, l, k) {
1974     that.__chart__ = {x: view.x, y: view.y, k: view.k};
1975
1976     scaleTo(Math.pow(2, k));
1977     translateTo(center0 = p, l);
1978
1979     that = d3.select(that);
1980     if (duration > 0) that = that.transition().duration(duration);
1981     that.call(zoom.event);
1982   }
1983
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));
1987   }
1988
1989   function zoomstarted(dispatch) {
1990     if (!zooming++) dispatch({type: "zoomstart"});
1991   }
1992
1993   function zoomed(dispatch) {
1994     rescale();
1995     dispatch({type: "zoom", scale: view.k, translate: [view.x, view.y]});
1996   }
1997
1998   function zoomended(dispatch) {
1999     if (!--zooming) dispatch({type: "zoomend"});
2000     center0 = null;
2001   }
2002
2003   function mousedowned() {
2004     var that = this,
2005         target = d3.event.target,
2006         dispatch = event.of(that, arguments),
2007         dragged = 0,
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);
2011
2012     d3_selection_interrupt.call(that);
2013     zoomstarted(dispatch);
2014
2015     function moved() {
2016       dragged = 1;
2017       translateTo(d3.mouse(that), location0);
2018       zoomed(dispatch);
2019     }
2020
2021     function ended() {
2022       subject.on(mousemove, null).on(mouseup, null);
2023       dragRestore(dragged && d3.event.target === target);
2024       zoomended(dispatch);
2025     }
2026   }
2027
2028   // These closures persist for as long as at least one touch is active.
2029   function touchstarted() {
2030     var that = this,
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,
2038         targets = [],
2039         subject = d3.select(that),
2040         dragRestore = d3_event_dragSuppress(that);
2041
2042     started();
2043     zoomstarted(dispatch);
2044
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
2048
2049     // Updates locations of any touches in locations0.
2050     function relocate() {
2051       var touches = d3.touches(that);
2052       scale0 = view.k;
2053       touches.forEach(function(t) {
2054         if (t.identifier in locations0) locations0[t.identifier] = location(t);
2055       });
2056       return touches;
2057     }
2058
2059     // Temporarily override touchstart while gesture is active.
2060     function started() {
2061
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);
2066
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;
2071       }
2072
2073       var touches = relocate(),
2074           now = Date.now();
2075
2076       if (touches.length === 1) {
2077         if (now - touchtime < 500) { // dbltap
2078           var p = touches[0];
2079           zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1);
2080           d3_eventPreventDefault();
2081         }
2082         touchtime = now;
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;
2087       }
2088     }
2089
2090     function moved() {
2091       var touches = d3.touches(that),
2092           p0, l0,
2093           p1, l1;
2094
2095       d3_selection_interrupt.call(that);
2096
2097       for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
2098         p1 = touches[i];
2099         if (l1 = locations0[p1.identifier]) {
2100           if (l0) break;
2101           p0 = p1, l0 = l1;
2102         }
2103       }
2104
2105       if (l1) {
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);
2111       }
2112
2113       touchtime = null;
2114       translateTo(p0, l0);
2115       zoomed(dispatch);
2116     }
2117
2118     function ended() {
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];
2125         }
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
2130         }
2131       }
2132       // Otherwise, remove touchmove and touchend listeners.
2133       d3.selectAll(targets).on(zoomName, null);
2134       subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
2135       dragRestore();
2136       zoomended(dispatch);
2137     }
2138   }
2139
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);
2148     zoomed(dispatch);
2149   }
2150
2151   function dblclicked() {
2152     var p = d3.mouse(this),
2153         k = Math.log(view.k) / Math.LN2;
2154
2155     zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1);
2156   }
2157
2158   return d3.rebind(zoom, event, "on");
2159 };
2160
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; };
2166 }
2167
2168 d3.functor = d3_functor;
2169
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);
2175     }
2176   }
2177 };
2178
2179 var d3_timer_queueHead,
2180     d3_timer_queueTail,
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); };
2185
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();
2191
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;
2197
2198   // Start animatin'!
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);
2203   }
2204 };
2205
2206 function d3_timer_step() {
2207   var now = d3_timer_mark(),
2208       delay = d3_timer_sweep() - now;
2209   if (delay > 24) {
2210     if (isFinite(delay)) {
2211       clearTimeout(d3_timer_timeout);
2212       d3_timer_timeout = setTimeout(d3_timer_step, delay);
2213     }
2214     d3_timer_interval = 0;
2215   } else {
2216     d3_timer_interval = 1;
2217     d3_timer_frame(d3_timer_step);
2218   }
2219 }
2220
2221 d3.timer.flush = function() {
2222   d3_timer_mark();
2223   d3_timer_sweep();
2224 };
2225
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;
2232   }
2233   return now;
2234 }
2235
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() {
2239   var t0,
2240       t1 = d3_timer_queueHead,
2241       time = Infinity;
2242   while (t1) {
2243     if (t1.f) {
2244       t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
2245     } else {
2246       if (t1.t < time) time = t1.t;
2247       t1 = (t0 = t1).n;
2248     }
2249   }
2250   d3_timer_queueTail = t0;
2251   return time;
2252 }
2253 d3.geo = {};
2254
2255 d3.geo.stream = function(object, listener) {
2256   if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
2257     d3_geo_streamObjectType[object.type](object, listener);
2258   } else {
2259     d3_geo_streamGeometry(object, listener);
2260   }
2261 };
2262
2263 function d3_geo_streamGeometry(geometry, listener) {
2264   if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
2265     d3_geo_streamGeometryType[geometry.type](geometry, listener);
2266   }
2267 }
2268
2269 var d3_geo_streamObjectType = {
2270   Feature: function(feature, listener) {
2271     d3_geo_streamGeometry(feature.geometry, listener);
2272   },
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);
2276   }
2277 };
2278
2279 var d3_geo_streamGeometryType = {
2280   Sphere: function(object, listener) {
2281     listener.sphere();
2282   },
2283   Point: function(object, listener) {
2284     object = object.coordinates;
2285     listener.point(object[0], object[1], object[2]);
2286   },
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]);
2290   },
2291   LineString: function(object, listener) {
2292     d3_geo_streamLine(object.coordinates, listener, 0);
2293   },
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);
2297   },
2298   Polygon: function(object, listener) {
2299     d3_geo_streamPolygon(object.coordinates, listener);
2300   },
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);
2304   },
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);
2308   }
2309 };
2310
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]);
2315   listener.lineEnd();
2316 }
2317
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();
2323 }
2324
2325 d3.geo.length = function(object) {
2326   d3_geo_lengthSum = 0;
2327   d3.geo.stream(object, d3_geo_length);
2328   return d3_geo_lengthSum;
2329 };
2330
2331 var d3_geo_lengthSum;
2332
2333 var d3_geo_length = {
2334   sphere: d3_noop,
2335   point: d3_noop,
2336   lineStart: d3_geo_lengthLineStart,
2337   lineEnd: d3_noop,
2338   polygonStart: d3_noop,
2339   polygonEnd: d3_noop
2340 };
2341
2342 function d3_geo_lengthLineStart() {
2343   var λ0, sinφ0, cosφ0;
2344
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;
2348   };
2349
2350   d3_geo_length.lineEnd = function() {
2351     d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
2352   };
2353
2354   function nextPoint(λ, φ) {
2355     var sinφ = Math.sin(φ *= d3_radians),
2356         cosφ = Math.cos(φ),
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φ;
2361   }
2362 }
2363 function d3_identity(d) {
2364   return d;
2365 }
2366 function d3_true() {
2367   return true;
2368 }
2369
2370 function d3_geo_spherical(cartesian) {
2371   return [
2372     Math.atan2(cartesian[1], cartesian[0]),
2373     d3_asin(cartesian[2])
2374   ];
2375 }
2376
2377 function d3_geo_sphericalEqual(a, b) {
2378   return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
2379 }
2380
2381 // General spherical polygon clipping algorithm: takes a polygon, cuts it into
2382 // visible line segments and rejoins the segments by interpolating along the
2383 // clip edge.
2384 function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
2385   var subject = [],
2386       clip = [];
2387
2388   segments.forEach(function(segment) {
2389     if ((n = segment.length - 1) <= 0) return;
2390     var n, p0 = segment[0], p1 = segment[n];
2391
2392     // If the first and last points of a segment are coincident, then treat as
2393     // a closed ring.
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]);
2399       listener.lineEnd();
2400       return;
2401     }
2402
2403     var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true),
2404         b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
2405     a.o = b;
2406     subject.push(a);
2407     clip.push(b);
2408     a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
2409     b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
2410     a.o = b;
2411     subject.push(a);
2412     clip.push(b);
2413   });
2414   clip.sort(compare);
2415   d3_geo_clipPolygonLinkCircular(subject);
2416   d3_geo_clipPolygonLinkCircular(clip);
2417   if (!subject.length) return;
2418
2419   for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
2420     clip[i].e = entry = !entry;
2421   }
2422
2423   var start = subject[0],
2424       points,
2425       point;
2426   while (1) {
2427     // Find first unvisited intersection.
2428     var current = start,
2429         isSubject = true;
2430     while (current.v) if ((current = current.n) === start) return;
2431     points = current.z;
2432     listener.lineStart();
2433     do {
2434       current.v = current.o.v = true;
2435       if (current.e) {
2436         if (isSubject) {
2437           for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
2438         } else {
2439           interpolate(current.x, current.n.x, 1, listener);
2440         }
2441         current = current.n;
2442       } else {
2443         if (isSubject) {
2444           points = current.p.z;
2445           for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
2446         } else {
2447           interpolate(current.x, current.p.x, -1, listener);
2448         }
2449         current = current.p;
2450       }
2451       current = current.o;
2452       points = current.z;
2453       isSubject = !isSubject;
2454     } while (!current.v);
2455     listener.lineEnd();
2456   }
2457 }
2458
2459 function d3_geo_clipPolygonLinkCircular(array) {
2460   if (!(n = array.length)) return;
2461   var n,
2462       i = 0,
2463       a = array[0],
2464       b;
2465   while (++i < n) {
2466     a.n = b = array[i];
2467     b.p = a;
2468     a = b;
2469   }
2470   a.n = b = array[0];
2471   b.p = a;
2472 }
2473
2474 function d3_geo_clipPolygonIntersection(point, points, other, entry) {
2475   this.x = point;
2476   this.z = points;
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
2481 }
2482
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]);
2487
2488     var clip = {
2489       point: point,
2490       lineStart: lineStart,
2491       lineEnd: lineEnd,
2492       polygonStart: function() {
2493         clip.point = pointRing;
2494         clip.lineStart = ringStart;
2495         clip.lineEnd = ringEnd;
2496         segments = [];
2497         polygon = [];
2498       },
2499       polygonEnd: function() {
2500         clip.point = point;
2501         clip.lineStart = lineStart;
2502         clip.lineEnd = lineEnd;
2503
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);
2513           listener.lineEnd();
2514         }
2515         if (polygonStarted) listener.polygonEnd(), polygonStarted = false;
2516         segments = polygon = null;
2517       },
2518       sphere: function() {
2519         listener.polygonStart();
2520         listener.lineStart();
2521         interpolate(null, null, 1, listener);
2522         listener.lineEnd();
2523         listener.polygonEnd();
2524       }
2525     };
2526
2527     function point(λ, φ) {
2528       var point = rotate(λ, φ);
2529       if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
2530     }
2531     function pointLine(λ, φ) {
2532       var point = rotate(λ, φ);
2533       line.point(point[0], point[1]);
2534     }
2535     function lineStart() { clip.point = pointLine; line.lineStart(); }
2536     function lineEnd() { clip.point = point; line.lineEnd(); }
2537
2538     var segments;
2539
2540     var buffer = d3_geo_clipBufferListener(),
2541         ringListener = clipLine(buffer),
2542         polygonStarted = false,
2543         polygon,
2544         ring;
2545
2546     function pointRing(λ, φ) {
2547       ring.push([λ, φ]);
2548       var point = rotate(λ, φ);
2549       ringListener.point(point[0], point[1]);
2550     }
2551
2552     function ringStart() {
2553       ringListener.lineStart();
2554       ring = [];
2555     }
2556
2557     function ringEnd() {
2558       pointRing(ring[0][0], ring[0][1]);
2559       ringListener.lineEnd();
2560
2561       var clean = ringListener.clean(),
2562           ringSegments = buffer.buffer(),
2563           segment,
2564           n = ringSegments.length;
2565
2566       ring.pop();
2567       polygon.push(ring);
2568       ring = null;
2569
2570       if (!n) return;
2571
2572       // No intersections.
2573       if (clean & 1) {
2574         segment = ringSegments[0];
2575         var n = segment.length - 1,
2576             i = -1,
2577             point;
2578         if (n > 0) {
2579           if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2580           listener.lineStart();
2581           while (++i < n) listener.point((point = segment[i])[0], point[1]);
2582           listener.lineEnd();
2583         }
2584         return;
2585       }
2586
2587       // Rejoin connected segments.
2588       // TODO reuse bufferListener.rejoin()?
2589       if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
2590
2591       segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
2592     }
2593
2594     return clip;
2595   };
2596 }
2597
2598 function d3_geo_clipSegmentLength1(segment) {
2599   return segment.length > 1;
2600 }
2601
2602 function d3_geo_clipBufferListener() {
2603   var lines = [],
2604       line;
2605   return {
2606     lineStart: function() { lines.push(line = []); },
2607     point: function(λ, φ) { line.push([λ, φ]); },
2608     lineEnd: d3_noop,
2609     buffer: function() {
2610       var buffer = lines;
2611       lines = [];
2612       line = null;
2613       return buffer;
2614     },
2615     rejoin: function() {
2616       if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
2617     }
2618   };
2619 }
2620
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]);
2626 }
2627
2628 var d3_geo_clipAntimeridian = d3_geo_clip(
2629     d3_true,
2630     d3_geo_clipAntimeridianLine,
2631     d3_geo_clipAntimeridianInterpolate,
2632     [-π, -π / 2]);
2633
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
2638 //      rejoined.
2639 function d3_geo_clipAntimeridianLine(listener) {
2640   var λ0 = NaN,
2641       φ0 = NaN,
2642       sλ0 = NaN,
2643       clean; // no intersections
2644
2645   return {
2646     lineStart: function() {
2647       listener.lineStart();
2648       clean = 1;
2649     },
2650     point: function(λ1, φ1) {
2651       var sλ1 = λ1 > 0 ? π : -π,
2652           dλ = abs(λ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);
2656         listener.lineEnd();
2657         listener.lineStart();
2658         listener.point(sλ1, φ0);
2659         listener.point(λ1, φ0);
2660         clean = 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);
2667         listener.lineEnd();
2668         listener.lineStart();
2669         listener.point(sλ1, φ0);
2670         clean = 0;
2671       }
2672       listener.point(λ0 = λ1, φ0 = φ1);
2673       sλ0 = sλ1;
2674     },
2675     lineEnd: function() {
2676       listener.lineEnd();
2677       λ0 = φ0 = NaN;
2678     },
2679     // if there are intersections, we always rejoin the first and last segments.
2680     clean: function() { return 2 - clean; }
2681   };
2682 }
2683
2684 function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
2685   var cosφ0,
2686       cosφ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))
2692       : (φ0 + φ1) / 2;
2693 }
2694
2695 function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
2696   var φ;
2697   if (from == null) {
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, φ);
2714   } else {
2715     listener.point(to[0], to[1]);
2716   }
2717 }
2718 // TODO
2719 // cross and scale return new vectors,
2720 // whereas add and normalize operate in-place
2721
2722 function d3_geo_cartesian(spherical) {
2723   var λ = spherical[0],
2724       φ = spherical[1],
2725       cosφ = Math.cos(φ);
2726   return [
2727     cosφ * Math.cos(λ),
2728     cosφ * Math.sin(λ),
2729     Math.sin(φ)
2730   ];
2731 }
2732
2733 function d3_geo_cartesianDot(a, b) {
2734   return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
2735 }
2736
2737 function d3_geo_cartesianCross(a, b) {
2738   return [
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]
2742   ];
2743 }
2744
2745 function d3_geo_cartesianAdd(a, b) {
2746   a[0] += b[0];
2747   a[1] += b[1];
2748   a[2] += b[2];
2749 }
2750
2751 function d3_geo_cartesianScale(vector, k) {
2752   return [
2753     vector[0] * k,
2754     vector[1] * k,
2755     vector[2] * k
2756   ];
2757 }
2758
2759 function d3_geo_cartesianNormalize(d) {
2760   var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
2761   d[0] /= l;
2762   d[1] /= l;
2763   d[2] /= l;
2764 }
2765 function d3_geo_compose(a, b) {
2766
2767   function compose(x, y) {
2768     return x = a(x, y), b(x[0], x[1]);
2769   }
2770
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]);
2773   };
2774
2775   return compose;
2776 }
2777
2778 function d3_geo_equirectangular(λ, φ) {
2779   return [λ, φ];
2780 }
2781
2782 (d3.geo.equirectangular = function() {
2783   return d3_geo_projection(d3_geo_equirectangular);
2784 }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
2785
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);
2788
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;
2792   }
2793
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;
2797   };
2798
2799   return forward;
2800 };
2801
2802 function d3_geo_identityRotation(λ, φ) {
2803   return [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ];
2804 }
2805
2806 d3_geo_identityRotation.invert = d3_geo_equirectangular;
2807
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);
2814 }
2815
2816 function d3_geo_forwardRotationλ(δλ) {
2817   return function(λ, φ) {
2818     return λ += δλ, [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ];
2819   };
2820 }
2821
2822 function d3_geo_rotationλ(δλ) {
2823   var rotation = d3_geo_forwardRotationλ(δλ);
2824   rotation.invert = d3_geo_forwardRotationλ(-δλ);
2825   return rotation;
2826 }
2827
2828 function d3_geo_rotationφγ(δφ, δγ) {
2829   var cosδφ = Math.cos(δφ),
2830       sinδφ = Math.sin(δφ),
2831       cosδγ = Math.cos(δγ),
2832       sinδγ = Math.sin(δγ);
2833
2834   function rotation(λ, φ) {
2835     var cosφ = Math.cos(φ),
2836         x = Math.cos(λ) * cosφ,
2837         y = Math.sin(λ) * cosφ,
2838         z = Math.sin(φ),
2839         k = z * cosδφ + x * sinδφ;
2840     return [
2841       Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ),
2842       d3_asin(k * cosδγ + y * sinδγ)
2843     ];
2844   }
2845
2846   rotation.invert = function(λ, φ) {
2847     var cosφ = Math.cos(φ),
2848         x = Math.cos(λ) * cosφ,
2849         y = Math.sin(λ) * cosφ,
2850         z = Math.sin(φ),
2851         k = z * cosδγ - y * sinδγ;
2852     return [
2853       Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ),
2854       d3_asin(k * cosδφ - x * sinδφ)
2855     ];
2856   };
2857
2858   return rotation;
2859 }
2860
2861 d3.geo.circle = function() {
2862   var origin = [0, 0],
2863       angle,
2864       precision = 6,
2865       interpolate;
2866
2867   function circle() {
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,
2870         ring = [];
2871
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;
2876       }
2877     });
2878
2879     return {type: "Polygon", coordinates: [ring]};
2880   }
2881
2882   circle.origin = function(x) {
2883     if (!arguments.length) return origin;
2884     origin = x;
2885     return circle;
2886   };
2887
2888   circle.angle = function(x) {
2889     if (!arguments.length) return angle;
2890     interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
2891     return circle;
2892   };
2893
2894   circle.precision = function(_) {
2895     if (!arguments.length) return precision;
2896     interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
2897     return circle;
2898   };
2899
2900   return circle.angle(90);
2901 };
2902
2903 // Interpolates along a circle centered at [0°, 0°], with a given radius and
2904 // precision.
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;
2910     if (from != null) {
2911       from = d3_geo_circleAngle(cr, from);
2912       to = d3_geo_circleAngle(cr, to);
2913       if (direction > 0 ? from < to: from > to) from += direction * τ;
2914     } else {
2915       from = radius + direction * τ;
2916       to = radius - .5 * step;
2917     }
2918     for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
2919       listener.point((point = d3_geo_spherical([
2920         cr,
2921         -sr * Math.cos(t),
2922         -sr * Math.sin(t)
2923       ]))[0], point[1]);
2924     }
2925   };
2926 }
2927
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);
2931   a[0] -= cr;
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);
2935 }
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)
2939 // 305–363 (1997).
2940 // Code adapted from GeographicLib by Charles F. F. Karney,
2941 // http://geographiclib.sourceforge.net/
2942 // See lib/geographiclib/LICENSE for details.
2943
2944 function d3_adder() {}
2945
2946 d3_adder.prototype = {
2947   s: 0, // rounded value
2948   t: 0, // exact error
2949   add: function(y) {
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;
2954   },
2955   reset: function() {
2956     this.s = this.t = 0;
2957   },
2958   valueOf: function() {
2959     return this.s;
2960   }
2961 };
2962
2963 var d3_adderTemp = new d3_adder;
2964
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
2969 }
2970
2971 d3.geo.area = function(object) {
2972   d3_geo_areaSum = 0;
2973   d3.geo.stream(object, d3_geo_area);
2974   return d3_geo_areaSum;
2975 };
2976
2977 var d3_geo_areaSum,
2978     d3_geo_areaRingSum = new d3_adder;
2979
2980 var d3_geo_area = {
2981   sphere: function() { d3_geo_areaSum += 4 * π; },
2982   point: d3_noop,
2983   lineStart: d3_noop,
2984   lineEnd: d3_noop,
2985
2986   // Only count area for polygon rings.
2987   polygonStart: function() {
2988     d3_geo_areaRingSum.reset();
2989     d3_geo_area.lineStart = d3_geo_areaRingStart;
2990   },
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;
2995   }
2996 };
2997
2998 function d3_geo_areaRingStart() {
2999   var λ00, φ00, λ0, cosφ0, sinφ0; // start point and previous point
3000
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(φ);
3005   };
3006
3007   // For subsequent points, …
3008   function nextPoint(λ, φ) {
3009     λ *= d3_radians;
3010     φ = φ * d3_radians / 2 + π / 4; // half the angular distance from south pole
3011
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).
3015     var dλ = λ - λ0,
3016         sdλ = dλ >= 0 ? 1 : -1,
3017         adλ = sdλ * dλ,
3018         cosφ = Math.cos(φ),
3019         sinφ = Math.sin(φ),
3020         k = sinφ0 * sinφ,
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));
3024
3025     // Advance the previous points.
3026     λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
3027   }
3028
3029   // For the last point, return to the start.
3030   d3_geo_area.lineEnd = function() {
3031     nextPoint(λ00, φ00);
3032   };
3033 }
3034
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],
3039       polarAngle = 0,
3040       winding = 0;
3041   d3_geo_areaRingSum.reset();
3042
3043   for (var i = 0, n = polygon.length; i < n; ++i) {
3044     var ring = polygon[i],
3045         m = ring.length;
3046     if (!m) continue;
3047     var point0 = ring[0],
3048         λ0 = point0[0],
3049         φ0 = point0[1] / 2 + π / 4,
3050         sinφ0 = Math.sin(φ0),
3051         cosφ0 = Math.cos(φ0),
3052         j = 1;
3053
3054     while (true) {
3055       if (j === m) j = 0;
3056       point = ring[j];
3057       var λ = point[0],
3058           φ = point[1] / 2 + π / 4,
3059           sinφ = Math.sin(φ),
3060           cosφ = Math.cos(φ),
3061           dλ = λ - λ0,
3062           sdλ = dλ >= 0 ? 1 : -1,
3063           adλ = sdλ * dλ,
3064           antimeridian = adλ > π,
3065           k = sinφ0 * sinφ;
3066       d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
3067
3068       polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
3069
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;
3080         }
3081       }
3082       if (!j++) break;
3083       λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
3084     }
3085   }
3086
3087   // First, determine whether the South pole is inside or outside:
3088   //
3089   // It is inside if:
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.
3093   //
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.
3097
3098   return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ (winding & 1);
3099 }
3100
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);
3107
3108   return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-π, radius - π]);
3109
3110   function visible(λ, φ) {
3111     return Math.cos(λ) * Math.cos(φ) > cr;
3112   }
3113
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
3119   //      rejoined.
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
3126     return {
3127       lineStart: function() {
3128         v00 = v0 = false;
3129         clean = 1;
3130       },
3131       point: function(λ, φ) {
3132         var point1 = [λ, φ],
3133             point2,
3134             v = visible(λ, φ),
3135             c = smallRadius
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.
3141         if (v !== v0) {
3142           point2 = intersect(point0, point1);
3143           if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
3144             point1[0] += ε;
3145             point1[1] += ε;
3146             v = visible(point1[0], point1[1]);
3147           }
3148         }
3149         if (v !== v0) {
3150           clean = 0;
3151           if (v) {
3152             // outside going in
3153             listener.lineStart();
3154             point2 = intersect(point1, point0);
3155             listener.point(point2[0], point2[1]);
3156           } else {
3157             // inside going out
3158             point2 = intersect(point0, point1);
3159             listener.point(point2[0], point2[1]);
3160             listener.lineEnd();
3161           }
3162           point0 = point2;
3163         } else if (notHemisphere && point0 && smallRadius ^ v) {
3164           var t;
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))) {
3168             clean = 0;
3169             if (smallRadius) {
3170               listener.lineStart();
3171               listener.point(t[0][0], t[0][1]);
3172               listener.point(t[1][0], t[1][1]);
3173               listener.lineEnd();
3174             } else {
3175               listener.point(t[1][0], t[1][1]);
3176               listener.lineEnd();
3177               listener.lineStart();
3178               listener.point(t[0][0], t[0][1]);
3179             }
3180           }
3181         }
3182         if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
3183           listener.point(point1[0], point1[1]);
3184         }
3185         point0 = point1, v0 = v, c0 = c;
3186       },
3187       lineEnd: function() {
3188         if (v0) listener.lineEnd();
3189         point0 = null;
3190       },
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); }
3194     };
3195   }
3196
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);
3201
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;
3209
3210     // Two polar points.
3211     if (!determinant) return !two && a;
3212
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);
3219
3220     // Solve |p(t)|^2 = 1.
3221     var u = n1xn2,
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);
3225
3226     if (t2 < 0) return;
3227
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);
3232     if (!two) return q;
3233
3234     // Two intersection points.
3235     var λ0 = a[0],
3236         λ1 = b[0],
3237         φ0 = a[1],
3238         φ1 = b[1],
3239         z;
3240     if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
3241     var δλ = λ1 - λ0,
3242         polar = abs(δλ - π) < ε,
3243         meridian = polar || δλ < ε;
3244
3245     if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
3246
3247     // Check that the first point is between a and b.
3248     if (meridian
3249         ? polar
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)];
3256     }
3257   }
3258
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,
3263         code = 0;
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
3268     return code;
3269   }
3270 }
3271
3272 // Liang–Barsky line clipping.
3273 function d3_geom_clipLine(x0, y0, x1, y1) {
3274   return function(line) {
3275     var a = line.a,
3276         b = line.b,
3277         ax = a.x,
3278         ay = a.y,
3279         bx = b.x,
3280         by = b.y,
3281         t0 = 0,
3282         t1 = 1,
3283         dx = bx - ax,
3284         dy = by - ay,
3285         r;
3286
3287     r = x0 - ax;
3288     if (!dx && r > 0) return;
3289     r /= dx;
3290     if (dx < 0) {
3291       if (r < t0) return;
3292       if (r < t1) t1 = r;
3293     } else if (dx > 0) {
3294       if (r > t1) return;
3295       if (r > t0) t0 = r;
3296     }
3297
3298     r = x1 - ax;
3299     if (!dx && r < 0) return;
3300     r /= dx;
3301     if (dx < 0) {
3302       if (r > t1) return;
3303       if (r > t0) t0 = r;
3304     } else if (dx > 0) {
3305       if (r < t0) return;
3306       if (r < t1) t1 = r;
3307     }
3308
3309     r = y0 - ay;
3310     if (!dy && r > 0) return;
3311     r /= dy;
3312     if (dy < 0) {
3313       if (r < t0) return;
3314       if (r < t1) t1 = r;
3315     } else if (dy > 0) {
3316       if (r > t1) return;
3317       if (r > t0) t0 = r;
3318     }
3319
3320     r = y1 - ay;
3321     if (!dy && r < 0) return;
3322     r /= dy;
3323     if (dy < 0) {
3324       if (r > t1) return;
3325       if (r > t0) t0 = r;
3326     } else if (dy > 0) {
3327       if (r < t0) return;
3328       if (r < t1) t1 = r;
3329     }
3330
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};
3333     return line;
3334   };
3335 }
3336
3337 var d3_geo_clipExtentMAX = 1e9;
3338
3339 d3.geo.clipExtent = function() {
3340   var x0, y0, x1, y1,
3341       stream,
3342       clip,
3343       clipExtent = {
3344         stream: function(output) {
3345           if (stream) stream.valid = false;
3346           stream = clip(output);
3347           stream.valid = true; // allow caching by d3.geo.path
3348           return stream;
3349         },
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;
3354           return clipExtent;
3355         }
3356       };
3357   return clipExtent.extent([[0, 0], [960, 500]]);
3358 };
3359
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),
3365         segments,
3366         polygon,
3367         ring;
3368
3369     var clip = {
3370       point: point,
3371       lineStart: lineStart,
3372       lineEnd: lineEnd,
3373       polygonStart: function() {
3374         listener = bufferListener;
3375         segments = [];
3376         polygon = [];
3377         clean = true;
3378       },
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();
3387           if (inside) {
3388             listener.lineStart();
3389             interpolate(null, null, 1, listener);
3390             listener.lineEnd();
3391           }
3392           if (visible) {
3393             d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
3394           }
3395           listener.polygonEnd();
3396         }
3397         segments = polygon = ring = null;
3398       }
3399     };
3400
3401     function insidePolygon(p) {
3402       var wn = 0, // the winding number counter
3403           n = polygon.length,
3404           y = p[1];
3405
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) {
3408           b = v[j];
3409           if (a[1] <= y) {
3410             if (b[1] >  y && d3_cross2d(a, b, p) > 0) ++wn;
3411           } else {
3412             if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn;
3413           }
3414           a = b;
3415         }
3416       }
3417       return wn !== 0;
3418     }
3419
3420     function interpolate(from, to, direction, listener) {
3421       var a = 0, a1 = 0;
3422       if (from == null ||
3423           (a = corner(from, direction)) !== (a1 = corner(to, direction)) ||
3424           comparePoints(from, to) < 0 ^ direction > 0) {
3425         do {
3426           listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
3427         } while ((a = (a + direction + 4) % 4) !== a1);
3428       } else {
3429         listener.point(to[0], to[1]);
3430       }
3431     }
3432
3433     function pointVisible(x, y) {
3434       return x0 <= x && x <= x1 && y0 <= y && y <= y1;
3435     }
3436
3437     function point(x, y) {
3438       if (pointVisible(x, y)) listener.point(x, y);
3439     }
3440
3441     var x__, y__, v__, // first point
3442         x_, y_, v_, // previous point
3443         first,
3444         clean;
3445
3446     function lineStart() {
3447       clip.point = linePoint;
3448       if (polygon) polygon.push(ring = []);
3449       first = true;
3450       v_ = false;
3451       x_ = y_ = NaN;
3452     }
3453
3454     function lineEnd() {
3455       // TODO rather than special-case polygons, simply handle them separately.
3456       // Ideally, coincident intersection points should be jittered to avoid
3457       // clipping issues.
3458       if (segments) {
3459         linePoint(x__, y__);
3460         if (v__ && v_) bufferListener.rejoin();
3461         segments.push(bufferListener.buffer());
3462       }
3463       clip.point = point;
3464       if (v_) listener.lineEnd();
3465     }
3466
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]);
3472       if (first) {
3473         x__ = x, y__ = y, v__ = v;
3474         first = false;
3475         if (v) {
3476           listener.lineStart();
3477           listener.point(x, y);
3478         }
3479       } else {
3480         if (v && v_) listener.point(x, y);
3481         else {
3482           var l = {a: {x: x_, y: y_}, b: {x: x, y: y}};
3483           if (clipLine(l)) {
3484             if (!v_) {
3485               listener.lineStart();
3486               listener.point(l.a.x, l.a.y);
3487             }
3488             listener.point(l.b.x, l.b.y);
3489             if (!v) listener.lineEnd();
3490             clean = false;
3491           } else if (v) {
3492             listener.lineStart();
3493             listener.point(x, y);
3494             clean = false;
3495           }
3496         }
3497       }
3498       x_ = x, y_ = y, v_ = v;
3499     }
3500
3501     return clip;
3502   };
3503
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) < ε
3509   }
3510
3511   function compare(a, b) {
3512     return comparePoints(a.x, b.x);
3513   }
3514
3515   function comparePoints(a, b) {
3516     var ca = corner(a, 1),
3517         cb = corner(b, 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]
3522         : b[0] - a[0];
3523   }
3524 }
3525
3526 function d3_geo_conic(projectAt) {
3527   var φ0 = 0,
3528       φ1 = π / 3,
3529       m = d3_geo_projectionMutator(projectAt),
3530       p = m(φ0, φ1);
3531
3532   p.parallels = function(_) {
3533     if (!arguments.length) return [φ0 / π * 180, φ1 / π * 180];
3534     return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180);
3535   };
3536
3537   return p;
3538 }
3539
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;
3545
3546   function forward(λ, φ) {
3547     var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n;
3548     return [
3549       ρ * Math.sin(λ *= n),
3550       ρ0 - ρ * Math.cos(λ)
3551     ];
3552   }
3553
3554   forward.invert = function(x, y) {
3555     var ρ0_y = ρ0 - y;
3556     return [
3557       Math.atan2(x, ρ0_y) / n,
3558       d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n))
3559     ];
3560   };
3561
3562   return forward;
3563 }
3564
3565 (d3.geo.conicEqualArea = function() {
3566   return d3_geo_conic(d3_geo_conicEqualArea);
3567 }).raw = d3_geo_conicEqualArea;
3568
3569 // ESRI:102003
3570 d3.geo.albers = function() {
3571   return d3.geo.conicEqualArea()
3572       .rotate([96, 0])
3573       .center([-.6, 38.7])
3574       .parallels([29.5, 45.5])
3575       .scale(1070);
3576 };
3577
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();
3584
3585   // EPSG:3338
3586   var alaska = d3.geo.conicEqualArea()
3587       .rotate([154, 0])
3588       .center([-2, 58.5])
3589       .parallels([55, 65]);
3590
3591   // ESRI:102007
3592   var hawaii = d3.geo.conicEqualArea()
3593       .rotate([157, 0])
3594       .center([-3, 19.9])
3595       .parallels([8, 18]);
3596
3597   var point,
3598       pointStream = {point: function(x, y) { point = [x, y]; }},
3599       lower48Point,
3600       alaskaPoint,
3601       hawaiiPoint;
3602
3603   function albersUsa(coordinates) {
3604     var x = coordinates[0], y = coordinates[1];
3605     point = null;
3606     (lower48Point(x, y), point)
3607         || (alaskaPoint(x, y), point)
3608         || hawaiiPoint(x, y);
3609     return point;
3610   }
3611
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);
3620   };
3621
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);
3629     return {
3630       point: function(x, y) {
3631         lower48Stream.point(x, y);
3632         alaskaStream.point(x, y);
3633         hawaiiStream.point(x, y);
3634       },
3635       sphere: function() {
3636         lower48Stream.sphere();
3637         alaskaStream.sphere();
3638         hawaiiStream.sphere();
3639       },
3640       lineStart: function() {
3641         lower48Stream.lineStart();
3642         alaskaStream.lineStart();
3643         hawaiiStream.lineStart();
3644       },
3645       lineEnd: function() {
3646         lower48Stream.lineEnd();
3647         alaskaStream.lineEnd();
3648         hawaiiStream.lineEnd();
3649       },
3650       polygonStart: function() {
3651         lower48Stream.polygonStart();
3652         alaskaStream.polygonStart();
3653         hawaiiStream.polygonStart();
3654       },
3655       polygonEnd: function() {
3656         lower48Stream.polygonEnd();
3657         alaskaStream.polygonEnd();
3658         hawaiiStream.polygonEnd();
3659       }
3660     };
3661   };
3662
3663   albersUsa.precision = function(_) {
3664     if (!arguments.length) return lower48.precision();
3665     lower48.precision(_);
3666     alaska.precision(_);
3667     hawaii.precision(_);
3668     return albersUsa;
3669   };
3670
3671   albersUsa.scale = function(_) {
3672     if (!arguments.length) return lower48.scale();
3673     lower48.scale(_);
3674     alaska.scale(_ * .35);
3675     hawaii.scale(_);
3676     return albersUsa.translate(lower48.translate());
3677   };
3678
3679   albersUsa.translate = function(_) {
3680     if (!arguments.length) return lower48.translate();
3681     var k = lower48.scale(), x = +_[0], y = +_[1];
3682
3683     lower48Point = lower48
3684         .translate(_)
3685         .clipExtent([[x - .455 * k, y - .238 * k], [x + .455 * k, y + .238 * k]])
3686         .stream(pointStream).point;
3687
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;
3692
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;
3697
3698     return albersUsa;
3699   };
3700
3701   return albersUsa.scale(1070);
3702 };
3703
3704 d3.geo.bounds = (function() {
3705   var λ0, φ0, λ1, φ1, // bounds
3706       λ_, // previous λ-coordinate
3707       λ__, φ__, // first point
3708       p0, // previous 3D point
3709       dλSum,
3710       ranges,
3711       range;
3712
3713   var bound = {
3714     point: point,
3715     lineStart: lineStart,
3716     lineEnd: lineEnd,
3717
3718     polygonStart: function() {
3719       bound.point = ringPoint;
3720       bound.lineStart = ringStart;
3721       bound.lineEnd = ringEnd;
3722       dλSum = 0;
3723       d3_geo_area.polygonStart();
3724     },
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;
3734     }
3735   };
3736
3737   function point(λ, φ) {
3738     ranges.push(range = [λ0 = λ, λ1 = λ]);
3739     if (φ < φ0) φ0 = φ;
3740     if (φ > φ1) φ1 = φ;
3741   }
3742
3743   function linePoint(λ, φ) {
3744     var p = d3_geo_cartesian([λ * d3_radians, φ * d3_radians]);
3745     if (p0) {
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);
3751       var dλ = λ - λ_,
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;
3761       } else {
3762         if (φ < φ0) φ0 = φ;
3763         if (φ > φ1) φ1 = φ;
3764       }
3765       if (antimeridian) {
3766         if (λ < λ_) {
3767           if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3768         } else {
3769           if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3770         }
3771       } else {
3772         if (λ1 >= λ0) {
3773           if (λ < λ0) λ0 = λ;
3774           if (λ > λ1) λ1 = λ;
3775         } else {
3776           if (λ > λ_) {
3777             if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3778           } else {
3779             if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3780           }
3781         }
3782       }
3783     } else {
3784       point(λ, φ);
3785     }
3786     p0 = p, λ_ = λ;
3787   }
3788
3789   function lineStart() { bound.point = linePoint; }
3790   function lineEnd() {
3791     range[0] = λ0, range[1] = λ1;
3792     bound.point = point;
3793     p0 = null;
3794   }
3795
3796   function ringPoint(λ, φ) {
3797     if (p0) {
3798       var dλ = λ - λ_;
3799       dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
3800     } else λ__ = λ, φ__ = φ;
3801     d3_geo_area.point(λ, φ);
3802     linePoint(λ, φ);
3803   }
3804
3805   function ringStart() {
3806     d3_geo_area.lineStart();
3807   }
3808
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;
3814     p0 = null;
3815   }
3816
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; }
3821
3822   function compareRanges(a, b) { return a[0] - b[0]; }
3823
3824   function withinRange(x, range) {
3825     return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
3826   }
3827
3828   return function(feature) {
3829     φ1 = λ1 = -(λ0 = φ0 = Infinity);
3830     ranges = [];
3831
3832     d3.geo.stream(feature, bound);
3833
3834     var n = ranges.length;
3835     if (n) {
3836       // First, sort ranges by their minimum longitudes.
3837       ranges.sort(compareRanges);
3838
3839       // Then, merge any ranges that overlap.
3840       for (var i = 1, a = ranges[0], b, merged = [a]; i < n; ++i) {
3841         b = ranges[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];
3845         } else {
3846           merged.push(a = b);
3847         }
3848       }
3849
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) {
3854         b = merged[i];
3855         if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
3856       }
3857     }
3858     ranges = range = null;
3859
3860     return λ0 === Infinity || φ0 === Infinity
3861         ? [[NaN, NaN], [NaN, NaN]]
3862         : [[λ0, φ0], [λ1, φ1]];
3863   };
3864 })();
3865
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);
3872
3873   var x = d3_geo_centroidX2,
3874       y = d3_geo_centroidY2,
3875       z = d3_geo_centroidZ2,
3876       m = x * x + y * y + z * z;
3877
3878   // If the area-weighted centroid is undefined, fall back to length-weighted centroid.
3879   if (m < ε2) {
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];
3886   }
3887
3888   return [Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees];
3889 };
3890
3891 var d3_geo_centroidW0,
3892     d3_geo_centroidW1,
3893     d3_geo_centroidX0,
3894     d3_geo_centroidY0,
3895     d3_geo_centroidZ0,
3896     d3_geo_centroidX1,
3897     d3_geo_centroidY1,
3898     d3_geo_centroidZ1,
3899     d3_geo_centroidX2,
3900     d3_geo_centroidY2,
3901     d3_geo_centroidZ2;
3902
3903 var d3_geo_centroid = {
3904   sphere: d3_noop,
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;
3910   },
3911   polygonEnd: function() {
3912     d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
3913   }
3914 };
3915
3916 // Arithmetic mean of Cartesian vectors.
3917 function d3_geo_centroidPoint(λ, φ) {
3918   λ *= d3_radians;
3919   var cosφ = Math.cos(φ *= d3_radians);
3920   d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
3921 }
3922
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;
3928 }
3929
3930 function d3_geo_centroidLineStart() {
3931   var x0, y0, z0; // previous point
3932
3933   d3_geo_centroid.point = function(λ, φ) {
3934     λ *= d3_radians;
3935     var cosφ = Math.cos(φ *= d3_radians);
3936     x0 = cosφ * Math.cos(λ);
3937     y0 = cosφ * Math.sin(λ);
3938     z0 = Math.sin(φ);
3939     d3_geo_centroid.point = nextPoint;
3940     d3_geo_centroidPointXYZ(x0, y0, z0);
3941   };
3942
3943   function nextPoint(λ, φ) {
3944     λ *= d3_radians;
3945     var cosφ = Math.cos(φ *= d3_radians),
3946         x = cosφ * Math.cos(λ),
3947         y = cosφ * Math.sin(λ),
3948         z = Math.sin(φ),
3949         w = Math.atan2(
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);
3957   }
3958 }
3959
3960 function d3_geo_centroidLineEnd() {
3961   d3_geo_centroid.point = d3_geo_centroidPoint;
3962 }
3963
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
3969
3970   d3_geo_centroid.point = function(λ, φ) {
3971     λ00 = λ, φ00 = φ;
3972     d3_geo_centroid.point = nextPoint;
3973     λ *= d3_radians;
3974     var cosφ = Math.cos(φ *= d3_radians);
3975     x0 = cosφ * Math.cos(λ);
3976     y0 = cosφ * Math.sin(λ);
3977     z0 = Math.sin(φ);
3978     d3_geo_centroidPointXYZ(x0, y0, z0);
3979   };
3980
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;
3985   };
3986
3987   function nextPoint(λ, φ) {
3988     λ *= d3_radians;
3989     var cosφ = Math.cos(φ *= d3_radians),
3990         x = cosφ * Math.cos(λ),
3991         y = cosφ * Math.sin(λ),
3992         z = 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);
4008   }
4009 }
4010
4011 // TODO Unify this code with d3.geom.polygon area?
4012
4013 var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
4014   point: d3_noop,
4015   lineStart: d3_noop,
4016   lineEnd: d3_noop,
4017
4018   // Only count area for polygon rings.
4019   polygonStart: function() {
4020     d3_geo_pathAreaPolygon = 0;
4021     d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
4022   },
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);
4026   }
4027 };
4028
4029 function d3_geo_pathAreaRingStart() {
4030   var x00, y00, x0, y0;
4031
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;
4036   };
4037
4038   // For subsequent points, …
4039   function nextPoint(x, y) {
4040     d3_geo_pathAreaPolygon += y0 * x - x0 * y;
4041     x0 = x, y0 = y;
4042   }
4043
4044   // For the last point, return to the start.
4045   d3_geo_pathArea.lineEnd = function() {
4046     nextPoint(x00, y00);
4047   };
4048 }
4049
4050 var d3_geo_pathBoundsX0,
4051     d3_geo_pathBoundsY0,
4052     d3_geo_pathBoundsX1,
4053     d3_geo_pathBoundsY1;
4054
4055 var d3_geo_pathBounds = {
4056   point: d3_geo_pathBoundsPoint,
4057   lineStart: d3_noop,
4058   lineEnd: d3_noop,
4059   polygonStart: d3_noop,
4060   polygonEnd: d3_noop
4061 };
4062
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;
4068 }
4069 function d3_geo_pathBuffer() {
4070   var pointCircle = d3_geo_pathBufferCircle(4.5),
4071       buffer = [];
4072
4073   var stream = {
4074     point: point,
4075
4076     // While inside a line, override point to moveTo then lineTo.
4077     lineStart: function() { stream.point = pointLineStart; },
4078     lineEnd: lineEnd,
4079
4080     // While inside a polygon, override lineEnd to closePath.
4081     polygonStart: function() { stream.lineEnd = lineEndPolygon; },
4082     polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; },
4083
4084     pointRadius: function(_) {
4085       pointCircle = d3_geo_pathBufferCircle(_);
4086       return stream;
4087     },
4088
4089     result: function() {
4090       if (buffer.length) {
4091         var result = buffer.join("");
4092         buffer = [];
4093         return result;
4094       }
4095     }
4096   };
4097
4098   function point(x, y) {
4099     buffer.push("M", x, ",", y, pointCircle);
4100   }
4101
4102   function pointLineStart(x, y) {
4103     buffer.push("M", x, ",", y);
4104     stream.point = pointLine;
4105   }
4106
4107   function pointLine(x, y) {
4108     buffer.push("L", x, ",", y);
4109   }
4110
4111   function lineEnd() {
4112     stream.point = point;
4113   }
4114
4115   function lineEndPolygon() {
4116     buffer.push("Z");
4117   }
4118
4119   return stream;
4120 }
4121
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
4126       + "z";
4127 }
4128
4129 // TODO Unify this code with d3.geom.polygon centroid?
4130 // TODO Enforce positive area for exterior, negative area for interior?
4131
4132 var d3_geo_pathCentroid = {
4133   point: d3_geo_pathCentroidPoint,
4134
4135   // For lines, weight by length.
4136   lineStart: d3_geo_pathCentroidLineStart,
4137   lineEnd: d3_geo_pathCentroidLineEnd,
4138
4139   // For polygons, weight by area.
4140   polygonStart: function() {
4141     d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
4142   },
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;
4147   }
4148 };
4149
4150 function d3_geo_pathCentroidPoint(x, y) {
4151   d3_geo_centroidX0 += x;
4152   d3_geo_centroidY0 += y;
4153   ++d3_geo_centroidZ0;
4154 }
4155
4156 function d3_geo_pathCentroidLineStart() {
4157   var x0, y0;
4158
4159   d3_geo_pathCentroid.point = function(x, y) {
4160     d3_geo_pathCentroid.point = nextPoint;
4161     d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4162   };
4163
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);
4170   }
4171 }
4172
4173 function d3_geo_pathCentroidLineEnd() {
4174   d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
4175 }
4176
4177 function d3_geo_pathCentroidRingStart() {
4178   var x00, y00, x0, y0;
4179
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);
4184   };
4185
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;
4192
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);
4198   }
4199
4200   // For the last point, return to the start.
4201   d3_geo_pathCentroid.lineEnd = function() {
4202     nextPoint(x00, y00);
4203   };
4204 }
4205
4206 function d3_geo_pathContext(context) {
4207   var pointRadius = 4.5;
4208
4209   var stream = {
4210     point: point,
4211
4212     // While inside a line, override point to moveTo then lineTo.
4213     lineStart: function() { stream.point = pointLineStart; },
4214     lineEnd: lineEnd,
4215
4216     // While inside a polygon, override lineEnd to closePath.
4217     polygonStart: function() { stream.lineEnd = lineEndPolygon; },
4218     polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; },
4219
4220     pointRadius: function(_) {
4221       pointRadius = _;
4222       return stream;
4223     },
4224
4225     result: d3_noop
4226   };
4227
4228   function point(x, y) {
4229     context.moveTo(x + pointRadius, y);
4230     context.arc(x, y, pointRadius, 0, τ);
4231   }
4232
4233   function pointLineStart(x, y) {
4234     context.moveTo(x, y);
4235     stream.point = pointLine;
4236   }
4237
4238   function pointLine(x, y) {
4239     context.lineTo(x, y);
4240   }
4241
4242   function lineEnd() {
4243     stream.point = point;
4244   }
4245
4246   function lineEndPolygon() {
4247     context.closePath();
4248   }
4249
4250   return stream;
4251 }
4252
4253 function d3_geo_resample(project) {
4254   var δ2 = .5, // precision, px²
4255       cosMinDistance = Math.cos(30 * d3_radians), // cos(minimum angular distance)
4256       maxDepth = 16;
4257
4258   function resample(stream) {
4259     return (maxDepth ? resampleRecursive : resampleNone)(stream);
4260   }
4261
4262   function resampleNone(stream) {
4263     return d3_geo_transformPoint(stream, function(x, y) {
4264       x = project(x, y);
4265       stream.point(x[0], x[1]);
4266     });
4267   }
4268
4269   function resampleRecursive(stream) {
4270     var λ00, φ00, x00, y00, a00, b00, c00, // first point
4271         λ0, x0, y0, a0, b0, c0; // previous point
4272
4273     var resample = {
4274       point: point,
4275       lineStart: lineStart,
4276       lineEnd: lineEnd,
4277       polygonStart: function() { stream.polygonStart(); resample.lineStart = ringStart; },
4278       polygonEnd: function() { stream.polygonEnd(); resample.lineStart = lineStart; }
4279     };
4280
4281     function point(x, y) {
4282       x = project(x, y);
4283       stream.point(x[0], x[1]);
4284     }
4285
4286     function lineStart() {
4287       x0 = NaN;
4288       resample.point = linePoint;
4289       stream.lineStart();
4290     }
4291
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);
4296     }
4297
4298     function lineEnd() {
4299       resample.point = point;
4300       stream.lineEnd();
4301     }
4302
4303     function ringStart() {
4304       lineStart();
4305       resample.point = ringPoint;
4306       resample.lineEnd = ringEnd;
4307     }
4308
4309     function ringPoint(λ, φ) {
4310       linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
4311       resample.point = linePoint;
4312     }
4313
4314     function ringEnd() {
4315       resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
4316       resample.lineEnd = lineEnd;
4317       lineEnd();
4318     }
4319
4320     return resample;
4321   }
4322
4323   function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
4324     var dx = x1 - x0,
4325         dy = y1 - y0,
4326         d2 = dx * dx + dy * dy;
4327     if (d2 > 4 * δ2 && depth--) {
4328       var a = a0 + a1,
4329           b = b0 + b1,
4330           c = c0 + c1,
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),
4335           x2 = p[0],
4336           y2 = p[1],
4337           dx2 = x2 - x0,
4338           dy2 = y2 - y0,
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);
4346       }
4347     }
4348   }
4349
4350   resample.precision = function(_) {
4351     if (!arguments.length) return Math.sqrt(δ2);
4352     maxDepth = (δ2 = _ * _) > 0 && 16;
4353     return resample;
4354   };
4355
4356   return resample;
4357 }
4358
4359 d3.geo.path = function() {
4360   var pointRadius = 4.5,
4361       projection,
4362       context,
4363       projectStream,
4364       contextStream,
4365       cacheStream;
4366
4367   function path(object) {
4368     if (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);
4372     }
4373     return contextStream.result();
4374   }
4375
4376   path.area = function(object) {
4377     d3_geo_pathAreaSum = 0;
4378     d3.geo.stream(object, projectStream(d3_geo_pathArea));
4379     return d3_geo_pathAreaSum;
4380   };
4381
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]
4390         : [NaN, NaN];
4391   };
4392
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]];
4397   };
4398
4399   path.projection = function(_) {
4400     if (!arguments.length) return projection;
4401     projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
4402     return reset();
4403   };
4404
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);
4409     return reset();
4410   };
4411
4412   path.pointRadius = function(_) {
4413     if (!arguments.length) return pointRadius;
4414     pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
4415     return path;
4416   };
4417
4418   function reset() {
4419     cacheStream = null;
4420     return path;
4421   }
4422
4423   return path.projection(d3.geo.albersUsa()).context(null);
4424 };
4425
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)); };
4429 }
4430
4431 d3.geo.transform = function(methods) {
4432   return {
4433     stream: function(stream) {
4434       var transform = new d3_geo_transform(stream);
4435       for (var k in methods) transform[k] = methods[k];
4436       return transform;
4437     }
4438   };
4439 };
4440
4441 function d3_geo_transform(stream) {
4442   this.stream = stream;
4443 }
4444
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(); }
4452 };
4453
4454 function d3_geo_transformPoint(stream, point) {
4455   return {
4456     point: 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(); },
4462   };
4463 }
4464
4465 d3.geo.projection = d3_geo_projection;
4466 d3.geo.projectionMutator = d3_geo_projectionMutator;
4467
4468 function d3_geo_projection(project) {
4469   return d3_geo_projectionMutator(function() { return project; })();
4470 }
4471
4472 function d3_geo_projectionMutator(projectAt) {
4473   var project,
4474       rotate,
4475       projectRotate,
4476       projectResample = d3_geo_resample(function(x, y) { x = project(x, y); return [x[0] * k + δx, δy - x[1] * k]; }),
4477       k = 150, // scale
4478       x = 480, y = 250, // translate
4479       λ = 0, φ = 0, // center
4480       δλ = 0, δφ = 0, δγ = 0, // rotate
4481       δx, δy, // center
4482       preclip = d3_geo_clipAntimeridian,
4483       postclip = d3_identity,
4484       clipAngle = null,
4485       clipExtent = null,
4486       stream;
4487
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];
4491   }
4492
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];
4496   }
4497
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
4502     return stream;
4503   };
4504
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();
4509   };
4510
4511   projection.clipExtent = function(_) {
4512     if (!arguments.length) return clipExtent;
4513     clipExtent = _;
4514     postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
4515     return invalidate();
4516   };
4517
4518   projection.scale = function(_) {
4519     if (!arguments.length) return k;
4520     k = +_;
4521     return reset();
4522   };
4523
4524   projection.translate = function(_) {
4525     if (!arguments.length) return [x, y];
4526     x = +_[0];
4527     y = +_[1];
4528     return reset();
4529   };
4530
4531   projection.center = function(_) {
4532     if (!arguments.length) return [λ * d3_degrees, φ * d3_degrees];
4533     λ = _[0] % 360 * d3_radians;
4534     φ = _[1] % 360 * d3_radians;
4535     return reset();
4536   };
4537
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;
4543     return reset();
4544   };
4545
4546   d3.rebind(projection, projectResample, "precision");
4547
4548   function reset() {
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();
4554   }
4555
4556   function invalidate() {
4557     if (stream) stream.valid = false, stream = null;
4558     return projection;
4559   }
4560
4561   return function() {
4562     project = projectAt.apply(this, arguments);
4563     projection.invert = project.invert && invert;
4564     return reset();
4565   };
4566 }
4567
4568 function d3_geo_projectionRadians(stream) {
4569   return d3_geo_transformPoint(stream, function(x, y) {
4570     stream.point(x * d3_radians, y * d3_radians);
4571   });
4572 }
4573
4574 function d3_geo_mercator(λ, φ) {
4575   return [λ, Math.log(Math.tan(π / 4 + φ / 2))];
4576 }
4577
4578 d3_geo_mercator.invert = function(x, y) {
4579   return [x, 2 * Math.atan(Math.exp(y)) - halfπ];
4580 };
4581
4582 function d3_geo_mercatorProjection(project) {
4583   var m = d3_geo_projection(project),
4584       scale = m.scale,
4585       translate = m.translate,
4586       clipExtent = m.clipExtent,
4587       clipAuto;
4588
4589   m.scale = function() {
4590     var v = scale.apply(m, arguments);
4591     return v === m ? (clipAuto ? m.clipExtent(null) : m) : v;
4592   };
4593
4594   m.translate = function() {
4595     var v = translate.apply(m, arguments);
4596     return v === m ? (clipAuto ? m.clipExtent(null) : m) : v;
4597   };
4598
4599   m.clipExtent = function(_) {
4600     var v = clipExtent.apply(m, arguments);
4601     if (v === m) {
4602       if (clipAuto = _ == null) {
4603         var k = π * scale(), t = translate();
4604         clipExtent([[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]]);
4605       }
4606     } else if (clipAuto) {
4607       v = null;
4608     }
4609     return v;
4610   };
4611
4612   return m.clipExtent(null);
4613 }
4614
4615 (d3.geo.mercator = function() {
4616   return d3_geo_mercatorProjection(d3_geo_mercator);
4617 }).raw = d3_geo_mercator;
4618 d3.geom = {};
4619
4620 d3.geom.polygon = function(coordinates) {
4621   d3_subclass(coordinates, d3_geom_polygonPrototype);
4622   return coordinates;
4623 };
4624
4625 var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
4626
4627 d3_geom_polygonPrototype.area = function() {
4628   var i = -1,
4629       n = this.length,
4630       a,
4631       b = this[n - 1],
4632       area = 0;
4633
4634   while (++i < n) {
4635     a = b;
4636     b = this[i];
4637     area += a[1] * b[0] - a[0] * b[1];
4638   }
4639
4640   return area * .5;
4641 };
4642
4643 d3_geom_polygonPrototype.centroid = function(k) {
4644   var i = -1,
4645       n = this.length,
4646       x = 0,
4647       y = 0,
4648       a,
4649       b = this[n - 1],
4650       c;
4651
4652   if (!arguments.length) k = -1 / (6 * this.area());
4653
4654   while (++i < n) {
4655     a = b;
4656     b = this[i];
4657     c = a[0] * b[1] - b[0] * a[1];
4658     x += (a[0] + b[0]) * c;
4659     y += (a[1] + b[1]) * c;
4660   }
4661
4662   return [x * k, y * k];
4663 };
4664
4665 // The Sutherland-Hodgman clipping algorithm.
4666 // Note: requires the clip polygon to be counterclockwise and convex.
4667 d3_geom_polygonPrototype.clip = function(subject) {
4668   var input,
4669       closed = d3_geom_polygonClosed(subject),
4670       i = -1,
4671       n = this.length - d3_geom_polygonClosed(this),
4672       j,
4673       m,
4674       a = this[n - 1],
4675       b,
4676       c,
4677       d;
4678
4679   while (++i < n) {
4680     input = subject.slice();
4681     subject.length = 0;
4682     b = this[i];
4683     c = input[(m = input.length - closed) - 1];
4684     j = -1;
4685     while (++j < m) {
4686       d = input[j];
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));
4690         }
4691         subject.push(d);
4692       } else if (d3_geom_polygonInside(c, a, b)) {
4693         subject.push(d3_geom_polygonIntersect(c, d, a, b));
4694       }
4695       c = d;
4696     }
4697     if (closed) subject.push(subject[0]);
4698     a = b;
4699   }
4700
4701   return subject;
4702 };
4703
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]);
4706 }
4707
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];
4714 }
4715
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]);
4721 }
4722 function d3_geom_pointX(d) {
4723   return d[0];
4724 }
4725
4726 function d3_geom_pointY(d) {
4727   return d[1];
4728 }
4729
4730 /**
4731  * Computes the 2D convex hull of a set of points using the monotone chain
4732  * algorithm:
4733  * http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain)
4734  *
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.
4737  *
4738  * @param vertices [[x1, y1], [x2, y2], ...]
4739  * @returns polygon [[x1, y1], [x2, y2], ...]
4740  */
4741 d3.geom.hull = function(vertices) {
4742   var x = d3_geom_pointX,
4743       y = d3_geom_pointY;
4744
4745   if (arguments.length) return hull(vertices);
4746
4747   function hull(data) {
4748     // Hull of < 3 points is not well-defined
4749     if (data.length < 3) return [];
4750
4751     var fx = d3_functor(x),
4752         fy = d3_functor(y),
4753         i,
4754         n = data.length,
4755         points = [], // of the form [[x0, y0, 0], ..., [xn, yn, n]]
4756         flippedPoints = [];
4757
4758     for (i = 0 ; i < n; i++) {
4759       points.push([+fx.call(this, data[i], i), +fy.call(this, data[i], i), i]);
4760     }
4761
4762     // sort ascending by x-coord first, y-coord second
4763     points.sort(d3_geom_hullOrder);
4764
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]]);
4767
4768     var upper = d3_geom_hullUpper(points),
4769         lower = d3_geom_hullUpper(flippedPoints);
4770
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],
4774         polygon = [];
4775
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]]);
4780
4781     return polygon;
4782   }
4783
4784   hull.x = function(_) {
4785     return arguments.length ? (x = _, hull) : x;
4786   };
4787
4788   hull.y = function(_) {
4789     return arguments.length ? (y = _, hull) : y;
4790   };
4791
4792   return hull;
4793 };
4794
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,
4800       hull = [0, 1],
4801       hs = 2; // hull size
4802
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;
4805     hull[hs++] = i;
4806   }
4807
4808   // we slice to make sure that the points we 'popped' from hull don't stay behind
4809   return hull.slice(0, hs);
4810 }
4811
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];
4815 }
4816 // import "../transition/transition";
4817
4818 d3_selectionPrototype.transition = function(name) {
4819   var id = d3_transitionInheritId || ++d3_transitionId,
4820       ns = d3_transitionNamespace(name),
4821       subgroups = [],
4822       subgroup,
4823       node,
4824       transition = d3_transitionInherit || {time: Date.now(), ease: d3_ease_cubicInOut, delay: 0, duration: 250};
4825
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);
4831     }
4832   }
4833
4834   return d3_transition(subgroups, ns, id);
4835 };
4836 // import "../transition/transition";
4837
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)));
4843 };
4844
4845 var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace());
4846
4847 function d3_selection_interruptNS(ns) {
4848   return function() {
4849     var lock, active;
4850     if ((lock = this[ns]) && (active = lock[lock.active])) {
4851       if (--lock.count) delete lock[lock.active];
4852       else delete this[ns];
4853       lock.active += .5;
4854       active.event && active.event.interrupt.call(this, this.__data__, active.index);
4855     }
4856   };
4857 }
4858
4859 function d3_transition(groups, ns, id) {
4860   d3_subclass(groups, d3_transitionPrototype);
4861
4862   // Note: read-only!
4863   groups.namespace = ns;
4864   groups.id = id;
4865
4866   return groups;
4867 }
4868
4869 var d3_transitionPrototype = [],
4870     d3_transitionId = 0,
4871     d3_transitionInheritId,
4872     d3_transitionInherit;
4873
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;
4878
4879 d3.transition = function(selection, name) {
4880   return selection && selection.transition
4881       ? (d3_transitionInheritId ? selection.transition(name) : selection)
4882       : d3.selection().transition(selection);
4883 };
4884
4885 d3.transition.prototype = d3_transitionPrototype;
4886
4887
4888 d3_transitionPrototype.select = function(selector) {
4889   var id = this.id,
4890       ns = this.namespace,
4891       subgroups = [],
4892       subgroup,
4893       subnode,
4894       node;
4895
4896   selector = d3_selection_selector(selector);
4897
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);
4905       } else {
4906         subgroup.push(null);
4907       }
4908     }
4909   }
4910
4911   return d3_transition(subgroups, ns, id);
4912 };
4913
4914 d3_transitionPrototype.selectAll = function(selector) {
4915   var id = this.id,
4916       ns = this.namespace,
4917       subgroups = [],
4918       subgroup,
4919       subnodes,
4920       node,
4921       subnode,
4922       transition;
4923
4924   selector = d3_selection_selectorAll(selector);
4925
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);
4935         }
4936       }
4937     }
4938   }
4939
4940   return d3_transition(subgroups, ns, id);
4941 };
4942
4943 d3_transitionPrototype.filter = function(filter) {
4944   var subgroups = [],
4945       subgroup,
4946       group,
4947       node;
4948
4949   if (typeof filter !== "function") filter = d3_selection_filter(filter);
4950
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);
4956       }
4957     }
4958   }
4959
4960   return d3_transition(subgroups, this.namespace, this.id);
4961 };
4962 d3.color = d3_color;
4963
4964 function d3_color() {}
4965
4966 d3_color.prototype.toString = function() {
4967   return this.rgb() + "";
4968 };
4969
4970 d3.hsl = d3_hsl;
4971
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);
4977 }
4978
4979 var d3_hslPrototype = d3_hsl.prototype = new d3_color;
4980
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);
4984 };
4985
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);
4989 };
4990
4991 d3_hslPrototype.rgb = function() {
4992   return d3_hsl_rgb(this.h, this.s, this.l);
4993 };
4994
4995 function d3_hsl_rgb(h, s, l) {
4996   var m1,
4997       m2;
4998
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;
5003
5004   /* From FvD 13.37, CSS Color Module Level 3 */
5005   m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
5006   m1 = 2 * l - m2;
5007
5008   function v(h) {
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;
5014     return m1;
5015   }
5016
5017   function vv(h) {
5018     return Math.round(v(h) * 255);
5019   }
5020
5021   return new d3_rgb(vv(h + 120), vv(h), vv(h - 120));
5022 }
5023
5024 d3.hcl = d3_hcl;
5025
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);
5032 }
5033
5034 var d3_hclPrototype = d3_hcl.prototype = new d3_color;
5035
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)));
5038 };
5039
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)));
5042 };
5043
5044 d3_hclPrototype.rgb = function() {
5045   return d3_hcl_lab(this.h, this.c, this.l).rgb();
5046 };
5047
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);
5052 }
5053
5054 d3.lab = d3_lab;
5055
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);
5062 }
5063
5064 // Corresponds roughly to RGB brighter/darker
5065 var d3_lab_K = 18;
5066
5067 // D65 standard referent
5068 var d3_lab_X = 0.950470,
5069     d3_lab_Y = 1,
5070     d3_lab_Z = 1.088830;
5071
5072 var d3_labPrototype = d3_lab.prototype = new d3_color;
5073
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);
5076 };
5077
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);
5080 };
5081
5082 d3_labPrototype.rgb = function() {
5083   return d3_lab_rgb(this.l, this.a, this.b);
5084 };
5085
5086 function d3_lab_rgb(l, a, b) {
5087   var y = (l + 16) / 116,
5088       x = y + a / 500,
5089       z = y - b / 200;
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;
5093   return new d3_rgb(
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)
5097   );
5098 }
5099
5100 function d3_lab_hcl(l, a, b) {
5101   return l > 0
5102       ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l)
5103       : new d3_hcl(NaN, NaN, l);
5104 }
5105
5106 function d3_lab_xyz(x) {
5107   return x > 0.206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
5108 }
5109 function d3_xyz_lab(x) {
5110   return x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
5111 }
5112
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));
5115 }
5116
5117 d3.rgb = d3_rgb;
5118
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);
5124 }
5125
5126 function d3_rgbNumber(value) {
5127   return new d3_rgb(value >> 16, value >> 8 & 0xff, value & 0xff);
5128 }
5129
5130 function d3_rgbString(value) {
5131   return d3_rgbNumber(value) + "";
5132 }
5133
5134 var d3_rgbPrototype = d3_rgb.prototype = new d3_color;
5135
5136 d3_rgbPrototype.brighter = function(k) {
5137   k = Math.pow(0.7, arguments.length ? k : 1);
5138   var r = this.r,
5139       g = this.g,
5140       b = this.b,
5141       i = 30;
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));
5147 };
5148
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);
5152 };
5153
5154 d3_rgbPrototype.hsl = function() {
5155   return d3_rgb_hsl(this.r, this.g, this.b);
5156 };
5157
5158 d3_rgbPrototype.toString = function() {
5159   return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
5160 };
5161
5162 function d3_rgb_hex(v) {
5163   return v < 0x10
5164       ? "0" + Math.max(0, v).toString(16)
5165       : Math.min(255, v).toString(16);
5166 }
5167
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)
5174       color;
5175
5176   /* Handle hsl, rgb. */
5177   m1 = /([a-z]+)\((.*)\)/i.exec(format);
5178   if (m1) {
5179     m2 = m1[2].split(",");
5180     switch (m1[1]) {
5181       case "hsl": {
5182         return hsl(
5183           parseFloat(m2[0]), // degrees
5184           parseFloat(m2[1]) / 100, // percentage
5185           parseFloat(m2[2]) / 100 // percentage
5186         );
5187       }
5188       case "rgb": {
5189         return rgb(
5190           d3_rgb_parseNumber(m2[0]),
5191           d3_rgb_parseNumber(m2[1]),
5192           d3_rgb_parseNumber(m2[2])
5193         );
5194       }
5195     }
5196   }
5197
5198   /* Named colors. */
5199   if (color = d3_rgb_names.get(format.toLowerCase())) {
5200     return rgb(color.r, color.g, color.b);
5201   }
5202
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;
5212       b = (color & 0xff);
5213     }
5214   }
5215
5216   return rgb(r, g, b);
5217 }
5218
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),
5222       d = max - min,
5223       h,
5224       s,
5225       l = (max + min) / 2;
5226   if (d) {
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;
5231     h *= 60;
5232   } else {
5233     h = NaN;
5234     s = l > 0 && l < 1 ? 0 : h;
5235   }
5236   return new d3_hsl(h, s, l);
5237 }
5238
5239 function d3_rgb_lab(r, g, b) {
5240   r = d3_rgb_xyz(r);
5241   g = d3_rgb_xyz(g);
5242   b = d3_rgb_xyz(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));
5247 }
5248
5249 function d3_rgb_xyz(r) {
5250   return (r /= 255) <= 0.04045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
5251 }
5252
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;
5256 }
5257
5258 var d3_rgb_names = d3.map({
5259   aliceblue: 0xf0f8ff,
5260   antiquewhite: 0xfaebd7,
5261   aqua: 0x00ffff,
5262   aquamarine: 0x7fffd4,
5263   azure: 0xf0ffff,
5264   beige: 0xf5f5dc,
5265   bisque: 0xffe4c4,
5266   black: 0x000000,
5267   blanchedalmond: 0xffebcd,
5268   blue: 0x0000ff,
5269   blueviolet: 0x8a2be2,
5270   brown: 0xa52a2a,
5271   burlywood: 0xdeb887,
5272   cadetblue: 0x5f9ea0,
5273   chartreuse: 0x7fff00,
5274   chocolate: 0xd2691e,
5275   coral: 0xff7f50,
5276   cornflowerblue: 0x6495ed,
5277   cornsilk: 0xfff8dc,
5278   crimson: 0xdc143c,
5279   cyan: 0x00ffff,
5280   darkblue: 0x00008b,
5281   darkcyan: 0x008b8b,
5282   darkgoldenrod: 0xb8860b,
5283   darkgray: 0xa9a9a9,
5284   darkgreen: 0x006400,
5285   darkgrey: 0xa9a9a9,
5286   darkkhaki: 0xbdb76b,
5287   darkmagenta: 0x8b008b,
5288   darkolivegreen: 0x556b2f,
5289   darkorange: 0xff8c00,
5290   darkorchid: 0x9932cc,
5291   darkred: 0x8b0000,
5292   darksalmon: 0xe9967a,
5293   darkseagreen: 0x8fbc8f,
5294   darkslateblue: 0x483d8b,
5295   darkslategray: 0x2f4f4f,
5296   darkslategrey: 0x2f4f4f,
5297   darkturquoise: 0x00ced1,
5298   darkviolet: 0x9400d3,
5299   deeppink: 0xff1493,
5300   deepskyblue: 0x00bfff,
5301   dimgray: 0x696969,
5302   dimgrey: 0x696969,
5303   dodgerblue: 0x1e90ff,
5304   firebrick: 0xb22222,
5305   floralwhite: 0xfffaf0,
5306   forestgreen: 0x228b22,
5307   fuchsia: 0xff00ff,
5308   gainsboro: 0xdcdcdc,
5309   ghostwhite: 0xf8f8ff,
5310   gold: 0xffd700,
5311   goldenrod: 0xdaa520,
5312   gray: 0x808080,
5313   green: 0x008000,
5314   greenyellow: 0xadff2f,
5315   grey: 0x808080,
5316   honeydew: 0xf0fff0,
5317   hotpink: 0xff69b4,
5318   indianred: 0xcd5c5c,
5319   indigo: 0x4b0082,
5320   ivory: 0xfffff0,
5321   khaki: 0xf0e68c,
5322   lavender: 0xe6e6fa,
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,
5341   lime: 0x00ff00,
5342   limegreen: 0x32cd32,
5343   linen: 0xfaf0e6,
5344   magenta: 0xff00ff,
5345   maroon: 0x800000,
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,
5358   moccasin: 0xffe4b5,
5359   navajowhite: 0xffdead,
5360   navy: 0x000080,
5361   oldlace: 0xfdf5e6,
5362   olive: 0x808000,
5363   olivedrab: 0x6b8e23,
5364   orange: 0xffa500,
5365   orangered: 0xff4500,
5366   orchid: 0xda70d6,
5367   palegoldenrod: 0xeee8aa,
5368   palegreen: 0x98fb98,
5369   paleturquoise: 0xafeeee,
5370   palevioletred: 0xdb7093,
5371   papayawhip: 0xffefd5,
5372   peachpuff: 0xffdab9,
5373   peru: 0xcd853f,
5374   pink: 0xffc0cb,
5375   plum: 0xdda0dd,
5376   powderblue: 0xb0e0e6,
5377   purple: 0x800080,
5378   rebeccapurple: 0x663399,
5379   red: 0xff0000,
5380   rosybrown: 0xbc8f8f,
5381   royalblue: 0x4169e1,
5382   saddlebrown: 0x8b4513,
5383   salmon: 0xfa8072,
5384   sandybrown: 0xf4a460,
5385   seagreen: 0x2e8b57,
5386   seashell: 0xfff5ee,
5387   sienna: 0xa0522d,
5388   silver: 0xc0c0c0,
5389   skyblue: 0x87ceeb,
5390   slateblue: 0x6a5acd,
5391   slategray: 0x708090,
5392   slategrey: 0x708090,
5393   snow: 0xfffafa,
5394   springgreen: 0x00ff7f,
5395   steelblue: 0x4682b4,
5396   tan: 0xd2b48c,
5397   teal: 0x008080,
5398   thistle: 0xd8bfd8,
5399   tomato: 0xff6347,
5400   turquoise: 0x40e0d0,
5401   violet: 0xee82ee,
5402   wheat: 0xf5deb3,
5403   white: 0xffffff,
5404   whitesmoke: 0xf5f5f5,
5405   yellow: 0xffff00,
5406   yellowgreen: 0x9acd32
5407 });
5408
5409 d3_rgb_names.forEach(function(key, value) {
5410   d3_rgb_names.set(key, d3_rgbNumber(value));
5411 });
5412
5413 d3.interpolateRgb = d3_interpolateRgb;
5414
5415 function d3_interpolateRgb(a, b) {
5416   a = d3.rgb(a);
5417   b = d3.rgb(b);
5418   var ar = a.r,
5419       ag = a.g,
5420       ab = a.b,
5421       br = b.r - ar,
5422       bg = b.g - ag,
5423       bb = b.b - ab;
5424   return function(t) {
5425     return "#"
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));
5429   };
5430 }
5431
5432 d3.interpolateObject = d3_interpolateObject;
5433
5434 function d3_interpolateObject(a, b) {
5435   var i = {},
5436       c = {},
5437       k;
5438   for (k in a) {
5439     if (k in b) {
5440       i[k] = d3_interpolate(a[k], b[k]);
5441     } else {
5442       c[k] = a[k];
5443     }
5444   }
5445   for (k in b) {
5446     if (!(k in a)) {
5447       c[k] = b[k];
5448     }
5449   }
5450   return function(t) {
5451     for (k in i) c[k] = i[k](t);
5452     return c;
5453   };
5454 }
5455
5456 d3.interpolateArray = d3_interpolateArray;
5457
5458 function d3_interpolateArray(a, b) {
5459   var x = [],
5460       c = [],
5461       na = a.length,
5462       nb = b.length,
5463       n0 = Math.min(a.length, b.length),
5464       i;
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);
5470     return c;
5471   };
5472 }
5473 d3.interpolateNumber = d3_interpolateNumber;
5474
5475 function d3_interpolateNumber(a, b) {
5476   a = +a, b = +b;
5477   return function(t) { return a * (1 - t) + b * t; };
5478 }
5479
5480 d3.interpolateString = d3_interpolateString;
5481
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
5490
5491   // Coerce inputs to strings.
5492   a = a + "", b = b + "";
5493
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
5500       else s[++i] = bs;
5501     }
5502     if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
5503       if (s[i]) s[i] += bm; // coalesce with previous string
5504       else s[++i] = bm;
5505     } else { // interpolate non-matching numbers
5506       s[++i] = null;
5507       q.push({i: i, x: d3_interpolateNumber(am, bm)});
5508     }
5509     bi = d3_interpolate_numberB.lastIndex;
5510   }
5511
5512   // Add remains of b.
5513   if (bi < b.length) {
5514     bs = b.slice(bi);
5515     if (s[i]) s[i] += bs; // coalesce with previous string
5516     else s[++i] = bs;
5517   }
5518
5519   // Special optimization for only a single match.
5520   // Otherwise, interpolate each of the numbers and rejoin the string.
5521   return s.length < 2
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);
5526           return s.join("");
5527         });
5528 }
5529
5530 var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
5531     d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g");
5532
5533 d3.interpolate = d3_interpolate;
5534
5535 function d3_interpolate(a, b) {
5536   var i = d3.interpolators.length, f;
5537   while (--i >= 0 && !(f = d3.interpolators[i](a, b)));
5538   return f;
5539 }
5540
5541 d3.interpolators = [
5542   function(a, b) {
5543     var t = typeof b;
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);
5549   }
5550 ];
5551
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();
5558     }
5559     return new d3_transform(t ? t.matrix : d3_transformIdentity);
5560   })(string);
5561 };
5562
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],
5569       r1 = [m.c, m.d],
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]) {
5574     r0[0] *= -1;
5575     r0[1] *= -1;
5576     kx *= -1;
5577     kz *= -1;
5578   }
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;
5583 };
5584
5585 d3_transform.prototype.toString = function() {
5586   return "translate(" + this.translate
5587       + ")rotate(" + this.rotate
5588       + ")skewX(" + this.skew
5589       + ")scale(" + this.scale
5590       + ")";
5591 };
5592
5593 function d3_transformDot(a, b) {
5594   return a[0] * b[0] + a[1] * b[1];
5595 }
5596
5597 function d3_transformNormalize(a) {
5598   var k = Math.sqrt(d3_transformDot(a, a));
5599   if (k) {
5600     a[0] /= k;
5601     a[1] /= k;
5602   }
5603   return k;
5604 }
5605
5606 function d3_transformCombine(a, b, k) {
5607   a[0] += k * b[0];
5608   a[1] += k * b[1];
5609   return a;
5610 }
5611
5612 var d3_transformIdentity = {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0};
5613
5614 d3.interpolateTransform = d3_interpolateTransform;
5615
5616 function d3_interpolateTransform(a, b) {
5617   var s = [], // string constants and placeholders
5618       q = [], // number interpolators
5619       n,
5620       A = d3.transform(a),
5621       B = d3.transform(b),
5622       ta = A.translate,
5623       tb = B.translate,
5624       ra = A.rotate,
5625       rb = B.rotate,
5626       wa = A.skew,
5627       wb = B.skew,
5628       ka = A.scale,
5629       kb = B.scale;
5630
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 + ")");
5636   } else {
5637     s.push("");
5638   }
5639
5640   if (ra != rb) {
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)});
5643   } else if (rb) {
5644     s.push(s.pop() + "rotate(" + rb + ")");
5645   }
5646
5647   if (wa != wb) {
5648     q.push({i: s.push(s.pop() + "skewX(", null, ")") - 2, x: d3_interpolateNumber(wa, wb)});
5649   } else if (wb) {
5650     s.push(s.pop() + "skewX(" + wb + ")");
5651   }
5652
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 + ")");
5658   }
5659
5660   n = q.length;
5661   return function(t) {
5662     var i = -1, o;
5663     while (++i < n) s[(o = q[i]).i] = o.x(t);
5664     return s.join("");
5665   };
5666 }
5667
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); });
5674 };
5675
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); }));
5681 }
5682
5683 d3_transitionPrototype.attr = function(nameNS, value) {
5684   if (arguments.length < 2) {
5685
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]);
5690     return this;
5691   }
5692
5693   var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate,
5694       name = d3.ns.qualify(nameNS);
5695
5696   // For attr(string, null), remove the attribute with the specified name.
5697   function attrNull() {
5698     this.removeAttribute(name);
5699   }
5700   function attrNullNS() {
5701     this.removeAttributeNS(name.space, name.local);
5702   }
5703
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)); });
5709     });
5710   }
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)); });
5715     });
5716   }
5717
5718   return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
5719 };
5720
5721 d3_transitionPrototype.attrTween = function(nameNS, tween) {
5722   var name = d3.ns.qualify(nameNS);
5723
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)); };
5727   }
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)); };
5731   }
5732
5733   return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
5734 };
5735
5736 d3_transitionPrototype.style = function(name, value, priority) {
5737   var n = arguments.length;
5738   if (n < 3) {
5739
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);
5747       return this;
5748     }
5749
5750     // For style(string, string) or style(string, function), use the default
5751     // priority. The priority is ignored for style(string, null).
5752     priority = "";
5753   }
5754
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);
5759   }
5760
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); });
5768     });
5769   }
5770
5771   return d3_transition_tween(this, "style." + name, value, styleString);
5772 };
5773
5774 d3_transitionPrototype.styleTween = function(name, tween, priority) {
5775   if (arguments.length < 3) priority = "";
5776
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); };
5780   }
5781
5782   return this.tween("style." + name, styleTween);
5783 };
5784
5785 d3_transitionPrototype.text = function(value) {
5786   return d3_transition_tween(this, "text", value, d3_transition_text);
5787 };
5788
5789 function d3_transition_text(b) {
5790   if (b == null) b = "";
5791   return function() { this.textContent = b; };
5792 }
5793
5794 d3_transitionPrototype.remove = function() {
5795   var ns = this.namespace;
5796   return this.each("end.transition", function() {
5797     var p;
5798     if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this);
5799   });
5800 };
5801
5802 var d3_ease_default = function() { return d3_identity; };
5803
5804 var d3_ease = d3.map({
5805   linear: d3_ease_default,
5806   poly: d3_ease_poly,
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,
5813   back: d3_ease_back,
5814   bounce: function() { return d3_ease_bounce; }
5815 });
5816
5817 var d3_ease_mode = d3.map({
5818   "in": d3_identity,
5819   "out": d3_ease_reverse,
5820   "in-out": d3_ease_reflect,
5821   "out-in": function(f) { return d3_ease_reflect(d3_ease_reverse(f)); }
5822 });
5823
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))));
5831 };
5832
5833 function d3_ease_clamp(f) {
5834   return function(t) {
5835     return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
5836   };
5837 }
5838
5839 function d3_ease_reverse(f) {
5840   return function(t) {
5841     return 1 - f(1 - t);
5842   };
5843 }
5844
5845 function d3_ease_reflect(f) {
5846   return function(t) {
5847     return .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t)));
5848   };
5849 }
5850
5851 function d3_ease_quad(t) {
5852   return t * t;
5853 }
5854
5855 function d3_ease_cubic(t) {
5856   return t * t * t;
5857 }
5858
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);
5865 }
5866
5867 function d3_ease_poly(e) {
5868   return function(t) {
5869     return Math.pow(t, e);
5870   };
5871 }
5872
5873 function d3_ease_sin(t) {
5874   return 1 - Math.cos(t * halfπ);
5875 }
5876
5877 function d3_ease_exp(t) {
5878   return Math.pow(2, 10 * (t - 1));
5879 }
5880
5881 function d3_ease_circle(t) {
5882   return 1 - Math.sqrt(1 - t * t);
5883 }
5884
5885 function d3_ease_elastic(a, p) {
5886   var s;
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);
5892   };
5893 }
5894
5895 function d3_ease_back(s) {
5896   if (!s) s = 1.70158;
5897   return function(t) {
5898     return t * t * ((s + 1) * t - s);
5899   };
5900 }
5901
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;
5907 }
5908
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; });
5914 };
5915
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; }));
5922 };
5923
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; }));
5930 };
5931
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;
5937     try {
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);
5942       });
5943     } finally {
5944       d3_transitionInherit = inherit;
5945       d3_transitionInheritId = inheritId;
5946     }
5947   } else {
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);
5951     });
5952   }
5953   return this;
5954 };
5955
5956 d3_transitionPrototype.transition = function() {
5957   var id0 = this.id,
5958       id1 = ++d3_transitionId,
5959       ns = this.namespace,
5960       subgroups = [],
5961       subgroup,
5962       group,
5963       node,
5964       transition;
5965
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});
5972       }
5973       subgroup.push(node);
5974     }
5975   }
5976
5977   return d3_transition(subgroups, ns, id1);
5978 };
5979
5980 function d3_transitionNamespace(name) {
5981   return name == null ? "__transition__" : "__transition_" + name + "__";
5982 }
5983
5984 function d3_transitionNode(node, i, ns, id, inherit) {
5985   var lock = node[ns] || (node[ns] = {active: 0, count: 0}),
5986       transition = lock[id];
5987
5988   if (!transition) {
5989     var time = inherit.time;
5990
5991     transition = lock[id] = {
5992       tween: new d3_Map,
5993       time: time,
5994       delay: inherit.delay,
5995       duration: inherit.duration,
5996       ease: inherit.ease,
5997       index: i
5998     };
5999
6000     inherit = null; // allow gc
6001
6002     ++lock.count;
6003
6004     d3.timer(function(elapsed) {
6005       var delay = transition.delay,
6006           duration,
6007           ease,
6008           timer = d3_timer_active,
6009           tweened = [];
6010
6011       timer.t = delay + time;
6012       if (delay <= elapsed) return start(elapsed - delay);
6013       timer.c = start;
6014
6015       function start(elapsed) {
6016         if (lock.active > id) return stop();
6017
6018         var active = lock[lock.active];
6019         if (active) {
6020           --lock.count;
6021           delete lock[lock.active];
6022           active.event && active.event.interrupt.call(node, node.__data__, active.index);
6023         }
6024
6025         lock.active = id;
6026
6027         transition.event && transition.event.start.call(node, node.__data__, i);
6028
6029         transition.tween.forEach(function(key, value) {
6030           if (value = value.call(node, node.__data__, i)) {
6031             tweened.push(value);
6032           }
6033         });
6034
6035         // Deferred capture to allow tweens to initialize ease & duration.
6036         ease = transition.ease;
6037         duration = transition.duration;
6038
6039         d3.timer(function() { // defer to end of current frame
6040           timer.c = tick(elapsed || 1) ? d3_true : tick;
6041           return 1;
6042         }, 0, time);
6043       }
6044
6045       function tick(elapsed) {
6046         if (lock.active !== id) return 1;
6047
6048         var t = elapsed / duration,
6049             e = ease(t),
6050             n = tweened.length;
6051
6052         while (n > 0) {
6053           tweened[--n].call(node, e);
6054         }
6055
6056         if (t >= 1) {
6057           transition.event && transition.event.end.call(node, node.__data__, i);
6058           return stop();
6059         }
6060       }
6061
6062       function stop() {
6063         if (--lock.count) delete lock[id];
6064         else delete node[ns];
6065         return 1;
6066       }
6067     }, 0, time);
6068   }
6069 }
6070
6071 d3.xhr = d3_xhrType(d3_identity);
6072
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);
6077   };
6078 }
6079
6080 function d3_xhr(url, mimeType, response, callback) {
6081   var xhr = {},
6082       dispatch = d3.dispatch("beforesend", "progress", "load", "error"),
6083       headers = {},
6084       request = new XMLHttpRequest,
6085       responseType = null;
6086
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;
6091
6092   "onload" in request
6093       ? request.onload = request.onerror = respond
6094       : request.onreadystatechange = function() { request.readyState > 3 && respond(); };
6095
6096   function respond() {
6097     var status = request.status, result;
6098     if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) {
6099       try {
6100         result = response.call(xhr, request);
6101       } catch (e) {
6102         dispatch.error.call(xhr, e);
6103         return;
6104       }
6105       dispatch.load.call(xhr, result);
6106     } else {
6107       dispatch.error.call(xhr, request);
6108     }
6109   }
6110
6111   request.onprogress = function(event) {
6112     var o = d3.event;
6113     d3.event = event;
6114     try { dispatch.progress.call(xhr, request); }
6115     finally { d3.event = o; }
6116   };
6117
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 + "";
6123     return xhr;
6124   };
6125
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 + "";
6130     return xhr;
6131   };
6132
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;
6138     return xhr;
6139   };
6140
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) {
6144     response = value;
6145     return xhr;
6146   };
6147
6148   // Convenience methods.
6149   ["get", "post"].forEach(function(method) {
6150     xhr[method] = function() {
6151       return xhr.send.apply(xhr, [method].concat(d3_array(arguments)));
6152     };
6153   });
6154
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);
6166     return xhr;
6167   };
6168
6169   xhr.abort = function() {
6170     request.abort();
6171     return xhr;
6172   };
6173
6174   d3.rebind(xhr, dispatch, "on");
6175
6176   return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
6177 };
6178
6179 function d3_xhr_fixCallback(callback) {
6180   return callback.length === 1
6181       ? function(error, request) { callback(error == null ? request : null); }
6182       : callback;
6183 }
6184
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
6190 }
6191
6192 d3.text = d3_xhrType(function(request) {
6193   return request.responseText;
6194 });
6195
6196 d3.json = function(url, callback) {
6197   return d3_xhr(url, "application/json", d3_json, callback);
6198 };
6199
6200 function d3_json(request) {
6201   return JSON.parse(request.responseText);
6202 }
6203
6204 d3.html = function(url, callback) {
6205   return d3_xhr(url, "text/html", d3_html, callback);
6206 };
6207
6208 function d3_html(request) {
6209   var range = d3_document.createRange();
6210   range.selectNode(d3_document.body);
6211   return range.createContextualFragment(request.responseText);
6212 }
6213
6214 d3.xml = d3_xhrType(function(request) {
6215   return request.responseXML;
6216 });
6217   if (typeof define === "function" && define.amd) define(d3);
6218   else if (typeof module === "object" && module.exports) module.exports = d3;
6219   this.d3 = d3;
6220 }();
6221 d3.combobox = function() {
6222     var event = d3.dispatch('accept'),
6223         data = [],
6224         suggestions = [],
6225         minItems = 2,
6226         caseSensitive = false;
6227
6228     var fetcher = function(val, cb) {
6229         cb(data.filter(function(d) {
6230             return d.value
6231                 .toString()
6232                 .toLowerCase()
6233                 .indexOf(val.toLowerCase()) !== -1;
6234         }));
6235     };
6236
6237     var combobox = function(input, attachTo) {
6238         var idx = -1,
6239             container = d3.select(document.body)
6240                 .selectAll('div.combobox')
6241                 .filter(function(d) { return d === input.node(); }),
6242             shown = !container.empty();
6243
6244         input
6245             .classed('combobox-input', true)
6246             .on('focus.typeahead', focus)
6247             .on('blur.typeahead', blur)
6248             .on('keydown.typeahead', keydown)
6249             .on('keyup.typeahead', keyup)
6250             .on('input.typeahead', change)
6251             .each(function() {
6252                 var parent = this.parentNode,
6253                     sibling = this.nextSibling;
6254
6255                 var caret = d3.select(parent).selectAll('.combobox-caret')
6256                     .filter(function(d) { return d === input.node(); })
6257                     .data([input.node()]);
6258
6259                 caret.enter().insert('div', function() { return sibling; })
6260                     .attr('class', 'combobox-caret');
6261
6262                 caret
6263                     .on('mousedown', function () {
6264                         // prevent the form element from blurring. it blurs
6265                         // on mousedown
6266                         d3.event.stopPropagation();
6267                         d3.event.preventDefault();
6268                         if (!shown) {
6269                             input.node().focus();
6270                             fetch('', render);
6271                         } else {
6272                             hide();
6273                         }
6274                     });
6275             });
6276
6277         function focus() {
6278             fetch(value(), render);
6279         }
6280
6281         function blur() {
6282             window.setTimeout(hide, 150);
6283         }
6284
6285         function show() {
6286             if (!shown) {
6287                 container = d3.select(document.body)
6288                     .insert('div', ':first-child')
6289                     .datum(input.node())
6290                     .attr('class', 'combobox')
6291                     .style({
6292                         position: 'absolute',
6293                         display: 'block',
6294                         left: '0px'
6295                     })
6296                     .on('mousedown', function () {
6297                         // prevent moving focus out of the text field
6298                         d3.event.preventDefault();
6299                     });
6300
6301                 d3.select(document.body)
6302                     .on('scroll.combobox', render, true);
6303
6304                 shown = true;
6305             }
6306         }
6307
6308         function hide() {
6309             if (shown) {
6310                 idx = -1;
6311                 container.remove();
6312
6313                 d3.select(document.body)
6314                     .on('scroll.combobox', null);
6315
6316                 shown = false;
6317             }
6318         }
6319
6320         function keydown() {
6321            switch (d3.event.keyCode) {
6322                // backspace, delete
6323                case 8:
6324                case 46:
6325                    input.on('input.typeahead', function() {
6326                        idx = -1;
6327                        render();
6328                        var start = input.property('selectionStart');
6329                        input.node().setSelectionRange(start, start);
6330                        input.on('input.typeahead', change);
6331                    });
6332                    break;
6333                // tab
6334                case 9:
6335                    container.selectAll('a.selected').each(event.accept);
6336                    break;
6337                // return
6338                case 13:
6339                    d3.event.preventDefault();
6340                    break;
6341                // up arrow
6342                case 38:
6343                    nav(-1);
6344                    d3.event.preventDefault();
6345                    break;
6346                // down arrow
6347                case 40:
6348                    nav(+1);
6349                    d3.event.preventDefault();
6350                    break;
6351            }
6352            d3.event.stopPropagation();
6353         }
6354
6355         function keyup() {
6356             switch (d3.event.keyCode) {
6357                 // escape
6358                 case 27:
6359                     hide();
6360                     break;
6361                 // return
6362                 case 13:
6363                     container.selectAll('a.selected').each(event.accept);
6364                     hide();
6365                     break;
6366             }
6367         }
6368
6369         function change() {
6370             fetch(value(), function() {
6371                 if (input.property('selectionEnd') === input.property('value').length) {
6372                     autocomplete();
6373                 }
6374                 render();
6375             });
6376         }
6377
6378         function nav(dir) {
6379             if (!suggestions.length) return;
6380             idx = Math.max(Math.min(idx + dir, suggestions.length - 1), 0);
6381             input.property('value', suggestions[idx].value);
6382             render();
6383             ensureVisible();
6384         }
6385
6386         function value() {
6387             var value = input.property('value'),
6388                 start = input.property('selectionStart'),
6389                 end = input.property('selectionEnd');
6390
6391             if (start && end) {
6392                 value = value.substring(0, start);
6393             }
6394
6395             return value;
6396         }
6397
6398         function fetch(v, cb) {
6399             fetcher.call(input, v, function(_) {
6400                 suggestions = _;
6401                 cb();
6402             });
6403         }
6404
6405         function autocomplete() {
6406             var v = caseSensitive ? value() : value().toLowerCase();
6407             idx = -1;
6408             if (!v) return;
6409
6410             for (var i = 0; i < suggestions.length; i++) {
6411                 var suggestion = suggestions[i].value,
6412                     compare = caseSensitive ? suggestion : suggestion.toLowerCase();
6413
6414                 if (compare.indexOf(v) === 0) {
6415                     idx = i;
6416                     input.property('value', suggestion);
6417                     input.node().setSelectionRange(v.length, suggestion.length);
6418                     return;
6419                 }
6420             }
6421         }
6422
6423         function render() {
6424             if (suggestions.length >= minItems && document.activeElement === input.node()) {
6425                 show();
6426             } else {
6427                 hide();
6428                 return;
6429             }
6430
6431             var options = container
6432                 .selectAll('a.combobox-option')
6433                 .data(suggestions, function(d) { return d.value; });
6434
6435             options.enter().append('a')
6436                 .attr('class', 'combobox-option')
6437                 .text(function(d) { return d.value; });
6438
6439             options
6440                 .attr('title', function(d) { return d.title; })
6441                 .classed('selected', function(d, i) { return i == idx; })
6442                 .on('mouseover', select)
6443                 .on('click', accept)
6444                 .order();
6445
6446             options.exit()
6447                 .remove();
6448
6449             var node = attachTo ? attachTo.node() : input.node(),
6450                 rect = node.getBoundingClientRect();
6451
6452             container.style({
6453                 'left': rect.left + 'px',
6454                 'width': rect.width + 'px',
6455                 'top': rect.height + rect.top + 'px'
6456             });
6457         }
6458
6459         function select(d, i) {
6460             idx = i;
6461             render();
6462         }
6463
6464         function ensureVisible() {
6465             var node = container.selectAll('a.selected').node();
6466             if (node) node.scrollIntoView();
6467         }
6468
6469         function accept(d) {
6470             if (!shown) return;
6471             input
6472                 .property('value', d.value)
6473                 .trigger('change');
6474             event.accept(d);
6475             hide();
6476         }
6477     };
6478
6479     combobox.fetcher = function(_) {
6480         if (!arguments.length) return fetcher;
6481         fetcher = _;
6482         return combobox;
6483     };
6484
6485     combobox.data = function(_) {
6486         if (!arguments.length) return data;
6487         data = _;
6488         return combobox;
6489     };
6490
6491     combobox.minItems = function(_) {
6492         if (!arguments.length) return minItems;
6493         minItems = _;
6494         return combobox;
6495     };
6496
6497     combobox.caseSensitive = function(_) {
6498         if (!arguments.length) return caseSensitive;
6499         caseSensitive = _;
6500         return combobox;
6501     };
6502
6503     return d3.rebind(combobox, event, 'on');
6504 };
6505
6506 d3.combobox.off = function(input) {
6507     data = null;
6508     fetcher = null;
6509
6510     input
6511         .on('focus.typeahead', null)
6512         .on('blur.typeahead', null)
6513         .on('keydown.typeahead', null)
6514         .on('keyup.typeahead', null)
6515         .on('input.typeahead', null)
6516         .each(function() {
6517             d3.select(this.parentNode).selectAll('.combobox-caret')
6518                 .filter(function(d) { return d === input.node(); })
6519                 .on('mousedown', null);
6520         });
6521
6522     d3.select(document.body)
6523         .on('scroll.combobox', null);
6524 };
6525 d3.geo.tile = function() {
6526   var size = [960, 500],
6527       scale = 256,
6528       scaleExtent = [0, 20],
6529       translate = [size[0] / 2, size[1] / 2],
6530       zoomDelta = 0;
6531
6532   function bound(_) {
6533       return Math.min(scaleExtent[1], Math.max(scaleExtent[0], _));
6534   }
6535
6536   function tile() {
6537     var z = Math.max(Math.log(scale) / Math.LN2 - 8, 0),
6538         z0 = bound(Math.round(z + zoomDelta)),
6539         k = Math.pow(2, z - z0 + 8),
6540         origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k],
6541         tiles = [],
6542         cols = d3.range(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))),
6543         rows = d3.range(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1])));
6544
6545     rows.forEach(function(y) {
6546       cols.forEach(function(x) {
6547         tiles.push([x, y, z0]);
6548       });
6549     });
6550
6551     tiles.translate = origin;
6552     tiles.scale = k;
6553
6554     return tiles;
6555   }
6556
6557   tile.scaleExtent = function(_) {
6558     if (!arguments.length) return scaleExtent;
6559     scaleExtent = _;
6560     return tile;
6561   };
6562
6563   tile.size = function(_) {
6564     if (!arguments.length) return size;
6565     size = _;
6566     return tile;
6567   };
6568
6569   tile.scale = function(_) {
6570     if (!arguments.length) return scale;
6571     scale = _;
6572     return tile;
6573   };
6574
6575   tile.translate = function(_) {
6576     if (!arguments.length) return translate;
6577     translate = _;
6578     return tile;
6579   };
6580
6581   tile.zoomDelta = function(_) {
6582     if (!arguments.length) return zoomDelta;
6583     zoomDelta = +_;
6584     return tile;
6585   };
6586
6587   return tile;
6588 };
6589 d3.jsonp = function (url, callback) {
6590   function rand() {
6591     var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
6592       c = '', i = -1;
6593     while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
6594     return c;
6595   }
6596
6597   function create(url) {
6598     var e = url.match(/callback=d3.jsonp.(\w+)/),
6599       c = e ? e[1] : rand();
6600     d3.jsonp[c] = function(data) {
6601       callback(data);
6602       delete d3.jsonp[c];
6603       script.remove();
6604     };
6605     return 'd3.jsonp.' + c;
6606   }
6607
6608   var cb = create(url),
6609     script = d3.select('head')
6610     .append('script')
6611     .attr('type', 'text/javascript')
6612     .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
6613 };
6614 /*
6615  * This code is licensed under the MIT license.
6616  *
6617  * Copyright © 2013, iD authors.
6618  *
6619  * Portions copyright © 2011, Keith Cirkel
6620  * See https://github.com/keithamus/jwerty
6621  *
6622  */
6623 d3.keybinding = function(namespace) {
6624     var bindings = [];
6625
6626     function matches(binding, event) {
6627         for (var p in binding.event) {
6628             if (event[p] != binding.event[p])
6629                 return false;
6630         }
6631         return true;
6632     }
6633
6634     function testBindings(isCapturing) {
6635         for (var i = 0; i < bindings.length; i++) {
6636             var binding = bindings[i];
6637
6638             if (!!binding.capture === isCapturing && matches(binding, d3.event)) {
6639                 binding.callback();
6640             }
6641         }
6642     }
6643
6644     function capture() {
6645         testBindings(true);
6646     }
6647
6648     function bubble() {
6649         var tagName = d3.select(d3.event.target).node().tagName;
6650         if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
6651             return;
6652         }
6653         testBindings(false);
6654     }
6655
6656     function keybinding(selection) {
6657         selection = selection || d3.select(document);
6658         selection.on('keydown.capture' + namespace, capture, true);
6659         selection.on('keydown.bubble' + namespace, bubble, false);
6660         return keybinding;
6661     }
6662
6663     keybinding.off = function(selection) {
6664         bindings = [];
6665         selection = selection || d3.select(document);
6666         selection.on('keydown.capture' + namespace, null);
6667         selection.on('keydown.bubble' + namespace, null);
6668         return keybinding;
6669     };
6670
6671     keybinding.on = function(code, callback, capture) {
6672         var binding = {
6673             event: {
6674                 keyCode: 0,
6675                 shiftKey: false,
6676                 ctrlKey: false,
6677                 altKey: false,
6678                 metaKey: false
6679             },
6680             capture: capture,
6681             callback: callback
6682         };
6683
6684         code = code.toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
6685
6686         for (var i = 0; i < code.length; i++) {
6687             // Normalise matching errors
6688             if (code[i] === '++') code[i] = '+';
6689
6690             if (code[i] in d3.keybinding.modifierCodes) {
6691                 binding.event[d3.keybinding.modifierProperties[d3.keybinding.modifierCodes[code[i]]]] = true;
6692             } else if (code[i] in d3.keybinding.keyCodes) {
6693                 binding.event.keyCode = d3.keybinding.keyCodes[code[i]];
6694             }
6695         }
6696
6697         bindings.push(binding);
6698
6699         return keybinding;
6700     };
6701
6702     return keybinding;
6703 };
6704
6705 (function () {
6706     d3.keybinding.modifierCodes = {
6707         // Shift key, ⇧
6708         '⇧': 16, shift: 16,
6709         // CTRL key, on Mac: ⌃
6710         '⌃': 17, ctrl: 17,
6711         // ALT key, on Mac: ⌥ (Alt)
6712         '⌥': 18, alt: 18, option: 18,
6713         // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
6714         '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
6715     };
6716
6717     d3.keybinding.modifierProperties = {
6718         16: 'shiftKey',
6719         17: 'ctrlKey',
6720         18: 'altKey',
6721         91: 'metaKey'
6722     };
6723
6724     d3.keybinding.keyCodes = {
6725         // Backspace key, on Mac: ⌫ (Backspace)
6726         '⌫': 8, backspace: 8,
6727         // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
6728         '⇥': 9, '⇆': 9, tab: 9,
6729         // Return key, ↩
6730         '↩': 13, 'return': 13, enter: 13, '⌅': 13,
6731         // Pause/Break key
6732         'pause': 19, 'pause-break': 19,
6733         // Caps Lock key, ⇪
6734         '⇪': 20, caps: 20, 'caps-lock': 20,
6735         // Escape key, on Mac: ⎋, on Windows: Esc
6736         '⎋': 27, escape: 27, esc: 27,
6737         // Space key
6738         space: 32,
6739         // Page-Up key, or pgup, on Mac: ↖
6740         '↖': 33, pgup: 33, 'page-up': 33,
6741         // Page-Down key, or pgdown, on Mac: ↘
6742         '↘': 34, pgdown: 34, 'page-down': 34,
6743         // END key, on Mac: ⇟
6744         '⇟': 35, end: 35,
6745         // HOME key, on Mac: ⇞
6746         '⇞': 36, home: 36,
6747         // Insert key, or ins
6748         ins: 45, insert: 45,
6749         // Delete key, on Mac: ⌦ (Delete)
6750         '⌦': 46, del: 46, 'delete': 46,
6751         // Left Arrow Key, or ←
6752         '←': 37, left: 37, 'arrow-left': 37,
6753         // Up Arrow Key, or ↑
6754         '↑': 38, up: 38, 'arrow-up': 38,
6755         // Right Arrow Key, or →
6756         '→': 39, right: 39, 'arrow-right': 39,
6757         // Up Arrow Key, or ↓
6758         '↓': 40, down: 40, 'arrow-down': 40,
6759         // odities, printing characters that come out wrong:
6760         // Firefox Equals
6761         'ffequals': 61,
6762         // Num-Multiply, or *
6763         '*': 106, star: 106, asterisk: 106, multiply: 106,
6764         // Num-Plus or +
6765         '+': 107, 'plus': 107,
6766         // Num-Subtract, or -
6767         '-': 109, subtract: 109,
6768         // Firefox Plus
6769         'ffplus': 171,
6770         // Firefox Minus
6771         'ffminus': 173,
6772         // Semicolon
6773         ';': 186, semicolon: 186,
6774         // = or equals
6775         '=': 187, 'equals': 187,
6776         // Comma, or ,
6777         ',': 188, comma: 188,
6778         'dash': 189, //???
6779         // Period, or ., or full-stop
6780         '.': 190, period: 190, 'full-stop': 190,
6781         // Slash, or /, or forward-slash
6782         '/': 191, slash: 191, 'forward-slash': 191,
6783         // Tick, or `, or back-quote
6784         '`': 192, tick: 192, 'back-quote': 192,
6785         // Open bracket, or [
6786         '[': 219, 'open-bracket': 219,
6787         // Back slash, or \
6788         '\\': 220, 'back-slash': 220,
6789         // Close backet, or ]
6790         ']': 221, 'close-bracket': 221,
6791         // Apostrophe, or Quote, or '
6792         '\'': 222, quote: 222, apostrophe: 222
6793     };
6794
6795     // NUMPAD 0-9
6796     var i = 95, n = 0;
6797     while (++i < 106) {
6798         d3.keybinding.keyCodes['num-' + n] = i;
6799         ++n;
6800     }
6801
6802     // 0-9
6803     i = 47; n = 0;
6804     while (++i < 58) {
6805         d3.keybinding.keyCodes[n] = i;
6806         ++n;
6807     }
6808
6809     // F1-F25
6810     i = 111; n = 1;
6811     while (++i < 136) {
6812         d3.keybinding.keyCodes['f' + n] = i;
6813         ++n;
6814     }
6815
6816     // a-z
6817     i = 64;
6818     while (++i < 91) {
6819         d3.keybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
6820     }
6821 })();
6822 d3.selection.prototype.one = function (type, listener, capture) {
6823     var target = this, typeOnce = type + ".once";
6824     function one() {
6825         target.on(typeOnce, null);
6826         listener.apply(this, arguments);
6827     }
6828     target.on(typeOnce, one, capture);
6829     return this;
6830 };
6831 d3.selection.prototype.dimensions = function (dimensions) {
6832     var refresh = (function(node) {
6833         var cr = node.getBoundingClientRect();
6834         prop = [cr.width, cr.height];
6835         this.property('__dimensions__', prop);
6836         return prop;
6837     }).bind(this);
6838
6839     var node = this.node();
6840
6841     if (!arguments.length) {
6842         if (!node) return [0,0];
6843         return this.property('__dimensions__') || refresh(node);
6844     }
6845     if (dimensions === null) {
6846         if (!node) return [0,0];
6847         return refresh(node);
6848     }
6849
6850     return this
6851         .property('__dimensions__', [dimensions[0], dimensions[1]])
6852         .attr({width: dimensions[0], height: dimensions[1]});
6853 };
6854 d3.selection.prototype.trigger = function (type) {
6855     this.each(function() {
6856         var evt = document.createEvent('HTMLEvents');
6857         evt.initEvent(type, true, true);
6858         this.dispatchEvent(evt);
6859     });
6860 };
6861 // Tooltips and svg mask used to highlight certain features
6862 d3.curtain = function() {
6863
6864     var event = d3.dispatch(),
6865         surface,
6866         tooltip,
6867         darkness;
6868
6869     function curtain(selection) {
6870
6871         surface = selection.append('svg')
6872             .attr('id', 'curtain')
6873             .style({
6874                 'z-index': 1000,
6875                 'pointer-events': 'none',
6876                 'position': 'absolute',
6877                 'top': 0,
6878                 'left': 0
6879             });
6880
6881         darkness = surface.append('path')
6882             .attr({
6883                 x: 0,
6884                 y: 0,
6885                 'class': 'curtain-darkness'
6886             });
6887
6888         d3.select(window).on('resize.curtain', resize);
6889
6890         tooltip = selection.append('div')
6891             .attr('class', 'tooltip')
6892             .style('z-index', 1002);
6893
6894         tooltip.append('div').attr('class', 'tooltip-arrow');
6895         tooltip.append('div').attr('class', 'tooltip-inner');
6896
6897         resize();
6898
6899         function resize() {
6900             surface.attr({
6901                 width: window.innerWidth,
6902                 height: window.innerHeight
6903             });
6904             curtain.cut(darkness.datum());
6905         }
6906     }
6907
6908     curtain.reveal = function(box, text, tooltipclass, duration) {
6909         if (typeof box === 'string') box = d3.select(box).node();
6910         if (box.getBoundingClientRect) box = box.getBoundingClientRect();
6911
6912         curtain.cut(box, duration);
6913
6914         if (text) {
6915             // pseudo markdown bold text hack
6916             var parts = text.split('**');
6917             var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
6918             if (parts[1]) html += '<span class="bold">' + parts[1] + '</span>';
6919
6920             var dimensions = tooltip.classed('in', true)
6921                 .select('.tooltip-inner')
6922                     .html(html)
6923                     .dimensions();
6924
6925             var pos;
6926
6927             var w = window.innerWidth,
6928                 h = window.innerHeight;
6929
6930             if (box.top + box.height < Math.min(100, box.width + box.left)) {
6931                 side = 'bottom';
6932                 pos = [box.left + box.width / 2 - dimensions[0]/ 2, box.top + box.height];
6933
6934             } else if (box.left + box.width + 300 < window.innerWidth) {
6935                 side = 'right';
6936                 pos = [box.left + box.width, box.top + box.height / 2 - dimensions[1] / 2];
6937
6938             } else if (box.left > 300) {
6939                 side = 'left';
6940                 pos = [box.left - 200, box.top + box.height / 2 - dimensions[1] / 2];
6941             } else {
6942                 side = 'bottom';
6943                 pos = [box.left, box.top + box.height];
6944             }
6945
6946             pos = [
6947                 Math.min(Math.max(10, pos[0]), w - dimensions[0] - 10),
6948                 Math.min(Math.max(10, pos[1]), h - dimensions[1] - 10)
6949             ];
6950
6951
6952             if (duration !== 0 || !tooltip.classed(side)) tooltip.call(iD.ui.Toggle(true));
6953
6954             tooltip
6955                 .style('top', pos[1] + 'px')
6956                 .style('left', pos[0] + 'px')
6957                 .attr('class', 'curtain-tooltip tooltip in ' + side + ' ' + tooltipclass)
6958                 .select('.tooltip-inner')
6959                     .html(html);
6960
6961         } else {
6962             tooltip.call(iD.ui.Toggle(false));
6963         }
6964     };
6965
6966     curtain.cut = function(datum, duration) {
6967         darkness.datum(datum);
6968
6969         (duration === 0 ? darkness : darkness.transition().duration(duration || 600))
6970             .attr('d', function(d) {
6971                 var string = "M 0,0 L 0," + window.innerHeight + " L " +
6972                     window.innerWidth + "," + window.innerHeight + "L" +
6973                     window.innerWidth + ",0 Z";
6974
6975                 if (!d) return string;
6976                 return string + 'M' +
6977                     d.left + ',' + d.top + 'L' +
6978                     d.left + ',' + (d.top + d.height) + 'L' +
6979                     (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
6980                     (d.left + d.width) + ',' + (d.top) + 'Z';
6981
6982             });
6983     };
6984
6985     curtain.remove = function() {
6986         surface.remove();
6987         tooltip.remove();
6988     };
6989
6990     return d3.rebind(curtain, event, 'on');
6991 };
6992 // Like selection.property('value', ...), but avoids no-op value sets,
6993 // which can result in layout/repaint thrashing in some situations.
6994 d3.selection.prototype.value = function(value) {
6995     function d3_selection_value(value) {
6996       function valueNull() {
6997         delete this.value;
6998       }
6999
7000       function valueConstant() {
7001         if (this.value !== value) this.value = value;
7002       }
7003
7004       function valueFunction() {
7005         var x = value.apply(this, arguments);
7006         if (x == null) delete this.value;
7007         else if (this.value !== x) this.value = x;
7008       }
7009
7010       return value == null
7011           ? valueNull : (typeof value === "function"
7012           ? valueFunction : valueConstant);
7013     }
7014
7015     if (!arguments.length) return this.property('value');
7016     return this.each(d3_selection_value(value));
7017 };
7018 // Copyright (c) 2006, 2008 Tony Garnock-Jones <tonyg@lshift.net>
7019 // Copyright (c) 2006, 2008 LShift Ltd. <query@lshift.net>
7020 //
7021 // Permission is hereby granted, free of charge, to any person
7022 // obtaining a copy of this software and associated documentation files
7023 // (the "Software"), to deal in the Software without restriction,
7024 // including without limitation the rights to use, copy, modify, merge,
7025 // publish, distribute, sublicense, and/or sell copies of the Software,
7026 // and to permit persons to whom the Software is furnished to do so,
7027 // subject to the following conditions:
7028 //
7029 // The above copyright notice and this permission notice shall be
7030 // included in all copies or substantial portions of the Software.
7031 //
7032 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
7033 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
7034 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
7035 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
7036 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
7037 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7038 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7039 // SOFTWARE.
7040
7041 // source:  https://bitbucket.org/lshift/synchrotron/src
7042
7043 Diff3 = (function() {
7044     'use strict';
7045
7046     var diff3 = {
7047         longest_common_subsequence: function(file1, file2) {
7048             /* Text diff algorithm following Hunt and McIlroy 1976.
7049              * J. W. Hunt and M. D. McIlroy, An algorithm for differential file
7050              * comparison, Bell Telephone Laboratories CSTR #41 (1976)
7051              * http://www.cs.dartmouth.edu/~doug/
7052              *
7053              * Expects two arrays of strings.
7054              */
7055             var equivalenceClasses;
7056             var file2indices;
7057             var newCandidate;
7058             var candidates;
7059             var line;
7060             var c, i, j, jX, r, s;
7061
7062             equivalenceClasses = {};
7063             for (j = 0; j < file2.length; j++) {
7064                 line = file2[j];
7065                 if (equivalenceClasses[line]) {
7066                     equivalenceClasses[line].push(j);
7067                 } else {
7068                     equivalenceClasses[line] = [j];
7069                 }
7070             }
7071
7072             candidates = [{file1index: -1,
7073                            file2index: -1,
7074                            chain: null}];
7075
7076             for (i = 0; i < file1.length; i++) {
7077                 line = file1[i];
7078                 file2indices = equivalenceClasses[line] || [];
7079
7080                 r = 0;
7081                 c = candidates[0];
7082
7083                 for (jX = 0; jX < file2indices.length; jX++) {
7084                     j = file2indices[jX];
7085
7086                     for (s = 0; s < candidates.length; s++) {
7087                         if ((candidates[s].file2index < j) &&
7088                             ((s == candidates.length - 1) ||
7089                              (candidates[s + 1].file2index > j)))
7090                             break;
7091                     }
7092
7093                     if (s < candidates.length) {
7094                         newCandidate = {file1index: i,
7095                                         file2index: j,
7096                                         chain: candidates[s]};
7097                         if (r == candidates.length) {
7098                             candidates.push(c);
7099                         } else {
7100                             candidates[r] = c;
7101                         }
7102                         r = s + 1;
7103                         c = newCandidate;
7104                         if (r == candidates.length) {
7105                             break; // no point in examining further (j)s
7106                         }
7107                     }
7108                 }
7109
7110                 candidates[r] = c;
7111             }
7112
7113             // At this point, we know the LCS: it's in the reverse of the
7114             // linked-list through .chain of
7115             // candidates[candidates.length - 1].
7116
7117             return candidates[candidates.length - 1];
7118         },
7119
7120         diff_comm: function(file1, file2) {
7121             // We apply the LCS to build a "comm"-style picture of the
7122             // differences between file1 and file2.
7123
7124             var result = [];
7125             var tail1 = file1.length;
7126             var tail2 = file2.length;
7127             var common = {common: []};
7128
7129             function processCommon() {
7130                 if (common.common.length) {
7131                     common.common.reverse();
7132                     result.push(common);
7133                     common = {common: []};
7134                 }
7135             }
7136
7137             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7138                  candidate !== null;
7139                  candidate = candidate.chain)
7140             {
7141                 var different = {file1: [], file2: []};
7142
7143                 while (--tail1 > candidate.file1index) {
7144                     different.file1.push(file1[tail1]);
7145                 }
7146
7147                 while (--tail2 > candidate.file2index) {
7148                     different.file2.push(file2[tail2]);
7149                 }
7150
7151                 if (different.file1.length || different.file2.length) {
7152                     processCommon();
7153                     different.file1.reverse();
7154                     different.file2.reverse();
7155                     result.push(different);
7156                 }
7157
7158                 if (tail1 >= 0) {
7159                     common.common.push(file1[tail1]);
7160                 }
7161             }
7162
7163             processCommon();
7164
7165             result.reverse();
7166             return result;
7167         },
7168
7169         diff_patch: function(file1, file2) {
7170             // We apply the LCD to build a JSON representation of a
7171             // diff(1)-style patch.
7172
7173             var result = [];
7174             var tail1 = file1.length;
7175             var tail2 = file2.length;
7176
7177             function chunkDescription(file, offset, length) {
7178                 var chunk = [];
7179                 for (var i = 0; i < length; i++) {
7180                     chunk.push(file[offset + i]);
7181                 }
7182                 return {offset: offset,
7183                         length: length,
7184                         chunk: chunk};
7185             }
7186
7187             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7188                  candidate !== null;
7189                  candidate = candidate.chain)
7190             {
7191                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7192                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7193                 tail1 = candidate.file1index;
7194                 tail2 = candidate.file2index;
7195
7196                 if (mismatchLength1 || mismatchLength2) {
7197                     result.push({file1: chunkDescription(file1,
7198                                                          candidate.file1index + 1,
7199                                                          mismatchLength1),
7200                                  file2: chunkDescription(file2,
7201                                                          candidate.file2index + 1,
7202                                                          mismatchLength2)});
7203                 }
7204             }
7205
7206             result.reverse();
7207             return result;
7208         },
7209
7210         strip_patch: function(patch) {
7211         // Takes the output of Diff3.diff_patch(), and removes
7212         // information from it. It can still be used by patch(),
7213         // below, but can no longer be inverted.
7214         var newpatch = [];
7215         for (var i = 0; i < patch.length; i++) {
7216             var chunk = patch[i];
7217             newpatch.push({file1: {offset: chunk.file1.offset,
7218                        length: chunk.file1.length},
7219                    file2: {chunk: chunk.file2.chunk}});
7220         }
7221         return newpatch;
7222         },
7223
7224         invert_patch: function(patch) {
7225             // Takes the output of Diff3.diff_patch(), and inverts the
7226             // sense of it, so that it can be applied to file2 to give
7227             // file1 rather than the other way around.
7228
7229             for (var i = 0; i < patch.length; i++) {
7230                 var chunk = patch[i];
7231                 var tmp = chunk.file1;
7232                 chunk.file1 = chunk.file2;
7233                 chunk.file2 = tmp;
7234             }
7235         },
7236
7237         patch: function (file, patch) {
7238             // Applies a patch to a file.
7239             //
7240             // Given file1 and file2, Diff3.patch(file1,
7241             // Diff3.diff_patch(file1, file2)) should give file2.
7242
7243             var result = [];
7244             var commonOffset = 0;
7245
7246             function copyCommon(targetOffset) {
7247                 while (commonOffset < targetOffset) {
7248                     result.push(file[commonOffset]);
7249                     commonOffset++;
7250                 }
7251             }
7252
7253             for (var chunkIndex = 0; chunkIndex < patch.length; chunkIndex++) {
7254                 var chunk = patch[chunkIndex];
7255                 copyCommon(chunk.file1.offset);
7256                 for (var lineIndex = 0; lineIndex < chunk.file2.chunk.length; lineIndex++) {
7257                     result.push(chunk.file2.chunk[lineIndex]);
7258                 }
7259                 commonOffset += chunk.file1.length;
7260             }
7261
7262             copyCommon(file.length);
7263             return result;
7264         },
7265
7266         diff_indices: function(file1, file2) {
7267             // We apply the LCS to give a simple representation of the
7268             // offsets and lengths of mismatched chunks in the input
7269             // files. This is used by diff3_merge_indices below.
7270
7271             var result = [];
7272             var tail1 = file1.length;
7273             var tail2 = file2.length;
7274
7275             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7276                  candidate !== null;
7277                  candidate = candidate.chain)
7278             {
7279                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7280                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7281                 tail1 = candidate.file1index;
7282                 tail2 = candidate.file2index;
7283
7284                 if (mismatchLength1 || mismatchLength2) {
7285                     result.push({file1: [tail1 + 1, mismatchLength1],
7286                                  file2: [tail2 + 1, mismatchLength2]});
7287                 }
7288             }
7289
7290             result.reverse();
7291             return result;
7292         },
7293
7294         diff3_merge_indices: function (a, o, b) {
7295             // Given three files, A, O, and B, where both A and B are
7296             // independently derived from O, returns a fairly complicated
7297             // internal representation of merge decisions it's taken. The
7298             // interested reader may wish to consult
7299             //
7300             // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce. "A
7301             // Formal Investigation of Diff3." In Arvind and Prasad,
7302             // editors, Foundations of Software Technology and Theoretical
7303             // Computer Science (FSTTCS), December 2007.
7304             //
7305             // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
7306             var i;
7307
7308             var m1 = Diff3.diff_indices(o, a);
7309             var m2 = Diff3.diff_indices(o, b);
7310
7311             var hunks = [];
7312             function addHunk(h, side) {
7313                 hunks.push([h.file1[0], side, h.file1[1], h.file2[0], h.file2[1]]);
7314             }
7315             for (i = 0; i < m1.length; i++) { addHunk(m1[i], 0); }
7316             for (i = 0; i < m2.length; i++) { addHunk(m2[i], 2); }
7317             hunks.sort();
7318
7319             var result = [];
7320             var commonOffset = 0;
7321             function copyCommon(targetOffset) {
7322                 if (targetOffset > commonOffset) {
7323                     result.push([1, commonOffset, targetOffset - commonOffset]);
7324                     commonOffset = targetOffset;
7325                 }
7326             }
7327
7328             for (var hunkIndex = 0; hunkIndex < hunks.length; hunkIndex++) {
7329                 var firstHunkIndex = hunkIndex;
7330                 var hunk = hunks[hunkIndex];
7331                 var regionLhs = hunk[0];
7332                 var regionRhs = regionLhs + hunk[2];
7333                 while (hunkIndex < hunks.length - 1) {
7334                     var maybeOverlapping = hunks[hunkIndex + 1];
7335                     var maybeLhs = maybeOverlapping[0];
7336                     if (maybeLhs > regionRhs) break;
7337                     regionRhs = maybeLhs + maybeOverlapping[2];
7338                     hunkIndex++;
7339                 }
7340
7341                 copyCommon(regionLhs);
7342                 if (firstHunkIndex == hunkIndex) {
7343             // The "overlap" was only one hunk long, meaning that
7344             // there's no conflict here. Either a and o were the
7345             // same, or b and o were the same.
7346                     if (hunk[4] > 0) {
7347                         result.push([hunk[1], hunk[3], hunk[4]]);
7348                     }
7349                 } else {
7350             // A proper conflict. Determine the extents of the
7351             // regions involved from a, o and b. Effectively merge
7352             // all the hunks on the left into one giant hunk, and
7353             // do the same for the right; then, correct for skew
7354             // in the regions of o that each side changed, and
7355             // report appropriate spans for the three sides.
7356             var regions = {
7357                 0: [a.length, -1, o.length, -1],
7358                 2: [b.length, -1, o.length, -1]
7359             };
7360                     for (i = firstHunkIndex; i <= hunkIndex; i++) {
7361                 hunk = hunks[i];
7362                         var side = hunk[1];
7363                 var r = regions[side];
7364                 var oLhs = hunk[0];
7365                 var oRhs = oLhs + hunk[2];
7366                         var abLhs = hunk[3];
7367                         var abRhs = abLhs + hunk[4];
7368                 r[0] = Math.min(abLhs, r[0]);
7369                 r[1] = Math.max(abRhs, r[1]);
7370                 r[2] = Math.min(oLhs, r[2]);
7371                 r[3] = Math.max(oRhs, r[3]);
7372                     }
7373             var aLhs = regions[0][0] + (regionLhs - regions[0][2]);
7374             var aRhs = regions[0][1] + (regionRhs - regions[0][3]);
7375             var bLhs = regions[2][0] + (regionLhs - regions[2][2]);
7376             var bRhs = regions[2][1] + (regionRhs - regions[2][3]);
7377                     result.push([-1,
7378                      aLhs,      aRhs      - aLhs,
7379                      regionLhs, regionRhs - regionLhs,
7380                      bLhs,      bRhs      - bLhs]);
7381                 }
7382                 commonOffset = regionRhs;
7383             }
7384
7385             copyCommon(o.length);
7386             return result;
7387         },
7388
7389         diff3_merge: function (a, o, b, excludeFalseConflicts) {
7390             // Applies the output of Diff3.diff3_merge_indices to actually
7391             // construct the merged file; the returned result alternates
7392             // between "ok" and "conflict" blocks.
7393
7394             var result = [];
7395             var files = [a, o, b];
7396             var indices = Diff3.diff3_merge_indices(a, o, b);
7397
7398             var okLines = [];
7399             function flushOk() {
7400                 if (okLines.length) {
7401                     result.push({ok: okLines});
7402                 }
7403                 okLines = [];
7404             }
7405             function pushOk(xs) {
7406                 for (var j = 0; j < xs.length; j++) {
7407                     okLines.push(xs[j]);
7408                 }
7409             }
7410
7411             function isTrueConflict(rec) {
7412                 if (rec[2] != rec[6]) return true;
7413                 var aoff = rec[1];
7414                 var boff = rec[5];
7415                 for (var j = 0; j < rec[2]; j++) {
7416                     if (a[j + aoff] != b[j + boff]) return true;
7417                 }
7418                 return false;
7419             }
7420
7421             for (var i = 0; i < indices.length; i++) {
7422                 var x = indices[i];
7423                 var side = x[0];
7424                 if (side == -1) {
7425                     if (excludeFalseConflicts && !isTrueConflict(x)) {
7426                         pushOk(files[0].slice(x[1], x[1] + x[2]));
7427                     } else {
7428                         flushOk();
7429                         result.push({conflict: {a: a.slice(x[1], x[1] + x[2]),
7430                                                 aIndex: x[1],
7431                                                 o: o.slice(x[3], x[3] + x[4]),
7432                                                 oIndex: x[3],
7433                                                 b: b.slice(x[5], x[5] + x[6]),
7434                                                 bIndex: x[5]}});
7435                     }
7436                 } else {
7437                     pushOk(files[side].slice(x[1], x[1] + x[2]));
7438                 }
7439             }
7440
7441             flushOk();
7442             return result;
7443         }
7444     };
7445     return diff3;
7446 })();
7447
7448 if (typeof module !== 'undefined') module.exports = Diff3;
7449 var JXON = new (function () {
7450   var
7451     sValueProp = "keyValue", sAttributesProp = "keyAttributes", sAttrPref = "@", /* you can customize these values */
7452     aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
7453
7454   function parseText (sValue) {
7455     if (rIsNull.test(sValue)) { return null; }
7456     if (rIsBool.test(sValue)) { return sValue.toLowerCase() === "true"; }
7457     if (isFinite(sValue)) { return parseFloat(sValue); }
7458     if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
7459     return sValue;
7460   }
7461
7462   function EmptyTree () { }
7463   EmptyTree.prototype.toString = function () { return "null"; };
7464   EmptyTree.prototype.valueOf = function () { return null; };
7465
7466   function objectify (vValue) {
7467     return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
7468   }
7469
7470   function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
7471     var
7472       nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
7473       bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
7474
7475     var
7476       sProp, vContent, nLength = 0, sCollectedTxt = "",
7477       vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
7478
7479     if (bChildren) {
7480       for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
7481         oNode = oParentNode.childNodes.item(nItem);
7482         if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is "CDATASection" (4) */
7483         else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is "Text" (3) */
7484         else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is "Element" (1) */
7485       }
7486     }
7487
7488     var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
7489
7490     if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
7491
7492     for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
7493       sProp = aCache[nElId].nodeName.toLowerCase();
7494       vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
7495       if (vResult.hasOwnProperty(sProp)) {
7496         if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
7497         vResult[sProp].push(vContent);
7498       } else {
7499         vResult[sProp] = vContent;
7500         nLength++;
7501       }
7502     }
7503
7504     if (bAttributes) {
7505       var
7506         nAttrLen = oParentNode.attributes.length,
7507         sAPrefix = bNesteAttr ? "" : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
7508
7509       for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
7510         oAttrib = oParentNode.attributes.item(nAttrib);
7511         oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
7512       }
7513
7514       if (bNesteAttr) {
7515         if (bFreeze) { Object.freeze(oAttrParent); }
7516         vResult[sAttributesProp] = oAttrParent;
7517         nLength -= nAttrLen - 1;
7518       }
7519     }
7520
7521     if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
7522       vResult[sValueProp] = vBuiltVal;
7523     } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
7524       vResult = vBuiltVal;
7525     }
7526
7527     if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
7528
7529     aCache.length = nLevelStart;
7530
7531     return vResult;
7532   }
7533
7534   function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
7535     var vValue, oChild;
7536
7537     if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
7538       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
7539     } else if (oParentObj.constructor === Date) {
7540       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));    
7541     }
7542
7543     for (var sName in oParentObj) {
7544       vValue = oParentObj[sName];
7545       if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
7546       if (sName === sValueProp) {
7547         if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
7548       } else if (sName === sAttributesProp) { /* verbosity level is 3 */
7549         for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
7550       } else if (sName.charAt(0) === sAttrPref) {
7551         oParentEl.setAttribute(sName.slice(1), vValue);
7552       } else if (vValue.constructor === Array) {
7553         for (var nItem = 0; nItem < vValue.length; nItem++) {
7554           oChild = oXMLDoc.createElement(sName);
7555           loadObjTree(oXMLDoc, oChild, vValue[nItem]);
7556           oParentEl.appendChild(oChild);
7557         }
7558       } else {
7559         oChild = oXMLDoc.createElement(sName);
7560         if (vValue instanceof Object) {
7561           loadObjTree(oXMLDoc, oChild, vValue);
7562         } else if (vValue !== null && vValue !== true) {
7563           oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
7564         }
7565         oParentEl.appendChild(oChild);
7566      }
7567    }
7568   }
7569
7570   this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
7571     var _nVerb = arguments.length > 1 && typeof nVerbosity === "number" ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
7572     return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);    
7573   };
7574
7575   this.unbuild = function (oObjTree) {    
7576     var oNewDoc = document.implementation.createDocument("", "", null);
7577     loadObjTree(oNewDoc, oNewDoc, oObjTree);
7578     return oNewDoc;
7579   };
7580
7581   this.stringify = function (oObjTree) {
7582     return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
7583   };
7584 })();
7585 // var myObject = JXON.build(doc);
7586 // we got our javascript object! try: alert(JSON.stringify(myObject));
7587
7588 // var newDoc = JXON.unbuild(myObject);
7589 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
7590 /**
7591  * @license
7592  * lodash (Custom Build) <https://lodash.com/>
7593  * Build: `lodash --development --output js/lib/lodash.js include="includes,toPairs,assign,bind,chunk,clone,compact,debounce,difference,each,every,extend,filter,find,first,forEach,forOwn,groupBy,indexOf,intersection,isEmpty,isEqual,isFunction,keys,last,map,omit,reject,some,throttle,union,uniq,values,without,flatten,value,chain,cloneDeep,merge,pick,reduce" exports="global,node"`
7594  * Copyright jQuery Foundation and other contributors <https://jquery.org/>
7595  * Released under MIT license <https://lodash.com/license>
7596  * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
7597  * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
7598  */
7599 ;(function() {
7600
7601   /** Used as a safe reference for `undefined` in pre-ES5 environments. */
7602   var undefined;
7603
7604   /** Used as the semantic version number. */
7605   var VERSION = '4.12.0';
7606
7607   /** Used as the size to enable large array optimizations. */
7608   var LARGE_ARRAY_SIZE = 200;
7609
7610   /** Used as the `TypeError` message for "Functions" methods. */
7611   var FUNC_ERROR_TEXT = 'Expected a function';
7612
7613   /** Used to stand-in for `undefined` hash values. */
7614   var HASH_UNDEFINED = '__lodash_hash_undefined__';
7615
7616   /** Used as the internal argument placeholder. */
7617   var PLACEHOLDER = '__lodash_placeholder__';
7618
7619   /** Used to compose bitmasks for wrapper metadata. */
7620   var BIND_FLAG = 1,
7621       BIND_KEY_FLAG = 2,
7622       CURRY_BOUND_FLAG = 4,
7623       CURRY_FLAG = 8,
7624       CURRY_RIGHT_FLAG = 16,
7625       PARTIAL_FLAG = 32,
7626       PARTIAL_RIGHT_FLAG = 64,
7627       ARY_FLAG = 128,
7628       REARG_FLAG = 256,
7629       FLIP_FLAG = 512;
7630
7631   /** Used to compose bitmasks for comparison styles. */
7632   var UNORDERED_COMPARE_FLAG = 1,
7633       PARTIAL_COMPARE_FLAG = 2;
7634
7635   /** Used to detect hot functions by number of calls within a span of milliseconds. */
7636   var HOT_COUNT = 150,
7637       HOT_SPAN = 16;
7638
7639   /** Used to indicate the type of lazy iteratees. */
7640   var LAZY_FILTER_FLAG = 1,
7641       LAZY_MAP_FLAG = 2,
7642       LAZY_WHILE_FLAG = 3;
7643
7644   /** Used as references for various `Number` constants. */
7645   var INFINITY = 1 / 0,
7646       MAX_SAFE_INTEGER = 9007199254740991,
7647       MAX_INTEGER = 1.7976931348623157e+308,
7648       NAN = 0 / 0;
7649
7650   /** Used as references for the maximum length and index of an array. */
7651   var MAX_ARRAY_LENGTH = 4294967295;
7652
7653   /** `Object#toString` result references. */
7654   var argsTag = '[object Arguments]',
7655       arrayTag = '[object Array]',
7656       boolTag = '[object Boolean]',
7657       dateTag = '[object Date]',
7658       errorTag = '[object Error]',
7659       funcTag = '[object Function]',
7660       genTag = '[object GeneratorFunction]',
7661       mapTag = '[object Map]',
7662       numberTag = '[object Number]',
7663       objectTag = '[object Object]',
7664       promiseTag = '[object Promise]',
7665       regexpTag = '[object RegExp]',
7666       setTag = '[object Set]',
7667       stringTag = '[object String]',
7668       symbolTag = '[object Symbol]',
7669       weakMapTag = '[object WeakMap]';
7670
7671   var arrayBufferTag = '[object ArrayBuffer]',
7672       dataViewTag = '[object DataView]',
7673       float32Tag = '[object Float32Array]',
7674       float64Tag = '[object Float64Array]',
7675       int8Tag = '[object Int8Array]',
7676       int16Tag = '[object Int16Array]',
7677       int32Tag = '[object Int32Array]',
7678       uint8Tag = '[object Uint8Array]',
7679       uint8ClampedTag = '[object Uint8ClampedArray]',
7680       uint16Tag = '[object Uint16Array]',
7681       uint32Tag = '[object Uint32Array]';
7682
7683   /** Used to match property names within property paths. */
7684   var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
7685       reIsPlainProp = /^\w*$/,
7686       rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]/g;
7687
7688   /**
7689    * Used to match `RegExp`
7690    * [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns).
7691    */
7692   var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
7693
7694   /** Used to match leading and trailing whitespace. */
7695   var reTrim = /^\s+|\s+$/g;
7696
7697   /** Used to match backslashes in property paths. */
7698   var reEscapeChar = /\\(\\)?/g;
7699
7700   /** Used to match `RegExp` flags from their coerced string values. */
7701   var reFlags = /\w*$/;
7702
7703   /** Used to detect bad signed hexadecimal string values. */
7704   var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
7705
7706   /** Used to detect binary string values. */
7707   var reIsBinary = /^0b[01]+$/i;
7708
7709   /** Used to detect host constructors (Safari). */
7710   var reIsHostCtor = /^\[object .+?Constructor\]$/;
7711
7712   /** Used to detect octal string values. */
7713   var reIsOctal = /^0o[0-7]+$/i;
7714
7715   /** Used to detect unsigned integer values. */
7716   var reIsUint = /^(?:0|[1-9]\d*)$/;
7717
7718   /** Used to compose unicode character classes. */
7719   var rsAstralRange = '\\ud800-\\udfff',
7720       rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23',
7721       rsComboSymbolsRange = '\\u20d0-\\u20f0',
7722       rsVarRange = '\\ufe0e\\ufe0f';
7723
7724   /** Used to compose unicode capture groups. */
7725   var rsAstral = '[' + rsAstralRange + ']',
7726       rsCombo = '[' + rsComboMarksRange + rsComboSymbolsRange + ']',
7727       rsFitz = '\\ud83c[\\udffb-\\udfff]',
7728       rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
7729       rsNonAstral = '[^' + rsAstralRange + ']',
7730       rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
7731       rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
7732       rsZWJ = '\\u200d';
7733
7734   /** Used to compose unicode regexes. */
7735   var reOptMod = rsModifier + '?',
7736       rsOptVar = '[' + rsVarRange + ']?',
7737       rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
7738       rsSeq = rsOptVar + reOptMod + rsOptJoin,
7739       rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
7740
7741   /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
7742   var reComplexSymbol = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
7743
7744   /** Used to identify `toStringTag` values of typed arrays. */
7745   var typedArrayTags = {};
7746   typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
7747   typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
7748   typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
7749   typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
7750   typedArrayTags[uint32Tag] = true;
7751   typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
7752   typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
7753   typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
7754   typedArrayTags[errorTag] = typedArrayTags[funcTag] =
7755   typedArrayTags[mapTag] = typedArrayTags[numberTag] =
7756   typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
7757   typedArrayTags[setTag] = typedArrayTags[stringTag] =
7758   typedArrayTags[weakMapTag] = false;
7759
7760   /** Used to identify `toStringTag` values supported by `_.clone`. */
7761   var cloneableTags = {};
7762   cloneableTags[argsTag] = cloneableTags[arrayTag] =
7763   cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
7764   cloneableTags[boolTag] = cloneableTags[dateTag] =
7765   cloneableTags[float32Tag] = cloneableTags[float64Tag] =
7766   cloneableTags[int8Tag] = cloneableTags[int16Tag] =
7767   cloneableTags[int32Tag] = cloneableTags[mapTag] =
7768   cloneableTags[numberTag] = cloneableTags[objectTag] =
7769   cloneableTags[regexpTag] = cloneableTags[setTag] =
7770   cloneableTags[stringTag] = cloneableTags[symbolTag] =
7771   cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
7772   cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
7773   cloneableTags[errorTag] = cloneableTags[funcTag] =
7774   cloneableTags[weakMapTag] = false;
7775
7776   /** Used to determine if values are of the language type `Object`. */
7777   var objectTypes = {
7778     'function': true,
7779     'object': true
7780   };
7781
7782   /** Built-in method references without a dependency on `root`. */
7783   var freeParseInt = parseInt;
7784
7785   /** Detect free variable `exports`. */
7786   var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType)
7787     ? exports
7788     : undefined;
7789
7790   /** Detect free variable `module`. */
7791   var freeModule = (objectTypes[typeof module] && module && !module.nodeType)
7792     ? module
7793     : undefined;
7794
7795   /** Detect the popular CommonJS extension `module.exports`. */
7796   var moduleExports = (freeModule && freeModule.exports === freeExports)
7797     ? freeExports
7798     : undefined;
7799
7800   /** Detect free variable `global` from Node.js. */
7801   var freeGlobal = checkGlobal(freeExports && freeModule && typeof global == 'object' && global);
7802
7803   /** Detect free variable `self`. */
7804   var freeSelf = checkGlobal(objectTypes[typeof self] && self);
7805
7806   /** Detect free variable `window`. */
7807   var freeWindow = checkGlobal(objectTypes[typeof window] && window);
7808
7809   /** Detect `this` as the global object. */
7810   var thisGlobal = checkGlobal(objectTypes[typeof this] && this);
7811
7812   /**
7813    * Used as a reference to the global object.
7814    *
7815    * The `this` value is used if it's the global object to avoid Greasemonkey's
7816    * restricted `window` object, otherwise the `window` object is used.
7817    */
7818   var root = freeGlobal ||
7819     ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) ||
7820       freeSelf || thisGlobal || Function('return this')();
7821
7822   /*--------------------------------------------------------------------------*/
7823
7824   /**
7825    * Adds the key-value `pair` to `map`.
7826    *
7827    * @private
7828    * @param {Object} map The map to modify.
7829    * @param {Array} pair The key-value pair to add.
7830    * @returns {Object} Returns `map`.
7831    */
7832   function addMapEntry(map, pair) {
7833     // Don't return `Map#set` because it doesn't return the map instance in IE 11.
7834     map.set(pair[0], pair[1]);
7835     return map;
7836   }
7837
7838   /**
7839    * Adds `value` to `set`.
7840    *
7841    * @private
7842    * @param {Object} set The set to modify.
7843    * @param {*} value The value to add.
7844    * @returns {Object} Returns `set`.
7845    */
7846   function addSetEntry(set, value) {
7847     set.add(value);
7848     return set;
7849   }
7850
7851   /**
7852    * A faster alternative to `Function#apply`, this function invokes `func`
7853    * with the `this` binding of `thisArg` and the arguments of `args`.
7854    *
7855    * @private
7856    * @param {Function} func The function to invoke.
7857    * @param {*} thisArg The `this` binding of `func`.
7858    * @param {Array} args The arguments to invoke `func` with.
7859    * @returns {*} Returns the result of `func`.
7860    */
7861   function apply(func, thisArg, args) {
7862     var length = args.length;
7863     switch (length) {
7864       case 0: return func.call(thisArg);
7865       case 1: return func.call(thisArg, args[0]);
7866       case 2: return func.call(thisArg, args[0], args[1]);
7867       case 3: return func.call(thisArg, args[0], args[1], args[2]);
7868     }
7869     return func.apply(thisArg, args);
7870   }
7871
7872   /**
7873    * A specialized version of `baseAggregator` for arrays.
7874    *
7875    * @private
7876    * @param {Array} array The array to iterate over.
7877    * @param {Function} setter The function to set `accumulator` values.
7878    * @param {Function} iteratee The iteratee to transform keys.
7879    * @param {Object} accumulator The initial aggregated object.
7880    * @returns {Function} Returns `accumulator`.
7881    */
7882   function arrayAggregator(array, setter, iteratee, accumulator) {
7883     var index = -1,
7884         length = array.length;
7885
7886     while (++index < length) {
7887       var value = array[index];
7888       setter(accumulator, value, iteratee(value), array);
7889     }
7890     return accumulator;
7891   }
7892
7893   /**
7894    * A specialized version of `_.forEach` for arrays without support for
7895    * iteratee shorthands.
7896    *
7897    * @private
7898    * @param {Array} array The array to iterate over.
7899    * @param {Function} iteratee The function invoked per iteration.
7900    * @returns {Array} Returns `array`.
7901    */
7902   function arrayEach(array, iteratee) {
7903     var index = -1,
7904         length = array.length;
7905
7906     while (++index < length) {
7907       if (iteratee(array[index], index, array) === false) {
7908         break;
7909       }
7910     }
7911     return array;
7912   }
7913
7914   /**
7915    * A specialized version of `_.every` for arrays without support for
7916    * iteratee shorthands.
7917    *
7918    * @private
7919    * @param {Array} array The array to iterate over.
7920    * @param {Function} predicate The function invoked per iteration.
7921    * @returns {boolean} Returns `true` if all elements pass the predicate check,
7922    *  else `false`.
7923    */
7924   function arrayEvery(array, predicate) {
7925     var index = -1,
7926         length = array.length;
7927
7928     while (++index < length) {
7929       if (!predicate(array[index], index, array)) {
7930         return false;
7931       }
7932     }
7933     return true;
7934   }
7935
7936   /**
7937    * A specialized version of `_.filter` for arrays without support for
7938    * iteratee shorthands.
7939    *
7940    * @private
7941    * @param {Array} array The array to iterate over.
7942    * @param {Function} predicate The function invoked per iteration.
7943    * @returns {Array} Returns the new filtered array.
7944    */
7945   function arrayFilter(array, predicate) {
7946     var index = -1,
7947         length = array.length,
7948         resIndex = 0,
7949         result = [];
7950
7951     while (++index < length) {
7952       var value = array[index];
7953       if (predicate(value, index, array)) {
7954         result[resIndex++] = value;
7955       }
7956     }
7957     return result;
7958   }
7959
7960   /**
7961    * A specialized version of `_.includes` for arrays without support for
7962    * specifying an index to search from.
7963    *
7964    * @private
7965    * @param {Array} array The array to search.
7966    * @param {*} target The value to search for.
7967    * @returns {boolean} Returns `true` if `target` is found, else `false`.
7968    */
7969   function arrayIncludes(array, value) {
7970     return !!array.length && baseIndexOf(array, value, 0) > -1;
7971   }
7972
7973   /**
7974    * This function is like `arrayIncludes` except that it accepts a comparator.
7975    *
7976    * @private
7977    * @param {Array} array The array to search.
7978    * @param {*} target The value to search for.
7979    * @param {Function} comparator The comparator invoked per element.
7980    * @returns {boolean} Returns `true` if `target` is found, else `false`.
7981    */
7982   function arrayIncludesWith(array, value, comparator) {
7983     var index = -1,
7984         length = array.length;
7985
7986     while (++index < length) {
7987       if (comparator(value, array[index])) {
7988         return true;
7989       }
7990     }
7991     return false;
7992   }
7993
7994   /**
7995    * A specialized version of `_.map` for arrays without support for iteratee
7996    * shorthands.
7997    *
7998    * @private
7999    * @param {Array} array The array to iterate over.
8000    * @param {Function} iteratee The function invoked per iteration.
8001    * @returns {Array} Returns the new mapped array.
8002    */
8003   function arrayMap(array, iteratee) {
8004     var index = -1,
8005         length = array.length,
8006         result = Array(length);
8007
8008     while (++index < length) {
8009       result[index] = iteratee(array[index], index, array);
8010     }
8011     return result;
8012   }
8013
8014   /**
8015    * Appends the elements of `values` to `array`.
8016    *
8017    * @private
8018    * @param {Array} array The array to modify.
8019    * @param {Array} values The values to append.
8020    * @returns {Array} Returns `array`.
8021    */
8022   function arrayPush(array, values) {
8023     var index = -1,
8024         length = values.length,
8025         offset = array.length;
8026
8027     while (++index < length) {
8028       array[offset + index] = values[index];
8029     }
8030     return array;
8031   }
8032
8033   /**
8034    * A specialized version of `_.reduce` for arrays without support for
8035    * iteratee shorthands.
8036    *
8037    * @private
8038    * @param {Array} array The array to iterate over.
8039    * @param {Function} iteratee The function invoked per iteration.
8040    * @param {*} [accumulator] The initial value.
8041    * @param {boolean} [initAccum] Specify using the first element of `array` as
8042    *  the initial value.
8043    * @returns {*} Returns the accumulated value.
8044    */
8045   function arrayReduce(array, iteratee, accumulator, initAccum) {
8046     var index = -1,
8047         length = array.length;
8048
8049     if (initAccum && length) {
8050       accumulator = array[++index];
8051     }
8052     while (++index < length) {
8053       accumulator = iteratee(accumulator, array[index], index, array);
8054     }
8055     return accumulator;
8056   }
8057
8058   /**
8059    * A specialized version of `_.some` for arrays without support for iteratee
8060    * shorthands.
8061    *
8062    * @private
8063    * @param {Array} array The array to iterate over.
8064    * @param {Function} predicate The function invoked per iteration.
8065    * @returns {boolean} Returns `true` if any element passes the predicate check,
8066    *  else `false`.
8067    */
8068   function arraySome(array, predicate) {
8069     var index = -1,
8070         length = array.length;
8071
8072     while (++index < length) {
8073       if (predicate(array[index], index, array)) {
8074         return true;
8075       }
8076     }
8077     return false;
8078   }
8079
8080   /**
8081    * The base implementation of methods like `_.find` and `_.findKey`, without
8082    * support for iteratee shorthands, which iterates over `collection` using
8083    * `eachFunc`.
8084    *
8085    * @private
8086    * @param {Array|Object} collection The collection to search.
8087    * @param {Function} predicate The function invoked per iteration.
8088    * @param {Function} eachFunc The function to iterate over `collection`.
8089    * @param {boolean} [retKey] Specify returning the key of the found element
8090    *  instead of the element itself.
8091    * @returns {*} Returns the found element or its key, else `undefined`.
8092    */
8093   function baseFind(collection, predicate, eachFunc, retKey) {
8094     var result;
8095     eachFunc(collection, function(value, key, collection) {
8096       if (predicate(value, key, collection)) {
8097         result = retKey ? key : value;
8098         return false;
8099       }
8100     });
8101     return result;
8102   }
8103
8104   /**
8105    * The base implementation of `_.findIndex` and `_.findLastIndex` without
8106    * support for iteratee shorthands.
8107    *
8108    * @private
8109    * @param {Array} array The array to search.
8110    * @param {Function} predicate The function invoked per iteration.
8111    * @param {boolean} [fromRight] Specify iterating from right to left.
8112    * @returns {number} Returns the index of the matched value, else `-1`.
8113    */
8114   function baseFindIndex(array, predicate, fromRight) {
8115     var length = array.length,
8116         index = fromRight ? length : -1;
8117
8118     while ((fromRight ? index-- : ++index < length)) {
8119       if (predicate(array[index], index, array)) {
8120         return index;
8121       }
8122     }
8123     return -1;
8124   }
8125
8126   /**
8127    * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
8128    *
8129    * @private
8130    * @param {Array} array The array to search.
8131    * @param {*} value The value to search for.
8132    * @param {number} fromIndex The index to search from.
8133    * @returns {number} Returns the index of the matched value, else `-1`.
8134    */
8135   function baseIndexOf(array, value, fromIndex) {
8136     if (value !== value) {
8137       return indexOfNaN(array, fromIndex);
8138     }
8139     var index = fromIndex - 1,
8140         length = array.length;
8141
8142     while (++index < length) {
8143       if (array[index] === value) {
8144         return index;
8145       }
8146     }
8147     return -1;
8148   }
8149
8150   /**
8151    * The base implementation of `_.reduce` and `_.reduceRight`, without support
8152    * for iteratee shorthands, which iterates over `collection` using `eachFunc`.
8153    *
8154    * @private
8155    * @param {Array|Object} collection The collection to iterate over.
8156    * @param {Function} iteratee The function invoked per iteration.
8157    * @param {*} accumulator The initial value.
8158    * @param {boolean} initAccum Specify using the first or last element of
8159    *  `collection` as the initial value.
8160    * @param {Function} eachFunc The function to iterate over `collection`.
8161    * @returns {*} Returns the accumulated value.
8162    */
8163   function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {
8164     eachFunc(collection, function(value, index, collection) {
8165       accumulator = initAccum
8166         ? (initAccum = false, value)
8167         : iteratee(accumulator, value, index, collection);
8168     });
8169     return accumulator;
8170   }
8171
8172   /**
8173    * The base implementation of `_.times` without support for iteratee shorthands
8174    * or max array length checks.
8175    *
8176    * @private
8177    * @param {number} n The number of times to invoke `iteratee`.
8178    * @param {Function} iteratee The function invoked per iteration.
8179    * @returns {Array} Returns the array of results.
8180    */
8181   function baseTimes(n, iteratee) {
8182     var index = -1,
8183         result = Array(n);
8184
8185     while (++index < n) {
8186       result[index] = iteratee(index);
8187     }
8188     return result;
8189   }
8190
8191   /**
8192    * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array
8193    * of key-value pairs for `object` corresponding to the property names of `props`.
8194    *
8195    * @private
8196    * @param {Object} object The object to query.
8197    * @param {Array} props The property names to get values for.
8198    * @returns {Object} Returns the key-value pairs.
8199    */
8200   function baseToPairs(object, props) {
8201     return arrayMap(props, function(key) {
8202       return [key, object[key]];
8203     });
8204   }
8205
8206   /**
8207    * The base implementation of `_.unary` without support for storing wrapper metadata.
8208    *
8209    * @private
8210    * @param {Function} func The function to cap arguments for.
8211    * @returns {Function} Returns the new capped function.
8212    */
8213   function baseUnary(func) {
8214     return function(value) {
8215       return func(value);
8216     };
8217   }
8218
8219   /**
8220    * The base implementation of `_.values` and `_.valuesIn` which creates an
8221    * array of `object` property values corresponding to the property names
8222    * of `props`.
8223    *
8224    * @private
8225    * @param {Object} object The object to query.
8226    * @param {Array} props The property names to get values for.
8227    * @returns {Object} Returns the array of property values.
8228    */
8229   function baseValues(object, props) {
8230     return arrayMap(props, function(key) {
8231       return object[key];
8232     });
8233   }
8234
8235   /**
8236    * Checks if a cache value for `key` exists.
8237    *
8238    * @private
8239    * @param {Object} cache The cache to query.
8240    * @param {string} key The key of the entry to check.
8241    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
8242    */
8243   function cacheHas(cache, key) {
8244     return cache.has(key);
8245   }
8246
8247   /**
8248    * Checks if `value` is a global object.
8249    *
8250    * @private
8251    * @param {*} value The value to check.
8252    * @returns {null|Object} Returns `value` if it's a global object, else `null`.
8253    */
8254   function checkGlobal(value) {
8255     return (value && value.Object === Object) ? value : null;
8256   }
8257
8258   /**
8259    * Gets the number of `placeholder` occurrences in `array`.
8260    *
8261    * @private
8262    * @param {Array} array The array to inspect.
8263    * @param {*} placeholder The placeholder to search for.
8264    * @returns {number} Returns the placeholder count.
8265    */
8266   function countHolders(array, placeholder) {
8267     var length = array.length,
8268         result = 0;
8269
8270     while (length--) {
8271       if (array[length] === placeholder) {
8272         result++;
8273       }
8274     }
8275     return result;
8276   }
8277
8278   /**
8279    * Gets the index at which the first occurrence of `NaN` is found in `array`.
8280    *
8281    * @private
8282    * @param {Array} array The array to search.
8283    * @param {number} fromIndex The index to search from.
8284    * @param {boolean} [fromRight] Specify iterating from right to left.
8285    * @returns {number} Returns the index of the matched `NaN`, else `-1`.
8286    */
8287   function indexOfNaN(array, fromIndex, fromRight) {
8288     var length = array.length,
8289         index = fromIndex + (fromRight ? 0 : -1);
8290
8291     while ((fromRight ? index-- : ++index < length)) {
8292       var other = array[index];
8293       if (other !== other) {
8294         return index;
8295       }
8296     }
8297     return -1;
8298   }
8299
8300   /**
8301    * Checks if `value` is a host object in IE < 9.
8302    *
8303    * @private
8304    * @param {*} value The value to check.
8305    * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
8306    */
8307   function isHostObject(value) {
8308     // Many host objects are `Object` objects that can coerce to strings
8309     // despite having improperly defined `toString` methods.
8310     var result = false;
8311     if (value != null && typeof value.toString != 'function') {
8312       try {
8313         result = !!(value + '');
8314       } catch (e) {}
8315     }
8316     return result;
8317   }
8318
8319   /**
8320    * Converts `iterator` to an array.
8321    *
8322    * @private
8323    * @param {Object} iterator The iterator to convert.
8324    * @returns {Array} Returns the converted array.
8325    */
8326   function iteratorToArray(iterator) {
8327     var data,
8328         result = [];
8329
8330     while (!(data = iterator.next()).done) {
8331       result.push(data.value);
8332     }
8333     return result;
8334   }
8335
8336   /**
8337    * Converts `map` to its key-value pairs.
8338    *
8339    * @private
8340    * @param {Object} map The map to convert.
8341    * @returns {Array} Returns the key-value pairs.
8342    */
8343   function mapToArray(map) {
8344     var index = -1,
8345         result = Array(map.size);
8346
8347     map.forEach(function(value, key) {
8348       result[++index] = [key, value];
8349     });
8350     return result;
8351   }
8352
8353   /**
8354    * Replaces all `placeholder` elements in `array` with an internal placeholder
8355    * and returns an array of their indexes.
8356    *
8357    * @private
8358    * @param {Array} array The array to modify.
8359    * @param {*} placeholder The placeholder to replace.
8360    * @returns {Array} Returns the new array of placeholder indexes.
8361    */
8362   function replaceHolders(array, placeholder) {
8363     var index = -1,
8364         length = array.length,
8365         resIndex = 0,
8366         result = [];
8367
8368     while (++index < length) {
8369       var value = array[index];
8370       if (value === placeholder || value === PLACEHOLDER) {
8371         array[index] = PLACEHOLDER;
8372         result[resIndex++] = index;
8373       }
8374     }
8375     return result;
8376   }
8377
8378   /**
8379    * Converts `set` to an array of its values.
8380    *
8381    * @private
8382    * @param {Object} set The set to convert.
8383    * @returns {Array} Returns the values.
8384    */
8385   function setToArray(set) {
8386     var index = -1,
8387         result = Array(set.size);
8388
8389     set.forEach(function(value) {
8390       result[++index] = value;
8391     });
8392     return result;
8393   }
8394
8395   /**
8396    * Converts `set` to its value-value pairs.
8397    *
8398    * @private
8399    * @param {Object} set The set to convert.
8400    * @returns {Array} Returns the value-value pairs.
8401    */
8402   function setToPairs(set) {
8403     var index = -1,
8404         result = Array(set.size);
8405
8406     set.forEach(function(value) {
8407       result[++index] = [value, value];
8408     });
8409     return result;
8410   }
8411
8412   /**
8413    * Converts `string` to an array.
8414    *
8415    * @private
8416    * @param {string} string The string to convert.
8417    * @returns {Array} Returns the converted array.
8418    */
8419   function stringToArray(string) {
8420     return string.match(reComplexSymbol);
8421   }
8422
8423   /*--------------------------------------------------------------------------*/
8424
8425   /** Used for built-in method references. */
8426   var arrayProto = Array.prototype,
8427       objectProto = Object.prototype;
8428
8429   /** Used to resolve the decompiled source of functions. */
8430   var funcToString = Function.prototype.toString;
8431
8432   /** Used to check objects for own properties. */
8433   var hasOwnProperty = objectProto.hasOwnProperty;
8434
8435   /** Used to infer the `Object` constructor. */
8436   var objectCtorString = funcToString.call(Object);
8437
8438   /**
8439    * Used to resolve the
8440    * [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
8441    * of values.
8442    */
8443   var objectToString = objectProto.toString;
8444
8445   /** Used to detect if a method is native. */
8446   var reIsNative = RegExp('^' +
8447     funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
8448     .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
8449   );
8450
8451   /** Built-in value references. */
8452   var Buffer = moduleExports ? root.Buffer : undefined,
8453       Reflect = root.Reflect,
8454       Symbol = root.Symbol,
8455       Uint8Array = root.Uint8Array,
8456       enumerate = Reflect ? Reflect.enumerate : undefined,
8457       getOwnPropertySymbols = Object.getOwnPropertySymbols,
8458       iteratorSymbol = typeof (iteratorSymbol = Symbol && Symbol.iterator) == 'symbol' ? iteratorSymbol : undefined,
8459       objectCreate = Object.create,
8460       propertyIsEnumerable = objectProto.propertyIsEnumerable,
8461       splice = arrayProto.splice;
8462
8463   /* Built-in method references for those with the same name as other `lodash` methods. */
8464   var nativeCeil = Math.ceil,
8465       nativeGetPrototype = Object.getPrototypeOf,
8466       nativeKeys = Object.keys,
8467       nativeMax = Math.max,
8468       nativeMin = Math.min,
8469       nativeReverse = arrayProto.reverse;
8470
8471   /* Built-in method references that are verified to be native. */
8472   var DataView = getNative(root, 'DataView'),
8473       Map = getNative(root, 'Map'),
8474       Promise = getNative(root, 'Promise'),
8475       Set = getNative(root, 'Set'),
8476       WeakMap = getNative(root, 'WeakMap'),
8477       nativeCreate = getNative(Object, 'create');
8478
8479   /** Used to store function metadata. */
8480   var metaMap = WeakMap && new WeakMap;
8481
8482   /** Detect if properties shadowing those on `Object.prototype` are non-enumerable. */
8483   var nonEnumShadows = !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf');
8484
8485   /** Used to lookup unminified function names. */
8486   var realNames = {};
8487
8488   /** Used to detect maps, sets, and weakmaps. */
8489   var dataViewCtorString = toSource(DataView),
8490       mapCtorString = toSource(Map),
8491       promiseCtorString = toSource(Promise),
8492       setCtorString = toSource(Set),
8493       weakMapCtorString = toSource(WeakMap);
8494
8495   /** Used to convert symbols to primitives and strings. */
8496   var symbolProto = Symbol ? Symbol.prototype : undefined,
8497       symbolValueOf = symbolProto ? symbolProto.valueOf : undefined,
8498       symbolToString = symbolProto ? symbolProto.toString : undefined;
8499
8500   /*------------------------------------------------------------------------*/
8501
8502   /**
8503    * Creates a `lodash` object which wraps `value` to enable implicit method
8504    * chain sequences. Methods that operate on and return arrays, collections,
8505    * and functions can be chained together. Methods that retrieve a single value
8506    * or may return a primitive value will automatically end the chain sequence
8507    * and return the unwrapped value. Otherwise, the value must be unwrapped
8508    * with `_#value`.
8509    *
8510    * Explicit chain sequences, which must be unwrapped with `_#value`, may be
8511    * enabled using `_.chain`.
8512    *
8513    * The execution of chained methods is lazy, that is, it's deferred until
8514    * `_#value` is implicitly or explicitly called.
8515    *
8516    * Lazy evaluation allows several methods to support shortcut fusion.
8517    * Shortcut fusion is an optimization to merge iteratee calls; this avoids
8518    * the creation of intermediate arrays and can greatly reduce the number of
8519    * iteratee executions. Sections of a chain sequence qualify for shortcut
8520    * fusion if the section is applied to an array of at least `200` elements
8521    * and any iteratees accept only one argument. The heuristic for whether a
8522    * section qualifies for shortcut fusion is subject to change.
8523    *
8524    * Chaining is supported in custom builds as long as the `_#value` method is
8525    * directly or indirectly included in the build.
8526    *
8527    * In addition to lodash methods, wrappers have `Array` and `String` methods.
8528    *
8529    * The wrapper `Array` methods are:
8530    * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`
8531    *
8532    * The wrapper `String` methods are:
8533    * `replace` and `split`
8534    *
8535    * The wrapper methods that support shortcut fusion are:
8536    * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,
8537    * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,
8538    * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`
8539    *
8540    * The chainable wrapper methods are:
8541    * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,
8542    * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,
8543    * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,
8544    * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,
8545    * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,
8546    * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,
8547    * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,
8548    * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,
8549    * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,
8550    * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,
8551    * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
8552    * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,
8553    * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,
8554    * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,
8555    * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,
8556    * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,
8557    * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,
8558    * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,
8559    * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,
8560    * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,
8561    * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,
8562    * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,
8563    * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,
8564    * `zipObject`, `zipObjectDeep`, and `zipWith`
8565    *
8566    * The wrapper methods that are **not** chainable by default are:
8567    * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,
8568    * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `deburr`, `divide`, `each`,
8569    * `eachRight`, `endsWith`, `eq`, `escape`, `escapeRegExp`, `every`, `find`,
8570    * `findIndex`, `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `first`,
8571    * `floor`, `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`,
8572    * `forOwnRight`, `get`, `gt`, `gte`, `has`, `hasIn`, `head`, `identity`,
8573    * `includes`, `indexOf`, `inRange`, `invoke`, `isArguments`, `isArray`,
8574    * `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, `isBoolean`,
8575    * `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isEqualWith`,
8576    * `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, `isMap`,
8577    * `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, `isNumber`,
8578    * `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, `isSafeInteger`,
8579    * `isSet`, `isString`, `isUndefined`, `isTypedArray`, `isWeakMap`, `isWeakSet`,
8580    * `join`, `kebabCase`, `last`, `lastIndexOf`, `lowerCase`, `lowerFirst`,
8581    * `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, `min`, `minBy`, `multiply`,
8582    * `noConflict`, `noop`, `now`, `nth`, `pad`, `padEnd`, `padStart`, `parseInt`,
8583    * `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`, `round`,
8584    * `runInContext`, `sample`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`,
8585    * `sortedIndexBy`, `sortedLastIndex`, `sortedLastIndexBy`, `startCase`,
8586    * `startsWith`, `subtract`, `sum`, `sumBy`, `template`, `times`, `toFinite`,
8587    * `toInteger`, `toJSON`, `toLength`, `toLower`, `toNumber`, `toSafeInteger`,
8588    * `toString`, `toUpper`, `trim`, `trimEnd`, `trimStart`, `truncate`, `unescape`,
8589    * `uniqueId`, `upperCase`, `upperFirst`, `value`, and `words`
8590    *
8591    * @name _
8592    * @constructor
8593    * @category Seq
8594    * @param {*} value The value to wrap in a `lodash` instance.
8595    * @returns {Object} Returns the new `lodash` wrapper instance.
8596    * @example
8597    *
8598    * function square(n) {
8599    *   return n * n;
8600    * }
8601    *
8602    * var wrapped = _([1, 2, 3]);
8603    *
8604    * // Returns an unwrapped value.
8605    * wrapped.reduce(_.add);
8606    * // => 6
8607    *
8608    * // Returns a wrapped value.
8609    * var squares = wrapped.map(square);
8610    *
8611    * _.isArray(squares);
8612    * // => false
8613    *
8614    * _.isArray(squares.value());
8615    * // => true
8616    */
8617   function lodash(value) {
8618     if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
8619       if (value instanceof LodashWrapper) {
8620         return value;
8621       }
8622       if (hasOwnProperty.call(value, '__wrapped__')) {
8623         return wrapperClone(value);
8624       }
8625     }
8626     return new LodashWrapper(value);
8627   }
8628
8629   /**
8630    * The function whose prototype chain sequence wrappers inherit from.
8631    *
8632    * @private
8633    */
8634   function baseLodash() {
8635     // No operation performed.
8636   }
8637
8638   /**
8639    * The base constructor for creating `lodash` wrapper objects.
8640    *
8641    * @private
8642    * @param {*} value The value to wrap.
8643    * @param {boolean} [chainAll] Enable explicit method chain sequences.
8644    */
8645   function LodashWrapper(value, chainAll) {
8646     this.__wrapped__ = value;
8647     this.__actions__ = [];
8648     this.__chain__ = !!chainAll;
8649     this.__index__ = 0;
8650     this.__values__ = undefined;
8651   }
8652
8653   // Ensure wrappers are instances of `baseLodash`.
8654   lodash.prototype = baseLodash.prototype;
8655   lodash.prototype.constructor = lodash;
8656
8657   LodashWrapper.prototype = baseCreate(baseLodash.prototype);
8658   LodashWrapper.prototype.constructor = LodashWrapper;
8659
8660   /*------------------------------------------------------------------------*/
8661
8662   /**
8663    * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
8664    *
8665    * @private
8666    * @constructor
8667    * @param {*} value The value to wrap.
8668    */
8669   function LazyWrapper(value) {
8670     this.__wrapped__ = value;
8671     this.__actions__ = [];
8672     this.__dir__ = 1;
8673     this.__filtered__ = false;
8674     this.__iteratees__ = [];
8675     this.__takeCount__ = MAX_ARRAY_LENGTH;
8676     this.__views__ = [];
8677   }
8678
8679   /**
8680    * Creates a clone of the lazy wrapper object.
8681    *
8682    * @private
8683    * @name clone
8684    * @memberOf LazyWrapper
8685    * @returns {Object} Returns the cloned `LazyWrapper` object.
8686    */
8687   function lazyClone() {
8688     var result = new LazyWrapper(this.__wrapped__);
8689     result.__actions__ = copyArray(this.__actions__);
8690     result.__dir__ = this.__dir__;
8691     result.__filtered__ = this.__filtered__;
8692     result.__iteratees__ = copyArray(this.__iteratees__);
8693     result.__takeCount__ = this.__takeCount__;
8694     result.__views__ = copyArray(this.__views__);
8695     return result;
8696   }
8697
8698   /**
8699    * Reverses the direction of lazy iteration.
8700    *
8701    * @private
8702    * @name reverse
8703    * @memberOf LazyWrapper
8704    * @returns {Object} Returns the new reversed `LazyWrapper` object.
8705    */
8706   function lazyReverse() {
8707     if (this.__filtered__) {
8708       var result = new LazyWrapper(this);
8709       result.__dir__ = -1;
8710       result.__filtered__ = true;
8711     } else {
8712       result = this.clone();
8713       result.__dir__ *= -1;
8714     }
8715     return result;
8716   }
8717
8718   /**
8719    * Extracts the unwrapped value from its lazy wrapper.
8720    *
8721    * @private
8722    * @name value
8723    * @memberOf LazyWrapper
8724    * @returns {*} Returns the unwrapped value.
8725    */
8726   function lazyValue() {
8727     var array = this.__wrapped__.value(),
8728         dir = this.__dir__,
8729         isArr = isArray(array),
8730         isRight = dir < 0,
8731         arrLength = isArr ? array.length : 0,
8732         view = getView(0, arrLength, this.__views__),
8733         start = view.start,
8734         end = view.end,
8735         length = end - start,
8736         index = isRight ? end : (start - 1),
8737         iteratees = this.__iteratees__,
8738         iterLength = iteratees.length,
8739         resIndex = 0,
8740         takeCount = nativeMin(length, this.__takeCount__);
8741
8742     if (!isArr || arrLength < LARGE_ARRAY_SIZE ||
8743         (arrLength == length && takeCount == length)) {
8744       return baseWrapperValue(array, this.__actions__);
8745     }
8746     var result = [];
8747
8748     outer:
8749     while (length-- && resIndex < takeCount) {
8750       index += dir;
8751
8752       var iterIndex = -1,
8753           value = array[index];
8754
8755       while (++iterIndex < iterLength) {
8756         var data = iteratees[iterIndex],
8757             iteratee = data.iteratee,
8758             type = data.type,
8759             computed = iteratee(value);
8760
8761         if (type == LAZY_MAP_FLAG) {
8762           value = computed;
8763         } else if (!computed) {
8764           if (type == LAZY_FILTER_FLAG) {
8765             continue outer;
8766           } else {
8767             break outer;
8768           }
8769         }
8770       }
8771       result[resIndex++] = value;
8772     }
8773     return result;
8774   }
8775
8776   // Ensure `LazyWrapper` is an instance of `baseLodash`.
8777   LazyWrapper.prototype = baseCreate(baseLodash.prototype);
8778   LazyWrapper.prototype.constructor = LazyWrapper;
8779
8780   /*------------------------------------------------------------------------*/
8781
8782   /**
8783    * Creates a hash object.
8784    *
8785    * @private
8786    * @constructor
8787    * @param {Array} [entries] The key-value pairs to cache.
8788    */
8789   function Hash(entries) {
8790     var index = -1,
8791         length = entries ? entries.length : 0;
8792
8793     this.clear();
8794     while (++index < length) {
8795       var entry = entries[index];
8796       this.set(entry[0], entry[1]);
8797     }
8798   }
8799
8800   /**
8801    * Removes all key-value entries from the hash.
8802    *
8803    * @private
8804    * @name clear
8805    * @memberOf Hash
8806    */
8807   function hashClear() {
8808     this.__data__ = nativeCreate ? nativeCreate(null) : {};
8809   }
8810
8811   /**
8812    * Removes `key` and its value from the hash.
8813    *
8814    * @private
8815    * @name delete
8816    * @memberOf Hash
8817    * @param {Object} hash The hash to modify.
8818    * @param {string} key The key of the value to remove.
8819    * @returns {boolean} Returns `true` if the entry was removed, else `false`.
8820    */
8821   function hashDelete(key) {
8822     return this.has(key) && delete this.__data__[key];
8823   }
8824
8825   /**
8826    * Gets the hash value for `key`.
8827    *
8828    * @private
8829    * @name get
8830    * @memberOf Hash
8831    * @param {string} key The key of the value to get.
8832    * @returns {*} Returns the entry value.
8833    */
8834   function hashGet(key) {
8835     var data = this.__data__;
8836     if (nativeCreate) {
8837       var result = data[key];
8838       return result === HASH_UNDEFINED ? undefined : result;
8839     }
8840     return hasOwnProperty.call(data, key) ? data[key] : undefined;
8841   }
8842
8843   /**
8844    * Checks if a hash value for `key` exists.
8845    *
8846    * @private
8847    * @name has
8848    * @memberOf Hash
8849    * @param {string} key The key of the entry to check.
8850    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
8851    */
8852   function hashHas(key) {
8853     var data = this.__data__;
8854     return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);
8855   }
8856
8857   /**
8858    * Sets the hash `key` to `value`.
8859    *
8860    * @private
8861    * @name set
8862    * @memberOf Hash
8863    * @param {string} key The key of the value to set.
8864    * @param {*} value The value to set.
8865    * @returns {Object} Returns the hash instance.
8866    */
8867   function hashSet(key, value) {
8868     var data = this.__data__;
8869     data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
8870     return this;
8871   }
8872
8873   // Add methods to `Hash`.
8874   Hash.prototype.clear = hashClear;
8875   Hash.prototype['delete'] = hashDelete;
8876   Hash.prototype.get = hashGet;
8877   Hash.prototype.has = hashHas;
8878   Hash.prototype.set = hashSet;
8879
8880   /*------------------------------------------------------------------------*/
8881
8882   /**
8883    * Creates an list cache object.
8884    *
8885    * @private
8886    * @constructor
8887    * @param {Array} [entries] The key-value pairs to cache.
8888    */
8889   function ListCache(entries) {
8890     var index = -1,
8891         length = entries ? entries.length : 0;
8892
8893     this.clear();
8894     while (++index < length) {
8895       var entry = entries[index];
8896       this.set(entry[0], entry[1]);
8897     }
8898   }
8899
8900   /**
8901    * Removes all key-value entries from the list cache.
8902    *
8903    * @private
8904    * @name clear
8905    * @memberOf ListCache
8906    */
8907   function listCacheClear() {
8908     this.__data__ = [];
8909   }
8910
8911   /**
8912    * Removes `key` and its value from the list cache.
8913    *
8914    * @private
8915    * @name delete
8916    * @memberOf ListCache
8917    * @param {string} key The key of the value to remove.
8918    * @returns {boolean} Returns `true` if the entry was removed, else `false`.
8919    */
8920   function listCacheDelete(key) {
8921     var data = this.__data__,
8922         index = assocIndexOf(data, key);
8923
8924     if (index < 0) {
8925       return false;
8926     }
8927     var lastIndex = data.length - 1;
8928     if (index == lastIndex) {
8929       data.pop();
8930     } else {
8931       splice.call(data, index, 1);
8932     }
8933     return true;
8934   }
8935
8936   /**
8937    * Gets the list cache value for `key`.
8938    *
8939    * @private
8940    * @name get
8941    * @memberOf ListCache
8942    * @param {string} key The key of the value to get.
8943    * @returns {*} Returns the entry value.
8944    */
8945   function listCacheGet(key) {
8946     var data = this.__data__,
8947         index = assocIndexOf(data, key);
8948
8949     return index < 0 ? undefined : data[index][1];
8950   }
8951
8952   /**
8953    * Checks if a list cache value for `key` exists.
8954    *
8955    * @private
8956    * @name has
8957    * @memberOf ListCache
8958    * @param {string} key The key of the entry to check.
8959    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
8960    */
8961   function listCacheHas(key) {
8962     return assocIndexOf(this.__data__, key) > -1;
8963   }
8964
8965   /**
8966    * Sets the list cache `key` to `value`.
8967    *
8968    * @private
8969    * @name set
8970    * @memberOf ListCache
8971    * @param {string} key The key of the value to set.
8972    * @param {*} value The value to set.
8973    * @returns {Object} Returns the list cache instance.
8974    */
8975   function listCacheSet(key, value) {
8976     var data = this.__data__,
8977         index = assocIndexOf(data, key);
8978
8979     if (index < 0) {
8980       data.push([key, value]);
8981     } else {
8982       data[index][1] = value;
8983     }
8984     return this;
8985   }
8986
8987   // Add methods to `ListCache`.
8988   ListCache.prototype.clear = listCacheClear;
8989   ListCache.prototype['delete'] = listCacheDelete;
8990   ListCache.prototype.get = listCacheGet;
8991   ListCache.prototype.has = listCacheHas;
8992   ListCache.prototype.set = listCacheSet;
8993
8994   /*------------------------------------------------------------------------*/
8995
8996   /**
8997    * Creates a map cache object to store key-value pairs.
8998    *
8999    * @private
9000    * @constructor
9001    * @param {Array} [entries] The key-value pairs to cache.
9002    */
9003   function MapCache(entries) {
9004     var index = -1,
9005         length = entries ? entries.length : 0;
9006
9007     this.clear();
9008     while (++index < length) {
9009       var entry = entries[index];
9010       this.set(entry[0], entry[1]);
9011     }
9012   }
9013
9014   /**
9015    * Removes all key-value entries from the map.
9016    *
9017    * @private
9018    * @name clear
9019    * @memberOf MapCache
9020    */
9021   function mapCacheClear() {
9022     this.__data__ = {
9023       'hash': new Hash,
9024       'map': new (Map || ListCache),
9025       'string': new Hash
9026     };
9027   }
9028
9029   /**
9030    * Removes `key` and its value from the map.
9031    *
9032    * @private
9033    * @name delete
9034    * @memberOf MapCache
9035    * @param {string} key The key of the value to remove.
9036    * @returns {boolean} Returns `true` if the entry was removed, else `false`.
9037    */
9038   function mapCacheDelete(key) {
9039     return getMapData(this, key)['delete'](key);
9040   }
9041
9042   /**
9043    * Gets the map value for `key`.
9044    *
9045    * @private
9046    * @name get
9047    * @memberOf MapCache
9048    * @param {string} key The key of the value to get.
9049    * @returns {*} Returns the entry value.
9050    */
9051   function mapCacheGet(key) {
9052     return getMapData(this, key).get(key);
9053   }
9054
9055   /**
9056    * Checks if a map value for `key` exists.
9057    *
9058    * @private
9059    * @name has
9060    * @memberOf MapCache
9061    * @param {string} key The key of the entry to check.
9062    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
9063    */
9064   function mapCacheHas(key) {
9065     return getMapData(this, key).has(key);
9066   }
9067
9068   /**
9069    * Sets the map `key` to `value`.
9070    *
9071    * @private
9072    * @name set
9073    * @memberOf MapCache
9074    * @param {string} key The key of the value to set.
9075    * @param {*} value The value to set.
9076    * @returns {Object} Returns the map cache instance.
9077    */
9078   function mapCacheSet(key, value) {
9079     getMapData(this, key).set(key, value);
9080     return this;
9081   }
9082
9083   // Add methods to `MapCache`.
9084   MapCache.prototype.clear = mapCacheClear;
9085   MapCache.prototype['delete'] = mapCacheDelete;
9086   MapCache.prototype.get = mapCacheGet;
9087   MapCache.prototype.has = mapCacheHas;
9088   MapCache.prototype.set = mapCacheSet;
9089
9090   /*------------------------------------------------------------------------*/
9091
9092   /**
9093    *
9094    * Creates an array cache object to store unique values.
9095    *
9096    * @private
9097    * @constructor
9098    * @param {Array} [values] The values to cache.
9099    */
9100   function SetCache(values) {
9101     var index = -1,
9102         length = values ? values.length : 0;
9103
9104     this.__data__ = new MapCache;
9105     while (++index < length) {
9106       this.add(values[index]);
9107     }
9108   }
9109
9110   /**
9111    * Adds `value` to the array cache.
9112    *
9113    * @private
9114    * @name add
9115    * @memberOf SetCache
9116    * @alias push
9117    * @param {*} value The value to cache.
9118    * @returns {Object} Returns the cache instance.
9119    */
9120   function setCacheAdd(value) {
9121     this.__data__.set(value, HASH_UNDEFINED);
9122     return this;
9123   }
9124
9125   /**
9126    * Checks if `value` is in the array cache.
9127    *
9128    * @private
9129    * @name has
9130    * @memberOf SetCache
9131    * @param {*} value The value to search for.
9132    * @returns {number} Returns `true` if `value` is found, else `false`.
9133    */
9134   function setCacheHas(value) {
9135     return this.__data__.has(value);
9136   }
9137
9138   // Add methods to `SetCache`.
9139   SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
9140   SetCache.prototype.has = setCacheHas;
9141
9142   /*------------------------------------------------------------------------*/
9143
9144   /**
9145    * Creates a stack cache object to store key-value pairs.
9146    *
9147    * @private
9148    * @constructor
9149    * @param {Array} [entries] The key-value pairs to cache.
9150    */
9151   function Stack(entries) {
9152     this.__data__ = new ListCache(entries);
9153   }
9154
9155   /**
9156    * Removes all key-value entries from the stack.
9157    *
9158    * @private
9159    * @name clear
9160    * @memberOf Stack
9161    */
9162   function stackClear() {
9163     this.__data__ = new ListCache;
9164   }
9165
9166   /**
9167    * Removes `key` and its value from the stack.
9168    *
9169    * @private
9170    * @name delete
9171    * @memberOf Stack
9172    * @param {string} key The key of the value to remove.
9173    * @returns {boolean} Returns `true` if the entry was removed, else `false`.
9174    */
9175   function stackDelete(key) {
9176     return this.__data__['delete'](key);
9177   }
9178
9179   /**
9180    * Gets the stack value for `key`.
9181    *
9182    * @private
9183    * @name get
9184    * @memberOf Stack
9185    * @param {string} key The key of the value to get.
9186    * @returns {*} Returns the entry value.
9187    */
9188   function stackGet(key) {
9189     return this.__data__.get(key);
9190   }
9191
9192   /**
9193    * Checks if a stack value for `key` exists.
9194    *
9195    * @private
9196    * @name has
9197    * @memberOf Stack
9198    * @param {string} key The key of the entry to check.
9199    * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
9200    */
9201   function stackHas(key) {
9202     return this.__data__.has(key);
9203   }
9204
9205   /**
9206    * Sets the stack `key` to `value`.
9207    *
9208    * @private
9209    * @name set
9210    * @memberOf Stack
9211    * @param {string} key The key of the value to set.
9212    * @param {*} value The value to set.
9213    * @returns {Object} Returns the stack cache instance.
9214    */
9215   function stackSet(key, value) {
9216     var cache = this.__data__;
9217     if (cache instanceof ListCache && cache.__data__.length == LARGE_ARRAY_SIZE) {
9218       cache = this.__data__ = new MapCache(cache.__data__);
9219     }
9220     cache.set(key, value);
9221     return this;
9222   }
9223
9224   // Add methods to `Stack`.
9225   Stack.prototype.clear = stackClear;
9226   Stack.prototype['delete'] = stackDelete;
9227   Stack.prototype.get = stackGet;
9228   Stack.prototype.has = stackHas;
9229   Stack.prototype.set = stackSet;
9230
9231   /*------------------------------------------------------------------------*/
9232
9233   /**
9234    * This function is like `assignValue` except that it doesn't assign
9235    * `undefined` values.
9236    *
9237    * @private
9238    * @param {Object} object The object to modify.
9239    * @param {string} key The key of the property to assign.
9240    * @param {*} value The value to assign.
9241    */
9242   function assignMergeValue(object, key, value) {
9243     if ((value !== undefined && !eq(object[key], value)) ||
9244         (typeof key == 'number' && value === undefined && !(key in object))) {
9245       object[key] = value;
9246     }
9247   }
9248
9249   /**
9250    * Assigns `value` to `key` of `object` if the existing value is not equivalent
9251    * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
9252    * for equality comparisons.
9253    *
9254    * @private
9255    * @param {Object} object The object to modify.
9256    * @param {string} key The key of the property to assign.
9257    * @param {*} value The value to assign.
9258    */
9259   function assignValue(object, key, value) {
9260     var objValue = object[key];
9261     if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
9262         (value === undefined && !(key in object))) {
9263       object[key] = value;
9264     }
9265   }
9266
9267   /**
9268    * Gets the index at which the `key` is found in `array` of key-value pairs.
9269    *
9270    * @private
9271    * @param {Array} array The array to search.
9272    * @param {*} key The key to search for.
9273    * @returns {number} Returns the index of the matched value, else `-1`.
9274    */
9275   function assocIndexOf(array, key) {
9276     var length = array.length;
9277     while (length--) {
9278       if (eq(array[length][0], key)) {
9279         return length;
9280       }
9281     }
9282     return -1;
9283   }
9284
9285   /**
9286    * Aggregates elements of `collection` on `accumulator` with keys transformed
9287    * by `iteratee` and values set by `setter`.
9288    *
9289    * @private
9290    * @param {Array|Object} collection The collection to iterate over.
9291    * @param {Function} setter The function to set `accumulator` values.
9292    * @param {Function} iteratee The iteratee to transform keys.
9293    * @param {Object} accumulator The initial aggregated object.
9294    * @returns {Function} Returns `accumulator`.
9295    */
9296   function baseAggregator(collection, setter, iteratee, accumulator) {
9297     baseEach(collection, function(value, key, collection) {
9298       setter(accumulator, value, iteratee(value), collection);
9299     });
9300     return accumulator;
9301   }
9302
9303   /**
9304    * The base implementation of `_.assign` without support for multiple sources
9305    * or `customizer` functions.
9306    *
9307    * @private
9308    * @param {Object} object The destination object.
9309    * @param {Object} source The source object.
9310    * @returns {Object} Returns `object`.
9311    */
9312   function baseAssign(object, source) {
9313     return object && copyObject(source, keys(source), object);
9314   }
9315
9316   /**
9317    * The base implementation of `_.at` without support for individual paths.
9318    *
9319    * @private
9320    * @param {Object} object The object to iterate over.
9321    * @param {string[]} paths The property paths of elements to pick.
9322    * @returns {Array} Returns the picked elements.
9323    */
9324   function baseAt(object, paths) {
9325     var index = -1,
9326         isNil = object == null,
9327         length = paths.length,
9328         result = Array(length);
9329
9330     while (++index < length) {
9331       result[index] = isNil ? undefined : get(object, paths[index]);
9332     }
9333     return result;
9334   }
9335
9336   /**
9337    * The base implementation of `_.clone` and `_.cloneDeep` which tracks
9338    * traversed objects.
9339    *
9340    * @private
9341    * @param {*} value The value to clone.
9342    * @param {boolean} [isDeep] Specify a deep clone.
9343    * @param {boolean} [isFull] Specify a clone including symbols.
9344    * @param {Function} [customizer] The function to customize cloning.
9345    * @param {string} [key] The key of `value`.
9346    * @param {Object} [object] The parent object of `value`.
9347    * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
9348    * @returns {*} Returns the cloned value.
9349    */
9350   function baseClone(value, isDeep, isFull, customizer, key, object, stack) {
9351     var result;
9352     if (customizer) {
9353       result = object ? customizer(value, key, object, stack) : customizer(value);
9354     }
9355     if (result !== undefined) {
9356       return result;
9357     }
9358     if (!isObject(value)) {
9359       return value;
9360     }
9361     var isArr = isArray(value);
9362     if (isArr) {
9363       result = initCloneArray(value);
9364       if (!isDeep) {
9365         return copyArray(value, result);
9366       }
9367     } else {
9368       var tag = getTag(value),
9369           isFunc = tag == funcTag || tag == genTag;
9370
9371       if (isBuffer(value)) {
9372         return cloneBuffer(value, isDeep);
9373       }
9374       if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
9375         if (isHostObject(value)) {
9376           return object ? value : {};
9377         }
9378         result = initCloneObject(isFunc ? {} : value);
9379         if (!isDeep) {
9380           return copySymbols(value, baseAssign(result, value));
9381         }
9382       } else {
9383         if (!cloneableTags[tag]) {
9384           return object ? value : {};
9385         }
9386         result = initCloneByTag(value, tag, baseClone, isDeep);
9387       }
9388     }
9389     // Check for circular references and return its corresponding clone.
9390     stack || (stack = new Stack);
9391     var stacked = stack.get(value);
9392     if (stacked) {
9393       return stacked;
9394     }
9395     stack.set(value, result);
9396
9397     if (!isArr) {
9398       var props = isFull ? getAllKeys(value) : keys(value);
9399     }
9400     // Recursively populate clone (susceptible to call stack limits).
9401     arrayEach(props || value, function(subValue, key) {
9402       if (props) {
9403         key = subValue;
9404         subValue = value[key];
9405       }
9406       assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack));
9407     });
9408     return result;
9409   }
9410
9411   /**
9412    * The base implementation of `_.create` without support for assigning
9413    * properties to the created object.
9414    *
9415    * @private
9416    * @param {Object} prototype The object to inherit from.
9417    * @returns {Object} Returns the new object.
9418    */
9419   function baseCreate(proto) {
9420     return isObject(proto) ? objectCreate(proto) : {};
9421   }
9422
9423   /**
9424    * The base implementation of methods like `_.difference` without support
9425    * for excluding multiple arrays or iteratee shorthands.
9426    *
9427    * @private
9428    * @param {Array} array The array to inspect.
9429    * @param {Array} values The values to exclude.
9430    * @param {Function} [iteratee] The iteratee invoked per element.
9431    * @param {Function} [comparator] The comparator invoked per element.
9432    * @returns {Array} Returns the new array of filtered values.
9433    */
9434   function baseDifference(array, values, iteratee, comparator) {
9435     var index = -1,
9436         includes = arrayIncludes,
9437         isCommon = true,
9438         length = array.length,
9439         result = [],
9440         valuesLength = values.length;
9441
9442     if (!length) {
9443       return result;
9444     }
9445     if (iteratee) {
9446       values = arrayMap(values, baseUnary(iteratee));
9447     }
9448     if (comparator) {
9449       includes = arrayIncludesWith;
9450       isCommon = false;
9451     }
9452     else if (values.length >= LARGE_ARRAY_SIZE) {
9453       includes = cacheHas;
9454       isCommon = false;
9455       values = new SetCache(values);
9456     }
9457     outer:
9458     while (++index < length) {
9459       var value = array[index],
9460           computed = iteratee ? iteratee(value) : value;
9461
9462       value = (comparator || value !== 0) ? value : 0;
9463       if (isCommon && computed === computed) {
9464         var valuesIndex = valuesLength;
9465         while (valuesIndex--) {
9466           if (values[valuesIndex] === computed) {
9467             continue outer;
9468           }
9469         }
9470         result.push(value);
9471       }
9472       else if (!includes(values, computed, comparator)) {
9473         result.push(value);
9474       }
9475     }
9476     return result;
9477   }
9478
9479   /**
9480    * The base implementation of `_.forEach` without support for iteratee shorthands.
9481    *
9482    * @private
9483    * @param {Array|Object} collection The collection to iterate over.
9484    * @param {Function} iteratee The function invoked per iteration.
9485    * @returns {Array|Object} Returns `collection`.
9486    */
9487   var baseEach = createBaseEach(baseForOwn);
9488
9489   /**
9490    * The base implementation of `_.every` without support for iteratee shorthands.
9491    *
9492    * @private
9493    * @param {Array|Object} collection The collection to iterate over.
9494    * @param {Function} predicate The function invoked per iteration.
9495    * @returns {boolean} Returns `true` if all elements pass the predicate check,
9496    *  else `false`
9497    */
9498   function baseEvery(collection, predicate) {
9499     var result = true;
9500     baseEach(collection, function(value, index, collection) {
9501       result = !!predicate(value, index, collection);
9502       return result;
9503     });
9504     return result;
9505   }
9506
9507   /**
9508    * The base implementation of `_.filter` without support for iteratee shorthands.
9509    *
9510    * @private
9511    * @param {Array|Object} collection The collection to iterate over.
9512    * @param {Function} predicate The function invoked per iteration.
9513    * @returns {Array} Returns the new filtered array.
9514    */
9515   function baseFilter(collection, predicate) {
9516     var result = [];
9517     baseEach(collection, function(value, index, collection) {
9518       if (predicate(value, index, collection)) {
9519         result.push(value);
9520       }
9521     });
9522     return result;
9523   }
9524
9525   /**
9526    * The base implementation of `_.flatten` with support for restricting flattening.
9527    *
9528    * @private
9529    * @param {Array} array The array to flatten.
9530    * @param {number} depth The maximum recursion depth.
9531    * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
9532    * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
9533    * @param {Array} [result=[]] The initial result value.
9534    * @returns {Array} Returns the new flattened array.
9535    */
9536   function baseFlatten(array, depth, predicate, isStrict, result) {
9537     var index = -1,
9538         length = array.length;
9539
9540     predicate || (predicate = isFlattenable);
9541     result || (result = []);
9542
9543     while (++index < length) {
9544       var value = array[index];
9545       if (depth > 0 && predicate(value)) {
9546         if (depth > 1) {
9547           // Recursively flatten arrays (susceptible to call stack limits).
9548           baseFlatten(value, depth - 1, predicate, isStrict, result);
9549         } else {
9550           arrayPush(result, value);
9551         }
9552       } else if (!isStrict) {
9553         result[result.length] = value;
9554       }
9555     }
9556     return result;
9557   }
9558
9559   /**
9560    * The base implementation of `baseForOwn` which iterates over `object`
9561    * properties returned by `keysFunc` and invokes `iteratee` for each property.
9562    * Iteratee functions may exit iteration early by explicitly returning `false`.
9563    *
9564    * @private
9565    * @param {Object} object The object to iterate over.
9566    * @param {Function} iteratee The function invoked per iteration.
9567    * @param {Function} keysFunc The function to get the keys of `object`.
9568    * @returns {Object} Returns `object`.
9569    */
9570   var baseFor = createBaseFor();
9571
9572   /**
9573    * The base implementation of `_.forOwn` without support for iteratee shorthands.
9574    *
9575    * @private
9576    * @param {Object} object The object to iterate over.
9577    * @param {Function} iteratee The function invoked per iteration.
9578    * @returns {Object} Returns `object`.
9579    */
9580   function baseForOwn(object, iteratee) {
9581     return object && baseFor(object, iteratee, keys);
9582   }
9583
9584   /**
9585    * The base implementation of `_.functions` which creates an array of
9586    * `object` function property names filtered from `props`.
9587    *
9588    * @private
9589    * @param {Object} object The object to inspect.
9590    * @param {Array} props The property names to filter.
9591    * @returns {Array} Returns the function names.
9592    */
9593   function baseFunctions(object, props) {
9594     return arrayFilter(props, function(key) {
9595       return isFunction(object[key]);
9596     });
9597   }
9598
9599   /**
9600    * The base implementation of `_.get` without support for default values.
9601    *
9602    * @private
9603    * @param {Object} object The object to query.
9604    * @param {Array|string} path The path of the property to get.
9605    * @returns {*} Returns the resolved value.
9606    */
9607   function baseGet(object, path) {
9608     path = isKey(path, object) ? [path] : castPath(path);
9609
9610     var index = 0,
9611         length = path.length;
9612
9613     while (object != null && index < length) {
9614       object = object[toKey(path[index++])];
9615     }
9616     return (index && index == length) ? object : undefined;
9617   }
9618
9619   /**
9620    * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
9621    * `keysFunc` and `symbolsFunc` to get the enumerable property names and
9622    * symbols of `object`.
9623    *
9624    * @private
9625    * @param {Object} object The object to query.
9626    * @param {Function} keysFunc The function to get the keys of `object`.
9627    * @param {Function} symbolsFunc The function to get the symbols of `object`.
9628    * @returns {Array} Returns the array of property names and symbols.
9629    */
9630   function baseGetAllKeys(object, keysFunc, symbolsFunc) {
9631     var result = keysFunc(object);
9632     return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
9633   }
9634
9635   /**
9636    * The base implementation of `_.has` without support for deep paths.
9637    *
9638    * @private
9639    * @param {Object} object The object to query.
9640    * @param {Array|string} key The key to check.
9641    * @returns {boolean} Returns `true` if `key` exists, else `false`.
9642    */
9643   function baseHas(object, key) {
9644     // Avoid a bug in IE 10-11 where objects with a [[Prototype]] of `null`,
9645     // that are composed entirely of index properties, return `false` for
9646     // `hasOwnProperty` checks of them.
9647     return hasOwnProperty.call(object, key) ||
9648       (typeof object == 'object' && key in object && getPrototype(object) === null);
9649   }
9650
9651   /**
9652    * The base implementation of `_.hasIn` without support for deep paths.
9653    *
9654    * @private
9655    * @param {Object} object The object to query.
9656    * @param {Array|string} key The key to check.
9657    * @returns {boolean} Returns `true` if `key` exists, else `false`.
9658    */
9659   function baseHasIn(object, key) {
9660     return key in Object(object);
9661   }
9662
9663   /**
9664    * The base implementation of methods like `_.intersection`, without support
9665    * for iteratee shorthands, that accepts an array of arrays to inspect.
9666    *
9667    * @private
9668    * @param {Array} arrays The arrays to inspect.
9669    * @param {Function} [iteratee] The iteratee invoked per element.
9670    * @param {Function} [comparator] The comparator invoked per element.
9671    * @returns {Array} Returns the new array of shared values.
9672    */
9673   function baseIntersection(arrays, iteratee, comparator) {
9674     var includes = comparator ? arrayIncludesWith : arrayIncludes,
9675         length = arrays[0].length,
9676         othLength = arrays.length,
9677         othIndex = othLength,
9678         caches = Array(othLength),
9679         maxLength = Infinity,
9680         result = [];
9681
9682     while (othIndex--) {
9683       var array = arrays[othIndex];
9684       if (othIndex && iteratee) {
9685         array = arrayMap(array, baseUnary(iteratee));
9686       }
9687       maxLength = nativeMin(array.length, maxLength);
9688       caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120))
9689         ? new SetCache(othIndex && array)
9690         : undefined;
9691     }
9692     array = arrays[0];
9693
9694     var index = -1,
9695         seen = caches[0];
9696
9697     outer:
9698     while (++index < length && result.length < maxLength) {
9699       var value = array[index],
9700           computed = iteratee ? iteratee(value) : value;
9701
9702       value = (comparator || value !== 0) ? value : 0;
9703       if (!(seen
9704             ? cacheHas(seen, computed)
9705             : includes(result, computed, comparator)
9706           )) {
9707         othIndex = othLength;
9708         while (--othIndex) {
9709           var cache = caches[othIndex];
9710           if (!(cache
9711                 ? cacheHas(cache, computed)
9712                 : includes(arrays[othIndex], computed, comparator))
9713               ) {
9714             continue outer;
9715           }
9716         }
9717         if (seen) {
9718           seen.push(computed);
9719         }
9720         result.push(value);
9721       }
9722     }
9723     return result;
9724   }
9725
9726   /**
9727    * The base implementation of `_.invoke` without support for individual
9728    * method arguments.
9729    *
9730    * @private
9731    * @param {Object} object The object to query.
9732    * @param {Array|string} path The path of the method to invoke.
9733    * @param {Array} args The arguments to invoke the method with.
9734    * @returns {*} Returns the result of the invoked method.
9735    */
9736   function baseInvoke(object, path, args) {
9737     if (!isKey(path, object)) {
9738       path = castPath(path);
9739       object = parent(object, path);
9740       path = last(path);
9741     }
9742     var func = object == null ? object : object[toKey(path)];
9743     return func == null ? undefined : apply(func, object, args);
9744   }
9745
9746   /**
9747    * The base implementation of `_.isEqual` which supports partial comparisons
9748    * and tracks traversed objects.
9749    *
9750    * @private
9751    * @param {*} value The value to compare.
9752    * @param {*} other The other value to compare.
9753    * @param {Function} [customizer] The function to customize comparisons.
9754    * @param {boolean} [bitmask] The bitmask of comparison flags.
9755    *  The bitmask may be composed of the following flags:
9756    *     1 - Unordered comparison
9757    *     2 - Partial comparison
9758    * @param {Object} [stack] Tracks traversed `value` and `other` objects.
9759    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
9760    */
9761   function baseIsEqual(value, other, customizer, bitmask, stack) {
9762     if (value === other) {
9763       return true;
9764     }
9765     if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
9766       return value !== value && other !== other;
9767     }
9768     return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack);
9769   }
9770
9771   /**
9772    * A specialized version of `baseIsEqual` for arrays and objects which performs
9773    * deep comparisons and tracks traversed objects enabling objects with circular
9774    * references to be compared.
9775    *
9776    * @private
9777    * @param {Object} object The object to compare.
9778    * @param {Object} other The other object to compare.
9779    * @param {Function} equalFunc The function to determine equivalents of values.
9780    * @param {Function} [customizer] The function to customize comparisons.
9781    * @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual`
9782    *  for more details.
9783    * @param {Object} [stack] Tracks traversed `object` and `other` objects.
9784    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9785    */
9786   function baseIsEqualDeep(object, other, equalFunc, customizer, bitmask, stack) {
9787     var objIsArr = isArray(object),
9788         othIsArr = isArray(other),
9789         objTag = arrayTag,
9790         othTag = arrayTag;
9791
9792     if (!objIsArr) {
9793       objTag = getTag(object);
9794       objTag = objTag == argsTag ? objectTag : objTag;
9795     }
9796     if (!othIsArr) {
9797       othTag = getTag(other);
9798       othTag = othTag == argsTag ? objectTag : othTag;
9799     }
9800     var objIsObj = objTag == objectTag && !isHostObject(object),
9801         othIsObj = othTag == objectTag && !isHostObject(other),
9802         isSameTag = objTag == othTag;
9803
9804     if (isSameTag && !objIsObj) {
9805       stack || (stack = new Stack);
9806       return (objIsArr || isTypedArray(object))
9807         ? equalArrays(object, other, equalFunc, customizer, bitmask, stack)
9808         : equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack);
9809     }
9810     if (!(bitmask & PARTIAL_COMPARE_FLAG)) {
9811       var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
9812           othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
9813
9814       if (objIsWrapped || othIsWrapped) {
9815         var objUnwrapped = objIsWrapped ? object.value() : object,
9816             othUnwrapped = othIsWrapped ? other.value() : other;
9817
9818         stack || (stack = new Stack);
9819         return equalFunc(objUnwrapped, othUnwrapped, customizer, bitmask, stack);
9820       }
9821     }
9822     if (!isSameTag) {
9823       return false;
9824     }
9825     stack || (stack = new Stack);
9826     return equalObjects(object, other, equalFunc, customizer, bitmask, stack);
9827   }
9828
9829   /**
9830    * The base implementation of `_.isMatch` without support for iteratee shorthands.
9831    *
9832    * @private
9833    * @param {Object} object The object to inspect.
9834    * @param {Object} source The object of property values to match.
9835    * @param {Array} matchData The property names, values, and compare flags to match.
9836    * @param {Function} [customizer] The function to customize comparisons.
9837    * @returns {boolean} Returns `true` if `object` is a match, else `false`.
9838    */
9839   function baseIsMatch(object, source, matchData, customizer) {
9840     var index = matchData.length,
9841         length = index,
9842         noCustomizer = !customizer;
9843
9844     if (object == null) {
9845       return !length;
9846     }
9847     object = Object(object);
9848     while (index--) {
9849       var data = matchData[index];
9850       if ((noCustomizer && data[2])
9851             ? data[1] !== object[data[0]]
9852             : !(data[0] in object)
9853           ) {
9854         return false;
9855       }
9856     }
9857     while (++index < length) {
9858       data = matchData[index];
9859       var key = data[0],
9860           objValue = object[key],
9861           srcValue = data[1];
9862
9863       if (noCustomizer && data[2]) {
9864         if (objValue === undefined && !(key in object)) {
9865           return false;
9866         }
9867       } else {
9868         var stack = new Stack;
9869         if (customizer) {
9870           var result = customizer(objValue, srcValue, key, object, source, stack);
9871         }
9872         if (!(result === undefined
9873               ? baseIsEqual(srcValue, objValue, customizer, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG, stack)
9874               : result
9875             )) {
9876           return false;
9877         }
9878       }
9879     }
9880     return true;
9881   }
9882
9883   /**
9884    * The base implementation of `_.iteratee`.
9885    *
9886    * @private
9887    * @param {*} [value=_.identity] The value to convert to an iteratee.
9888    * @returns {Function} Returns the iteratee.
9889    */
9890   function baseIteratee(value) {
9891     // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
9892     // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
9893     if (typeof value == 'function') {
9894       return value;
9895     }
9896     if (value == null) {
9897       return identity;
9898     }
9899     if (typeof value == 'object') {
9900       return isArray(value)
9901         ? baseMatchesProperty(value[0], value[1])
9902         : baseMatches(value);
9903     }
9904     return property(value);
9905   }
9906
9907   /**
9908    * The base implementation of `_.keys` which doesn't skip the constructor
9909    * property of prototypes or treat sparse arrays as dense.
9910    *
9911    * @private
9912    * @param {Object} object The object to query.
9913    * @returns {Array} Returns the array of property names.
9914    */
9915   function baseKeys(object) {
9916     return nativeKeys(Object(object));
9917   }
9918
9919   /**
9920    * The base implementation of `_.keysIn` which doesn't skip the constructor
9921    * property of prototypes or treat sparse arrays as dense.
9922    *
9923    * @private
9924    * @param {Object} object The object to query.
9925    * @returns {Array} Returns the array of property names.
9926    */
9927   function baseKeysIn(object) {
9928     object = object == null ? object : Object(object);
9929
9930     var result = [];
9931     for (var key in object) {
9932       result.push(key);
9933     }
9934     return result;
9935   }
9936
9937   // Fallback for IE < 9 with es6-shim.
9938   if (enumerate && !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf')) {
9939     baseKeysIn = function(object) {
9940       return iteratorToArray(enumerate(object));
9941     };
9942   }
9943
9944   /**
9945    * The base implementation of `_.map` without support for iteratee shorthands.
9946    *
9947    * @private
9948    * @param {Array|Object} collection The collection to iterate over.
9949    * @param {Function} iteratee The function invoked per iteration.
9950    * @returns {Array} Returns the new mapped array.
9951    */
9952   function baseMap(collection, iteratee) {
9953     var index = -1,
9954         result = isArrayLike(collection) ? Array(collection.length) : [];
9955
9956     baseEach(collection, function(value, key, collection) {
9957       result[++index] = iteratee(value, key, collection);
9958     });
9959     return result;
9960   }
9961
9962   /**
9963    * The base implementation of `_.matches` which doesn't clone `source`.
9964    *
9965    * @private
9966    * @param {Object} source The object of property values to match.
9967    * @returns {Function} Returns the new spec function.
9968    */
9969   function baseMatches(source) {
9970     var matchData = getMatchData(source);
9971     if (matchData.length == 1 && matchData[0][2]) {
9972       return matchesStrictComparable(matchData[0][0], matchData[0][1]);
9973     }
9974     return function(object) {
9975       return object === source || baseIsMatch(object, source, matchData);
9976     };
9977   }
9978
9979   /**
9980    * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.
9981    *
9982    * @private
9983    * @param {string} path The path of the property to get.
9984    * @param {*} srcValue The value to match.
9985    * @returns {Function} Returns the new spec function.
9986    */
9987   function baseMatchesProperty(path, srcValue) {
9988     if (isKey(path) && isStrictComparable(srcValue)) {
9989       return matchesStrictComparable(toKey(path), srcValue);
9990     }
9991     return function(object) {
9992       var objValue = get(object, path);
9993       return (objValue === undefined && objValue === srcValue)
9994         ? hasIn(object, path)
9995         : baseIsEqual(srcValue, objValue, undefined, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG);
9996     };
9997   }
9998
9999   /**
10000    * The base implementation of `_.merge` without support for multiple sources.
10001    *
10002    * @private
10003    * @param {Object} object The destination object.
10004    * @param {Object} source The source object.
10005    * @param {number} srcIndex The index of `source`.
10006    * @param {Function} [customizer] The function to customize merged values.
10007    * @param {Object} [stack] Tracks traversed source values and their merged
10008    *  counterparts.
10009    */
10010   function baseMerge(object, source, srcIndex, customizer, stack) {
10011     if (object === source) {
10012       return;
10013     }
10014     if (!(isArray(source) || isTypedArray(source))) {
10015       var props = keysIn(source);
10016     }
10017     arrayEach(props || source, function(srcValue, key) {
10018       if (props) {
10019         key = srcValue;
10020         srcValue = source[key];
10021       }
10022       if (isObject(srcValue)) {
10023         stack || (stack = new Stack);
10024         baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
10025       }
10026       else {
10027         var newValue = customizer
10028           ? customizer(object[key], srcValue, (key + ''), object, source, stack)
10029           : undefined;
10030
10031         if (newValue === undefined) {
10032           newValue = srcValue;
10033         }
10034         assignMergeValue(object, key, newValue);
10035       }
10036     });
10037   }
10038
10039   /**
10040    * A specialized version of `baseMerge` for arrays and objects which performs
10041    * deep merges and tracks traversed objects enabling objects with circular
10042    * references to be merged.
10043    *
10044    * @private
10045    * @param {Object} object The destination object.
10046    * @param {Object} source The source object.
10047    * @param {string} key The key of the value to merge.
10048    * @param {number} srcIndex The index of `source`.
10049    * @param {Function} mergeFunc The function to merge values.
10050    * @param {Function} [customizer] The function to customize assigned values.
10051    * @param {Object} [stack] Tracks traversed source values and their merged
10052    *  counterparts.
10053    */
10054   function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {
10055     var objValue = object[key],
10056         srcValue = source[key],
10057         stacked = stack.get(srcValue);
10058
10059     if (stacked) {
10060       assignMergeValue(object, key, stacked);
10061       return;
10062     }
10063     var newValue = customizer
10064       ? customizer(objValue, srcValue, (key + ''), object, source, stack)
10065       : undefined;
10066
10067     var isCommon = newValue === undefined;
10068
10069     if (isCommon) {
10070       newValue = srcValue;
10071       if (isArray(srcValue) || isTypedArray(srcValue)) {
10072         if (isArray(objValue)) {
10073           newValue = objValue;
10074         }
10075         else if (isArrayLikeObject(objValue)) {
10076           newValue = copyArray(objValue);
10077         }
10078         else {
10079           isCommon = false;
10080           newValue = baseClone(srcValue, true);
10081         }
10082       }
10083       else if (isPlainObject(srcValue) || isArguments(srcValue)) {
10084         if (isArguments(objValue)) {
10085           newValue = toPlainObject(objValue);
10086         }
10087         else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) {
10088           isCommon = false;
10089           newValue = baseClone(srcValue, true);
10090         }
10091         else {
10092           newValue = objValue;
10093         }
10094       }
10095       else {
10096         isCommon = false;
10097       }
10098     }
10099     stack.set(srcValue, newValue);
10100
10101     if (isCommon) {
10102       // Recursively merge objects and arrays (susceptible to call stack limits).
10103       mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
10104     }
10105     stack['delete'](srcValue);
10106     assignMergeValue(object, key, newValue);
10107   }
10108
10109   /**
10110    * The base implementation of `_.pick` without support for individual
10111    * property identifiers.
10112    *
10113    * @private
10114    * @param {Object} object The source object.
10115    * @param {string[]} props The property identifiers to pick.
10116    * @returns {Object} Returns the new object.
10117    */
10118   function basePick(object, props) {
10119     object = Object(object);
10120     return arrayReduce(props, function(result, key) {
10121       if (key in object) {
10122         result[key] = object[key];
10123       }
10124       return result;
10125     }, {});
10126   }
10127
10128   /**
10129    * The base implementation of `_.property` without support for deep paths.
10130    *
10131    * @private
10132    * @param {string} key The key of the property to get.
10133    * @returns {Function} Returns the new accessor function.
10134    */
10135   function baseProperty(key) {
10136     return function(object) {
10137       return object == null ? undefined : object[key];
10138     };
10139   }
10140
10141   /**
10142    * A specialized version of `baseProperty` which supports deep paths.
10143    *
10144    * @private
10145    * @param {Array|string} path The path of the property to get.
10146    * @returns {Function} Returns the new accessor function.
10147    */
10148   function basePropertyDeep(path) {
10149     return function(object) {
10150       return baseGet(object, path);
10151     };
10152   }
10153
10154   /**
10155    * The base implementation of `setData` without support for hot loop detection.
10156    *
10157    * @private
10158    * @param {Function} func The function to associate metadata with.
10159    * @param {*} data The metadata.
10160    * @returns {Function} Returns `func`.
10161    */
10162   var baseSetData = !metaMap ? identity : function(func, data) {
10163     metaMap.set(func, data);
10164     return func;
10165   };
10166
10167   /**
10168    * The base implementation of `_.slice` without an iteratee call guard.
10169    *
10170    * @private
10171    * @param {Array} array The array to slice.
10172    * @param {number} [start=0] The start position.
10173    * @param {number} [end=array.length] The end position.
10174    * @returns {Array} Returns the slice of `array`.
10175    */
10176   function baseSlice(array, start, end) {
10177     var index = -1,
10178         length = array.length;
10179
10180     if (start < 0) {
10181       start = -start > length ? 0 : (length + start);
10182     }
10183     end = end > length ? length : end;
10184     if (end < 0) {
10185       end += length;
10186     }
10187     length = start > end ? 0 : ((end - start) >>> 0);
10188     start >>>= 0;
10189
10190     var result = Array(length);
10191     while (++index < length) {
10192       result[index] = array[index + start];
10193     }
10194     return result;
10195   }
10196
10197   /**
10198    * The base implementation of `_.some` without support for iteratee shorthands.
10199    *
10200    * @private
10201    * @param {Array|Object} collection The collection to iterate over.
10202    * @param {Function} predicate The function invoked per iteration.
10203    * @returns {boolean} Returns `true` if any element passes the predicate check,
10204    *  else `false`.
10205    */
10206   function baseSome(collection, predicate) {
10207     var result;
10208
10209     baseEach(collection, function(value, index, collection) {
10210       result = predicate(value, index, collection);
10211       return !result;
10212     });
10213     return !!result;
10214   }
10215
10216   /**
10217    * The base implementation of `_.toString` which doesn't convert nullish
10218    * values to empty strings.
10219    *
10220    * @private
10221    * @param {*} value The value to process.
10222    * @returns {string} Returns the string.
10223    */
10224   function baseToString(value) {
10225     // Exit early for strings to avoid a performance hit in some environments.
10226     if (typeof value == 'string') {
10227       return value;
10228     }
10229     if (isSymbol(value)) {
10230       return symbolToString ? symbolToString.call(value) : '';
10231     }
10232     var result = (value + '');
10233     return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
10234   }
10235
10236   /**
10237    * The base implementation of `_.uniqBy` without support for iteratee shorthands.
10238    *
10239    * @private
10240    * @param {Array} array The array to inspect.
10241    * @param {Function} [iteratee] The iteratee invoked per element.
10242    * @param {Function} [comparator] The comparator invoked per element.
10243    * @returns {Array} Returns the new duplicate free array.
10244    */
10245   function baseUniq(array, iteratee, comparator) {
10246     var index = -1,
10247         includes = arrayIncludes,
10248         length = array.length,
10249         isCommon = true,
10250         result = [],
10251         seen = result;
10252
10253     if (comparator) {
10254       isCommon = false;
10255       includes = arrayIncludesWith;
10256     }
10257     else if (length >= LARGE_ARRAY_SIZE) {
10258       var set = iteratee ? null : createSet(array);
10259       if (set) {
10260         return setToArray(set);
10261       }
10262       isCommon = false;
10263       includes = cacheHas;
10264       seen = new SetCache;
10265     }
10266     else {
10267       seen = iteratee ? [] : result;
10268     }
10269     outer:
10270     while (++index < length) {
10271       var value = array[index],
10272           computed = iteratee ? iteratee(value) : value;
10273
10274       value = (comparator || value !== 0) ? value : 0;
10275       if (isCommon && computed === computed) {
10276         var seenIndex = seen.length;
10277         while (seenIndex--) {
10278           if (seen[seenIndex] === computed) {
10279             continue outer;
10280           }
10281         }
10282         if (iteratee) {
10283           seen.push(computed);
10284         }
10285         result.push(value);
10286       }
10287       else if (!includes(seen, computed, comparator)) {
10288         if (seen !== result) {
10289           seen.push(computed);
10290         }
10291         result.push(value);
10292       }
10293     }
10294     return result;
10295   }
10296
10297   /**
10298    * The base implementation of `wrapperValue` which returns the result of
10299    * performing a sequence of actions on the unwrapped `value`, where each
10300    * successive action is supplied the return value of the previous.
10301    *
10302    * @private
10303    * @param {*} value The unwrapped value.
10304    * @param {Array} actions Actions to perform to resolve the unwrapped value.
10305    * @returns {*} Returns the resolved value.
10306    */
10307   function baseWrapperValue(value, actions) {
10308     var result = value;
10309     if (result instanceof LazyWrapper) {
10310       result = result.value();
10311     }
10312     return arrayReduce(actions, function(result, action) {
10313       return action.func.apply(action.thisArg, arrayPush([result], action.args));
10314     }, result);
10315   }
10316
10317   /**
10318    * Casts `value` to an empty array if it's not an array like object.
10319    *
10320    * @private
10321    * @param {*} value The value to inspect.
10322    * @returns {Array|Object} Returns the cast array-like object.
10323    */
10324   function castArrayLikeObject(value) {
10325     return isArrayLikeObject(value) ? value : [];
10326   }
10327
10328   /**
10329    * Casts `value` to a path array if it's not one.
10330    *
10331    * @private
10332    * @param {*} value The value to inspect.
10333    * @returns {Array} Returns the cast property path array.
10334    */
10335   function castPath(value) {
10336     return isArray(value) ? value : stringToPath(value);
10337   }
10338
10339   /**
10340    * Creates a clone of  `buffer`.
10341    *
10342    * @private
10343    * @param {Buffer} buffer The buffer to clone.
10344    * @param {boolean} [isDeep] Specify a deep clone.
10345    * @returns {Buffer} Returns the cloned buffer.
10346    */
10347   function cloneBuffer(buffer, isDeep) {
10348     if (isDeep) {
10349       return buffer.slice();
10350     }
10351     var result = new buffer.constructor(buffer.length);
10352     buffer.copy(result);
10353     return result;
10354   }
10355
10356   /**
10357    * Creates a clone of `arrayBuffer`.
10358    *
10359    * @private
10360    * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
10361    * @returns {ArrayBuffer} Returns the cloned array buffer.
10362    */
10363   function cloneArrayBuffer(arrayBuffer) {
10364     var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
10365     new Uint8Array(result).set(new Uint8Array(arrayBuffer));
10366     return result;
10367   }
10368
10369   /**
10370    * Creates a clone of `dataView`.
10371    *
10372    * @private
10373    * @param {Object} dataView The data view to clone.
10374    * @param {boolean} [isDeep] Specify a deep clone.
10375    * @returns {Object} Returns the cloned data view.
10376    */
10377   function cloneDataView(dataView, isDeep) {
10378     var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
10379     return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
10380   }
10381
10382   /**
10383    * Creates a clone of `map`.
10384    *
10385    * @private
10386    * @param {Object} map The map to clone.
10387    * @param {Function} cloneFunc The function to clone values.
10388    * @param {boolean} [isDeep] Specify a deep clone.
10389    * @returns {Object} Returns the cloned map.
10390    */
10391   function cloneMap(map, isDeep, cloneFunc) {
10392     var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map);
10393     return arrayReduce(array, addMapEntry, new map.constructor);
10394   }
10395
10396   /**
10397    * Creates a clone of `regexp`.
10398    *
10399    * @private
10400    * @param {Object} regexp The regexp to clone.
10401    * @returns {Object} Returns the cloned regexp.
10402    */
10403   function cloneRegExp(regexp) {
10404     var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
10405     result.lastIndex = regexp.lastIndex;
10406     return result;
10407   }
10408
10409   /**
10410    * Creates a clone of `set`.
10411    *
10412    * @private
10413    * @param {Object} set The set to clone.
10414    * @param {Function} cloneFunc The function to clone values.
10415    * @param {boolean} [isDeep] Specify a deep clone.
10416    * @returns {Object} Returns the cloned set.
10417    */
10418   function cloneSet(set, isDeep, cloneFunc) {
10419     var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set);
10420     return arrayReduce(array, addSetEntry, new set.constructor);
10421   }
10422
10423   /**
10424    * Creates a clone of the `symbol` object.
10425    *
10426    * @private
10427    * @param {Object} symbol The symbol object to clone.
10428    * @returns {Object} Returns the cloned symbol object.
10429    */
10430   function cloneSymbol(symbol) {
10431     return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
10432   }
10433
10434   /**
10435    * Creates a clone of `typedArray`.
10436    *
10437    * @private
10438    * @param {Object} typedArray The typed array to clone.
10439    * @param {boolean} [isDeep] Specify a deep clone.
10440    * @returns {Object} Returns the cloned typed array.
10441    */
10442   function cloneTypedArray(typedArray, isDeep) {
10443     var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
10444     return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
10445   }
10446
10447   /**
10448    * Creates an array that is the composition of partially applied arguments,
10449    * placeholders, and provided arguments into a single array of arguments.
10450    *
10451    * @private
10452    * @param {Array} args The provided arguments.
10453    * @param {Array} partials The arguments to prepend to those provided.
10454    * @param {Array} holders The `partials` placeholder indexes.
10455    * @params {boolean} [isCurried] Specify composing for a curried function.
10456    * @returns {Array} Returns the new array of composed arguments.
10457    */
10458   function composeArgs(args, partials, holders, isCurried) {
10459     var argsIndex = -1,
10460         argsLength = args.length,
10461         holdersLength = holders.length,
10462         leftIndex = -1,
10463         leftLength = partials.length,
10464         rangeLength = nativeMax(argsLength - holdersLength, 0),
10465         result = Array(leftLength + rangeLength),
10466         isUncurried = !isCurried;
10467
10468     while (++leftIndex < leftLength) {
10469       result[leftIndex] = partials[leftIndex];
10470     }
10471     while (++argsIndex < holdersLength) {
10472       if (isUncurried || argsIndex < argsLength) {
10473         result[holders[argsIndex]] = args[argsIndex];
10474       }
10475     }
10476     while (rangeLength--) {
10477       result[leftIndex++] = args[argsIndex++];
10478     }
10479     return result;
10480   }
10481
10482   /**
10483    * This function is like `composeArgs` except that the arguments composition
10484    * is tailored for `_.partialRight`.
10485    *
10486    * @private
10487    * @param {Array} args The provided arguments.
10488    * @param {Array} partials The arguments to append to those provided.
10489    * @param {Array} holders The `partials` placeholder indexes.
10490    * @params {boolean} [isCurried] Specify composing for a curried function.
10491    * @returns {Array} Returns the new array of composed arguments.
10492    */
10493   function composeArgsRight(args, partials, holders, isCurried) {
10494     var argsIndex = -1,
10495         argsLength = args.length,
10496         holdersIndex = -1,
10497         holdersLength = holders.length,
10498         rightIndex = -1,
10499         rightLength = partials.length,
10500         rangeLength = nativeMax(argsLength - holdersLength, 0),
10501         result = Array(rangeLength + rightLength),
10502         isUncurried = !isCurried;
10503
10504     while (++argsIndex < rangeLength) {
10505       result[argsIndex] = args[argsIndex];
10506     }
10507     var offset = argsIndex;
10508     while (++rightIndex < rightLength) {
10509       result[offset + rightIndex] = partials[rightIndex];
10510     }
10511     while (++holdersIndex < holdersLength) {
10512       if (isUncurried || argsIndex < argsLength) {
10513         result[offset + holders[holdersIndex]] = args[argsIndex++];
10514       }
10515     }
10516     return result;
10517   }
10518
10519   /**
10520    * Copies the values of `source` to `array`.
10521    *
10522    * @private
10523    * @param {Array} source The array to copy values from.
10524    * @param {Array} [array=[]] The array to copy values to.
10525    * @returns {Array} Returns `array`.
10526    */
10527   function copyArray(source, array) {
10528     var index = -1,
10529         length = source.length;
10530
10531     array || (array = Array(length));
10532     while (++index < length) {
10533       array[index] = source[index];
10534     }
10535     return array;
10536   }
10537
10538   /**
10539    * Copies properties of `source` to `object`.
10540    *
10541    * @private
10542    * @param {Object} source The object to copy properties from.
10543    * @param {Array} props The property identifiers to copy.
10544    * @param {Object} [object={}] The object to copy properties to.
10545    * @param {Function} [customizer] The function to customize copied values.
10546    * @returns {Object} Returns `object`.
10547    */
10548   function copyObject(source, props, object, customizer) {
10549     object || (object = {});
10550
10551     var index = -1,
10552         length = props.length;
10553
10554     while (++index < length) {
10555       var key = props[index];
10556
10557       var newValue = customizer
10558         ? customizer(object[key], source[key], key, object, source)
10559         : source[key];
10560
10561       assignValue(object, key, newValue);
10562     }
10563     return object;
10564   }
10565
10566   /**
10567    * Copies own symbol properties of `source` to `object`.
10568    *
10569    * @private
10570    * @param {Object} source The object to copy symbols from.
10571    * @param {Object} [object={}] The object to copy symbols to.
10572    * @returns {Object} Returns `object`.
10573    */
10574   function copySymbols(source, object) {
10575     return copyObject(source, getSymbols(source), object);
10576   }
10577
10578   /**
10579    * Creates a function like `_.groupBy`.
10580    *
10581    * @private
10582    * @param {Function} setter The function to set accumulator values.
10583    * @param {Function} [initializer] The accumulator object initializer.
10584    * @returns {Function} Returns the new aggregator function.
10585    */
10586   function createAggregator(setter, initializer) {
10587     return function(collection, iteratee) {
10588       var func = isArray(collection) ? arrayAggregator : baseAggregator,
10589           accumulator = initializer ? initializer() : {};
10590
10591       return func(collection, setter, getIteratee(iteratee), accumulator);
10592     };
10593   }
10594
10595   /**
10596    * Creates a function like `_.assign`.
10597    *
10598    * @private
10599    * @param {Function} assigner The function to assign values.
10600    * @returns {Function} Returns the new assigner function.
10601    */
10602   function createAssigner(assigner) {
10603     return rest(function(object, sources) {
10604       var index = -1,
10605           length = sources.length,
10606           customizer = length > 1 ? sources[length - 1] : undefined,
10607           guard = length > 2 ? sources[2] : undefined;
10608
10609       customizer = (assigner.length > 3 && typeof customizer == 'function')
10610         ? (length--, customizer)
10611         : undefined;
10612
10613       if (guard && isIterateeCall(sources[0], sources[1], guard)) {
10614         customizer = length < 3 ? undefined : customizer;
10615         length = 1;
10616       }
10617       object = Object(object);
10618       while (++index < length) {
10619         var source = sources[index];
10620         if (source) {
10621           assigner(object, source, index, customizer);
10622         }
10623       }
10624       return object;
10625     });
10626   }
10627
10628   /**
10629    * Creates a `baseEach` or `baseEachRight` function.
10630    *
10631    * @private
10632    * @param {Function} eachFunc The function to iterate over a collection.
10633    * @param {boolean} [fromRight] Specify iterating from right to left.
10634    * @returns {Function} Returns the new base function.
10635    */
10636   function createBaseEach(eachFunc, fromRight) {
10637     return function(collection, iteratee) {
10638       if (collection == null) {
10639         return collection;
10640       }
10641       if (!isArrayLike(collection)) {
10642         return eachFunc(collection, iteratee);
10643       }
10644       var length = collection.length,
10645           index = fromRight ? length : -1,
10646           iterable = Object(collection);
10647
10648       while ((fromRight ? index-- : ++index < length)) {
10649         if (iteratee(iterable[index], index, iterable) === false) {
10650           break;
10651         }
10652       }
10653       return collection;
10654     };
10655   }
10656
10657   /**
10658    * Creates a base function for methods like `_.forIn` and `_.forOwn`.
10659    *
10660    * @private
10661    * @param {boolean} [fromRight] Specify iterating from right to left.
10662    * @returns {Function} Returns the new base function.
10663    */
10664   function createBaseFor(fromRight) {
10665     return function(object, iteratee, keysFunc) {
10666       var index = -1,
10667           iterable = Object(object),
10668           props = keysFunc(object),
10669           length = props.length;
10670
10671       while (length--) {
10672         var key = props[fromRight ? length : ++index];
10673         if (iteratee(iterable[key], key, iterable) === false) {
10674           break;
10675         }
10676       }
10677       return object;
10678     };
10679   }
10680
10681   /**
10682    * Creates a function that wraps `func` to invoke it with the optional `this`
10683    * binding of `thisArg`.
10684    *
10685    * @private
10686    * @param {Function} func The function to wrap.
10687    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10688    *  for more details.
10689    * @param {*} [thisArg] The `this` binding of `func`.
10690    * @returns {Function} Returns the new wrapped function.
10691    */
10692   function createBaseWrapper(func, bitmask, thisArg) {
10693     var isBind = bitmask & BIND_FLAG,
10694         Ctor = createCtorWrapper(func);
10695
10696     function wrapper() {
10697       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10698       return fn.apply(isBind ? thisArg : this, arguments);
10699     }
10700     return wrapper;
10701   }
10702
10703   /**
10704    * Creates a function that produces an instance of `Ctor` regardless of
10705    * whether it was invoked as part of a `new` expression or by `call` or `apply`.
10706    *
10707    * @private
10708    * @param {Function} Ctor The constructor to wrap.
10709    * @returns {Function} Returns the new wrapped function.
10710    */
10711   function createCtorWrapper(Ctor) {
10712     return function() {
10713       // Use a `switch` statement to work with class constructors. See
10714       // http://ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
10715       // for more details.
10716       var args = arguments;
10717       switch (args.length) {
10718         case 0: return new Ctor;
10719         case 1: return new Ctor(args[0]);
10720         case 2: return new Ctor(args[0], args[1]);
10721         case 3: return new Ctor(args[0], args[1], args[2]);
10722         case 4: return new Ctor(args[0], args[1], args[2], args[3]);
10723         case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
10724         case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
10725         case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
10726       }
10727       var thisBinding = baseCreate(Ctor.prototype),
10728           result = Ctor.apply(thisBinding, args);
10729
10730       // Mimic the constructor's `return` behavior.
10731       // See https://es5.github.io/#x13.2.2 for more details.
10732       return isObject(result) ? result : thisBinding;
10733     };
10734   }
10735
10736   /**
10737    * Creates a function that wraps `func` to enable currying.
10738    *
10739    * @private
10740    * @param {Function} func The function to wrap.
10741    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10742    *  for more details.
10743    * @param {number} arity The arity of `func`.
10744    * @returns {Function} Returns the new wrapped function.
10745    */
10746   function createCurryWrapper(func, bitmask, arity) {
10747     var Ctor = createCtorWrapper(func);
10748
10749     function wrapper() {
10750       var length = arguments.length,
10751           args = Array(length),
10752           index = length,
10753           placeholder = getHolder(wrapper);
10754
10755       while (index--) {
10756         args[index] = arguments[index];
10757       }
10758       var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder)
10759         ? []
10760         : replaceHolders(args, placeholder);
10761
10762       length -= holders.length;
10763       if (length < arity) {
10764         return createRecurryWrapper(
10765           func, bitmask, createHybridWrapper, wrapper.placeholder, undefined,
10766           args, holders, undefined, undefined, arity - length);
10767       }
10768       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10769       return apply(fn, this, args);
10770     }
10771     return wrapper;
10772   }
10773
10774   /**
10775    * Creates a function that wraps `func` to invoke it with optional `this`
10776    * binding of `thisArg`, partial application, and currying.
10777    *
10778    * @private
10779    * @param {Function|string} func The function or method name to wrap.
10780    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10781    *  for more details.
10782    * @param {*} [thisArg] The `this` binding of `func`.
10783    * @param {Array} [partials] The arguments to prepend to those provided to
10784    *  the new function.
10785    * @param {Array} [holders] The `partials` placeholder indexes.
10786    * @param {Array} [partialsRight] The arguments to append to those provided
10787    *  to the new function.
10788    * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
10789    * @param {Array} [argPos] The argument positions of the new function.
10790    * @param {number} [ary] The arity cap of `func`.
10791    * @param {number} [arity] The arity of `func`.
10792    * @returns {Function} Returns the new wrapped function.
10793    */
10794   function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
10795     var isAry = bitmask & ARY_FLAG,
10796         isBind = bitmask & BIND_FLAG,
10797         isBindKey = bitmask & BIND_KEY_FLAG,
10798         isCurried = bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG),
10799         isFlip = bitmask & FLIP_FLAG,
10800         Ctor = isBindKey ? undefined : createCtorWrapper(func);
10801
10802     function wrapper() {
10803       var length = arguments.length,
10804           args = Array(length),
10805           index = length;
10806
10807       while (index--) {
10808         args[index] = arguments[index];
10809       }
10810       if (isCurried) {
10811         var placeholder = getHolder(wrapper),
10812             holdersCount = countHolders(args, placeholder);
10813       }
10814       if (partials) {
10815         args = composeArgs(args, partials, holders, isCurried);
10816       }
10817       if (partialsRight) {
10818         args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
10819       }
10820       length -= holdersCount;
10821       if (isCurried && length < arity) {
10822         var newHolders = replaceHolders(args, placeholder);
10823         return createRecurryWrapper(
10824           func, bitmask, createHybridWrapper, wrapper.placeholder, thisArg,
10825           args, newHolders, argPos, ary, arity - length
10826         );
10827       }
10828       var thisBinding = isBind ? thisArg : this,
10829           fn = isBindKey ? thisBinding[func] : func;
10830
10831       length = args.length;
10832       if (argPos) {
10833         args = reorder(args, argPos);
10834       } else if (isFlip && length > 1) {
10835         args.reverse();
10836       }
10837       if (isAry && ary < length) {
10838         args.length = ary;
10839       }
10840       if (this && this !== root && this instanceof wrapper) {
10841         fn = Ctor || createCtorWrapper(fn);
10842       }
10843       return fn.apply(thisBinding, args);
10844     }
10845     return wrapper;
10846   }
10847
10848   /**
10849    * Creates a function that wraps `func` to invoke it with the `this` binding
10850    * of `thisArg` and `partials` prepended to the arguments it receives.
10851    *
10852    * @private
10853    * @param {Function} func The function to wrap.
10854    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10855    *  for more details.
10856    * @param {*} thisArg The `this` binding of `func`.
10857    * @param {Array} partials The arguments to prepend to those provided to
10858    *  the new function.
10859    * @returns {Function} Returns the new wrapped function.
10860    */
10861   function createPartialWrapper(func, bitmask, thisArg, partials) {
10862     var isBind = bitmask & BIND_FLAG,
10863         Ctor = createCtorWrapper(func);
10864
10865     function wrapper() {
10866       var argsIndex = -1,
10867           argsLength = arguments.length,
10868           leftIndex = -1,
10869           leftLength = partials.length,
10870           args = Array(leftLength + argsLength),
10871           fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10872
10873       while (++leftIndex < leftLength) {
10874         args[leftIndex] = partials[leftIndex];
10875       }
10876       while (argsLength--) {
10877         args[leftIndex++] = arguments[++argsIndex];
10878       }
10879       return apply(fn, isBind ? thisArg : this, args);
10880     }
10881     return wrapper;
10882   }
10883
10884   /**
10885    * Creates a function that wraps `func` to continue currying.
10886    *
10887    * @private
10888    * @param {Function} func The function to wrap.
10889    * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`
10890    *  for more details.
10891    * @param {Function} wrapFunc The function to create the `func` wrapper.
10892    * @param {*} placeholder The placeholder value.
10893    * @param {*} [thisArg] The `this` binding of `func`.
10894    * @param {Array} [partials] The arguments to prepend to those provided to
10895    *  the new function.
10896    * @param {Array} [holders] The `partials` placeholder indexes.
10897    * @param {Array} [argPos] The argument positions of the new function.
10898    * @param {number} [ary] The arity cap of `func`.
10899    * @param {number} [arity] The arity of `func`.
10900    * @returns {Function} Returns the new wrapped function.
10901    */
10902   function createRecurryWrapper(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {
10903     var isCurry = bitmask & CURRY_FLAG,
10904         newHolders = isCurry ? holders : undefined,
10905         newHoldersRight = isCurry ? undefined : holders,
10906         newPartials = isCurry ? partials : undefined,
10907         newPartialsRight = isCurry ? undefined : partials;
10908
10909     bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
10910     bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
10911
10912     if (!(bitmask & CURRY_BOUND_FLAG)) {
10913       bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
10914     }
10915     var newData = [
10916       func, bitmask, thisArg, newPartials, newHolders, newPartialsRight,
10917       newHoldersRight, argPos, ary, arity
10918     ];
10919
10920     var result = wrapFunc.apply(undefined, newData);
10921     if (isLaziable(func)) {
10922       setData(result, newData);
10923     }
10924     result.placeholder = placeholder;
10925     return result;
10926   }
10927
10928   /**
10929    * Creates a set of `values`.
10930    *
10931    * @private
10932    * @param {Array} values The values to add to the set.
10933    * @returns {Object} Returns the new set.
10934    */
10935   var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) {
10936     return new Set(values);
10937   };
10938
10939   /**
10940    * Creates a `_.toPairs` or `_.toPairsIn` function.
10941    *
10942    * @private
10943    * @param {Function} keysFunc The function to get the keys of a given object.
10944    * @returns {Function} Returns the new pairs function.
10945    */
10946   function createToPairs(keysFunc) {
10947     return function(object) {
10948       var tag = getTag(object);
10949       if (tag == mapTag) {
10950         return mapToArray(object);
10951       }
10952       if (tag == setTag) {
10953         return setToPairs(object);
10954       }
10955       return baseToPairs(object, keysFunc(object));
10956     };
10957   }
10958
10959   /**
10960    * Creates a function that either curries or invokes `func` with optional
10961    * `this` binding and partially applied arguments.
10962    *
10963    * @private
10964    * @param {Function|string} func The function or method name to wrap.
10965    * @param {number} bitmask The bitmask of wrapper flags.
10966    *  The bitmask may be composed of the following flags:
10967    *     1 - `_.bind`
10968    *     2 - `_.bindKey`
10969    *     4 - `_.curry` or `_.curryRight` of a bound function
10970    *     8 - `_.curry`
10971    *    16 - `_.curryRight`
10972    *    32 - `_.partial`
10973    *    64 - `_.partialRight`
10974    *   128 - `_.rearg`
10975    *   256 - `_.ary`
10976    *   512 - `_.flip`
10977    * @param {*} [thisArg] The `this` binding of `func`.
10978    * @param {Array} [partials] The arguments to be partially applied.
10979    * @param {Array} [holders] The `partials` placeholder indexes.
10980    * @param {Array} [argPos] The argument positions of the new function.
10981    * @param {number} [ary] The arity cap of `func`.
10982    * @param {number} [arity] The arity of `func`.
10983    * @returns {Function} Returns the new wrapped function.
10984    */
10985   function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
10986     var isBindKey = bitmask & BIND_KEY_FLAG;
10987     if (!isBindKey && typeof func != 'function') {
10988       throw new TypeError(FUNC_ERROR_TEXT);
10989     }
10990     var length = partials ? partials.length : 0;
10991     if (!length) {
10992       bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
10993       partials = holders = undefined;
10994     }
10995     ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);
10996     arity = arity === undefined ? arity : toInteger(arity);
10997     length -= holders ? holders.length : 0;
10998
10999     if (bitmask & PARTIAL_RIGHT_FLAG) {
11000       var partialsRight = partials,
11001           holdersRight = holders;
11002
11003       partials = holders = undefined;
11004     }
11005     var data = isBindKey ? undefined : getData(func);
11006
11007     var newData = [
11008       func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,
11009       argPos, ary, arity
11010     ];
11011
11012     if (data) {
11013       mergeData(newData, data);
11014     }
11015     func = newData[0];
11016     bitmask = newData[1];
11017     thisArg = newData[2];
11018     partials = newData[3];
11019     holders = newData[4];
11020     arity = newData[9] = newData[9] == null
11021       ? (isBindKey ? 0 : func.length)
11022       : nativeMax(newData[9] - length, 0);
11023
11024     if (!arity && bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG)) {
11025       bitmask &= ~(CURRY_FLAG | CURRY_RIGHT_FLAG);
11026     }
11027     if (!bitmask || bitmask == BIND_FLAG) {
11028       var result = createBaseWrapper(func, bitmask, thisArg);
11029     } else if (bitmask == CURRY_FLAG || bitmask == CURRY_RIGHT_FLAG) {
11030       result = createCurryWrapper(func, bitmask, arity);
11031     } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !holders.length) {
11032       result = createPartialWrapper(func, bitmask, thisArg, partials);
11033     } else {
11034       result = createHybridWrapper.apply(undefined, newData);
11035     }
11036     var setter = data ? baseSetData : setData;
11037     return setter(result, newData);
11038   }
11039
11040   /**
11041    * A specialized version of `baseIsEqualDeep` for arrays with support for
11042    * partial deep comparisons.
11043    *
11044    * @private
11045    * @param {Array} array The array to compare.
11046    * @param {Array} other The other array to compare.
11047    * @param {Function} equalFunc The function to determine equivalents of values.
11048    * @param {Function} customizer The function to customize comparisons.
11049    * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
11050    *  for more details.
11051    * @param {Object} stack Tracks traversed `array` and `other` objects.
11052    * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
11053    */
11054   function equalArrays(array, other, equalFunc, customizer, bitmask, stack) {
11055     var isPartial = bitmask & PARTIAL_COMPARE_FLAG,
11056         arrLength = array.length,
11057         othLength = other.length;
11058
11059     if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
11060       return false;
11061     }
11062     // Assume cyclic values are equal.
11063     var stacked = stack.get(array);
11064     if (stacked) {
11065       return stacked == other;
11066     }
11067     var index = -1,
11068         result = true,
11069         seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined;
11070
11071     stack.set(array, other);
11072
11073     // Ignore non-index properties.
11074     while (++index < arrLength) {
11075       var arrValue = array[index],
11076           othValue = other[index];
11077
11078       if (customizer) {
11079         var compared = isPartial
11080           ? customizer(othValue, arrValue, index, other, array, stack)
11081           : customizer(arrValue, othValue, index, array, other, stack);
11082       }
11083       if (compared !== undefined) {
11084         if (compared) {
11085           continue;
11086         }
11087         result = false;
11088         break;
11089       }
11090       // Recursively compare arrays (susceptible to call stack limits).
11091       if (seen) {
11092         if (!arraySome(other, function(othValue, othIndex) {
11093               if (!seen.has(othIndex) &&
11094                   (arrValue === othValue || equalFunc(arrValue, othValue, customizer, bitmask, stack))) {
11095                 return seen.add(othIndex);
11096               }
11097             })) {
11098           result = false;
11099           break;
11100         }
11101       } else if (!(
11102             arrValue === othValue ||
11103               equalFunc(arrValue, othValue, customizer, bitmask, stack)
11104           )) {
11105         result = false;
11106         break;
11107       }
11108     }
11109     stack['delete'](array);
11110     return result;
11111   }
11112
11113   /**
11114    * A specialized version of `baseIsEqualDeep` for comparing objects of
11115    * the same `toStringTag`.
11116    *
11117    * **Note:** This function only supports comparing values with tags of
11118    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
11119    *
11120    * @private
11121    * @param {Object} object The object to compare.
11122    * @param {Object} other The other object to compare.
11123    * @param {string} tag The `toStringTag` of the objects to compare.
11124    * @param {Function} equalFunc The function to determine equivalents of values.
11125    * @param {Function} customizer The function to customize comparisons.
11126    * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
11127    *  for more details.
11128    * @param {Object} stack Tracks traversed `object` and `other` objects.
11129    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
11130    */
11131   function equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) {
11132     switch (tag) {
11133       case dataViewTag:
11134         if ((object.byteLength != other.byteLength) ||
11135             (object.byteOffset != other.byteOffset)) {
11136           return false;
11137         }
11138         object = object.buffer;
11139         other = other.buffer;
11140
11141       case arrayBufferTag:
11142         if ((object.byteLength != other.byteLength) ||
11143             !equalFunc(new Uint8Array(object), new Uint8Array(other))) {
11144           return false;
11145         }
11146         return true;
11147
11148       case boolTag:
11149       case dateTag:
11150         // Coerce dates and booleans to numbers, dates to milliseconds and
11151         // booleans to `1` or `0` treating invalid dates coerced to `NaN` as
11152         // not equal.
11153         return +object == +other;
11154
11155       case errorTag:
11156         return object.name == other.name && object.message == other.message;
11157
11158       case numberTag:
11159         // Treat `NaN` vs. `NaN` as equal.
11160         return (object != +object) ? other != +other : object == +other;
11161
11162       case regexpTag:
11163       case stringTag:
11164         // Coerce regexes to strings and treat strings, primitives and objects,
11165         // as equal. See http://www.ecma-international.org/ecma-262/6.0/#sec-regexp.prototype.tostring
11166         // for more details.
11167         return object == (other + '');
11168
11169       case mapTag:
11170         var convert = mapToArray;
11171
11172       case setTag:
11173         var isPartial = bitmask & PARTIAL_COMPARE_FLAG;
11174         convert || (convert = setToArray);
11175
11176         if (object.size != other.size && !isPartial) {
11177           return false;
11178         }
11179         // Assume cyclic values are equal.
11180         var stacked = stack.get(object);
11181         if (stacked) {
11182           return stacked == other;
11183         }
11184         bitmask |= UNORDERED_COMPARE_FLAG;
11185         stack.set(object, other);
11186
11187         // Recursively compare objects (susceptible to call stack limits).
11188         return equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask, stack);
11189
11190       case symbolTag:
11191         if (symbolValueOf) {
11192           return symbolValueOf.call(object) == symbolValueOf.call(other);
11193         }
11194     }
11195     return false;
11196   }
11197
11198   /**
11199    * A specialized version of `baseIsEqualDeep` for objects with support for
11200    * partial deep comparisons.
11201    *
11202    * @private
11203    * @param {Object} object The object to compare.
11204    * @param {Object} other The other object to compare.
11205    * @param {Function} equalFunc The function to determine equivalents of values.
11206    * @param {Function} customizer The function to customize comparisons.
11207    * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
11208    *  for more details.
11209    * @param {Object} stack Tracks traversed `object` and `other` objects.
11210    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
11211    */
11212   function equalObjects(object, other, equalFunc, customizer, bitmask, stack) {
11213     var isPartial = bitmask & PARTIAL_COMPARE_FLAG,
11214         objProps = keys(object),
11215         objLength = objProps.length,
11216         othProps = keys(other),
11217         othLength = othProps.length;
11218
11219     if (objLength != othLength && !isPartial) {
11220       return false;
11221     }
11222     var index = objLength;
11223     while (index--) {
11224       var key = objProps[index];
11225       if (!(isPartial ? key in other : baseHas(other, key))) {
11226         return false;
11227       }
11228     }
11229     // Assume cyclic values are equal.
11230     var stacked = stack.get(object);
11231     if (stacked) {
11232       return stacked == other;
11233     }
11234     var result = true;
11235     stack.set(object, other);
11236
11237     var skipCtor = isPartial;
11238     while (++index < objLength) {
11239       key = objProps[index];
11240       var objValue = object[key],
11241           othValue = other[key];
11242
11243       if (customizer) {
11244         var compared = isPartial
11245           ? customizer(othValue, objValue, key, other, object, stack)
11246           : customizer(objValue, othValue, key, object, other, stack);
11247       }
11248       // Recursively compare objects (susceptible to call stack limits).
11249       if (!(compared === undefined
11250             ? (objValue === othValue || equalFunc(objValue, othValue, customizer, bitmask, stack))
11251             : compared
11252           )) {
11253         result = false;
11254         break;
11255       }
11256       skipCtor || (skipCtor = key == 'constructor');
11257     }
11258     if (result && !skipCtor) {
11259       var objCtor = object.constructor,
11260           othCtor = other.constructor;
11261
11262       // Non `Object` object instances with different constructors are not equal.
11263       if (objCtor != othCtor &&
11264           ('constructor' in object && 'constructor' in other) &&
11265           !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
11266             typeof othCtor == 'function' && othCtor instanceof othCtor)) {
11267         result = false;
11268       }
11269     }
11270     stack['delete'](object);
11271     return result;
11272   }
11273
11274   /**
11275    * Creates an array of own enumerable property names and symbols of `object`.
11276    *
11277    * @private
11278    * @param {Object} object The object to query.
11279    * @returns {Array} Returns the array of property names and symbols.
11280    */
11281   function getAllKeys(object) {
11282     return baseGetAllKeys(object, keys, getSymbols);
11283   }
11284
11285   /**
11286    * Creates an array of own and inherited enumerable property names and
11287    * symbols of `object`.
11288    *
11289    * @private
11290    * @param {Object} object The object to query.
11291    * @returns {Array} Returns the array of property names and symbols.
11292    */
11293   function getAllKeysIn(object) {
11294     return baseGetAllKeys(object, keysIn, getSymbolsIn);
11295   }
11296
11297   /**
11298    * Gets metadata for `func`.
11299    *
11300    * @private
11301    * @param {Function} func The function to query.
11302    * @returns {*} Returns the metadata for `func`.
11303    */
11304   var getData = !metaMap ? noop : function(func) {
11305     return metaMap.get(func);
11306   };
11307
11308   /**
11309    * Gets the name of `func`.
11310    *
11311    * @private
11312    * @param {Function} func The function to query.
11313    * @returns {string} Returns the function name.
11314    */
11315   function getFuncName(func) {
11316     var result = (func.name + ''),
11317         array = realNames[result],
11318         length = hasOwnProperty.call(realNames, result) ? array.length : 0;
11319
11320     while (length--) {
11321       var data = array[length],
11322           otherFunc = data.func;
11323       if (otherFunc == null || otherFunc == func) {
11324         return data.name;
11325       }
11326     }
11327     return result;
11328   }
11329
11330   /**
11331    * Gets the argument placeholder value for `func`.
11332    *
11333    * @private
11334    * @param {Function} func The function to inspect.
11335    * @returns {*} Returns the placeholder value.
11336    */
11337   function getHolder(func) {
11338     var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;
11339     return object.placeholder;
11340   }
11341
11342   /**
11343    * Gets the appropriate "iteratee" function. If `_.iteratee` is customized,
11344    * this function returns the custom method, otherwise it returns `baseIteratee`.
11345    * If arguments are provided, the chosen function is invoked with them and
11346    * its result is returned.
11347    *
11348    * @private
11349    * @param {*} [value] The value to convert to an iteratee.
11350    * @param {number} [arity] The arity of the created iteratee.
11351    * @returns {Function} Returns the chosen function or its result.
11352    */
11353   function getIteratee() {
11354     var result = lodash.iteratee || iteratee;
11355     result = result === iteratee ? baseIteratee : result;
11356     return arguments.length ? result(arguments[0], arguments[1]) : result;
11357   }
11358
11359   /**
11360    * Gets the "length" property value of `object`.
11361    *
11362    * **Note:** This function is used to avoid a
11363    * [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) that affects
11364    * Safari on at least iOS 8.1-8.3 ARM64.
11365    *
11366    * @private
11367    * @param {Object} object The object to query.
11368    * @returns {*} Returns the "length" value.
11369    */
11370   var getLength = baseProperty('length');
11371
11372   /**
11373    * Gets the data for `map`.
11374    *
11375    * @private
11376    * @param {Object} map The map to query.
11377    * @param {string} key The reference key.
11378    * @returns {*} Returns the map data.
11379    */
11380   function getMapData(map, key) {
11381     var data = map.__data__;
11382     return isKeyable(key)
11383       ? data[typeof key == 'string' ? 'string' : 'hash']
11384       : data.map;
11385   }
11386
11387   /**
11388    * Gets the property names, values, and compare flags of `object`.
11389    *
11390    * @private
11391    * @param {Object} object The object to query.
11392    * @returns {Array} Returns the match data of `object`.
11393    */
11394   function getMatchData(object) {
11395     var result = toPairs(object),
11396         length = result.length;
11397
11398     while (length--) {
11399       result[length][2] = isStrictComparable(result[length][1]);
11400     }
11401     return result;
11402   }
11403
11404   /**
11405    * Gets the native function at `key` of `object`.
11406    *
11407    * @private
11408    * @param {Object} object The object to query.
11409    * @param {string} key The key of the method to get.
11410    * @returns {*} Returns the function if it's native, else `undefined`.
11411    */
11412   function getNative(object, key) {
11413     var value = object[key];
11414     return isNative(value) ? value : undefined;
11415   }
11416
11417   /**
11418    * Gets the `[[Prototype]]` of `value`.
11419    *
11420    * @private
11421    * @param {*} value The value to query.
11422    * @returns {null|Object} Returns the `[[Prototype]]`.
11423    */
11424   function getPrototype(value) {
11425     return nativeGetPrototype(Object(value));
11426   }
11427
11428   /**
11429    * Creates an array of the own enumerable symbol properties of `object`.
11430    *
11431    * @private
11432    * @param {Object} object The object to query.
11433    * @returns {Array} Returns the array of symbols.
11434    */
11435   function getSymbols(object) {
11436     // Coerce `object` to an object to avoid non-object errors in V8.
11437     // See https://bugs.chromium.org/p/v8/issues/detail?id=3443 for more details.
11438     return getOwnPropertySymbols(Object(object));
11439   }
11440
11441   // Fallback for IE < 11.
11442   if (!getOwnPropertySymbols) {
11443     getSymbols = function() {
11444       return [];
11445     };
11446   }
11447
11448   /**
11449    * Creates an array of the own and inherited enumerable symbol properties
11450    * of `object`.
11451    *
11452    * @private
11453    * @param {Object} object The object to query.
11454    * @returns {Array} Returns the array of symbols.
11455    */
11456   var getSymbolsIn = !getOwnPropertySymbols ? getSymbols : function(object) {
11457     var result = [];
11458     while (object) {
11459       arrayPush(result, getSymbols(object));
11460       object = getPrototype(object);
11461     }
11462     return result;
11463   };
11464
11465   /**
11466    * Gets the `toStringTag` of `value`.
11467    *
11468    * @private
11469    * @param {*} value The value to query.
11470    * @returns {string} Returns the `toStringTag`.
11471    */
11472   function getTag(value) {
11473     return objectToString.call(value);
11474   }
11475
11476   // Fallback for data views, maps, sets, and weak maps in IE 11,
11477   // for data views in Edge, and promises in Node.js.
11478   if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
11479       (Map && getTag(new Map) != mapTag) ||
11480       (Promise && getTag(Promise.resolve()) != promiseTag) ||
11481       (Set && getTag(new Set) != setTag) ||
11482       (WeakMap && getTag(new WeakMap) != weakMapTag)) {
11483     getTag = function(value) {
11484       var result = objectToString.call(value),
11485           Ctor = result == objectTag ? value.constructor : undefined,
11486           ctorString = Ctor ? toSource(Ctor) : undefined;
11487
11488       if (ctorString) {
11489         switch (ctorString) {
11490           case dataViewCtorString: return dataViewTag;
11491           case mapCtorString: return mapTag;
11492           case promiseCtorString: return promiseTag;
11493           case setCtorString: return setTag;
11494           case weakMapCtorString: return weakMapTag;
11495         }
11496       }
11497       return result;
11498     };
11499   }
11500
11501   /**
11502    * Gets the view, applying any `transforms` to the `start` and `end` positions.
11503    *
11504    * @private
11505    * @param {number} start The start of the view.
11506    * @param {number} end The end of the view.
11507    * @param {Array} transforms The transformations to apply to the view.
11508    * @returns {Object} Returns an object containing the `start` and `end`
11509    *  positions of the view.
11510    */
11511   function getView(start, end, transforms) {
11512     var index = -1,
11513         length = transforms.length;
11514
11515     while (++index < length) {
11516       var data = transforms[index],
11517           size = data.size;
11518
11519       switch (data.type) {
11520         case 'drop':      start += size; break;
11521         case 'dropRight': end -= size; break;
11522         case 'take':      end = nativeMin(end, start + size); break;
11523         case 'takeRight': start = nativeMax(start, end - size); break;
11524       }
11525     }
11526     return { 'start': start, 'end': end };
11527   }
11528
11529   /**
11530    * Checks if `path` exists on `object`.
11531    *
11532    * @private
11533    * @param {Object} object The object to query.
11534    * @param {Array|string} path The path to check.
11535    * @param {Function} hasFunc The function to check properties.
11536    * @returns {boolean} Returns `true` if `path` exists, else `false`.
11537    */
11538   function hasPath(object, path, hasFunc) {
11539     path = isKey(path, object) ? [path] : castPath(path);
11540
11541     var result,
11542         index = -1,
11543         length = path.length;
11544
11545     while (++index < length) {
11546       var key = toKey(path[index]);
11547       if (!(result = object != null && hasFunc(object, key))) {
11548         break;
11549       }
11550       object = object[key];
11551     }
11552     if (result) {
11553       return result;
11554     }
11555     var length = object ? object.length : 0;
11556     return !!length && isLength(length) && isIndex(key, length) &&
11557       (isArray(object) || isString(object) || isArguments(object));
11558   }
11559
11560   /**
11561    * Initializes an array clone.
11562    *
11563    * @private
11564    * @param {Array} array The array to clone.
11565    * @returns {Array} Returns the initialized clone.
11566    */
11567   function initCloneArray(array) {
11568     var length = array.length,
11569         result = array.constructor(length);
11570
11571     // Add properties assigned by `RegExp#exec`.
11572     if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
11573       result.index = array.index;
11574       result.input = array.input;
11575     }
11576     return result;
11577   }
11578
11579   /**
11580    * Initializes an object clone.
11581    *
11582    * @private
11583    * @param {Object} object The object to clone.
11584    * @returns {Object} Returns the initialized clone.
11585    */
11586   function initCloneObject(object) {
11587     return (typeof object.constructor == 'function' && !isPrototype(object))
11588       ? baseCreate(getPrototype(object))
11589       : {};
11590   }
11591
11592   /**
11593    * Initializes an object clone based on its `toStringTag`.
11594    *
11595    * **Note:** This function only supports cloning values with tags of
11596    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
11597    *
11598    * @private
11599    * @param {Object} object The object to clone.
11600    * @param {string} tag The `toStringTag` of the object to clone.
11601    * @param {Function} cloneFunc The function to clone values.
11602    * @param {boolean} [isDeep] Specify a deep clone.
11603    * @returns {Object} Returns the initialized clone.
11604    */
11605   function initCloneByTag(object, tag, cloneFunc, isDeep) {
11606     var Ctor = object.constructor;
11607     switch (tag) {
11608       case arrayBufferTag:
11609         return cloneArrayBuffer(object);
11610
11611       case boolTag:
11612       case dateTag:
11613         return new Ctor(+object);
11614
11615       case dataViewTag:
11616         return cloneDataView(object, isDeep);
11617
11618       case float32Tag: case float64Tag:
11619       case int8Tag: case int16Tag: case int32Tag:
11620       case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
11621         return cloneTypedArray(object, isDeep);
11622
11623       case mapTag:
11624         return cloneMap(object, isDeep, cloneFunc);
11625
11626       case numberTag:
11627       case stringTag:
11628         return new Ctor(object);
11629
11630       case regexpTag:
11631         return cloneRegExp(object);
11632
11633       case setTag:
11634         return cloneSet(object, isDeep, cloneFunc);
11635
11636       case symbolTag:
11637         return cloneSymbol(object);
11638     }
11639   }
11640
11641   /**
11642    * Creates an array of index keys for `object` values of arrays,
11643    * `arguments` objects, and strings, otherwise `null` is returned.
11644    *
11645    * @private
11646    * @param {Object} object The object to query.
11647    * @returns {Array|null} Returns index keys, else `null`.
11648    */
11649   function indexKeys(object) {
11650     var length = object ? object.length : undefined;
11651     if (isLength(length) &&
11652         (isArray(object) || isString(object) || isArguments(object))) {
11653       return baseTimes(length, String);
11654     }
11655     return null;
11656   }
11657
11658   /**
11659    * Checks if `value` is a flattenable `arguments` object or array.
11660    *
11661    * @private
11662    * @param {*} value The value to check.
11663    * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
11664    */
11665   function isFlattenable(value) {
11666     return isArray(value) || isArguments(value);
11667   }
11668
11669   /**
11670    * Checks if `value` is a valid array-like index.
11671    *
11672    * @private
11673    * @param {*} value The value to check.
11674    * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
11675    * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
11676    */
11677   function isIndex(value, length) {
11678     length = length == null ? MAX_SAFE_INTEGER : length;
11679     return !!length &&
11680       (typeof value == 'number' || reIsUint.test(value)) &&
11681       (value > -1 && value % 1 == 0 && value < length);
11682   }
11683
11684   /**
11685    * Checks if the given arguments are from an iteratee call.
11686    *
11687    * @private
11688    * @param {*} value The potential iteratee value argument.
11689    * @param {*} index The potential iteratee index or key argument.
11690    * @param {*} object The potential iteratee object argument.
11691    * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
11692    *  else `false`.
11693    */
11694   function isIterateeCall(value, index, object) {
11695     if (!isObject(object)) {
11696       return false;
11697     }
11698     var type = typeof index;
11699     if (type == 'number'
11700           ? (isArrayLike(object) && isIndex(index, object.length))
11701           : (type == 'string' && index in object)
11702         ) {
11703       return eq(object[index], value);
11704     }
11705     return false;
11706   }
11707
11708   /**
11709    * Checks if `value` is a property name and not a property path.
11710    *
11711    * @private
11712    * @param {*} value The value to check.
11713    * @param {Object} [object] The object to query keys on.
11714    * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
11715    */
11716   function isKey(value, object) {
11717     if (isArray(value)) {
11718       return false;
11719     }
11720     var type = typeof value;
11721     if (type == 'number' || type == 'symbol' || type == 'boolean' ||
11722         value == null || isSymbol(value)) {
11723       return true;
11724     }
11725     return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
11726       (object != null && value in Object(object));
11727   }
11728
11729   /**
11730    * Checks if `value` is suitable for use as unique object key.
11731    *
11732    * @private
11733    * @param {*} value The value to check.
11734    * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
11735    */
11736   function isKeyable(value) {
11737     var type = typeof value;
11738     return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
11739       ? (value !== '__proto__')
11740       : (value === null);
11741   }
11742
11743   /**
11744    * Checks if `func` has a lazy counterpart.
11745    *
11746    * @private
11747    * @param {Function} func The function to check.
11748    * @returns {boolean} Returns `true` if `func` has a lazy counterpart,
11749    *  else `false`.
11750    */
11751   function isLaziable(func) {
11752     var funcName = getFuncName(func),
11753         other = lodash[funcName];
11754
11755     if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {
11756       return false;
11757     }
11758     if (func === other) {
11759       return true;
11760     }
11761     var data = getData(other);
11762     return !!data && func === data[0];
11763   }
11764
11765   /**
11766    * Checks if `value` is likely a prototype object.
11767    *
11768    * @private
11769    * @param {*} value The value to check.
11770    * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
11771    */
11772   function isPrototype(value) {
11773     var Ctor = value && value.constructor,
11774         proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;
11775
11776     return value === proto;
11777   }
11778
11779   /**
11780    * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
11781    *
11782    * @private
11783    * @param {*} value The value to check.
11784    * @returns {boolean} Returns `true` if `value` if suitable for strict
11785    *  equality comparisons, else `false`.
11786    */
11787   function isStrictComparable(value) {
11788     return value === value && !isObject(value);
11789   }
11790
11791   /**
11792    * A specialized version of `matchesProperty` for source values suitable
11793    * for strict equality comparisons, i.e. `===`.
11794    *
11795    * @private
11796    * @param {string} key The key of the property to get.
11797    * @param {*} srcValue The value to match.
11798    * @returns {Function} Returns the new spec function.
11799    */
11800   function matchesStrictComparable(key, srcValue) {
11801     return function(object) {
11802       if (object == null) {
11803         return false;
11804       }
11805       return object[key] === srcValue &&
11806         (srcValue !== undefined || (key in Object(object)));
11807     };
11808   }
11809
11810   /**
11811    * Merges the function metadata of `source` into `data`.
11812    *
11813    * Merging metadata reduces the number of wrappers used to invoke a function.
11814    * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
11815    * may be applied regardless of execution order. Methods like `_.ary` and
11816    * `_.rearg` modify function arguments, making the order in which they are
11817    * executed important, preventing the merging of metadata. However, we make
11818    * an exception for a safe combined case where curried functions have `_.ary`
11819    * and or `_.rearg` applied.
11820    *
11821    * @private
11822    * @param {Array} data The destination metadata.
11823    * @param {Array} source The source metadata.
11824    * @returns {Array} Returns `data`.
11825    */
11826   function mergeData(data, source) {
11827     var bitmask = data[1],
11828         srcBitmask = source[1],
11829         newBitmask = bitmask | srcBitmask,
11830         isCommon = newBitmask < (BIND_FLAG | BIND_KEY_FLAG | ARY_FLAG);
11831
11832     var isCombo =
11833       ((srcBitmask == ARY_FLAG) && (bitmask == CURRY_FLAG)) ||
11834       ((srcBitmask == ARY_FLAG) && (bitmask == REARG_FLAG) && (data[7].length <= source[8])) ||
11835       ((srcBitmask == (ARY_FLAG | REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == CURRY_FLAG));
11836
11837     // Exit early if metadata can't be merged.
11838     if (!(isCommon || isCombo)) {
11839       return data;
11840     }
11841     // Use source `thisArg` if available.
11842     if (srcBitmask & BIND_FLAG) {
11843       data[2] = source[2];
11844       // Set when currying a bound function.
11845       newBitmask |= bitmask & BIND_FLAG ? 0 : CURRY_BOUND_FLAG;
11846     }
11847     // Compose partial arguments.
11848     var value = source[3];
11849     if (value) {
11850       var partials = data[3];
11851       data[3] = partials ? composeArgs(partials, value, source[4]) : value;
11852       data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];
11853     }
11854     // Compose partial right arguments.
11855     value = source[5];
11856     if (value) {
11857       partials = data[5];
11858       data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;
11859       data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];
11860     }
11861     // Use source `argPos` if available.
11862     value = source[7];
11863     if (value) {
11864       data[7] = value;
11865     }
11866     // Use source `ary` if it's smaller.
11867     if (srcBitmask & ARY_FLAG) {
11868       data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
11869     }
11870     // Use source `arity` if one is not provided.
11871     if (data[9] == null) {
11872       data[9] = source[9];
11873     }
11874     // Use source `func` and merge bitmasks.
11875     data[0] = source[0];
11876     data[1] = newBitmask;
11877
11878     return data;
11879   }
11880
11881   /**
11882    * Gets the parent value at `path` of `object`.
11883    *
11884    * @private
11885    * @param {Object} object The object to query.
11886    * @param {Array} path The path to get the parent value of.
11887    * @returns {*} Returns the parent value.
11888    */
11889   function parent(object, path) {
11890     return path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
11891   }
11892
11893   /**
11894    * Reorder `array` according to the specified indexes where the element at
11895    * the first index is assigned as the first element, the element at
11896    * the second index is assigned as the second element, and so on.
11897    *
11898    * @private
11899    * @param {Array} array The array to reorder.
11900    * @param {Array} indexes The arranged array indexes.
11901    * @returns {Array} Returns `array`.
11902    */
11903   function reorder(array, indexes) {
11904     var arrLength = array.length,
11905         length = nativeMin(indexes.length, arrLength),
11906         oldArray = copyArray(array);
11907
11908     while (length--) {
11909       var index = indexes[length];
11910       array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
11911     }
11912     return array;
11913   }
11914
11915   /**
11916    * Sets metadata for `func`.
11917    *
11918    * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
11919    * period of time, it will trip its breaker and transition to an identity
11920    * function to avoid garbage collection pauses in V8. See
11921    * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)
11922    * for more details.
11923    *
11924    * @private
11925    * @param {Function} func The function to associate metadata with.
11926    * @param {*} data The metadata.
11927    * @returns {Function} Returns `func`.
11928    */
11929   var setData = (function() {
11930     var count = 0,
11931         lastCalled = 0;
11932
11933     return function(key, value) {
11934       var stamp = now(),
11935           remaining = HOT_SPAN - (stamp - lastCalled);
11936
11937       lastCalled = stamp;
11938       if (remaining > 0) {
11939         if (++count >= HOT_COUNT) {
11940           return key;
11941         }
11942       } else {
11943         count = 0;
11944       }
11945       return baseSetData(key, value);
11946     };
11947   }());
11948
11949   /**
11950    * Converts `string` to a property path array.
11951    *
11952    * @private
11953    * @param {string} string The string to convert.
11954    * @returns {Array} Returns the property path array.
11955    */
11956   var stringToPath = memoize(function(string) {
11957     var result = [];
11958     toString(string).replace(rePropName, function(match, number, quote, string) {
11959       result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
11960     });
11961     return result;
11962   });
11963
11964   /**
11965    * Converts `value` to a string key if it's not a string or symbol.
11966    *
11967    * @private
11968    * @param {*} value The value to inspect.
11969    * @returns {string|symbol} Returns the key.
11970    */
11971   function toKey(value) {
11972     if (typeof value == 'string' || isSymbol(value)) {
11973       return value;
11974     }
11975     var result = (value + '');
11976     return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
11977   }
11978
11979   /**
11980    * Converts `func` to its source code.
11981    *
11982    * @private
11983    * @param {Function} func The function to process.
11984    * @returns {string} Returns the source code.
11985    */
11986   function toSource(func) {
11987     if (func != null) {
11988       try {
11989         return funcToString.call(func);
11990       } catch (e) {}
11991       try {
11992         return (func + '');
11993       } catch (e) {}
11994     }
11995     return '';
11996   }
11997
11998   /**
11999    * Creates a clone of `wrapper`.
12000    *
12001    * @private
12002    * @param {Object} wrapper The wrapper to clone.
12003    * @returns {Object} Returns the cloned wrapper.
12004    */
12005   function wrapperClone(wrapper) {
12006     if (wrapper instanceof LazyWrapper) {
12007       return wrapper.clone();
12008     }
12009     var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);
12010     result.__actions__ = copyArray(wrapper.__actions__);
12011     result.__index__  = wrapper.__index__;
12012     result.__values__ = wrapper.__values__;
12013     return result;
12014   }
12015
12016   /*------------------------------------------------------------------------*/
12017
12018   /**
12019    * Creates an array of elements split into groups the length of `size`.
12020    * If `array` can't be split evenly, the final chunk will be the remaining
12021    * elements.
12022    *
12023    * @static
12024    * @memberOf _
12025    * @since 3.0.0
12026    * @category Array
12027    * @param {Array} array The array to process.
12028    * @param {number} [size=1] The length of each chunk
12029    * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
12030    * @returns {Array} Returns the new array of chunks.
12031    * @example
12032    *
12033    * _.chunk(['a', 'b', 'c', 'd'], 2);
12034    * // => [['a', 'b'], ['c', 'd']]
12035    *
12036    * _.chunk(['a', 'b', 'c', 'd'], 3);
12037    * // => [['a', 'b', 'c'], ['d']]
12038    */
12039   function chunk(array, size, guard) {
12040     if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) {
12041       size = 1;
12042     } else {
12043       size = nativeMax(toInteger(size), 0);
12044     }
12045     var length = array ? array.length : 0;
12046     if (!length || size < 1) {
12047       return [];
12048     }
12049     var index = 0,
12050         resIndex = 0,
12051         result = Array(nativeCeil(length / size));
12052
12053     while (index < length) {
12054       result[resIndex++] = baseSlice(array, index, (index += size));
12055     }
12056     return result;
12057   }
12058
12059   /**
12060    * Creates an array with all falsey values removed. The values `false`, `null`,
12061    * `0`, `""`, `undefined`, and `NaN` are falsey.
12062    *
12063    * @static
12064    * @memberOf _
12065    * @since 0.1.0
12066    * @category Array
12067    * @param {Array} array The array to compact.
12068    * @returns {Array} Returns the new array of filtered values.
12069    * @example
12070    *
12071    * _.compact([0, 1, false, 2, '', 3]);
12072    * // => [1, 2, 3]
12073    */
12074   function compact(array) {
12075     var index = -1,
12076         length = array ? array.length : 0,
12077         resIndex = 0,
12078         result = [];
12079
12080     while (++index < length) {
12081       var value = array[index];
12082       if (value) {
12083         result[resIndex++] = value;
12084       }
12085     }
12086     return result;
12087   }
12088
12089   /**
12090    * Creates an array of unique `array` values not included in the other given
12091    * arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12092    * for equality comparisons. The order of result values is determined by the
12093    * order they occur in the first array.
12094    *
12095    * @static
12096    * @memberOf _
12097    * @since 0.1.0
12098    * @category Array
12099    * @param {Array} array The array to inspect.
12100    * @param {...Array} [values] The values to exclude.
12101    * @returns {Array} Returns the new array of filtered values.
12102    * @see _.without, _.xor
12103    * @example
12104    *
12105    * _.difference([3, 2, 1], [4, 2]);
12106    * // => [3, 1]
12107    */
12108   var difference = rest(function(array, values) {
12109     return isArrayLikeObject(array)
12110       ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
12111       : [];
12112   });
12113
12114   /**
12115    * Flattens `array` a single level deep.
12116    *
12117    * @static
12118    * @memberOf _
12119    * @since 0.1.0
12120    * @category Array
12121    * @param {Array} array The array to flatten.
12122    * @returns {Array} Returns the new flattened array.
12123    * @example
12124    *
12125    * _.flatten([1, [2, [3, [4]], 5]]);
12126    * // => [1, 2, [3, [4]], 5]
12127    */
12128   function flatten(array) {
12129     var length = array ? array.length : 0;
12130     return length ? baseFlatten(array, 1) : [];
12131   }
12132
12133   /**
12134    * Gets the first element of `array`.
12135    *
12136    * @static
12137    * @memberOf _
12138    * @since 0.1.0
12139    * @alias first
12140    * @category Array
12141    * @param {Array} array The array to query.
12142    * @returns {*} Returns the first element of `array`.
12143    * @example
12144    *
12145    * _.head([1, 2, 3]);
12146    * // => 1
12147    *
12148    * _.head([]);
12149    * // => undefined
12150    */
12151   function head(array) {
12152     return (array && array.length) ? array[0] : undefined;
12153   }
12154
12155   /**
12156    * Gets the index at which the first occurrence of `value` is found in `array`
12157    * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12158    * for equality comparisons. If `fromIndex` is negative, it's used as the
12159    * offset from the end of `array`.
12160    *
12161    * @static
12162    * @memberOf _
12163    * @since 0.1.0
12164    * @category Array
12165    * @param {Array} array The array to search.
12166    * @param {*} value The value to search for.
12167    * @param {number} [fromIndex=0] The index to search from.
12168    * @returns {number} Returns the index of the matched value, else `-1`.
12169    * @example
12170    *
12171    * _.indexOf([1, 2, 1, 2], 2);
12172    * // => 1
12173    *
12174    * // Search from the `fromIndex`.
12175    * _.indexOf([1, 2, 1, 2], 2, 2);
12176    * // => 3
12177    */
12178   function indexOf(array, value, fromIndex) {
12179     var length = array ? array.length : 0;
12180     if (!length) {
12181       return -1;
12182     }
12183     fromIndex = toInteger(fromIndex);
12184     if (fromIndex < 0) {
12185       fromIndex = nativeMax(length + fromIndex, 0);
12186     }
12187     return baseIndexOf(array, value, fromIndex);
12188   }
12189
12190   /**
12191    * Creates an array of unique values that are included in all given arrays
12192    * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12193    * for equality comparisons. The order of result values is determined by the
12194    * order they occur in the first array.
12195    *
12196    * @static
12197    * @memberOf _
12198    * @since 0.1.0
12199    * @category Array
12200    * @param {...Array} [arrays] The arrays to inspect.
12201    * @returns {Array} Returns the new array of intersecting values.
12202    * @example
12203    *
12204    * _.intersection([2, 1], [4, 2], [1, 2]);
12205    * // => [2]
12206    */
12207   var intersection = rest(function(arrays) {
12208     var mapped = arrayMap(arrays, castArrayLikeObject);
12209     return (mapped.length && mapped[0] === arrays[0])
12210       ? baseIntersection(mapped)
12211       : [];
12212   });
12213
12214   /**
12215    * Gets the last element of `array`.
12216    *
12217    * @static
12218    * @memberOf _
12219    * @since 0.1.0
12220    * @category Array
12221    * @param {Array} array The array to query.
12222    * @returns {*} Returns the last element of `array`.
12223    * @example
12224    *
12225    * _.last([1, 2, 3]);
12226    * // => 3
12227    */
12228   function last(array) {
12229     var length = array ? array.length : 0;
12230     return length ? array[length - 1] : undefined;
12231   }
12232
12233   /**
12234    * Reverses `array` so that the first element becomes the last, the second
12235    * element becomes the second to last, and so on.
12236    *
12237    * **Note:** This method mutates `array` and is based on
12238    * [`Array#reverse`](https://mdn.io/Array/reverse).
12239    *
12240    * @static
12241    * @memberOf _
12242    * @since 4.0.0
12243    * @category Array
12244    * @param {Array} array The array to modify.
12245    * @returns {Array} Returns `array`.
12246    * @example
12247    *
12248    * var array = [1, 2, 3];
12249    *
12250    * _.reverse(array);
12251    * // => [3, 2, 1]
12252    *
12253    * console.log(array);
12254    * // => [3, 2, 1]
12255    */
12256   function reverse(array) {
12257     return array ? nativeReverse.call(array) : array;
12258   }
12259
12260   /**
12261    * Creates an array of unique values, in order, from all given arrays using
12262    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12263    * for equality comparisons.
12264    *
12265    * @static
12266    * @memberOf _
12267    * @since 0.1.0
12268    * @category Array
12269    * @param {...Array} [arrays] The arrays to inspect.
12270    * @returns {Array} Returns the new array of combined values.
12271    * @example
12272    *
12273    * _.union([2, 1], [4, 2], [1, 2]);
12274    * // => [2, 1, 4]
12275    */
12276   var union = rest(function(arrays) {
12277     return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));
12278   });
12279
12280   /**
12281    * Creates a duplicate-free version of an array, using
12282    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12283    * for equality comparisons, in which only the first occurrence of each
12284    * element is kept.
12285    *
12286    * @static
12287    * @memberOf _
12288    * @since 0.1.0
12289    * @category Array
12290    * @param {Array} array The array to inspect.
12291    * @returns {Array} Returns the new duplicate free array.
12292    * @example
12293    *
12294    * _.uniq([2, 1, 2]);
12295    * // => [2, 1]
12296    */
12297   function uniq(array) {
12298     return (array && array.length)
12299       ? baseUniq(array)
12300       : [];
12301   }
12302
12303   /**
12304    * Creates an array excluding all given values using
12305    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12306    * for equality comparisons.
12307    *
12308    * @static
12309    * @memberOf _
12310    * @since 0.1.0
12311    * @category Array
12312    * @param {Array} array The array to inspect.
12313    * @param {...*} [values] The values to exclude.
12314    * @returns {Array} Returns the new array of filtered values.
12315    * @see _.difference, _.xor
12316    * @example
12317    *
12318    * _.without([1, 2, 1, 3], 1, 2);
12319    * // => [3]
12320    */
12321   var without = rest(function(array, values) {
12322     return isArrayLikeObject(array)
12323       ? baseDifference(array, values)
12324       : [];
12325   });
12326
12327   /*------------------------------------------------------------------------*/
12328
12329   /**
12330    * Creates a `lodash` wrapper instance that wraps `value` with explicit method
12331    * chain sequences enabled. The result of such sequences must be unwrapped
12332    * with `_#value`.
12333    *
12334    * @static
12335    * @memberOf _
12336    * @since 1.3.0
12337    * @category Seq
12338    * @param {*} value The value to wrap.
12339    * @returns {Object} Returns the new `lodash` wrapper instance.
12340    * @example
12341    *
12342    * var users = [
12343    *   { 'user': 'barney',  'age': 36 },
12344    *   { 'user': 'fred',    'age': 40 },
12345    *   { 'user': 'pebbles', 'age': 1 }
12346    * ];
12347    *
12348    * var youngest = _
12349    *   .chain(users)
12350    *   .sortBy('age')
12351    *   .map(function(o) {
12352    *     return o.user + ' is ' + o.age;
12353    *   })
12354    *   .head()
12355    *   .value();
12356    * // => 'pebbles is 1'
12357    */
12358   function chain(value) {
12359     var result = lodash(value);
12360     result.__chain__ = true;
12361     return result;
12362   }
12363
12364   /**
12365    * This method invokes `interceptor` and returns `value`. The interceptor
12366    * is invoked with one argument; (value). The purpose of this method is to
12367    * "tap into" a method chain sequence in order to modify intermediate results.
12368    *
12369    * @static
12370    * @memberOf _
12371    * @since 0.1.0
12372    * @category Seq
12373    * @param {*} value The value to provide to `interceptor`.
12374    * @param {Function} interceptor The function to invoke.
12375    * @returns {*} Returns `value`.
12376    * @example
12377    *
12378    * _([1, 2, 3])
12379    *  .tap(function(array) {
12380    *    // Mutate input array.
12381    *    array.pop();
12382    *  })
12383    *  .reverse()
12384    *  .value();
12385    * // => [2, 1]
12386    */
12387   function tap(value, interceptor) {
12388     interceptor(value);
12389     return value;
12390   }
12391
12392   /**
12393    * This method is like `_.tap` except that it returns the result of `interceptor`.
12394    * The purpose of this method is to "pass thru" values replacing intermediate
12395    * results in a method chain sequence.
12396    *
12397    * @static
12398    * @memberOf _
12399    * @since 3.0.0
12400    * @category Seq
12401    * @param {*} value The value to provide to `interceptor`.
12402    * @param {Function} interceptor The function to invoke.
12403    * @returns {*} Returns the result of `interceptor`.
12404    * @example
12405    *
12406    * _('  abc  ')
12407    *  .chain()
12408    *  .trim()
12409    *  .thru(function(value) {
12410    *    return [value];
12411    *  })
12412    *  .value();
12413    * // => ['abc']
12414    */
12415   function thru(value, interceptor) {
12416     return interceptor(value);
12417   }
12418
12419   /**
12420    * This method is the wrapper version of `_.at`.
12421    *
12422    * @name at
12423    * @memberOf _
12424    * @since 1.0.0
12425    * @category Seq
12426    * @param {...(string|string[])} [paths] The property paths of elements to pick.
12427    * @returns {Object} Returns the new `lodash` wrapper instance.
12428    * @example
12429    *
12430    * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
12431    *
12432    * _(object).at(['a[0].b.c', 'a[1]']).value();
12433    * // => [3, 4]
12434    *
12435    * _(['a', 'b', 'c']).at(0, 2).value();
12436    * // => ['a', 'c']
12437    */
12438   var wrapperAt = rest(function(paths) {
12439     paths = baseFlatten(paths, 1);
12440     var length = paths.length,
12441         start = length ? paths[0] : 0,
12442         value = this.__wrapped__,
12443         interceptor = function(object) { return baseAt(object, paths); };
12444
12445     if (length > 1 || this.__actions__.length ||
12446         !(value instanceof LazyWrapper) || !isIndex(start)) {
12447       return this.thru(interceptor);
12448     }
12449     value = value.slice(start, +start + (length ? 1 : 0));
12450     value.__actions__.push({
12451       'func': thru,
12452       'args': [interceptor],
12453       'thisArg': undefined
12454     });
12455     return new LodashWrapper(value, this.__chain__).thru(function(array) {
12456       if (length && !array.length) {
12457         array.push(undefined);
12458       }
12459       return array;
12460     });
12461   });
12462
12463   /**
12464    * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.
12465    *
12466    * @name chain
12467    * @memberOf _
12468    * @since 0.1.0
12469    * @category Seq
12470    * @returns {Object} Returns the new `lodash` wrapper instance.
12471    * @example
12472    *
12473    * var users = [
12474    *   { 'user': 'barney', 'age': 36 },
12475    *   { 'user': 'fred',   'age': 40 }
12476    * ];
12477    *
12478    * // A sequence without explicit chaining.
12479    * _(users).head();
12480    * // => { 'user': 'barney', 'age': 36 }
12481    *
12482    * // A sequence with explicit chaining.
12483    * _(users)
12484    *   .chain()
12485    *   .head()
12486    *   .pick('user')
12487    *   .value();
12488    * // => { 'user': 'barney' }
12489    */
12490   function wrapperChain() {
12491     return chain(this);
12492   }
12493
12494   /**
12495    * Executes the chain sequence and returns the wrapped result.
12496    *
12497    * @name commit
12498    * @memberOf _
12499    * @since 3.2.0
12500    * @category Seq
12501    * @returns {Object} Returns the new `lodash` wrapper instance.
12502    * @example
12503    *
12504    * var array = [1, 2];
12505    * var wrapped = _(array).push(3);
12506    *
12507    * console.log(array);
12508    * // => [1, 2]
12509    *
12510    * wrapped = wrapped.commit();
12511    * console.log(array);
12512    * // => [1, 2, 3]
12513    *
12514    * wrapped.last();
12515    * // => 3
12516    *
12517    * console.log(array);
12518    * // => [1, 2, 3]
12519    */
12520   function wrapperCommit() {
12521     return new LodashWrapper(this.value(), this.__chain__);
12522   }
12523
12524   /**
12525    * Gets the next value on a wrapped object following the
12526    * [iterator protocol](https://mdn.io/iteration_protocols#iterator).
12527    *
12528    * @name next
12529    * @memberOf _
12530    * @since 4.0.0
12531    * @category Seq
12532    * @returns {Object} Returns the next iterator value.
12533    * @example
12534    *
12535    * var wrapped = _([1, 2]);
12536    *
12537    * wrapped.next();
12538    * // => { 'done': false, 'value': 1 }
12539    *
12540    * wrapped.next();
12541    * // => { 'done': false, 'value': 2 }
12542    *
12543    * wrapped.next();
12544    * // => { 'done': true, 'value': undefined }
12545    */
12546   function wrapperNext() {
12547     if (this.__values__ === undefined) {
12548       this.__values__ = toArray(this.value());
12549     }
12550     var done = this.__index__ >= this.__values__.length,
12551         value = done ? undefined : this.__values__[this.__index__++];
12552
12553     return { 'done': done, 'value': value };
12554   }
12555
12556   /**
12557    * Enables the wrapper to be iterable.
12558    *
12559    * @name Symbol.iterator
12560    * @memberOf _
12561    * @since 4.0.0
12562    * @category Seq
12563    * @returns {Object} Returns the wrapper object.
12564    * @example
12565    *
12566    * var wrapped = _([1, 2]);
12567    *
12568    * wrapped[Symbol.iterator]() === wrapped;
12569    * // => true
12570    *
12571    * Array.from(wrapped);
12572    * // => [1, 2]
12573    */
12574   function wrapperToIterator() {
12575     return this;
12576   }
12577
12578   /**
12579    * Creates a clone of the chain sequence planting `value` as the wrapped value.
12580    *
12581    * @name plant
12582    * @memberOf _
12583    * @since 3.2.0
12584    * @category Seq
12585    * @param {*} value The value to plant.
12586    * @returns {Object} Returns the new `lodash` wrapper instance.
12587    * @example
12588    *
12589    * function square(n) {
12590    *   return n * n;
12591    * }
12592    *
12593    * var wrapped = _([1, 2]).map(square);
12594    * var other = wrapped.plant([3, 4]);
12595    *
12596    * other.value();
12597    * // => [9, 16]
12598    *
12599    * wrapped.value();
12600    * // => [1, 4]
12601    */
12602   function wrapperPlant(value) {
12603     var result,
12604         parent = this;
12605
12606     while (parent instanceof baseLodash) {
12607       var clone = wrapperClone(parent);
12608       clone.__index__ = 0;
12609       clone.__values__ = undefined;
12610       if (result) {
12611         previous.__wrapped__ = clone;
12612       } else {
12613         result = clone;
12614       }
12615       var previous = clone;
12616       parent = parent.__wrapped__;
12617     }
12618     previous.__wrapped__ = value;
12619     return result;
12620   }
12621
12622   /**
12623    * This method is the wrapper version of `_.reverse`.
12624    *
12625    * **Note:** This method mutates the wrapped array.
12626    *
12627    * @name reverse
12628    * @memberOf _
12629    * @since 0.1.0
12630    * @category Seq
12631    * @returns {Object} Returns the new `lodash` wrapper instance.
12632    * @example
12633    *
12634    * var array = [1, 2, 3];
12635    *
12636    * _(array).reverse().value()
12637    * // => [3, 2, 1]
12638    *
12639    * console.log(array);
12640    * // => [3, 2, 1]
12641    */
12642   function wrapperReverse() {
12643     var value = this.__wrapped__;
12644     if (value instanceof LazyWrapper) {
12645       var wrapped = value;
12646       if (this.__actions__.length) {
12647         wrapped = new LazyWrapper(this);
12648       }
12649       wrapped = wrapped.reverse();
12650       wrapped.__actions__.push({
12651         'func': thru,
12652         'args': [reverse],
12653         'thisArg': undefined
12654       });
12655       return new LodashWrapper(wrapped, this.__chain__);
12656     }
12657     return this.thru(reverse);
12658   }
12659
12660   /**
12661    * Executes the chain sequence to resolve the unwrapped value.
12662    *
12663    * @name value
12664    * @memberOf _
12665    * @since 0.1.0
12666    * @alias toJSON, valueOf
12667    * @category Seq
12668    * @returns {*} Returns the resolved unwrapped value.
12669    * @example
12670    *
12671    * _([1, 2, 3]).value();
12672    * // => [1, 2, 3]
12673    */
12674   function wrapperValue() {
12675     return baseWrapperValue(this.__wrapped__, this.__actions__);
12676   }
12677
12678   /*------------------------------------------------------------------------*/
12679
12680   /**
12681    * Checks if `predicate` returns truthy for **all** elements of `collection`.
12682    * Iteration is stopped once `predicate` returns falsey. The predicate is
12683    * invoked with three arguments: (value, index|key, collection).
12684    *
12685    * @static
12686    * @memberOf _
12687    * @since 0.1.0
12688    * @category Collection
12689    * @param {Array|Object} collection The collection to iterate over.
12690    * @param {Array|Function|Object|string} [predicate=_.identity]
12691    *  The function invoked per iteration.
12692    * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
12693    * @returns {boolean} Returns `true` if all elements pass the predicate check,
12694    *  else `false`.
12695    * @example
12696    *
12697    * _.every([true, 1, null, 'yes'], Boolean);
12698    * // => false
12699    *
12700    * var users = [
12701    *   { 'user': 'barney', 'age': 36, 'active': false },
12702    *   { 'user': 'fred',   'age': 40, 'active': false }
12703    * ];
12704    *
12705    * // The `_.matches` iteratee shorthand.
12706    * _.every(users, { 'user': 'barney', 'active': false });
12707    * // => false
12708    *
12709    * // The `_.matchesProperty` iteratee shorthand.
12710    * _.every(users, ['active', false]);
12711    * // => true
12712    *
12713    * // The `_.property` iteratee shorthand.
12714    * _.every(users, 'active');
12715    * // => false
12716    */
12717   function every(collection, predicate, guard) {
12718     var func = isArray(collection) ? arrayEvery : baseEvery;
12719     if (guard && isIterateeCall(collection, predicate, guard)) {
12720       predicate = undefined;
12721     }
12722     return func(collection, getIteratee(predicate, 3));
12723   }
12724
12725   /**
12726    * Iterates over elements of `collection`, returning an array of all elements
12727    * `predicate` returns truthy for. The predicate is invoked with three
12728    * arguments: (value, index|key, collection).
12729    *
12730    * @static
12731    * @memberOf _
12732    * @since 0.1.0
12733    * @category Collection
12734    * @param {Array|Object} collection The collection to iterate over.
12735    * @param {Array|Function|Object|string} [predicate=_.identity]
12736    *  The function invoked per iteration.
12737    * @returns {Array} Returns the new filtered array.
12738    * @see _.reject
12739    * @example
12740    *
12741    * var users = [
12742    *   { 'user': 'barney', 'age': 36, 'active': true },
12743    *   { 'user': 'fred',   'age': 40, 'active': false }
12744    * ];
12745    *
12746    * _.filter(users, function(o) { return !o.active; });
12747    * // => objects for ['fred']
12748    *
12749    * // The `_.matches` iteratee shorthand.
12750    * _.filter(users, { 'age': 36, 'active': true });
12751    * // => objects for ['barney']
12752    *
12753    * // The `_.matchesProperty` iteratee shorthand.
12754    * _.filter(users, ['active', false]);
12755    * // => objects for ['fred']
12756    *
12757    * // The `_.property` iteratee shorthand.
12758    * _.filter(users, 'active');
12759    * // => objects for ['barney']
12760    */
12761   function filter(collection, predicate) {
12762     var func = isArray(collection) ? arrayFilter : baseFilter;
12763     return func(collection, getIteratee(predicate, 3));
12764   }
12765
12766   /**
12767    * Iterates over elements of `collection`, returning the first element
12768    * `predicate` returns truthy for. The predicate is invoked with three
12769    * arguments: (value, index|key, collection).
12770    *
12771    * @static
12772    * @memberOf _
12773    * @since 0.1.0
12774    * @category Collection
12775    * @param {Array|Object} collection The collection to search.
12776    * @param {Array|Function|Object|string} [predicate=_.identity]
12777    *  The function invoked per iteration.
12778    * @returns {*} Returns the matched element, else `undefined`.
12779    * @example
12780    *
12781    * var users = [
12782    *   { 'user': 'barney',  'age': 36, 'active': true },
12783    *   { 'user': 'fred',    'age': 40, 'active': false },
12784    *   { 'user': 'pebbles', 'age': 1,  'active': true }
12785    * ];
12786    *
12787    * _.find(users, function(o) { return o.age < 40; });
12788    * // => object for 'barney'
12789    *
12790    * // The `_.matches` iteratee shorthand.
12791    * _.find(users, { 'age': 1, 'active': true });
12792    * // => object for 'pebbles'
12793    *
12794    * // The `_.matchesProperty` iteratee shorthand.
12795    * _.find(users, ['active', false]);
12796    * // => object for 'fred'
12797    *
12798    * // The `_.property` iteratee shorthand.
12799    * _.find(users, 'active');
12800    * // => object for 'barney'
12801    */
12802   function find(collection, predicate) {
12803     predicate = getIteratee(predicate, 3);
12804     if (isArray(collection)) {
12805       var index = baseFindIndex(collection, predicate);
12806       return index > -1 ? collection[index] : undefined;
12807     }
12808     return baseFind(collection, predicate, baseEach);
12809   }
12810
12811   /**
12812    * Iterates over elements of `collection` and invokes `iteratee` for each element.
12813    * The iteratee is invoked with three arguments: (value, index|key, collection).
12814    * Iteratee functions may exit iteration early by explicitly returning `false`.
12815    *
12816    * **Note:** As with other "Collections" methods, objects with a "length"
12817    * property are iterated like arrays. To avoid this behavior use `_.forIn`
12818    * or `_.forOwn` for object iteration.
12819    *
12820    * @static
12821    * @memberOf _
12822    * @since 0.1.0
12823    * @alias each
12824    * @category Collection
12825    * @param {Array|Object} collection The collection to iterate over.
12826    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12827    * @returns {Array|Object} Returns `collection`.
12828    * @see _.forEachRight
12829    * @example
12830    *
12831    * _([1, 2]).forEach(function(value) {
12832    *   console.log(value);
12833    * });
12834    * // => Logs `1` then `2`.
12835    *
12836    * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
12837    *   console.log(key);
12838    * });
12839    * // => Logs 'a' then 'b' (iteration order is not guaranteed).
12840    */
12841   function forEach(collection, iteratee) {
12842     var func = isArray(collection) ? arrayEach : baseEach;
12843     return func(collection, getIteratee(iteratee, 3));
12844   }
12845
12846   /**
12847    * Creates an object composed of keys generated from the results of running
12848    * each element of `collection` thru `iteratee`. The order of grouped values
12849    * is determined by the order they occur in `collection`. The corresponding
12850    * value of each key is an array of elements responsible for generating the
12851    * key. The iteratee is invoked with one argument: (value).
12852    *
12853    * @static
12854    * @memberOf _
12855    * @since 0.1.0
12856    * @category Collection
12857    * @param {Array|Object} collection The collection to iterate over.
12858    * @param {Array|Function|Object|string} [iteratee=_.identity]
12859    *  The iteratee to transform keys.
12860    * @returns {Object} Returns the composed aggregate object.
12861    * @example
12862    *
12863    * _.groupBy([6.1, 4.2, 6.3], Math.floor);
12864    * // => { '4': [4.2], '6': [6.1, 6.3] }
12865    *
12866    * // The `_.property` iteratee shorthand.
12867    * _.groupBy(['one', 'two', 'three'], 'length');
12868    * // => { '3': ['one', 'two'], '5': ['three'] }
12869    */
12870   var groupBy = createAggregator(function(result, value, key) {
12871     if (hasOwnProperty.call(result, key)) {
12872       result[key].push(value);
12873     } else {
12874       result[key] = [value];
12875     }
12876   });
12877
12878   /**
12879    * Checks if `value` is in `collection`. If `collection` is a string, it's
12880    * checked for a substring of `value`, otherwise
12881    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
12882    * is used for equality comparisons. If `fromIndex` is negative, it's used as
12883    * the offset from the end of `collection`.
12884    *
12885    * @static
12886    * @memberOf _
12887    * @since 0.1.0
12888    * @category Collection
12889    * @param {Array|Object|string} collection The collection to search.
12890    * @param {*} value The value to search for.
12891    * @param {number} [fromIndex=0] The index to search from.
12892    * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
12893    * @returns {boolean} Returns `true` if `value` is found, else `false`.
12894    * @example
12895    *
12896    * _.includes([1, 2, 3], 1);
12897    * // => true
12898    *
12899    * _.includes([1, 2, 3], 1, 2);
12900    * // => false
12901    *
12902    * _.includes({ 'user': 'fred', 'age': 40 }, 'fred');
12903    * // => true
12904    *
12905    * _.includes('pebbles', 'eb');
12906    * // => true
12907    */
12908   function includes(collection, value, fromIndex, guard) {
12909     collection = isArrayLike(collection) ? collection : values(collection);
12910     fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0;
12911
12912     var length = collection.length;
12913     if (fromIndex < 0) {
12914       fromIndex = nativeMax(length + fromIndex, 0);
12915     }
12916     return isString(collection)
12917       ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1)
12918       : (!!length && baseIndexOf(collection, value, fromIndex) > -1);
12919   }
12920
12921   /**
12922    * Creates an array of values by running each element in `collection` thru
12923    * `iteratee`. The iteratee is invoked with three arguments:
12924    * (value, index|key, collection).
12925    *
12926    * Many lodash methods are guarded to work as iteratees for methods like
12927    * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
12928    *
12929    * The guarded methods are:
12930    * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
12931    * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
12932    * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
12933    * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
12934    *
12935    * @static
12936    * @memberOf _
12937    * @since 0.1.0
12938    * @category Collection
12939    * @param {Array|Object} collection The collection to iterate over.
12940    * @param {Array|Function|Object|string} [iteratee=_.identity]
12941    *  The function invoked per iteration.
12942    * @returns {Array} Returns the new mapped array.
12943    * @example
12944    *
12945    * function square(n) {
12946    *   return n * n;
12947    * }
12948    *
12949    * _.map([4, 8], square);
12950    * // => [16, 64]
12951    *
12952    * _.map({ 'a': 4, 'b': 8 }, square);
12953    * // => [16, 64] (iteration order is not guaranteed)
12954    *
12955    * var users = [
12956    *   { 'user': 'barney' },
12957    *   { 'user': 'fred' }
12958    * ];
12959    *
12960    * // The `_.property` iteratee shorthand.
12961    * _.map(users, 'user');
12962    * // => ['barney', 'fred']
12963    */
12964   function map(collection, iteratee) {
12965     var func = isArray(collection) ? arrayMap : baseMap;
12966     return func(collection, getIteratee(iteratee, 3));
12967   }
12968
12969   /**
12970    * Reduces `collection` to a value which is the accumulated result of running
12971    * each element in `collection` thru `iteratee`, where each successive
12972    * invocation is supplied the return value of the previous. If `accumulator`
12973    * is not given, the first element of `collection` is used as the initial
12974    * value. The iteratee is invoked with four arguments:
12975    * (accumulator, value, index|key, collection).
12976    *
12977    * Many lodash methods are guarded to work as iteratees for methods like
12978    * `_.reduce`, `_.reduceRight`, and `_.transform`.
12979    *
12980    * The guarded methods are:
12981    * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,
12982    * and `sortBy`
12983    *
12984    * @static
12985    * @memberOf _
12986    * @since 0.1.0
12987    * @category Collection
12988    * @param {Array|Object} collection The collection to iterate over.
12989    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12990    * @param {*} [accumulator] The initial value.
12991    * @returns {*} Returns the accumulated value.
12992    * @see _.reduceRight
12993    * @example
12994    *
12995    * _.reduce([1, 2], function(sum, n) {
12996    *   return sum + n;
12997    * }, 0);
12998    * // => 3
12999    *
13000    * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
13001    *   (result[value] || (result[value] = [])).push(key);
13002    *   return result;
13003    * }, {});
13004    * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)
13005    */
13006   function reduce(collection, iteratee, accumulator) {
13007     var func = isArray(collection) ? arrayReduce : baseReduce,
13008         initAccum = arguments.length < 3;
13009
13010     return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);
13011   }
13012
13013   /**
13014    * The opposite of `_.filter`; this method returns the elements of `collection`
13015    * that `predicate` does **not** return truthy for.
13016    *
13017    * @static
13018    * @memberOf _
13019    * @since 0.1.0
13020    * @category Collection
13021    * @param {Array|Object} collection The collection to iterate over.
13022    * @param {Array|Function|Object|string} [predicate=_.identity]
13023    *  The function invoked per iteration.
13024    * @returns {Array} Returns the new filtered array.
13025    * @see _.filter
13026    * @example
13027    *
13028    * var users = [
13029    *   { 'user': 'barney', 'age': 36, 'active': false },
13030    *   { 'user': 'fred',   'age': 40, 'active': true }
13031    * ];
13032    *
13033    * _.reject(users, function(o) { return !o.active; });
13034    * // => objects for ['fred']
13035    *
13036    * // The `_.matches` iteratee shorthand.
13037    * _.reject(users, { 'age': 40, 'active': true });
13038    * // => objects for ['barney']
13039    *
13040    * // The `_.matchesProperty` iteratee shorthand.
13041    * _.reject(users, ['active', false]);
13042    * // => objects for ['fred']
13043    *
13044    * // The `_.property` iteratee shorthand.
13045    * _.reject(users, 'active');
13046    * // => objects for ['barney']
13047    */
13048   function reject(collection, predicate) {
13049     var func = isArray(collection) ? arrayFilter : baseFilter;
13050     predicate = getIteratee(predicate, 3);
13051     return func(collection, function(value, index, collection) {
13052       return !predicate(value, index, collection);
13053     });
13054   }
13055
13056   /**
13057    * Checks if `predicate` returns truthy for **any** element of `collection`.
13058    * Iteration is stopped once `predicate` returns truthy. The predicate is
13059    * invoked with three arguments: (value, index|key, collection).
13060    *
13061    * @static
13062    * @memberOf _
13063    * @since 0.1.0
13064    * @category Collection
13065    * @param {Array|Object} collection The collection to iterate over.
13066    * @param {Array|Function|Object|string} [predicate=_.identity]
13067    *  The function invoked per iteration.
13068    * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
13069    * @returns {boolean} Returns `true` if any element passes the predicate check,
13070    *  else `false`.
13071    * @example
13072    *
13073    * _.some([null, 0, 'yes', false], Boolean);
13074    * // => true
13075    *
13076    * var users = [
13077    *   { 'user': 'barney', 'active': true },
13078    *   { 'user': 'fred',   'active': false }
13079    * ];
13080    *
13081    * // The `_.matches` iteratee shorthand.
13082    * _.some(users, { 'user': 'barney', 'active': false });
13083    * // => false
13084    *
13085    * // The `_.matchesProperty` iteratee shorthand.
13086    * _.some(users, ['active', false]);
13087    * // => true
13088    *
13089    * // The `_.property` iteratee shorthand.
13090    * _.some(users, 'active');
13091    * // => true
13092    */
13093   function some(collection, predicate, guard) {
13094     var func = isArray(collection) ? arraySome : baseSome;
13095     if (guard && isIterateeCall(collection, predicate, guard)) {
13096       predicate = undefined;
13097     }
13098     return func(collection, getIteratee(predicate, 3));
13099   }
13100
13101   /*------------------------------------------------------------------------*/
13102
13103   /**
13104    * Gets the timestamp of the number of milliseconds that have elapsed since
13105    * the Unix epoch (1 January 1970 00:00:00 UTC).
13106    *
13107    * @static
13108    * @memberOf _
13109    * @since 2.4.0
13110    * @type {Function}
13111    * @category Date
13112    * @returns {number} Returns the timestamp.
13113    * @example
13114    *
13115    * _.defer(function(stamp) {
13116    *   console.log(_.now() - stamp);
13117    * }, _.now());
13118    * // => Logs the number of milliseconds it took for the deferred function to be invoked.
13119    */
13120   var now = Date.now;
13121
13122   /*------------------------------------------------------------------------*/
13123
13124   /**
13125    * Creates a function that invokes `func` with the `this` binding of `thisArg`
13126    * and `partials` prepended to the arguments it receives.
13127    *
13128    * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
13129    * may be used as a placeholder for partially applied arguments.
13130    *
13131    * **Note:** Unlike native `Function#bind` this method doesn't set the "length"
13132    * property of bound functions.
13133    *
13134    * @static
13135    * @memberOf _
13136    * @since 0.1.0
13137    * @category Function
13138    * @param {Function} func The function to bind.
13139    * @param {*} thisArg The `this` binding of `func`.
13140    * @param {...*} [partials] The arguments to be partially applied.
13141    * @returns {Function} Returns the new bound function.
13142    * @example
13143    *
13144    * var greet = function(greeting, punctuation) {
13145    *   return greeting + ' ' + this.user + punctuation;
13146    * };
13147    *
13148    * var object = { 'user': 'fred' };
13149    *
13150    * var bound = _.bind(greet, object, 'hi');
13151    * bound('!');
13152    * // => 'hi fred!'
13153    *
13154    * // Bound with placeholders.
13155    * var bound = _.bind(greet, object, _, '!');
13156    * bound('hi');
13157    * // => 'hi fred!'
13158    */
13159   var bind = rest(function(func, thisArg, partials) {
13160     var bitmask = BIND_FLAG;
13161     if (partials.length) {
13162       var holders = replaceHolders(partials, getHolder(bind));
13163       bitmask |= PARTIAL_FLAG;
13164     }
13165     return createWrapper(func, bitmask, thisArg, partials, holders);
13166   });
13167
13168   /**
13169    * Creates a debounced function that delays invoking `func` until after `wait`
13170    * milliseconds have elapsed since the last time the debounced function was
13171    * invoked. The debounced function comes with a `cancel` method to cancel
13172    * delayed `func` invocations and a `flush` method to immediately invoke them.
13173    * Provide an options object to indicate whether `func` should be invoked on
13174    * the leading and/or trailing edge of the `wait` timeout. The `func` is invoked
13175    * with the last arguments provided to the debounced function. Subsequent calls
13176    * to the debounced function return the result of the last `func` invocation.
13177    *
13178    * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
13179    * on the trailing edge of the timeout only if the debounced function is
13180    * invoked more than once during the `wait` timeout.
13181    *
13182    * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
13183    * for details over the differences between `_.debounce` and `_.throttle`.
13184    *
13185    * @static
13186    * @memberOf _
13187    * @since 0.1.0
13188    * @category Function
13189    * @param {Function} func The function to debounce.
13190    * @param {number} [wait=0] The number of milliseconds to delay.
13191    * @param {Object} [options={}] The options object.
13192    * @param {boolean} [options.leading=false]
13193    *  Specify invoking on the leading edge of the timeout.
13194    * @param {number} [options.maxWait]
13195    *  The maximum time `func` is allowed to be delayed before it's invoked.
13196    * @param {boolean} [options.trailing=true]
13197    *  Specify invoking on the trailing edge of the timeout.
13198    * @returns {Function} Returns the new debounced function.
13199    * @example
13200    *
13201    * // Avoid costly calculations while the window size is in flux.
13202    * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
13203    *
13204    * // Invoke `sendMail` when clicked, debouncing subsequent calls.
13205    * jQuery(element).on('click', _.debounce(sendMail, 300, {
13206    *   'leading': true,
13207    *   'trailing': false
13208    * }));
13209    *
13210    * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
13211    * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
13212    * var source = new EventSource('/stream');
13213    * jQuery(source).on('message', debounced);
13214    *
13215    * // Cancel the trailing debounced invocation.
13216    * jQuery(window).on('popstate', debounced.cancel);
13217    */
13218   function debounce(func, wait, options) {
13219     var lastArgs,
13220         lastThis,
13221         maxWait,
13222         result,
13223         timerId,
13224         lastCallTime = 0,
13225         lastInvokeTime = 0,
13226         leading = false,
13227         maxing = false,
13228         trailing = true;
13229
13230     if (typeof func != 'function') {
13231       throw new TypeError(FUNC_ERROR_TEXT);
13232     }
13233     wait = toNumber(wait) || 0;
13234     if (isObject(options)) {
13235       leading = !!options.leading;
13236       maxing = 'maxWait' in options;
13237       maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
13238       trailing = 'trailing' in options ? !!options.trailing : trailing;
13239     }
13240
13241     function invokeFunc(time) {
13242       var args = lastArgs,
13243           thisArg = lastThis;
13244
13245       lastArgs = lastThis = undefined;
13246       lastInvokeTime = time;
13247       result = func.apply(thisArg, args);
13248       return result;
13249     }
13250
13251     function leadingEdge(time) {
13252       // Reset any `maxWait` timer.
13253       lastInvokeTime = time;
13254       // Start the timer for the trailing edge.
13255       timerId = setTimeout(timerExpired, wait);
13256       // Invoke the leading edge.
13257       return leading ? invokeFunc(time) : result;
13258     }
13259
13260     function remainingWait(time) {
13261       var timeSinceLastCall = time - lastCallTime,
13262           timeSinceLastInvoke = time - lastInvokeTime,
13263           result = wait - timeSinceLastCall;
13264
13265       return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
13266     }
13267
13268     function shouldInvoke(time) {
13269       var timeSinceLastCall = time - lastCallTime,
13270           timeSinceLastInvoke = time - lastInvokeTime;
13271
13272       // Either this is the first call, activity has stopped and we're at the
13273       // trailing edge, the system time has gone backwards and we're treating
13274       // it as the trailing edge, or we've hit the `maxWait` limit.
13275       return (!lastCallTime || (timeSinceLastCall >= wait) ||
13276         (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
13277     }
13278
13279     function timerExpired() {
13280       var time = now();
13281       if (shouldInvoke(time)) {
13282         return trailingEdge(time);
13283       }
13284       // Restart the timer.
13285       timerId = setTimeout(timerExpired, remainingWait(time));
13286     }
13287
13288     function trailingEdge(time) {
13289       clearTimeout(timerId);
13290       timerId = undefined;
13291
13292       // Only invoke if we have `lastArgs` which means `func` has been
13293       // debounced at least once.
13294       if (trailing && lastArgs) {
13295         return invokeFunc(time);
13296       }
13297       lastArgs = lastThis = undefined;
13298       return result;
13299     }
13300
13301     function cancel() {
13302       if (timerId !== undefined) {
13303         clearTimeout(timerId);
13304       }
13305       lastCallTime = lastInvokeTime = 0;
13306       lastArgs = lastThis = timerId = undefined;
13307     }
13308
13309     function flush() {
13310       return timerId === undefined ? result : trailingEdge(now());
13311     }
13312
13313     function debounced() {
13314       var time = now(),
13315           isInvoking = shouldInvoke(time);
13316
13317       lastArgs = arguments;
13318       lastThis = this;
13319       lastCallTime = time;
13320
13321       if (isInvoking) {
13322         if (timerId === undefined) {
13323           return leadingEdge(lastCallTime);
13324         }
13325         if (maxing) {
13326           // Handle invocations in a tight loop.
13327           clearTimeout(timerId);
13328           timerId = setTimeout(timerExpired, wait);
13329           return invokeFunc(lastCallTime);
13330         }
13331       }
13332       if (timerId === undefined) {
13333         timerId = setTimeout(timerExpired, wait);
13334       }
13335       return result;
13336     }
13337     debounced.cancel = cancel;
13338     debounced.flush = flush;
13339     return debounced;
13340   }
13341
13342   /**
13343    * Creates a function that memoizes the result of `func`. If `resolver` is
13344    * provided, it determines the cache key for storing the result based on the
13345    * arguments provided to the memoized function. By default, the first argument
13346    * provided to the memoized function is used as the map cache key. The `func`
13347    * is invoked with the `this` binding of the memoized function.
13348    *
13349    * **Note:** The cache is exposed as the `cache` property on the memoized
13350    * function. Its creation may be customized by replacing the `_.memoize.Cache`
13351    * constructor with one whose instances implement the
13352    * [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object)
13353    * method interface of `delete`, `get`, `has`, and `set`.
13354    *
13355    * @static
13356    * @memberOf _
13357    * @since 0.1.0
13358    * @category Function
13359    * @param {Function} func The function to have its output memoized.
13360    * @param {Function} [resolver] The function to resolve the cache key.
13361    * @returns {Function} Returns the new memoized function.
13362    * @example
13363    *
13364    * var object = { 'a': 1, 'b': 2 };
13365    * var other = { 'c': 3, 'd': 4 };
13366    *
13367    * var values = _.memoize(_.values);
13368    * values(object);
13369    * // => [1, 2]
13370    *
13371    * values(other);
13372    * // => [3, 4]
13373    *
13374    * object.a = 2;
13375    * values(object);
13376    * // => [1, 2]
13377    *
13378    * // Modify the result cache.
13379    * values.cache.set(object, ['a', 'b']);
13380    * values(object);
13381    * // => ['a', 'b']
13382    *
13383    * // Replace `_.memoize.Cache`.
13384    * _.memoize.Cache = WeakMap;
13385    */
13386   function memoize(func, resolver) {
13387     if (typeof func != 'function' || (resolver && typeof resolver != 'function')) {
13388       throw new TypeError(FUNC_ERROR_TEXT);
13389     }
13390     var memoized = function() {
13391       var args = arguments,
13392           key = resolver ? resolver.apply(this, args) : args[0],
13393           cache = memoized.cache;
13394
13395       if (cache.has(key)) {
13396         return cache.get(key);
13397       }
13398       var result = func.apply(this, args);
13399       memoized.cache = cache.set(key, result);
13400       return result;
13401     };
13402     memoized.cache = new (memoize.Cache || MapCache);
13403     return memoized;
13404   }
13405
13406   // Assign cache to `_.memoize`.
13407   memoize.Cache = MapCache;
13408
13409   /**
13410    * Creates a function that invokes `func` with the `this` binding of the
13411    * created function and arguments from `start` and beyond provided as
13412    * an array.
13413    *
13414    * **Note:** This method is based on the
13415    * [rest parameter](https://mdn.io/rest_parameters).
13416    *
13417    * @static
13418    * @memberOf _
13419    * @since 4.0.0
13420    * @category Function
13421    * @param {Function} func The function to apply a rest parameter to.
13422    * @param {number} [start=func.length-1] The start position of the rest parameter.
13423    * @returns {Function} Returns the new function.
13424    * @example
13425    *
13426    * var say = _.rest(function(what, names) {
13427    *   return what + ' ' + _.initial(names).join(', ') +
13428    *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
13429    * });
13430    *
13431    * say('hello', 'fred', 'barney', 'pebbles');
13432    * // => 'hello fred, barney, & pebbles'
13433    */
13434   function rest(func, start) {
13435     if (typeof func != 'function') {
13436       throw new TypeError(FUNC_ERROR_TEXT);
13437     }
13438     start = nativeMax(start === undefined ? (func.length - 1) : toInteger(start), 0);
13439     return function() {
13440       var args = arguments,
13441           index = -1,
13442           length = nativeMax(args.length - start, 0),
13443           array = Array(length);
13444
13445       while (++index < length) {
13446         array[index] = args[start + index];
13447       }
13448       switch (start) {
13449         case 0: return func.call(this, array);
13450         case 1: return func.call(this, args[0], array);
13451         case 2: return func.call(this, args[0], args[1], array);
13452       }
13453       var otherArgs = Array(start + 1);
13454       index = -1;
13455       while (++index < start) {
13456         otherArgs[index] = args[index];
13457       }
13458       otherArgs[start] = array;
13459       return apply(func, this, otherArgs);
13460     };
13461   }
13462
13463   /**
13464    * Creates a throttled function that only invokes `func` at most once per
13465    * every `wait` milliseconds. The throttled function comes with a `cancel`
13466    * method to cancel delayed `func` invocations and a `flush` method to
13467    * immediately invoke them. Provide an options object to indicate whether
13468    * `func` should be invoked on the leading and/or trailing edge of the `wait`
13469    * timeout. The `func` is invoked with the last arguments provided to the
13470    * throttled function. Subsequent calls to the throttled function return the
13471    * result of the last `func` invocation.
13472    *
13473    * **Note:** If `leading` and `trailing` options are `true`, `func` is
13474    * invoked on the trailing edge of the timeout only if the throttled function
13475    * is invoked more than once during the `wait` timeout.
13476    *
13477    * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
13478    * for details over the differences between `_.throttle` and `_.debounce`.
13479    *
13480    * @static
13481    * @memberOf _
13482    * @since 0.1.0
13483    * @category Function
13484    * @param {Function} func The function to throttle.
13485    * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
13486    * @param {Object} [options={}] The options object.
13487    * @param {boolean} [options.leading=true]
13488    *  Specify invoking on the leading edge of the timeout.
13489    * @param {boolean} [options.trailing=true]
13490    *  Specify invoking on the trailing edge of the timeout.
13491    * @returns {Function} Returns the new throttled function.
13492    * @example
13493    *
13494    * // Avoid excessively updating the position while scrolling.
13495    * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
13496    *
13497    * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
13498    * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
13499    * jQuery(element).on('click', throttled);
13500    *
13501    * // Cancel the trailing throttled invocation.
13502    * jQuery(window).on('popstate', throttled.cancel);
13503    */
13504   function throttle(func, wait, options) {
13505     var leading = true,
13506         trailing = true;
13507
13508     if (typeof func != 'function') {
13509       throw new TypeError(FUNC_ERROR_TEXT);
13510     }
13511     if (isObject(options)) {
13512       leading = 'leading' in options ? !!options.leading : leading;
13513       trailing = 'trailing' in options ? !!options.trailing : trailing;
13514     }
13515     return debounce(func, wait, {
13516       'leading': leading,
13517       'maxWait': wait,
13518       'trailing': trailing
13519     });
13520   }
13521
13522   /*------------------------------------------------------------------------*/
13523
13524   /**
13525    * Creates a shallow clone of `value`.
13526    *
13527    * **Note:** This method is loosely based on the
13528    * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)
13529    * and supports cloning arrays, array buffers, booleans, date objects, maps,
13530    * numbers, `Object` objects, regexes, sets, strings, symbols, and typed
13531    * arrays. The own enumerable properties of `arguments` objects are cloned
13532    * as plain objects. An empty object is returned for uncloneable values such
13533    * as error objects, functions, DOM nodes, and WeakMaps.
13534    *
13535    * @static
13536    * @memberOf _
13537    * @since 0.1.0
13538    * @category Lang
13539    * @param {*} value The value to clone.
13540    * @returns {*} Returns the cloned value.
13541    * @see _.cloneDeep
13542    * @example
13543    *
13544    * var objects = [{ 'a': 1 }, { 'b': 2 }];
13545    *
13546    * var shallow = _.clone(objects);
13547    * console.log(shallow[0] === objects[0]);
13548    * // => true
13549    */
13550   function clone(value) {
13551     return baseClone(value, false, true);
13552   }
13553
13554   /**
13555    * This method is like `_.clone` except that it recursively clones `value`.
13556    *
13557    * @static
13558    * @memberOf _
13559    * @since 1.0.0
13560    * @category Lang
13561    * @param {*} value The value to recursively clone.
13562    * @returns {*} Returns the deep cloned value.
13563    * @see _.clone
13564    * @example
13565    *
13566    * var objects = [{ 'a': 1 }, { 'b': 2 }];
13567    *
13568    * var deep = _.cloneDeep(objects);
13569    * console.log(deep[0] === objects[0]);
13570    * // => false
13571    */
13572   function cloneDeep(value) {
13573     return baseClone(value, true, true);
13574   }
13575
13576   /**
13577    * Performs a
13578    * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
13579    * comparison between two values to determine if they are equivalent.
13580    *
13581    * @static
13582    * @memberOf _
13583    * @since 4.0.0
13584    * @category Lang
13585    * @param {*} value The value to compare.
13586    * @param {*} other The other value to compare.
13587    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
13588    * @example
13589    *
13590    * var object = { 'user': 'fred' };
13591    * var other = { 'user': 'fred' };
13592    *
13593    * _.eq(object, object);
13594    * // => true
13595    *
13596    * _.eq(object, other);
13597    * // => false
13598    *
13599    * _.eq('a', 'a');
13600    * // => true
13601    *
13602    * _.eq('a', Object('a'));
13603    * // => false
13604    *
13605    * _.eq(NaN, NaN);
13606    * // => true
13607    */
13608   function eq(value, other) {
13609     return value === other || (value !== value && other !== other);
13610   }
13611
13612   /**
13613    * Checks if `value` is likely an `arguments` object.
13614    *
13615    * @static
13616    * @memberOf _
13617    * @since 0.1.0
13618    * @category Lang
13619    * @param {*} value The value to check.
13620    * @returns {boolean} Returns `true` if `value` is correctly classified,
13621    *  else `false`.
13622    * @example
13623    *
13624    * _.isArguments(function() { return arguments; }());
13625    * // => true
13626    *
13627    * _.isArguments([1, 2, 3]);
13628    * // => false
13629    */
13630   function isArguments(value) {
13631     // Safari 8.1 incorrectly makes `arguments.callee` enumerable in strict mode.
13632     return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&
13633       (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);
13634   }
13635
13636   /**
13637    * Checks if `value` is classified as an `Array` object.
13638    *
13639    * @static
13640    * @memberOf _
13641    * @since 0.1.0
13642    * @type {Function}
13643    * @category Lang
13644    * @param {*} value The value to check.
13645    * @returns {boolean} Returns `true` if `value` is correctly classified,
13646    *  else `false`.
13647    * @example
13648    *
13649    * _.isArray([1, 2, 3]);
13650    * // => true
13651    *
13652    * _.isArray(document.body.children);
13653    * // => false
13654    *
13655    * _.isArray('abc');
13656    * // => false
13657    *
13658    * _.isArray(_.noop);
13659    * // => false
13660    */
13661   var isArray = Array.isArray;
13662
13663   /**
13664    * Checks if `value` is array-like. A value is considered array-like if it's
13665    * not a function and has a `value.length` that's an integer greater than or
13666    * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
13667    *
13668    * @static
13669    * @memberOf _
13670    * @since 4.0.0
13671    * @category Lang
13672    * @param {*} value The value to check.
13673    * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
13674    * @example
13675    *
13676    * _.isArrayLike([1, 2, 3]);
13677    * // => true
13678    *
13679    * _.isArrayLike(document.body.children);
13680    * // => true
13681    *
13682    * _.isArrayLike('abc');
13683    * // => true
13684    *
13685    * _.isArrayLike(_.noop);
13686    * // => false
13687    */
13688   function isArrayLike(value) {
13689     return value != null && isLength(getLength(value)) && !isFunction(value);
13690   }
13691
13692   /**
13693    * This method is like `_.isArrayLike` except that it also checks if `value`
13694    * is an object.
13695    *
13696    * @static
13697    * @memberOf _
13698    * @since 4.0.0
13699    * @category Lang
13700    * @param {*} value The value to check.
13701    * @returns {boolean} Returns `true` if `value` is an array-like object,
13702    *  else `false`.
13703    * @example
13704    *
13705    * _.isArrayLikeObject([1, 2, 3]);
13706    * // => true
13707    *
13708    * _.isArrayLikeObject(document.body.children);
13709    * // => true
13710    *
13711    * _.isArrayLikeObject('abc');
13712    * // => false
13713    *
13714    * _.isArrayLikeObject(_.noop);
13715    * // => false
13716    */
13717   function isArrayLikeObject(value) {
13718     return isObjectLike(value) && isArrayLike(value);
13719   }
13720
13721   /**
13722    * Checks if `value` is a buffer.
13723    *
13724    * @static
13725    * @memberOf _
13726    * @since 4.3.0
13727    * @category Lang
13728    * @param {*} value The value to check.
13729    * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
13730    * @example
13731    *
13732    * _.isBuffer(new Buffer(2));
13733    * // => true
13734    *
13735    * _.isBuffer(new Uint8Array(2));
13736    * // => false
13737    */
13738   var isBuffer = !Buffer ? constant(false) : function(value) {
13739     return value instanceof Buffer;
13740   };
13741
13742   /**
13743    * Checks if `value` is an empty object, collection, map, or set.
13744    *
13745    * Objects are considered empty if they have no own enumerable string keyed
13746    * properties.
13747    *
13748    * Array-like values such as `arguments` objects, arrays, buffers, strings, or
13749    * jQuery-like collections are considered empty if they have a `length` of `0`.
13750    * Similarly, maps and sets are considered empty if they have a `size` of `0`.
13751    *
13752    * @static
13753    * @memberOf _
13754    * @since 0.1.0
13755    * @category Lang
13756    * @param {*} value The value to check.
13757    * @returns {boolean} Returns `true` if `value` is empty, else `false`.
13758    * @example
13759    *
13760    * _.isEmpty(null);
13761    * // => true
13762    *
13763    * _.isEmpty(true);
13764    * // => true
13765    *
13766    * _.isEmpty(1);
13767    * // => true
13768    *
13769    * _.isEmpty([1, 2, 3]);
13770    * // => false
13771    *
13772    * _.isEmpty({ 'a': 1 });
13773    * // => false
13774    */
13775   function isEmpty(value) {
13776     if (isArrayLike(value) &&
13777         (isArray(value) || isString(value) || isFunction(value.splice) ||
13778           isArguments(value) || isBuffer(value))) {
13779       return !value.length;
13780     }
13781     if (isObjectLike(value)) {
13782       var tag = getTag(value);
13783       if (tag == mapTag || tag == setTag) {
13784         return !value.size;
13785       }
13786     }
13787     for (var key in value) {
13788       if (hasOwnProperty.call(value, key)) {
13789         return false;
13790       }
13791     }
13792     return !(nonEnumShadows && keys(value).length);
13793   }
13794
13795   /**
13796    * Performs a deep comparison between two values to determine if they are
13797    * equivalent.
13798    *
13799    * **Note:** This method supports comparing arrays, array buffers, booleans,
13800    * date objects, error objects, maps, numbers, `Object` objects, regexes,
13801    * sets, strings, symbols, and typed arrays. `Object` objects are compared
13802    * by their own, not inherited, enumerable properties. Functions and DOM
13803    * nodes are **not** supported.
13804    *
13805    * @static
13806    * @memberOf _
13807    * @since 0.1.0
13808    * @category Lang
13809    * @param {*} value The value to compare.
13810    * @param {*} other The other value to compare.
13811    * @returns {boolean} Returns `true` if the values are equivalent,
13812    *  else `false`.
13813    * @example
13814    *
13815    * var object = { 'user': 'fred' };
13816    * var other = { 'user': 'fred' };
13817    *
13818    * _.isEqual(object, other);
13819    * // => true
13820    *
13821    * object === other;
13822    * // => false
13823    */
13824   function isEqual(value, other) {
13825     return baseIsEqual(value, other);
13826   }
13827
13828   /**
13829    * Checks if `value` is classified as a `Function` object.
13830    *
13831    * @static
13832    * @memberOf _
13833    * @since 0.1.0
13834    * @category Lang
13835    * @param {*} value The value to check.
13836    * @returns {boolean} Returns `true` if `value` is correctly classified,
13837    *  else `false`.
13838    * @example
13839    *
13840    * _.isFunction(_);
13841    * // => true
13842    *
13843    * _.isFunction(/abc/);
13844    * // => false
13845    */
13846   function isFunction(value) {
13847     // The use of `Object#toString` avoids issues with the `typeof` operator
13848     // in Safari 8 which returns 'object' for typed array and weak map constructors,
13849     // and PhantomJS 1.9 which returns 'function' for `NodeList` instances.
13850     var tag = isObject(value) ? objectToString.call(value) : '';
13851     return tag == funcTag || tag == genTag;
13852   }
13853
13854   /**
13855    * Checks if `value` is a valid array-like length.
13856    *
13857    * **Note:** This function is loosely based on
13858    * [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
13859    *
13860    * @static
13861    * @memberOf _
13862    * @since 4.0.0
13863    * @category Lang
13864    * @param {*} value The value to check.
13865    * @returns {boolean} Returns `true` if `value` is a valid length,
13866    *  else `false`.
13867    * @example
13868    *
13869    * _.isLength(3);
13870    * // => true
13871    *
13872    * _.isLength(Number.MIN_VALUE);
13873    * // => false
13874    *
13875    * _.isLength(Infinity);
13876    * // => false
13877    *
13878    * _.isLength('3');
13879    * // => false
13880    */
13881   function isLength(value) {
13882     return typeof value == 'number' &&
13883       value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
13884   }
13885
13886   /**
13887    * Checks if `value` is the
13888    * [language type](http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-language-types)
13889    * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
13890    *
13891    * @static
13892    * @memberOf _
13893    * @since 0.1.0
13894    * @category Lang
13895    * @param {*} value The value to check.
13896    * @returns {boolean} Returns `true` if `value` is an object, else `false`.
13897    * @example
13898    *
13899    * _.isObject({});
13900    * // => true
13901    *
13902    * _.isObject([1, 2, 3]);
13903    * // => true
13904    *
13905    * _.isObject(_.noop);
13906    * // => true
13907    *
13908    * _.isObject(null);
13909    * // => false
13910    */
13911   function isObject(value) {
13912     var type = typeof value;
13913     return !!value && (type == 'object' || type == 'function');
13914   }
13915
13916   /**
13917    * Checks if `value` is object-like. A value is object-like if it's not `null`
13918    * and has a `typeof` result of "object".
13919    *
13920    * @static
13921    * @memberOf _
13922    * @since 4.0.0
13923    * @category Lang
13924    * @param {*} value The value to check.
13925    * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
13926    * @example
13927    *
13928    * _.isObjectLike({});
13929    * // => true
13930    *
13931    * _.isObjectLike([1, 2, 3]);
13932    * // => true
13933    *
13934    * _.isObjectLike(_.noop);
13935    * // => false
13936    *
13937    * _.isObjectLike(null);
13938    * // => false
13939    */
13940   function isObjectLike(value) {
13941     return !!value && typeof value == 'object';
13942   }
13943
13944   /**
13945    * Checks if `value` is a native function.
13946    *
13947    * @static
13948    * @memberOf _
13949    * @since 3.0.0
13950    * @category Lang
13951    * @param {*} value The value to check.
13952    * @returns {boolean} Returns `true` if `value` is a native function,
13953    *  else `false`.
13954    * @example
13955    *
13956    * _.isNative(Array.prototype.push);
13957    * // => true
13958    *
13959    * _.isNative(_);
13960    * // => false
13961    */
13962   function isNative(value) {
13963     if (!isObject(value)) {
13964       return false;
13965     }
13966     var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;
13967     return pattern.test(toSource(value));
13968   }
13969
13970   /**
13971    * Checks if `value` is a plain object, that is, an object created by the
13972    * `Object` constructor or one with a `[[Prototype]]` of `null`.
13973    *
13974    * @static
13975    * @memberOf _
13976    * @since 0.8.0
13977    * @category Lang
13978    * @param {*} value The value to check.
13979    * @returns {boolean} Returns `true` if `value` is a plain object,
13980    *  else `false`.
13981    * @example
13982    *
13983    * function Foo() {
13984    *   this.a = 1;
13985    * }
13986    *
13987    * _.isPlainObject(new Foo);
13988    * // => false
13989    *
13990    * _.isPlainObject([1, 2, 3]);
13991    * // => false
13992    *
13993    * _.isPlainObject({ 'x': 0, 'y': 0 });
13994    * // => true
13995    *
13996    * _.isPlainObject(Object.create(null));
13997    * // => true
13998    */
13999   function isPlainObject(value) {
14000     if (!isObjectLike(value) ||
14001         objectToString.call(value) != objectTag || isHostObject(value)) {
14002       return false;
14003     }
14004     var proto = getPrototype(value);
14005     if (proto === null) {
14006       return true;
14007     }
14008     var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;
14009     return (typeof Ctor == 'function' &&
14010       Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
14011   }
14012
14013   /**
14014    * Checks if `value` is classified as a `String` primitive or object.
14015    *
14016    * @static
14017    * @since 0.1.0
14018    * @memberOf _
14019    * @category Lang
14020    * @param {*} value The value to check.
14021    * @returns {boolean} Returns `true` if `value` is correctly classified,
14022    *  else `false`.
14023    * @example
14024    *
14025    * _.isString('abc');
14026    * // => true
14027    *
14028    * _.isString(1);
14029    * // => false
14030    */
14031   function isString(value) {
14032     return typeof value == 'string' ||
14033       (!isArray(value) && isObjectLike(value) && objectToString.call(value) == stringTag);
14034   }
14035
14036   /**
14037    * Checks if `value` is classified as a `Symbol` primitive or object.
14038    *
14039    * @static
14040    * @memberOf _
14041    * @since 4.0.0
14042    * @category Lang
14043    * @param {*} value The value to check.
14044    * @returns {boolean} Returns `true` if `value` is correctly classified,
14045    *  else `false`.
14046    * @example
14047    *
14048    * _.isSymbol(Symbol.iterator);
14049    * // => true
14050    *
14051    * _.isSymbol('abc');
14052    * // => false
14053    */
14054   function isSymbol(value) {
14055     return typeof value == 'symbol' ||
14056       (isObjectLike(value) && objectToString.call(value) == symbolTag);
14057   }
14058
14059   /**
14060    * Checks if `value` is classified as a typed array.
14061    *
14062    * @static
14063    * @memberOf _
14064    * @since 3.0.0
14065    * @category Lang
14066    * @param {*} value The value to check.
14067    * @returns {boolean} Returns `true` if `value` is correctly classified,
14068    *  else `false`.
14069    * @example
14070    *
14071    * _.isTypedArray(new Uint8Array);
14072    * // => true
14073    *
14074    * _.isTypedArray([]);
14075    * // => false
14076    */
14077   function isTypedArray(value) {
14078     return isObjectLike(value) &&
14079       isLength(value.length) && !!typedArrayTags[objectToString.call(value)];
14080   }
14081
14082   /**
14083    * Converts `value` to an array.
14084    *
14085    * @static
14086    * @since 0.1.0
14087    * @memberOf _
14088    * @category Lang
14089    * @param {*} value The value to convert.
14090    * @returns {Array} Returns the converted array.
14091    * @example
14092    *
14093    * _.toArray({ 'a': 1, 'b': 2 });
14094    * // => [1, 2]
14095    *
14096    * _.toArray('abc');
14097    * // => ['a', 'b', 'c']
14098    *
14099    * _.toArray(1);
14100    * // => []
14101    *
14102    * _.toArray(null);
14103    * // => []
14104    */
14105   function toArray(value) {
14106     if (!value) {
14107       return [];
14108     }
14109     if (isArrayLike(value)) {
14110       return isString(value) ? stringToArray(value) : copyArray(value);
14111     }
14112     if (iteratorSymbol && value[iteratorSymbol]) {
14113       return iteratorToArray(value[iteratorSymbol]());
14114     }
14115     var tag = getTag(value),
14116         func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values);
14117
14118     return func(value);
14119   }
14120
14121   /**
14122    * Converts `value` to a finite number.
14123    *
14124    * @static
14125    * @memberOf _
14126    * @since 4.12.0
14127    * @category Lang
14128    * @param {*} value The value to convert.
14129    * @returns {number} Returns the converted number.
14130    * @example
14131    *
14132    * _.toFinite(3.2);
14133    * // => 3.2
14134    *
14135    * _.toFinite(Number.MIN_VALUE);
14136    * // => 5e-324
14137    *
14138    * _.toFinite(Infinity);
14139    * // => 1.7976931348623157e+308
14140    *
14141    * _.toFinite('3.2');
14142    * // => 3.2
14143    */
14144   function toFinite(value) {
14145     if (!value) {
14146       return value === 0 ? value : 0;
14147     }
14148     value = toNumber(value);
14149     if (value === INFINITY || value === -INFINITY) {
14150       var sign = (value < 0 ? -1 : 1);
14151       return sign * MAX_INTEGER;
14152     }
14153     return value === value ? value : 0;
14154   }
14155
14156   /**
14157    * Converts `value` to an integer.
14158    *
14159    * **Note:** This function is loosely based on
14160    * [`ToInteger`](http://www.ecma-international.org/ecma-262/6.0/#sec-tointeger).
14161    *
14162    * @static
14163    * @memberOf _
14164    * @since 4.0.0
14165    * @category Lang
14166    * @param {*} value The value to convert.
14167    * @returns {number} Returns the converted integer.
14168    * @example
14169    *
14170    * _.toInteger(3.2);
14171    * // => 3
14172    *
14173    * _.toInteger(Number.MIN_VALUE);
14174    * // => 0
14175    *
14176    * _.toInteger(Infinity);
14177    * // => 1.7976931348623157e+308
14178    *
14179    * _.toInteger('3.2');
14180    * // => 3
14181    */
14182   function toInteger(value) {
14183     var result = toFinite(value),
14184         remainder = result % 1;
14185
14186     return result === result ? (remainder ? result - remainder : result) : 0;
14187   }
14188
14189   /**
14190    * Converts `value` to a number.
14191    *
14192    * @static
14193    * @memberOf _
14194    * @since 4.0.0
14195    * @category Lang
14196    * @param {*} value The value to process.
14197    * @returns {number} Returns the number.
14198    * @example
14199    *
14200    * _.toNumber(3.2);
14201    * // => 3.2
14202    *
14203    * _.toNumber(Number.MIN_VALUE);
14204    * // => 5e-324
14205    *
14206    * _.toNumber(Infinity);
14207    * // => Infinity
14208    *
14209    * _.toNumber('3.2');
14210    * // => 3.2
14211    */
14212   function toNumber(value) {
14213     if (typeof value == 'number') {
14214       return value;
14215     }
14216     if (isSymbol(value)) {
14217       return NAN;
14218     }
14219     if (isObject(value)) {
14220       var other = isFunction(value.valueOf) ? value.valueOf() : value;
14221       value = isObject(other) ? (other + '') : other;
14222     }
14223     if (typeof value != 'string') {
14224       return value === 0 ? value : +value;
14225     }
14226     value = value.replace(reTrim, '');
14227     var isBinary = reIsBinary.test(value);
14228     return (isBinary || reIsOctal.test(value))
14229       ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
14230       : (reIsBadHex.test(value) ? NAN : +value);
14231   }
14232
14233   /**
14234    * Converts `value` to a plain object flattening inherited enumerable string
14235    * keyed properties of `value` to own properties of the plain object.
14236    *
14237    * @static
14238    * @memberOf _
14239    * @since 3.0.0
14240    * @category Lang
14241    * @param {*} value The value to convert.
14242    * @returns {Object} Returns the converted plain object.
14243    * @example
14244    *
14245    * function Foo() {
14246    *   this.b = 2;
14247    * }
14248    *
14249    * Foo.prototype.c = 3;
14250    *
14251    * _.assign({ 'a': 1 }, new Foo);
14252    * // => { 'a': 1, 'b': 2 }
14253    *
14254    * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
14255    * // => { 'a': 1, 'b': 2, 'c': 3 }
14256    */
14257   function toPlainObject(value) {
14258     return copyObject(value, keysIn(value));
14259   }
14260
14261   /**
14262    * Converts `value` to a string. An empty string is returned for `null`
14263    * and `undefined` values. The sign of `-0` is preserved.
14264    *
14265    * @static
14266    * @memberOf _
14267    * @since 4.0.0
14268    * @category Lang
14269    * @param {*} value The value to process.
14270    * @returns {string} Returns the string.
14271    * @example
14272    *
14273    * _.toString(null);
14274    * // => ''
14275    *
14276    * _.toString(-0);
14277    * // => '-0'
14278    *
14279    * _.toString([1, 2, 3]);
14280    * // => '1,2,3'
14281    */
14282   function toString(value) {
14283     return value == null ? '' : baseToString(value);
14284   }
14285
14286   /*------------------------------------------------------------------------*/
14287
14288   /**
14289    * Assigns own enumerable string keyed properties of source objects to the
14290    * destination object. Source objects are applied from left to right.
14291    * Subsequent sources overwrite property assignments of previous sources.
14292    *
14293    * **Note:** This method mutates `object` and is loosely based on
14294    * [`Object.assign`](https://mdn.io/Object/assign).
14295    *
14296    * @static
14297    * @memberOf _
14298    * @since 0.10.0
14299    * @category Object
14300    * @param {Object} object The destination object.
14301    * @param {...Object} [sources] The source objects.
14302    * @returns {Object} Returns `object`.
14303    * @see _.assignIn
14304    * @example
14305    *
14306    * function Foo() {
14307    *   this.c = 3;
14308    * }
14309    *
14310    * function Bar() {
14311    *   this.e = 5;
14312    * }
14313    *
14314    * Foo.prototype.d = 4;
14315    * Bar.prototype.f = 6;
14316    *
14317    * _.assign({ 'a': 1 }, new Foo, new Bar);
14318    * // => { 'a': 1, 'c': 3, 'e': 5 }
14319    */
14320   var assign = createAssigner(function(object, source) {
14321     if (nonEnumShadows || isPrototype(source) || isArrayLike(source)) {
14322       copyObject(source, keys(source), object);
14323       return;
14324     }
14325     for (var key in source) {
14326       if (hasOwnProperty.call(source, key)) {
14327         assignValue(object, key, source[key]);
14328       }
14329     }
14330   });
14331
14332   /**
14333    * This method is like `_.assign` except that it iterates over own and
14334    * inherited source properties.
14335    *
14336    * **Note:** This method mutates `object`.
14337    *
14338    * @static
14339    * @memberOf _
14340    * @since 4.0.0
14341    * @alias extend
14342    * @category Object
14343    * @param {Object} object The destination object.
14344    * @param {...Object} [sources] The source objects.
14345    * @returns {Object} Returns `object`.
14346    * @see _.assign
14347    * @example
14348    *
14349    * function Foo() {
14350    *   this.b = 2;
14351    * }
14352    *
14353    * function Bar() {
14354    *   this.d = 4;
14355    * }
14356    *
14357    * Foo.prototype.c = 3;
14358    * Bar.prototype.e = 5;
14359    *
14360    * _.assignIn({ 'a': 1 }, new Foo, new Bar);
14361    * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5 }
14362    */
14363   var assignIn = createAssigner(function(object, source) {
14364     if (nonEnumShadows || isPrototype(source) || isArrayLike(source)) {
14365       copyObject(source, keysIn(source), object);
14366       return;
14367     }
14368     for (var key in source) {
14369       assignValue(object, key, source[key]);
14370     }
14371   });
14372
14373   /**
14374    * Iterates over own enumerable string keyed properties of an object and
14375    * invokes `iteratee` for each property. The iteratee is invoked with three
14376    * arguments: (value, key, object). Iteratee functions may exit iteration
14377    * early by explicitly returning `false`.
14378    *
14379    * @static
14380    * @memberOf _
14381    * @since 0.3.0
14382    * @category Object
14383    * @param {Object} object The object to iterate over.
14384    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
14385    * @returns {Object} Returns `object`.
14386    * @see _.forOwnRight
14387    * @example
14388    *
14389    * function Foo() {
14390    *   this.a = 1;
14391    *   this.b = 2;
14392    * }
14393    *
14394    * Foo.prototype.c = 3;
14395    *
14396    * _.forOwn(new Foo, function(value, key) {
14397    *   console.log(key);
14398    * });
14399    * // => Logs 'a' then 'b' (iteration order is not guaranteed).
14400    */
14401   function forOwn(object, iteratee) {
14402     return object && baseForOwn(object, getIteratee(iteratee, 3));
14403   }
14404
14405   /**
14406    * Gets the value at `path` of `object`. If the resolved value is
14407    * `undefined`, the `defaultValue` is used in its place.
14408    *
14409    * @static
14410    * @memberOf _
14411    * @since 3.7.0
14412    * @category Object
14413    * @param {Object} object The object to query.
14414    * @param {Array|string} path The path of the property to get.
14415    * @param {*} [defaultValue] The value returned for `undefined` resolved values.
14416    * @returns {*} Returns the resolved value.
14417    * @example
14418    *
14419    * var object = { 'a': [{ 'b': { 'c': 3 } }] };
14420    *
14421    * _.get(object, 'a[0].b.c');
14422    * // => 3
14423    *
14424    * _.get(object, ['a', '0', 'b', 'c']);
14425    * // => 3
14426    *
14427    * _.get(object, 'a.b.c', 'default');
14428    * // => 'default'
14429    */
14430   function get(object, path, defaultValue) {
14431     var result = object == null ? undefined : baseGet(object, path);
14432     return result === undefined ? defaultValue : result;
14433   }
14434
14435   /**
14436    * Checks if `path` is a direct or inherited property of `object`.
14437    *
14438    * @static
14439    * @memberOf _
14440    * @since 4.0.0
14441    * @category Object
14442    * @param {Object} object The object to query.
14443    * @param {Array|string} path The path to check.
14444    * @returns {boolean} Returns `true` if `path` exists, else `false`.
14445    * @example
14446    *
14447    * var object = _.create({ 'a': _.create({ 'b': 2 }) });
14448    *
14449    * _.hasIn(object, 'a');
14450    * // => true
14451    *
14452    * _.hasIn(object, 'a.b');
14453    * // => true
14454    *
14455    * _.hasIn(object, ['a', 'b']);
14456    * // => true
14457    *
14458    * _.hasIn(object, 'b');
14459    * // => false
14460    */
14461   function hasIn(object, path) {
14462     return object != null && hasPath(object, path, baseHasIn);
14463   }
14464
14465   /**
14466    * Creates an array of the own enumerable property names of `object`.
14467    *
14468    * **Note:** Non-object values are coerced to objects. See the
14469    * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys)
14470    * for more details.
14471    *
14472    * @static
14473    * @since 0.1.0
14474    * @memberOf _
14475    * @category Object
14476    * @param {Object} object The object to query.
14477    * @returns {Array} Returns the array of property names.
14478    * @example
14479    *
14480    * function Foo() {
14481    *   this.a = 1;
14482    *   this.b = 2;
14483    * }
14484    *
14485    * Foo.prototype.c = 3;
14486    *
14487    * _.keys(new Foo);
14488    * // => ['a', 'b'] (iteration order is not guaranteed)
14489    *
14490    * _.keys('hi');
14491    * // => ['0', '1']
14492    */
14493   function keys(object) {
14494     var isProto = isPrototype(object);
14495     if (!(isProto || isArrayLike(object))) {
14496       return baseKeys(object);
14497     }
14498     var indexes = indexKeys(object),
14499         skipIndexes = !!indexes,
14500         result = indexes || [],
14501         length = result.length;
14502
14503     for (var key in object) {
14504       if (baseHas(object, key) &&
14505           !(skipIndexes && (key == 'length' || isIndex(key, length))) &&
14506           !(isProto && key == 'constructor')) {
14507         result.push(key);
14508       }
14509     }
14510     return result;
14511   }
14512
14513   /**
14514    * Creates an array of the own and inherited enumerable property names of `object`.
14515    *
14516    * **Note:** Non-object values are coerced to objects.
14517    *
14518    * @static
14519    * @memberOf _
14520    * @since 3.0.0
14521    * @category Object
14522    * @param {Object} object The object to query.
14523    * @returns {Array} Returns the array of property names.
14524    * @example
14525    *
14526    * function Foo() {
14527    *   this.a = 1;
14528    *   this.b = 2;
14529    * }
14530    *
14531    * Foo.prototype.c = 3;
14532    *
14533    * _.keysIn(new Foo);
14534    * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
14535    */
14536   function keysIn(object) {
14537     var index = -1,
14538         isProto = isPrototype(object),
14539         props = baseKeysIn(object),
14540         propsLength = props.length,
14541         indexes = indexKeys(object),
14542         skipIndexes = !!indexes,
14543         result = indexes || [],
14544         length = result.length;
14545
14546     while (++index < propsLength) {
14547       var key = props[index];
14548       if (!(skipIndexes && (key == 'length' || isIndex(key, length))) &&
14549           !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
14550         result.push(key);
14551       }
14552     }
14553     return result;
14554   }
14555
14556   /**
14557    * This method is like `_.assign` except that it recursively merges own and
14558    * inherited enumerable string keyed properties of source objects into the
14559    * destination object. Source properties that resolve to `undefined` are
14560    * skipped if a destination value exists. Array and plain object properties
14561    * are merged recursively. Other objects and value types are overridden by
14562    * assignment. Source objects are applied from left to right. Subsequent
14563    * sources overwrite property assignments of previous sources.
14564    *
14565    * **Note:** This method mutates `object`.
14566    *
14567    * @static
14568    * @memberOf _
14569    * @since 0.5.0
14570    * @category Object
14571    * @param {Object} object The destination object.
14572    * @param {...Object} [sources] The source objects.
14573    * @returns {Object} Returns `object`.
14574    * @example
14575    *
14576    * var users = {
14577    *   'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
14578    * };
14579    *
14580    * var ages = {
14581    *   'data': [{ 'age': 36 }, { 'age': 40 }]
14582    * };
14583    *
14584    * _.merge(users, ages);
14585    * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
14586    */
14587   var merge = createAssigner(function(object, source, srcIndex) {
14588     baseMerge(object, source, srcIndex);
14589   });
14590
14591   /**
14592    * The opposite of `_.pick`; this method creates an object composed of the
14593    * own and inherited enumerable string keyed properties of `object` that are
14594    * not omitted.
14595    *
14596    * @static
14597    * @since 0.1.0
14598    * @memberOf _
14599    * @category Object
14600    * @param {Object} object The source object.
14601    * @param {...(string|string[])} [props] The property identifiers to omit.
14602    * @returns {Object} Returns the new object.
14603    * @example
14604    *
14605    * var object = { 'a': 1, 'b': '2', 'c': 3 };
14606    *
14607    * _.omit(object, ['a', 'c']);
14608    * // => { 'b': '2' }
14609    */
14610   var omit = rest(function(object, props) {
14611     if (object == null) {
14612       return {};
14613     }
14614     props = arrayMap(baseFlatten(props, 1), toKey);
14615     return basePick(object, baseDifference(getAllKeysIn(object), props));
14616   });
14617
14618   /**
14619    * Creates an object composed of the picked `object` properties.
14620    *
14621    * @static
14622    * @since 0.1.0
14623    * @memberOf _
14624    * @category Object
14625    * @param {Object} object The source object.
14626    * @param {...(string|string[])} [props] The property identifiers to pick.
14627    * @returns {Object} Returns the new object.
14628    * @example
14629    *
14630    * var object = { 'a': 1, 'b': '2', 'c': 3 };
14631    *
14632    * _.pick(object, ['a', 'c']);
14633    * // => { 'a': 1, 'c': 3 }
14634    */
14635   var pick = rest(function(object, props) {
14636     return object == null ? {} : basePick(object, arrayMap(baseFlatten(props, 1), toKey));
14637   });
14638
14639   /**
14640    * Creates an array of own enumerable string keyed-value pairs for `object`
14641    * which can be consumed by `_.fromPairs`. If `object` is a map or set, its
14642    * entries are returned.
14643    *
14644    * @static
14645    * @memberOf _
14646    * @since 4.0.0
14647    * @alias entries
14648    * @category Object
14649    * @param {Object} object The object to query.
14650    * @returns {Array} Returns the key-value pairs.
14651    * @example
14652    *
14653    * function Foo() {
14654    *   this.a = 1;
14655    *   this.b = 2;
14656    * }
14657    *
14658    * Foo.prototype.c = 3;
14659    *
14660    * _.toPairs(new Foo);
14661    * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed)
14662    */
14663   var toPairs = createToPairs(keys);
14664
14665   /**
14666    * Creates an array of the own enumerable string keyed property values of `object`.
14667    *
14668    * **Note:** Non-object values are coerced to objects.
14669    *
14670    * @static
14671    * @since 0.1.0
14672    * @memberOf _
14673    * @category Object
14674    * @param {Object} object The object to query.
14675    * @returns {Array} Returns the array of property values.
14676    * @example
14677    *
14678    * function Foo() {
14679    *   this.a = 1;
14680    *   this.b = 2;
14681    * }
14682    *
14683    * Foo.prototype.c = 3;
14684    *
14685    * _.values(new Foo);
14686    * // => [1, 2] (iteration order is not guaranteed)
14687    *
14688    * _.values('hi');
14689    * // => ['h', 'i']
14690    */
14691   function values(object) {
14692     return object ? baseValues(object, keys(object)) : [];
14693   }
14694
14695   /*------------------------------------------------------------------------*/
14696
14697   /**
14698    * Creates a function that returns `value`.
14699    *
14700    * @static
14701    * @memberOf _
14702    * @since 2.4.0
14703    * @category Util
14704    * @param {*} value The value to return from the new function.
14705    * @returns {Function} Returns the new constant function.
14706    * @example
14707    *
14708    * var object = { 'user': 'fred' };
14709    * var getter = _.constant(object);
14710    *
14711    * getter() === object;
14712    * // => true
14713    */
14714   function constant(value) {
14715     return function() {
14716       return value;
14717     };
14718   }
14719
14720   /**
14721    * This method returns the first argument given to it.
14722    *
14723    * @static
14724    * @since 0.1.0
14725    * @memberOf _
14726    * @category Util
14727    * @param {*} value Any value.
14728    * @returns {*} Returns `value`.
14729    * @example
14730    *
14731    * var object = { 'user': 'fred' };
14732    *
14733    * _.identity(object) === object;
14734    * // => true
14735    */
14736   function identity(value) {
14737     return value;
14738   }
14739
14740   /**
14741    * Creates a function that invokes `func` with the arguments of the created
14742    * function. If `func` is a property name, the created function returns the
14743    * property value for a given element. If `func` is an array or object, the
14744    * created function returns `true` for elements that contain the equivalent
14745    * source properties, otherwise it returns `false`.
14746    *
14747    * @static
14748    * @since 4.0.0
14749    * @memberOf _
14750    * @category Util
14751    * @param {*} [func=_.identity] The value to convert to a callback.
14752    * @returns {Function} Returns the callback.
14753    * @example
14754    *
14755    * var users = [
14756    *   { 'user': 'barney', 'age': 36, 'active': true },
14757    *   { 'user': 'fred',   'age': 40, 'active': false }
14758    * ];
14759    *
14760    * // The `_.matches` iteratee shorthand.
14761    * _.filter(users, _.iteratee({ 'user': 'barney', 'active': true }));
14762    * // => [{ 'user': 'barney', 'age': 36, 'active': true }]
14763    *
14764    * // The `_.matchesProperty` iteratee shorthand.
14765    * _.filter(users, _.iteratee(['user', 'fred']));
14766    * // => [{ 'user': 'fred', 'age': 40 }]
14767    *
14768    * // The `_.property` iteratee shorthand.
14769    * _.map(users, _.iteratee('user'));
14770    * // => ['barney', 'fred']
14771    *
14772    * // Create custom iteratee shorthands.
14773    * _.iteratee = _.wrap(_.iteratee, function(iteratee, func) {
14774    *   return !_.isRegExp(func) ? iteratee(func) : function(string) {
14775    *     return func.test(string);
14776    *   };
14777    * });
14778    *
14779    * _.filter(['abc', 'def'], /ef/);
14780    * // => ['def']
14781    */
14782   function iteratee(func) {
14783     return baseIteratee(typeof func == 'function' ? func : baseClone(func, true));
14784   }
14785
14786   /**
14787    * Adds all own enumerable string keyed function properties of a source
14788    * object to the destination object. If `object` is a function, then methods
14789    * are added to its prototype as well.
14790    *
14791    * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
14792    * avoid conflicts caused by modifying the original.
14793    *
14794    * @static
14795    * @since 0.1.0
14796    * @memberOf _
14797    * @category Util
14798    * @param {Function|Object} [object=lodash] The destination object.
14799    * @param {Object} source The object of functions to add.
14800    * @param {Object} [options={}] The options object.
14801    * @param {boolean} [options.chain=true] Specify whether mixins are chainable.
14802    * @returns {Function|Object} Returns `object`.
14803    * @example
14804    *
14805    * function vowels(string) {
14806    *   return _.filter(string, function(v) {
14807    *     return /[aeiou]/i.test(v);
14808    *   });
14809    * }
14810    *
14811    * _.mixin({ 'vowels': vowels });
14812    * _.vowels('fred');
14813    * // => ['e']
14814    *
14815    * _('fred').vowels().value();
14816    * // => ['e']
14817    *
14818    * _.mixin({ 'vowels': vowels }, { 'chain': false });
14819    * _('fred').vowels();
14820    * // => ['e']
14821    */
14822   function mixin(object, source, options) {
14823     var props = keys(source),
14824         methodNames = baseFunctions(source, props);
14825
14826     if (options == null &&
14827         !(isObject(source) && (methodNames.length || !props.length))) {
14828       options = source;
14829       source = object;
14830       object = this;
14831       methodNames = baseFunctions(source, keys(source));
14832     }
14833     var chain = !(isObject(options) && 'chain' in options) || !!options.chain,
14834         isFunc = isFunction(object);
14835
14836     arrayEach(methodNames, function(methodName) {
14837       var func = source[methodName];
14838       object[methodName] = func;
14839       if (isFunc) {
14840         object.prototype[methodName] = function() {
14841           var chainAll = this.__chain__;
14842           if (chain || chainAll) {
14843             var result = object(this.__wrapped__),
14844                 actions = result.__actions__ = copyArray(this.__actions__);
14845
14846             actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
14847             result.__chain__ = chainAll;
14848             return result;
14849           }
14850           return func.apply(object, arrayPush([this.value()], arguments));
14851         };
14852       }
14853     });
14854
14855     return object;
14856   }
14857
14858   /**
14859    * A no-operation function that returns `undefined` regardless of the
14860    * arguments it receives.
14861    *
14862    * @static
14863    * @memberOf _
14864    * @since 2.3.0
14865    * @category Util
14866    * @example
14867    *
14868    * var object = { 'user': 'fred' };
14869    *
14870    * _.noop(object) === undefined;
14871    * // => true
14872    */
14873   function noop() {
14874     // No operation performed.
14875   }
14876
14877   /**
14878    * Creates a function that returns the value at `path` of a given object.
14879    *
14880    * @static
14881    * @memberOf _
14882    * @since 2.4.0
14883    * @category Util
14884    * @param {Array|string} path The path of the property to get.
14885    * @returns {Function} Returns the new accessor function.
14886    * @example
14887    *
14888    * var objects = [
14889    *   { 'a': { 'b': 2 } },
14890    *   { 'a': { 'b': 1 } }
14891    * ];
14892    *
14893    * _.map(objects, _.property('a.b'));
14894    * // => [2, 1]
14895    *
14896    * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b');
14897    * // => [1, 2]
14898    */
14899   function property(path) {
14900     return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path);
14901   }
14902
14903   /*------------------------------------------------------------------------*/
14904
14905   // Add methods that return wrapped values in chain sequences.
14906   lodash.assign = assign;
14907   lodash.assignIn = assignIn;
14908   lodash.bind = bind;
14909   lodash.chain = chain;
14910   lodash.chunk = chunk;
14911   lodash.compact = compact;
14912   lodash.constant = constant;
14913   lodash.debounce = debounce;
14914   lodash.difference = difference;
14915   lodash.filter = filter;
14916   lodash.flatten = flatten;
14917   lodash.groupBy = groupBy;
14918   lodash.intersection = intersection;
14919   lodash.iteratee = iteratee;
14920   lodash.keys = keys;
14921   lodash.keysIn = keysIn;
14922   lodash.map = map;
14923   lodash.memoize = memoize;
14924   lodash.merge = merge;
14925   lodash.mixin = mixin;
14926   lodash.omit = omit;
14927   lodash.pick = pick;
14928   lodash.property = property;
14929   lodash.reject = reject;
14930   lodash.rest = rest;
14931   lodash.reverse = reverse;
14932   lodash.tap = tap;
14933   lodash.throttle = throttle;
14934   lodash.thru = thru;
14935   lodash.toArray = toArray;
14936   lodash.toPairs = toPairs;
14937   lodash.toPlainObject = toPlainObject;
14938   lodash.union = union;
14939   lodash.uniq = uniq;
14940   lodash.values = values;
14941   lodash.without = without;
14942
14943   // Add aliases.
14944   lodash.entries = toPairs;
14945   lodash.extend = assignIn;
14946
14947   // Add methods to `lodash.prototype`.
14948   mixin(lodash, lodash);
14949
14950   /*------------------------------------------------------------------------*/
14951
14952   // Add methods that return unwrapped values in chain sequences.
14953   lodash.clone = clone;
14954   lodash.cloneDeep = cloneDeep;
14955   lodash.eq = eq;
14956   lodash.every = every;
14957   lodash.find = find;
14958   lodash.forEach = forEach;
14959   lodash.forOwn = forOwn;
14960   lodash.get = get;
14961   lodash.hasIn = hasIn;
14962   lodash.head = head;
14963   lodash.identity = identity;
14964   lodash.includes = includes;
14965   lodash.indexOf = indexOf;
14966   lodash.isArguments = isArguments;
14967   lodash.isArray = isArray;
14968   lodash.isArrayLike = isArrayLike;
14969   lodash.isArrayLikeObject = isArrayLikeObject;
14970   lodash.isBuffer = isBuffer;
14971   lodash.isEmpty = isEmpty;
14972   lodash.isEqual = isEqual;
14973   lodash.isFunction = isFunction;
14974   lodash.isLength = isLength;
14975   lodash.isNative = isNative;
14976   lodash.isObject = isObject;
14977   lodash.isObjectLike = isObjectLike;
14978   lodash.isPlainObject = isPlainObject;
14979   lodash.isString = isString;
14980   lodash.isSymbol = isSymbol;
14981   lodash.isTypedArray = isTypedArray;
14982   lodash.last = last;
14983   lodash.noop = noop;
14984   lodash.now = now;
14985   lodash.reduce = reduce;
14986   lodash.some = some;
14987   lodash.toFinite = toFinite;
14988   lodash.toInteger = toInteger;
14989   lodash.toNumber = toNumber;
14990   lodash.toString = toString;
14991
14992   // Add aliases.
14993   lodash.each = forEach;
14994   lodash.first = head;
14995
14996   mixin(lodash, (function() {
14997     var source = {};
14998     baseForOwn(lodash, function(func, methodName) {
14999       if (!hasOwnProperty.call(lodash.prototype, methodName)) {
15000         source[methodName] = func;
15001       }
15002     });
15003     return source;
15004   }()), { 'chain': false });
15005
15006   /*------------------------------------------------------------------------*/
15007
15008   /**
15009    * The semantic version number.
15010    *
15011    * @static
15012    * @memberOf _
15013    * @type {string}
15014    */
15015   lodash.VERSION = VERSION;
15016
15017   // Assign default placeholders.
15018   bind.placeholder = lodash;
15019
15020   // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
15021   arrayEach(['drop', 'take'], function(methodName, index) {
15022     LazyWrapper.prototype[methodName] = function(n) {
15023       var filtered = this.__filtered__;
15024       if (filtered && !index) {
15025         return new LazyWrapper(this);
15026       }
15027       n = n === undefined ? 1 : nativeMax(toInteger(n), 0);
15028
15029       var result = this.clone();
15030       if (filtered) {
15031         result.__takeCount__ = nativeMin(n, result.__takeCount__);
15032       } else {
15033         result.__views__.push({
15034           'size': nativeMin(n, MAX_ARRAY_LENGTH),
15035           'type': methodName + (result.__dir__ < 0 ? 'Right' : '')
15036         });
15037       }
15038       return result;
15039     };
15040
15041     LazyWrapper.prototype[methodName + 'Right'] = function(n) {
15042       return this.reverse()[methodName](n).reverse();
15043     };
15044   });
15045
15046   // Add `LazyWrapper` methods that accept an `iteratee` value.
15047   arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) {
15048     var type = index + 1,
15049         isFilter = type == LAZY_FILTER_FLAG || type == LAZY_WHILE_FLAG;
15050
15051     LazyWrapper.prototype[methodName] = function(iteratee) {
15052       var result = this.clone();
15053       result.__iteratees__.push({
15054         'iteratee': getIteratee(iteratee, 3),
15055         'type': type
15056       });
15057       result.__filtered__ = result.__filtered__ || isFilter;
15058       return result;
15059     };
15060   });
15061
15062   // Add `LazyWrapper` methods for `_.head` and `_.last`.
15063   arrayEach(['head', 'last'], function(methodName, index) {
15064     var takeName = 'take' + (index ? 'Right' : '');
15065
15066     LazyWrapper.prototype[methodName] = function() {
15067       return this[takeName](1).value()[0];
15068     };
15069   });
15070
15071   // Add `LazyWrapper` methods for `_.initial` and `_.tail`.
15072   arrayEach(['initial', 'tail'], function(methodName, index) {
15073     var dropName = 'drop' + (index ? '' : 'Right');
15074
15075     LazyWrapper.prototype[methodName] = function() {
15076       return this.__filtered__ ? new LazyWrapper(this) : this[dropName](1);
15077     };
15078   });
15079
15080   LazyWrapper.prototype.compact = function() {
15081     return this.filter(identity);
15082   };
15083
15084   LazyWrapper.prototype.find = function(predicate) {
15085     return this.filter(predicate).head();
15086   };
15087
15088   LazyWrapper.prototype.findLast = function(predicate) {
15089     return this.reverse().find(predicate);
15090   };
15091
15092   LazyWrapper.prototype.invokeMap = rest(function(path, args) {
15093     if (typeof path == 'function') {
15094       return new LazyWrapper(this);
15095     }
15096     return this.map(function(value) {
15097       return baseInvoke(value, path, args);
15098     });
15099   });
15100
15101   LazyWrapper.prototype.reject = function(predicate) {
15102     predicate = getIteratee(predicate, 3);
15103     return this.filter(function(value) {
15104       return !predicate(value);
15105     });
15106   };
15107
15108   LazyWrapper.prototype.slice = function(start, end) {
15109     start = toInteger(start);
15110
15111     var result = this;
15112     if (result.__filtered__ && (start > 0 || end < 0)) {
15113       return new LazyWrapper(result);
15114     }
15115     if (start < 0) {
15116       result = result.takeRight(-start);
15117     } else if (start) {
15118       result = result.drop(start);
15119     }
15120     if (end !== undefined) {
15121       end = toInteger(end);
15122       result = end < 0 ? result.dropRight(-end) : result.take(end - start);
15123     }
15124     return result;
15125   };
15126
15127   LazyWrapper.prototype.takeRightWhile = function(predicate) {
15128     return this.reverse().takeWhile(predicate).reverse();
15129   };
15130
15131   LazyWrapper.prototype.toArray = function() {
15132     return this.take(MAX_ARRAY_LENGTH);
15133   };
15134
15135   // Add `LazyWrapper` methods to `lodash.prototype`.
15136   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
15137     var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),
15138         isTaker = /^(?:head|last)$/.test(methodName),
15139         lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],
15140         retUnwrapped = isTaker || /^find/.test(methodName);
15141
15142     if (!lodashFunc) {
15143       return;
15144     }
15145     lodash.prototype[methodName] = function() {
15146       var value = this.__wrapped__,
15147           args = isTaker ? [1] : arguments,
15148           isLazy = value instanceof LazyWrapper,
15149           iteratee = args[0],
15150           useLazy = isLazy || isArray(value);
15151
15152       var interceptor = function(value) {
15153         var result = lodashFunc.apply(lodash, arrayPush([value], args));
15154         return (isTaker && chainAll) ? result[0] : result;
15155       };
15156
15157       if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
15158         // Avoid lazy use if the iteratee has a "length" value other than `1`.
15159         isLazy = useLazy = false;
15160       }
15161       var chainAll = this.__chain__,
15162           isHybrid = !!this.__actions__.length,
15163           isUnwrapped = retUnwrapped && !chainAll,
15164           onlyLazy = isLazy && !isHybrid;
15165
15166       if (!retUnwrapped && useLazy) {
15167         value = onlyLazy ? value : new LazyWrapper(this);
15168         var result = func.apply(value, args);
15169         result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
15170         return new LodashWrapper(result, chainAll);
15171       }
15172       if (isUnwrapped && onlyLazy) {
15173         return func.apply(this, args);
15174       }
15175       result = this.thru(interceptor);
15176       return isUnwrapped ? (isTaker ? result.value()[0] : result.value()) : result;
15177     };
15178   });
15179
15180   // Add `Array` methods to `lodash.prototype`.
15181   arrayEach(['pop', 'push', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {
15182     var func = arrayProto[methodName],
15183         chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
15184         retUnwrapped = /^(?:pop|shift)$/.test(methodName);
15185
15186     lodash.prototype[methodName] = function() {
15187       var args = arguments;
15188       if (retUnwrapped && !this.__chain__) {
15189         var value = this.value();
15190         return func.apply(isArray(value) ? value : [], args);
15191       }
15192       return this[chainName](function(value) {
15193         return func.apply(isArray(value) ? value : [], args);
15194       });
15195     };
15196   });
15197
15198   // Map minified method names to their real names.
15199   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
15200     var lodashFunc = lodash[methodName];
15201     if (lodashFunc) {
15202       var key = (lodashFunc.name + ''),
15203           names = realNames[key] || (realNames[key] = []);
15204
15205       names.push({ 'name': methodName, 'func': lodashFunc });
15206     }
15207   });
15208
15209   realNames[createHybridWrapper(undefined, BIND_KEY_FLAG).name] = [{
15210     'name': 'wrapper',
15211     'func': undefined
15212   }];
15213
15214   // Add methods to `LazyWrapper`.
15215   LazyWrapper.prototype.clone = lazyClone;
15216   LazyWrapper.prototype.reverse = lazyReverse;
15217   LazyWrapper.prototype.value = lazyValue;
15218
15219   // Add chain sequence methods to the `lodash` wrapper.
15220   lodash.prototype.chain = wrapperChain;
15221   lodash.prototype.commit = wrapperCommit;
15222   lodash.prototype.next = wrapperNext;
15223   lodash.prototype.plant = wrapperPlant;
15224   lodash.prototype.reverse = wrapperReverse;
15225   lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
15226
15227   if (iteratorSymbol) {
15228     lodash.prototype[iteratorSymbol] = wrapperToIterator;
15229   }
15230
15231   /*--------------------------------------------------------------------------*/
15232
15233   // Expose Lodash on the free variable `window` or `self` when available so it's
15234   // globally accessible, even when bundled with Browserify, Webpack, etc. This
15235   // also prevents errors in cases where Lodash is loaded by a script tag in the
15236   // presence of an AMD loader. See http://requirejs.org/docs/errors.html#mismatch
15237   // for more details. Use `_.noConflict` to remove Lodash from the global object.
15238   (freeWindow || freeSelf || {})._ = lodash;
15239
15240   if (freeExports && freeModule) {
15241     // Export for Node.js.
15242     if (moduleExports) {
15243       (freeModule.exports = lodash)._ = lodash;
15244     }
15245     // Export for CommonJS support.
15246     freeExports._ = lodash;
15247   }
15248   else {
15249     // Export to the global object.
15250     root._ = lodash;
15251   }
15252 }.call(this));
15253 (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;
15254 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){
15255 'use strict';
15256
15257 var ohauth = require('ohauth'),
15258     xtend = require('xtend'),
15259     store = require('store');
15260
15261 // # osm-auth
15262 //
15263 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
15264 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
15265 // does not support custom headers, which this uses everywhere.
15266 module.exports = function(o) {
15267
15268     var oauth = {};
15269
15270     // authenticated users will also have a request token secret, but it's
15271     // not used in transactions with the server
15272     oauth.authenticated = function() {
15273         return !!(token('oauth_token') && token('oauth_token_secret'));
15274     };
15275
15276     oauth.logout = function() {
15277         token('oauth_token', '');
15278         token('oauth_token_secret', '');
15279         token('oauth_request_token_secret', '');
15280         return oauth;
15281     };
15282
15283     // TODO: detect lack of click event
15284     oauth.authenticate = function(callback) {
15285         if (oauth.authenticated()) return callback();
15286
15287         oauth.logout();
15288
15289         // ## Getting a request token
15290         var params = timenonce(getAuth(o)),
15291             url = o.url + '/oauth/request_token',
15292             timer;
15293
15294         params.oauth_signature = ohauth.signature(
15295             o.oauth_secret, '',
15296             ohauth.baseString('POST', url, params));
15297
15298         if (!o.singlepage) {
15299             // Create a 600x550 popup window in the center of the screen
15300             var w = 600, h = 550,
15301                 settings = [
15302                     ['width', w], ['height', h],
15303                     ['left', screen.width / 2 - w / 2],
15304                     ['top', screen.height / 2 - h / 2]].map(function(x) {
15305                         return x.join('=');
15306                     }).join(','),
15307                 popup = window.open('about:blank', 'oauth_window', settings);
15308
15309
15310             timer = setInterval(function() {
15311                 if (popup.closed) {
15312                     o.done();
15313                     clearInterval(timer);
15314                     callback('not authenticated', null);
15315                 }
15316             }, 100);
15317         }
15318
15319         // Request a request token. When this is complete, the popup
15320         // window is redirected to OSM's authorization page.
15321         ohauth.xhr('POST', url, params, null, {}, reqTokenDone);
15322         o.loading();
15323
15324         function reqTokenDone(err, xhr) {
15325             o.done();
15326             if (err) {
15327                 if (timer) clearInterval(timer);
15328                 return callback(err);
15329             }
15330             var resp = ohauth.stringQs(xhr.response);
15331             token('oauth_request_token_secret', resp.oauth_token_secret);
15332             var authorize_url = o.url + '/oauth/authorize?' + ohauth.qsString({
15333                 oauth_token: resp.oauth_token,
15334                 oauth_callback: location.href.replace('index.html', '')
15335                     .replace(/#.*/, '') + o.landing
15336             });
15337
15338             if (o.singlepage) {
15339                 location.href = authorize_url;
15340             } else {
15341                 popup.location = authorize_url;
15342             }
15343         }
15344
15345         // Called by a function in a landing page, in the popup window. The
15346         // window closes itself.
15347         window.authComplete = function(token) {
15348             if (timer) clearInterval(timer);
15349             var oauth_token = ohauth.stringQs(token.split('?')[1]);
15350             get_access_token(oauth_token.oauth_token);
15351             delete window.authComplete;
15352         };
15353
15354         // ## Getting an request token
15355         //
15356         // At this point we have an `oauth_token`, brought in from a function
15357         // call on a landing page popup.
15358         function get_access_token(oauth_token) {
15359             var url = o.url + '/oauth/access_token',
15360                 params = timenonce(getAuth(o)),
15361                 request_token_secret = token('oauth_request_token_secret');
15362             params.oauth_token = oauth_token;
15363             params.oauth_signature = ohauth.signature(
15364                 o.oauth_secret,
15365                 request_token_secret,
15366                 ohauth.baseString('POST', url, params));
15367
15368             // ## Getting an access token
15369             //
15370             // The final token required for authentication. At this point
15371             // we have a `request token secret`
15372             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
15373             o.loading();
15374         }
15375
15376         function accessTokenDone(err, xhr) {
15377             o.done();
15378             if (timer) clearInterval(timer);
15379             if (err) return callback(err);
15380             var access_token = ohauth.stringQs(xhr.response);
15381             token('oauth_token', access_token.oauth_token);
15382             token('oauth_token_secret', access_token.oauth_token_secret);
15383             callback(null, oauth);
15384         }
15385     };
15386
15387     oauth.bootstrapToken = function(oauth_token, callback) {
15388         // ## Getting an request token
15389         // At this point we have an `oauth_token`, brought in from a function
15390         // call on a landing page popup.
15391         function get_access_token(oauth_token) {
15392             var url = o.url + '/oauth/access_token',
15393                 params = timenonce(getAuth(o)),
15394                 request_token_secret = token('oauth_request_token_secret');
15395             params.oauth_token = oauth_token;
15396             params.oauth_signature = ohauth.signature(
15397                 o.oauth_secret,
15398                 request_token_secret,
15399                 ohauth.baseString('POST', url, params));
15400
15401             // ## Getting an access token
15402             // The final token required for authentication. At this point
15403             // we have a `request token secret`
15404             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
15405             o.loading();
15406         }
15407
15408         function accessTokenDone(err, xhr) {
15409             o.done();
15410             if (err) return callback(err);
15411             var access_token = ohauth.stringQs(xhr.response);
15412             token('oauth_token', access_token.oauth_token);
15413             token('oauth_token_secret', access_token.oauth_token_secret);
15414             callback(null, oauth);
15415         }
15416
15417         get_access_token(oauth_token);
15418     };
15419
15420     // # xhr
15421     //
15422     // A single XMLHttpRequest wrapper that does authenticated calls if the
15423     // user has logged in.
15424     oauth.xhr = function(options, callback) {
15425         if (!oauth.authenticated()) {
15426             if (o.auto) return oauth.authenticate(run);
15427             else return callback('not authenticated', null);
15428         } else return run();
15429
15430         function run() {
15431             var params = timenonce(getAuth(o)),
15432                 url = o.url + options.path,
15433                 oauth_token_secret = token('oauth_token_secret');
15434
15435             // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
15436             if ((!options.options || !options.options.header ||
15437                 options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
15438                 options.content) {
15439                 params = xtend(params, ohauth.stringQs(options.content));
15440             }
15441
15442             params.oauth_token = token('oauth_token');
15443             params.oauth_signature = ohauth.signature(
15444                 o.oauth_secret,
15445                 oauth_token_secret,
15446                 ohauth.baseString(options.method, url, params));
15447
15448             ohauth.xhr(options.method,
15449                 url, params, options.content, options.options, done);
15450         }
15451
15452         function done(err, xhr) {
15453             if (err) return callback(err);
15454             else if (xhr.responseXML) return callback(err, xhr.responseXML);
15455             else return callback(err, xhr.response);
15456         }
15457     };
15458
15459     // pre-authorize this object, if we can just get a token and token_secret
15460     // from the start
15461     oauth.preauth = function(c) {
15462         if (!c) return;
15463         if (c.oauth_token) token('oauth_token', c.oauth_token);
15464         if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
15465         return oauth;
15466     };
15467
15468     oauth.options = function(_) {
15469         if (!arguments.length) return o;
15470
15471         o = _;
15472
15473         o.url = o.url || 'http://www.openstreetmap.org';
15474         o.landing = o.landing || 'land.html';
15475
15476         o.singlepage = o.singlepage || false;
15477
15478         // Optional loading and loading-done functions for nice UI feedback.
15479         // by default, no-ops
15480         o.loading = o.loading || function() {};
15481         o.done = o.done || function() {};
15482
15483         return oauth.preauth(o);
15484     };
15485
15486     // 'stamp' an authentication object from `getAuth()`
15487     // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
15488     // and timestamp
15489     function timenonce(o) {
15490         o.oauth_timestamp = ohauth.timestamp();
15491         o.oauth_nonce = ohauth.nonce();
15492         return o;
15493     }
15494
15495     // get/set tokens. These are prefixed with the base URL so that `osm-auth`
15496     // can be used with multiple APIs and the keys in `localStorage`
15497     // will not clash
15498     var token;
15499
15500     if (store.enabled) {
15501         token = function (x, y) {
15502             if (arguments.length === 1) return store.get(o.url + x);
15503             else if (arguments.length === 2) return store.set(o.url + x, y);
15504         };
15505     } else {
15506         var storage = {};
15507         token = function (x, y) {
15508             if (arguments.length === 1) return storage[o.url + x];
15509             else if (arguments.length === 2) return storage[o.url + x] = y;
15510         };
15511     }
15512
15513     // Get an authentication object. If you just add and remove properties
15514     // from a single object, you'll need to use `delete` to make sure that
15515     // it doesn't contain undesired properties for authentication
15516     function getAuth(o) {
15517         return {
15518             oauth_consumer_key: o.oauth_consumer_key,
15519             oauth_signature_method: "HMAC-SHA1"
15520         };
15521     }
15522
15523     // potentially pre-authorize
15524     oauth.options(o);
15525
15526     return oauth;
15527 };
15528
15529 },{"ohauth":2,"store":3,"xtend":4}],3:[function(require,module,exports){
15530 (function(global){;(function(win){
15531         var store = {},
15532                 doc = win.document,
15533                 localStorageName = 'localStorage',
15534                 storage
15535
15536         store.disabled = false
15537         store.set = function(key, value) {}
15538         store.get = function(key) {}
15539         store.remove = function(key) {}
15540         store.clear = function() {}
15541         store.transact = function(key, defaultVal, transactionFn) {
15542                 var val = store.get(key)
15543                 if (transactionFn == null) {
15544                         transactionFn = defaultVal
15545                         defaultVal = null
15546                 }
15547                 if (typeof val == 'undefined') { val = defaultVal || {} }
15548                 transactionFn(val)
15549                 store.set(key, val)
15550         }
15551         store.getAll = function() {}
15552         store.forEach = function() {}
15553
15554         store.serialize = function(value) {
15555                 return JSON.stringify(value)
15556         }
15557         store.deserialize = function(value) {
15558                 if (typeof value != 'string') { return undefined }
15559                 try { return JSON.parse(value) }
15560                 catch(e) { return value || undefined }
15561         }
15562
15563         // Functions to encapsulate questionable FireFox 3.6.13 behavior
15564         // when about.config::dom.storage.enabled === false
15565         // See https://github.com/marcuswestin/store.js/issues#issue/13
15566         function isLocalStorageNameSupported() {
15567                 try { return (localStorageName in win && win[localStorageName]) }
15568                 catch(err) { return false }
15569         }
15570
15571         if (isLocalStorageNameSupported()) {
15572                 storage = win[localStorageName]
15573                 store.set = function(key, val) {
15574                         if (val === undefined) { return store.remove(key) }
15575                         storage.setItem(key, store.serialize(val))
15576                         return val
15577                 }
15578                 store.get = function(key) { return store.deserialize(storage.getItem(key)) }
15579                 store.remove = function(key) { storage.removeItem(key) }
15580                 store.clear = function() { storage.clear() }
15581                 store.getAll = function() {
15582                         var ret = {}
15583                         store.forEach(function(key, val) {
15584                                 ret[key] = val
15585                         })
15586                         return ret
15587                 }
15588                 store.forEach = function(callback) {
15589                         for (var i=0; i<storage.length; i++) {
15590                                 var key = storage.key(i)
15591                                 callback(key, store.get(key))
15592                         }
15593                 }
15594         } else if (doc.documentElement.addBehavior) {
15595                 var storageOwner,
15596                         storageContainer
15597                 // Since #userData storage applies only to specific paths, we need to
15598                 // somehow link our data to a specific path.  We choose /favicon.ico
15599                 // as a pretty safe option, since all browsers already make a request to
15600                 // this URL anyway and being a 404 will not hurt us here.  We wrap an
15601                 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
15602                 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
15603                 // since the iframe access rules appear to allow direct access and
15604                 // manipulation of the document element, even for a 404 page.  This
15605                 // document can be used instead of the current document (which would
15606                 // have been limited to the current path) to perform #userData storage.
15607                 try {
15608                         storageContainer = new ActiveXObject('htmlfile')
15609                         storageContainer.open()
15610                         storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></iframe>')
15611                         storageContainer.close()
15612                         storageOwner = storageContainer.w.frames[0].document
15613                         storage = storageOwner.createElement('div')
15614                 } catch(e) {
15615                         // somehow ActiveXObject instantiation failed (perhaps some special
15616                         // security settings or otherwse), fall back to per-path storage
15617                         storage = doc.createElement('div')
15618                         storageOwner = doc.body
15619                 }
15620                 function withIEStorage(storeFunction) {
15621                         return function() {
15622                                 var args = Array.prototype.slice.call(arguments, 0)
15623                                 args.unshift(storage)
15624                                 // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
15625                                 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
15626                                 storageOwner.appendChild(storage)
15627                                 storage.addBehavior('#default#userData')
15628                                 storage.load(localStorageName)
15629                                 var result = storeFunction.apply(store, args)
15630                                 storageOwner.removeChild(storage)
15631                                 return result
15632                         }
15633                 }
15634
15635                 // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
15636                 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
15637                 function ieKeyFix(key) {
15638                         return key.replace(forbiddenCharsRegex, '___')
15639                 }
15640                 store.set = withIEStorage(function(storage, key, val) {
15641                         key = ieKeyFix(key)
15642                         if (val === undefined) { return store.remove(key) }
15643                         storage.setAttribute(key, store.serialize(val))
15644                         storage.save(localStorageName)
15645                         return val
15646                 })
15647                 store.get = withIEStorage(function(storage, key) {
15648                         key = ieKeyFix(key)
15649                         return store.deserialize(storage.getAttribute(key))
15650                 })
15651                 store.remove = withIEStorage(function(storage, key) {
15652                         key = ieKeyFix(key)
15653                         storage.removeAttribute(key)
15654                         storage.save(localStorageName)
15655                 })
15656                 store.clear = withIEStorage(function(storage) {
15657                         var attributes = storage.XMLDocument.documentElement.attributes
15658                         storage.load(localStorageName)
15659                         for (var i=0, attr; attr=attributes[i]; i++) {
15660                                 storage.removeAttribute(attr.name)
15661                         }
15662                         storage.save(localStorageName)
15663                 })
15664                 store.getAll = function(storage) {
15665                         var ret = {}
15666                         store.forEach(function(key, val) {
15667                                 ret[key] = val
15668                         })
15669                         return ret
15670                 }
15671                 store.forEach = withIEStorage(function(storage, callback) {
15672                         var attributes = storage.XMLDocument.documentElement.attributes
15673                         for (var i=0, attr; attr=attributes[i]; ++i) {
15674                                 callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
15675                         }
15676                 })
15677         }
15678
15679         try {
15680                 var testKey = '__storejs__'
15681                 store.set(testKey, testKey)
15682                 if (store.get(testKey) != testKey) { store.disabled = true }
15683                 store.remove(testKey)
15684         } catch(e) {
15685                 store.disabled = true
15686         }
15687         store.enabled = !store.disabled
15688
15689         if (typeof module != 'undefined' && module.exports) { module.exports = store }
15690         else if (typeof define === 'function' && define.amd) { define(store) }
15691         else { win.store = store }
15692
15693 })(this.window || global);
15694
15695 })(window)
15696 },{}],5:[function(require,module,exports){
15697 module.exports = hasKeys
15698
15699 function hasKeys(source) {
15700     return source !== null &&
15701         (typeof source === "object" ||
15702         typeof source === "function")
15703 }
15704
15705 },{}],4:[function(require,module,exports){
15706 var Keys = require("object-keys")
15707 var hasKeys = require("./has-keys")
15708
15709 module.exports = extend
15710
15711 function extend() {
15712     var target = {}
15713
15714     for (var i = 0; i < arguments.length; i++) {
15715         var source = arguments[i]
15716
15717         if (!hasKeys(source)) {
15718             continue
15719         }
15720
15721         var keys = Keys(source)
15722
15723         for (var j = 0; j < keys.length; j++) {
15724             var name = keys[j]
15725             target[name] = source[name]
15726         }
15727     }
15728
15729     return target
15730 }
15731
15732 },{"./has-keys":5,"object-keys":6}],7:[function(require,module,exports){
15733 (function(global){/**
15734  * jsHashes - A fast and independent hashing library pure JavaScript implemented (ES3 compliant) for both server and client side
15735  *
15736  * @class Hashes
15737  * @author Tomas Aparicio <tomas@rijndael-project.com>
15738  * @license New BSD (see LICENSE file)
15739  * @version 1.0.4
15740  *
15741  * Algorithms specification:
15742  *
15743  * MD5 <http://www.ietf.org/rfc/rfc1321.txt>
15744  * RIPEMD-160 <http://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
15745  * SHA1   <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
15746  * SHA256 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
15747  * SHA512 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
15748  * HMAC <http://www.ietf.org/rfc/rfc2104.txt>
15749  *
15750  */
15751 (function(){
15752   var Hashes;
15753
15754   // private helper methods
15755   function utf8Encode(str) {
15756     var  x, y, output = '', i = -1, l;
15757
15758     if (str && str.length) {
15759       l = str.length;
15760       while ((i+=1) < l) {
15761         /* Decode utf-16 surrogate pairs */
15762         x = str.charCodeAt(i);
15763         y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
15764         if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
15765             x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
15766             i += 1;
15767         }
15768         /* Encode output as utf-8 */
15769         if (x <= 0x7F) {
15770             output += String.fromCharCode(x);
15771         } else if (x <= 0x7FF) {
15772             output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
15773                         0x80 | ( x & 0x3F));
15774         } else if (x <= 0xFFFF) {
15775             output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
15776                         0x80 | ((x >>> 6 ) & 0x3F),
15777                         0x80 | ( x & 0x3F));
15778         } else if (x <= 0x1FFFFF) {
15779             output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
15780                         0x80 | ((x >>> 12) & 0x3F),
15781                         0x80 | ((x >>> 6 ) & 0x3F),
15782                         0x80 | ( x & 0x3F));
15783         }
15784       }
15785     }
15786     return output;
15787   }
15788
15789   function utf8Decode(str) {
15790     var i, ac, c1, c2, c3, arr = [], l;
15791     i = ac = c1 = c2 = c3 = 0;
15792
15793     if (str && str.length) {
15794       l = str.length;
15795       str += '';
15796
15797       while (i < l) {
15798           c1 = str.charCodeAt(i);
15799           ac += 1;
15800           if (c1 < 128) {
15801               arr[ac] = String.fromCharCode(c1);
15802               i+=1;
15803           } else if (c1 > 191 && c1 < 224) {
15804               c2 = str.charCodeAt(i + 1);
15805               arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
15806               i += 2;
15807           } else {
15808               c2 = str.charCodeAt(i + 1);
15809               c3 = str.charCodeAt(i + 2);
15810               arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
15811               i += 3;
15812           }
15813       }
15814     }
15815     return arr.join('');
15816   }
15817
15818   /**
15819    * Add integers, wrapping at 2^32. This uses 16-bit operations internally
15820    * to work around bugs in some JS interpreters.
15821    */
15822   function safe_add(x, y) {
15823     var lsw = (x & 0xFFFF) + (y & 0xFFFF),
15824         msw = (x >> 16) + (y >> 16) + (lsw >> 16);
15825     return (msw << 16) | (lsw & 0xFFFF);
15826   }
15827
15828   /**
15829    * Bitwise rotate a 32-bit number to the left.
15830    */
15831   function bit_rol(num, cnt) {
15832     return (num << cnt) | (num >>> (32 - cnt));
15833   }
15834
15835   /**
15836    * Convert a raw string to a hex string
15837    */
15838   function rstr2hex(input, hexcase) {
15839     var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
15840         output = '', x, i = 0, l = input.length;
15841     for (; i < l; i+=1) {
15842       x = input.charCodeAt(i);
15843       output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
15844     }
15845     return output;
15846   }
15847
15848   /**
15849    * Encode a string as utf-16
15850    */
15851   function str2rstr_utf16le(input) {
15852     var i, l = input.length, output = '';
15853     for (i = 0; i < l; i+=1) {
15854       output += String.fromCharCode( input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF);
15855     }
15856     return output;
15857   }
15858
15859   function str2rstr_utf16be(input) {
15860     var i, l = input.length, output = '';
15861     for (i = 0; i < l; i+=1) {
15862       output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF);
15863     }
15864     return output;
15865   }
15866
15867   /**
15868    * Convert an array of big-endian words to a string
15869    */
15870   function binb2rstr(input) {
15871     var i, l = input.length * 32, output = '';
15872     for (i = 0; i < l; i += 8) {
15873         output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
15874     }
15875     return output;
15876   }
15877
15878   /**
15879    * Convert an array of little-endian words to a string
15880    */
15881   function binl2rstr(input) {
15882     var i, l = input.length * 32, output = '';
15883     for (i = 0;i < l; i += 8) {
15884       output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
15885     }
15886     return output;
15887   }
15888
15889   /**
15890    * Convert a raw string to an array of little-endian words
15891    * Characters >255 have their high-byte silently ignored.
15892    */
15893   function rstr2binl(input) {
15894     var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
15895     for (i = 0; i < lo; i+=1) {
15896       output[i] = 0;
15897     }
15898     for (i = 0; i < l; i += 8) {
15899       output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
15900     }
15901     return output;
15902   }
15903
15904   /**
15905    * Convert a raw string to an array of big-endian words
15906    * Characters >255 have their high-byte silently ignored.
15907    */
15908    function rstr2binb(input) {
15909       var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
15910       for (i = 0; i < lo; i+=1) {
15911             output[i] = 0;
15912         }
15913       for (i = 0; i < l; i += 8) {
15914             output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
15915         }
15916       return output;
15917    }
15918
15919   /**
15920    * Convert a raw string to an arbitrary string encoding
15921    */
15922   function rstr2any(input, encoding) {
15923     var divisor = encoding.length,
15924         remainders = Array(),
15925         i, q, x, ld, quotient, dividend, output, full_length;
15926
15927     /* Convert to an array of 16-bit big-endian values, forming the dividend */
15928     dividend = Array(Math.ceil(input.length / 2));
15929     ld = dividend.length;
15930     for (i = 0; i < ld; i+=1) {
15931       dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
15932     }
15933
15934     /**
15935      * Repeatedly perform a long division. The binary array forms the dividend,
15936      * the length of the encoding is the divisor. Once computed, the quotient
15937      * forms the dividend for the next step. We stop when the dividend is zerHashes.
15938      * All remainders are stored for later use.
15939      */
15940     while(dividend.length > 0) {
15941       quotient = Array();
15942       x = 0;
15943       for (i = 0; i < dividend.length; i+=1) {
15944         x = (x << 16) + dividend[i];
15945         q = Math.floor(x / divisor);
15946         x -= q * divisor;
15947         if (quotient.length > 0 || q > 0) {
15948           quotient[quotient.length] = q;
15949         }
15950       }
15951       remainders[remainders.length] = x;
15952       dividend = quotient;
15953     }
15954
15955     /* Convert the remainders to the output string */
15956     output = '';
15957     for (i = remainders.length - 1; i >= 0; i--) {
15958       output += encoding.charAt(remainders[i]);
15959     }
15960
15961     /* Append leading zero equivalents */
15962     full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
15963     for (i = output.length; i < full_length; i+=1) {
15964       output = encoding[0] + output;
15965     }
15966     return output;
15967   }
15968
15969   /**
15970    * Convert a raw string to a base-64 string
15971    */
15972   function rstr2b64(input, b64pad) {
15973     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
15974         output = '',
15975         len = input.length, i, j, triplet;
15976     b64pad= b64pad || '=';
15977     for (i = 0; i < len; i += 3) {
15978       triplet = (input.charCodeAt(i) << 16)
15979             | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
15980             | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
15981       for (j = 0; j < 4; j+=1) {
15982         if (i * 8 + j * 6 > input.length * 8) {
15983           output += b64pad;
15984         } else {
15985           output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
15986         }
15987        }
15988     }
15989     return output;
15990   }
15991
15992   Hashes = {
15993   /**
15994    * @property {String} version
15995    * @readonly
15996    */
15997   VERSION : '1.0.3',
15998   /**
15999    * @member Hashes
16000    * @class Base64
16001    * @constructor
16002    */
16003   Base64 : function () {
16004     // private properties
16005     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
16006         pad = '=', // default pad according with the RFC standard
16007         url = false, // URL encoding support @todo
16008         utf8 = true; // by default enable UTF-8 support encoding
16009
16010     // public method for encoding
16011     this.encode = function (input) {
16012       var i, j, triplet,
16013           output = '',
16014           len = input.length;
16015
16016       pad = pad || '=';
16017       input = (utf8) ? utf8Encode(input) : input;
16018
16019       for (i = 0; i < len; i += 3) {
16020         triplet = (input.charCodeAt(i) << 16)
16021               | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
16022               | (i + 2 < len ? input.charCodeAt(i+2) : 0);
16023         for (j = 0; j < 4; j+=1) {
16024           if (i * 8 + j * 6 > len * 8) {
16025               output += pad;
16026           } else {
16027               output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
16028           }
16029         }
16030       }
16031       return output;
16032     };
16033
16034     // public method for decoding
16035     this.decode = function (input) {
16036       // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
16037       var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
16038         dec = '',
16039         arr = [];
16040       if (!input) { return input; }
16041
16042       i = ac = 0;
16043       input = input.replace(new RegExp('\\'+pad,'gi'),''); // use '='
16044       //input += '';
16045
16046       do { // unpack four hexets into three octets using index points in b64
16047         h1 = tab.indexOf(input.charAt(i+=1));
16048         h2 = tab.indexOf(input.charAt(i+=1));
16049         h3 = tab.indexOf(input.charAt(i+=1));
16050         h4 = tab.indexOf(input.charAt(i+=1));
16051
16052         bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
16053
16054         o1 = bits >> 16 & 0xff;
16055         o2 = bits >> 8 & 0xff;
16056         o3 = bits & 0xff;
16057         ac += 1;
16058
16059         if (h3 === 64) {
16060           arr[ac] = String.fromCharCode(o1);
16061         } else if (h4 === 64) {
16062           arr[ac] = String.fromCharCode(o1, o2);
16063         } else {
16064           arr[ac] = String.fromCharCode(o1, o2, o3);
16065         }
16066       } while (i < input.length);
16067
16068       dec = arr.join('');
16069       dec = (utf8) ? utf8Decode(dec) : dec;
16070
16071       return dec;
16072     };
16073
16074     // set custom pad string
16075     this.setPad = function (str) {
16076         pad = str || pad;
16077         return this;
16078     };
16079     // set custom tab string characters
16080     this.setTab = function (str) {
16081         tab = str || tab;
16082         return this;
16083     };
16084     this.setUTF8 = function (bool) {
16085         if (typeof bool === 'boolean') {
16086           utf8 = bool;
16087         }
16088         return this;
16089     };
16090   },
16091
16092   /**
16093    * CRC-32 calculation
16094    * @member Hashes
16095    * @method CRC32
16096    * @static
16097    * @param {String} str Input String
16098    * @return {String}
16099    */
16100   CRC32 : function (str) {
16101     var crc = 0, x = 0, y = 0, table, i, iTop;
16102     str = utf8Encode(str);
16103
16104     table = [
16105         '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
16106         '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
16107         '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
16108         '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
16109         'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
16110         '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
16111         'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
16112         '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
16113         'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
16114         '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
16115         'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
16116         '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
16117         'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
16118         '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
16119         '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
16120         '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
16121         '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
16122         'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
16123         '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
16124         'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
16125         '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
16126         'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
16127         '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
16128         'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
16129         '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
16130         'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
16131     ].join('');
16132
16133     crc = crc ^ (-1);
16134     for (i = 0, iTop = str.length; i < iTop; i+=1 ) {
16135         y = ( crc ^ str.charCodeAt( i ) ) & 0xFF;
16136         x = '0x' + table.substr( y * 9, 8 );
16137         crc = ( crc >>> 8 ) ^ x;
16138     }
16139     // always return a positive number (that's what >>> 0 does)
16140     return (crc ^ (-1)) >>> 0;
16141   },
16142   /**
16143    * @member Hashes
16144    * @class MD5
16145    * @constructor
16146    * @param {Object} [config]
16147    *
16148    * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
16149    * Digest Algorithm, as defined in RFC 1321.
16150    * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
16151    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
16152    * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
16153    */
16154   MD5 : function (options) {
16155     /**
16156      * Private config properties. You may need to tweak these to be compatible with
16157      * the server-side, but the defaults work in most cases.
16158      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
16159      */
16160     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
16161         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
16162         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
16163
16164     // privileged (public) methods
16165     this.hex = function (s) {
16166       return rstr2hex(rstr(s, utf8), hexcase);
16167     };
16168     this.b64 = function (s) {
16169       return rstr2b64(rstr(s), b64pad);
16170     };
16171     this.any = function(s, e) {
16172       return rstr2any(rstr(s, utf8), e);
16173     };
16174     this.hex_hmac = function (k, d) {
16175       return rstr2hex(rstr_hmac(k, d), hexcase);
16176     };
16177     this.b64_hmac = function (k, d) {
16178       return rstr2b64(rstr_hmac(k,d), b64pad);
16179     };
16180     this.any_hmac = function (k, d, e) {
16181       return rstr2any(rstr_hmac(k, d), e);
16182     };
16183     /**
16184      * Perform a simple self-test to see if the VM is working
16185      * @return {String} Hexadecimal hash sample
16186      */
16187     this.vm_test = function () {
16188       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
16189     };
16190     /**
16191      * Enable/disable uppercase hexadecimal returned string
16192      * @param {Boolean}
16193      * @return {Object} this
16194      */
16195     this.setUpperCase = function (a) {
16196       if (typeof a === 'boolean' ) {
16197         hexcase = a;
16198       }
16199       return this;
16200     };
16201     /**
16202      * Defines a base64 pad string
16203      * @param {String} Pad
16204      * @return {Object} this
16205      */
16206     this.setPad = function (a) {
16207       b64pad = a || b64pad;
16208       return this;
16209     };
16210     /**
16211      * Defines a base64 pad string
16212      * @param {Boolean}
16213      * @return {Object} [this]
16214      */
16215     this.setUTF8 = function (a) {
16216       if (typeof a === 'boolean') {
16217         utf8 = a;
16218       }
16219       return this;
16220     };
16221
16222     // private methods
16223
16224     /**
16225      * Calculate the MD5 of a raw string
16226      */
16227     function rstr(s) {
16228       s = (utf8) ? utf8Encode(s): s;
16229       return binl2rstr(binl(rstr2binl(s), s.length * 8));
16230     }
16231
16232     /**
16233      * Calculate the HMAC-MD5, of a key and some data (raw strings)
16234      */
16235     function rstr_hmac(key, data) {
16236       var bkey, ipad, opad, hash, i;
16237
16238       key = (utf8) ? utf8Encode(key) : key;
16239       data = (utf8) ? utf8Encode(data) : data;
16240       bkey = rstr2binl(key);
16241       if (bkey.length > 16) {
16242         bkey = binl(bkey, key.length * 8);
16243       }
16244
16245       ipad = Array(16), opad = Array(16);
16246       for (i = 0; i < 16; i+=1) {
16247           ipad[i] = bkey[i] ^ 0x36363636;
16248           opad[i] = bkey[i] ^ 0x5C5C5C5C;
16249       }
16250       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
16251       return binl2rstr(binl(opad.concat(hash), 512 + 128));
16252     }
16253
16254     /**
16255      * Calculate the MD5 of an array of little-endian words, and a bit length.
16256      */
16257     function binl(x, len) {
16258       var i, olda, oldb, oldc, oldd,
16259           a =  1732584193,
16260           b = -271733879,
16261           c = -1732584194,
16262           d =  271733878;
16263
16264       /* append padding */
16265       x[len >> 5] |= 0x80 << ((len) % 32);
16266       x[(((len + 64) >>> 9) << 4) + 14] = len;
16267
16268       for (i = 0; i < x.length; i += 16) {
16269         olda = a;
16270         oldb = b;
16271         oldc = c;
16272         oldd = d;
16273
16274         a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
16275         d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
16276         c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
16277         b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
16278         a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
16279         d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
16280         c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
16281         b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
16282         a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
16283         d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
16284         c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
16285         b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
16286         a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
16287         d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
16288         c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
16289         b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
16290
16291         a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
16292         d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
16293         c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
16294         b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
16295         a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
16296         d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
16297         c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
16298         b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
16299         a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
16300         d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
16301         c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
16302         b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
16303         a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
16304         d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
16305         c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
16306         b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
16307
16308         a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
16309         d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
16310         c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
16311         b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
16312         a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
16313         d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
16314         c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
16315         b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
16316         a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
16317         d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
16318         c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
16319         b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
16320         a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
16321         d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
16322         c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
16323         b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
16324
16325         a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
16326         d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
16327         c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
16328         b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
16329         a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
16330         d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
16331         c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
16332         b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
16333         a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
16334         d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
16335         c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
16336         b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
16337         a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
16338         d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
16339         c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
16340         b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
16341
16342         a = safe_add(a, olda);
16343         b = safe_add(b, oldb);
16344         c = safe_add(c, oldc);
16345         d = safe_add(d, oldd);
16346       }
16347       return Array(a, b, c, d);
16348     }
16349
16350     /**
16351      * These functions implement the four basic operations the algorithm uses.
16352      */
16353     function md5_cmn(q, a, b, x, s, t) {
16354       return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
16355     }
16356     function md5_ff(a, b, c, d, x, s, t) {
16357       return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
16358     }
16359     function md5_gg(a, b, c, d, x, s, t) {
16360       return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
16361     }
16362     function md5_hh(a, b, c, d, x, s, t) {
16363       return md5_cmn(b ^ c ^ d, a, b, x, s, t);
16364     }
16365     function md5_ii(a, b, c, d, x, s, t) {
16366       return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
16367     }
16368   },
16369   /**
16370    * @member Hashes
16371    * @class Hashes.SHA1
16372    * @param {Object} [config]
16373    * @constructor
16374    *
16375    * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
16376    * Version 2.2 Copyright Paul Johnston 2000 - 2009.
16377    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
16378    * See http://pajhome.org.uk/crypt/md5 for details.
16379    */
16380   SHA1 : function (options) {
16381    /**
16382      * Private config properties. You may need to tweak these to be compatible with
16383      * the server-side, but the defaults work in most cases.
16384      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
16385      */
16386     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
16387         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
16388         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
16389
16390     // public methods
16391     this.hex = function (s) {
16392         return rstr2hex(rstr(s, utf8), hexcase);
16393     };
16394     this.b64 = function (s) {
16395         return rstr2b64(rstr(s, utf8), b64pad);
16396     };
16397     this.any = function (s, e) {
16398         return rstr2any(rstr(s, utf8), e);
16399     };
16400     this.hex_hmac = function (k, d) {
16401         return rstr2hex(rstr_hmac(k, d));
16402     };
16403     this.b64_hmac = function (k, d) {
16404         return rstr2b64(rstr_hmac(k, d), b64pad);
16405     };
16406     this.any_hmac = function (k, d, e) {
16407         return rstr2any(rstr_hmac(k, d), e);
16408     };
16409     /**
16410      * Perform a simple self-test to see if the VM is working
16411      * @return {String} Hexadecimal hash sample
16412      * @public
16413      */
16414     this.vm_test = function () {
16415       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
16416     };
16417     /**
16418      * @description Enable/disable uppercase hexadecimal returned string
16419      * @param {boolean}
16420      * @return {Object} this
16421      * @public
16422      */
16423     this.setUpperCase = function (a) {
16424         if (typeof a === 'boolean') {
16425         hexcase = a;
16426       }
16427         return this;
16428     };
16429     /**
16430      * @description Defines a base64 pad string
16431      * @param {string} Pad
16432      * @return {Object} this
16433      * @public
16434      */
16435     this.setPad = function (a) {
16436       b64pad = a || b64pad;
16437         return this;
16438     };
16439     /**
16440      * @description Defines a base64 pad string
16441      * @param {boolean}
16442      * @return {Object} this
16443      * @public
16444      */
16445     this.setUTF8 = function (a) {
16446         if (typeof a === 'boolean') {
16447         utf8 = a;
16448       }
16449         return this;
16450     };
16451
16452     // private methods
16453
16454     /**
16455          * Calculate the SHA-512 of a raw string
16456          */
16457         function rstr(s) {
16458       s = (utf8) ? utf8Encode(s) : s;
16459       return binb2rstr(binb(rstr2binb(s), s.length * 8));
16460         }
16461
16462     /**
16463      * Calculate the HMAC-SHA1 of a key and some data (raw strings)
16464      */
16465     function rstr_hmac(key, data) {
16466         var bkey, ipad, opad, i, hash;
16467         key = (utf8) ? utf8Encode(key) : key;
16468         data = (utf8) ? utf8Encode(data) : data;
16469         bkey = rstr2binb(key);
16470
16471         if (bkey.length > 16) {
16472         bkey = binb(bkey, key.length * 8);
16473       }
16474         ipad = Array(16), opad = Array(16);
16475         for (i = 0; i < 16; i+=1) {
16476                 ipad[i] = bkey[i] ^ 0x36363636;
16477                 opad[i] = bkey[i] ^ 0x5C5C5C5C;
16478         }
16479         hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
16480         return binb2rstr(binb(opad.concat(hash), 512 + 160));
16481     }
16482
16483     /**
16484      * Calculate the SHA-1 of an array of big-endian words, and a bit length
16485      */
16486     function binb(x, len) {
16487       var i, j, t, olda, oldb, oldc, oldd, olde,
16488           w = Array(80),
16489           a =  1732584193,
16490           b = -271733879,
16491           c = -1732584194,
16492           d =  271733878,
16493           e = -1009589776;
16494
16495       /* append padding */
16496       x[len >> 5] |= 0x80 << (24 - len % 32);
16497       x[((len + 64 >> 9) << 4) + 15] = len;
16498
16499       for (i = 0; i < x.length; i += 16) {
16500         olda = a,
16501         oldb = b;
16502         oldc = c;
16503         oldd = d;
16504         olde = e;
16505
16506         for (j = 0; j < 80; j+=1)       {
16507           if (j < 16) {
16508             w[j] = x[i + j];
16509           } else {
16510             w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
16511           }
16512           t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
16513                                            safe_add(safe_add(e, w[j]), sha1_kt(j)));
16514           e = d;
16515           d = c;
16516           c = bit_rol(b, 30);
16517           b = a;
16518           a = t;
16519         }
16520
16521         a = safe_add(a, olda);
16522         b = safe_add(b, oldb);
16523         c = safe_add(c, oldc);
16524         d = safe_add(d, oldd);
16525         e = safe_add(e, olde);
16526       }
16527       return Array(a, b, c, d, e);
16528     }
16529
16530     /**
16531      * Perform the appropriate triplet combination function for the current
16532      * iteration
16533      */
16534     function sha1_ft(t, b, c, d) {
16535       if (t < 20) { return (b & c) | ((~b) & d); }
16536       if (t < 40) { return b ^ c ^ d; }
16537       if (t < 60) { return (b & c) | (b & d) | (c & d); }
16538       return b ^ c ^ d;
16539     }
16540
16541     /**
16542      * Determine the appropriate additive constant for the current iteration
16543      */
16544     function sha1_kt(t) {
16545       return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
16546                  (t < 60) ? -1894007588 : -899497514;
16547     }
16548   },
16549   /**
16550    * @class Hashes.SHA256
16551    * @param {config}
16552    *
16553    * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
16554    * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
16555    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
16556    * See http://pajhome.org.uk/crypt/md5 for details.
16557    * Also http://anmar.eu.org/projects/jssha2/
16558    */
16559   SHA256 : function (options) {
16560     /**
16561      * Private properties configuration variables. You may need to tweak these to be compatible with
16562      * the server-side, but the defaults work in most cases.
16563      * @see this.setUpperCase() method
16564      * @see this.setPad() method
16565      */
16566     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase  */
16567               b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', /* base-64 pad character. Default '=' for strict RFC compliance   */
16568               utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
16569               sha256_K;
16570
16571     /* privileged (public) methods */
16572     this.hex = function (s) {
16573       return rstr2hex(rstr(s, utf8));
16574     };
16575     this.b64 = function (s) {
16576       return rstr2b64(rstr(s, utf8), b64pad);
16577     };
16578     this.any = function (s, e) {
16579       return rstr2any(rstr(s, utf8), e);
16580     };
16581     this.hex_hmac = function (k, d) {
16582       return rstr2hex(rstr_hmac(k, d));
16583     };
16584     this.b64_hmac = function (k, d) {
16585       return rstr2b64(rstr_hmac(k, d), b64pad);
16586     };
16587     this.any_hmac = function (k, d, e) {
16588       return rstr2any(rstr_hmac(k, d), e);
16589     };
16590     /**
16591      * Perform a simple self-test to see if the VM is working
16592      * @return {String} Hexadecimal hash sample
16593      * @public
16594      */
16595     this.vm_test = function () {
16596       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
16597     };
16598     /**
16599      * Enable/disable uppercase hexadecimal returned string
16600      * @param {boolean}
16601      * @return {Object} this
16602      * @public
16603      */
16604     this.setUpperCase = function (a) {
16605       if (typeof a === 'boolean') {
16606         hexcase = a;
16607       }
16608       return this;
16609     };
16610     /**
16611      * @description Defines a base64 pad string
16612      * @param {string} Pad
16613      * @return {Object} this
16614      * @public
16615      */
16616     this.setPad = function (a) {
16617       b64pad = a || b64pad;
16618       return this;
16619     };
16620     /**
16621      * Defines a base64 pad string
16622      * @param {boolean}
16623      * @return {Object} this
16624      * @public
16625      */
16626     this.setUTF8 = function (a) {
16627       if (typeof a === 'boolean') {
16628         utf8 = a;
16629       }
16630       return this;
16631     };
16632
16633     // private methods
16634
16635     /**
16636      * Calculate the SHA-512 of a raw string
16637      */
16638     function rstr(s, utf8) {
16639       s = (utf8) ? utf8Encode(s) : s;
16640       return binb2rstr(binb(rstr2binb(s), s.length * 8));
16641     }
16642
16643     /**
16644      * Calculate the HMAC-sha256 of a key and some data (raw strings)
16645      */
16646     function rstr_hmac(key, data) {
16647       key = (utf8) ? utf8Encode(key) : key;
16648       data = (utf8) ? utf8Encode(data) : data;
16649       var hash, i = 0,
16650           bkey = rstr2binb(key),
16651           ipad = Array(16),
16652           opad = Array(16);
16653
16654       if (bkey.length > 16) { bkey = binb(bkey, key.length * 8); }
16655
16656       for (; i < 16; i+=1) {
16657         ipad[i] = bkey[i] ^ 0x36363636;
16658         opad[i] = bkey[i] ^ 0x5C5C5C5C;
16659       }
16660
16661       hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
16662       return binb2rstr(binb(opad.concat(hash), 512 + 256));
16663     }
16664
16665     /*
16666      * Main sha256 function, with its support functions
16667      */
16668     function sha256_S (X, n) {return ( X >>> n ) | (X << (32 - n));}
16669     function sha256_R (X, n) {return ( X >>> n );}
16670     function sha256_Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
16671     function sha256_Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
16672     function sha256_Sigma0256(x) {return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));}
16673     function sha256_Sigma1256(x) {return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));}
16674     function sha256_Gamma0256(x) {return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));}
16675     function sha256_Gamma1256(x) {return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));}
16676     function sha256_Sigma0512(x) {return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39));}
16677     function sha256_Sigma1512(x) {return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41));}
16678     function sha256_Gamma0512(x) {return (sha256_S(x, 1)  ^ sha256_S(x, 8) ^ sha256_R(x, 7));}
16679     function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));}
16680
16681     sha256_K = [
16682       1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,
16683       -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
16684       1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
16685       264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
16686       -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
16687       113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
16688       1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885,
16689       -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
16690       430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
16691       1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,
16692       -1866530822, -1538233109, -1090935817, -965641998
16693     ];
16694
16695     function binb(m, l) {
16696       var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
16697                  1359893119, -1694144372, 528734635, 1541459225];
16698       var W = new Array(64);
16699       var a, b, c, d, e, f, g, h;
16700       var i, j, T1, T2;
16701
16702       /* append padding */
16703       m[l >> 5] |= 0x80 << (24 - l % 32);
16704       m[((l + 64 >> 9) << 4) + 15] = l;
16705
16706       for (i = 0; i < m.length; i += 16)
16707       {
16708       a = HASH[0];
16709       b = HASH[1];
16710       c = HASH[2];
16711       d = HASH[3];
16712       e = HASH[4];
16713       f = HASH[5];
16714       g = HASH[6];
16715       h = HASH[7];
16716
16717       for (j = 0; j < 64; j+=1)
16718       {
16719         if (j < 16) {
16720           W[j] = m[j + i];
16721         } else {
16722           W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
16723                           sha256_Gamma0256(W[j - 15])), W[j - 16]);
16724         }
16725
16726         T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
16727                                   sha256_K[j]), W[j]);
16728         T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
16729         h = g;
16730         g = f;
16731         f = e;
16732         e = safe_add(d, T1);
16733         d = c;
16734         c = b;
16735         b = a;
16736         a = safe_add(T1, T2);
16737       }
16738
16739       HASH[0] = safe_add(a, HASH[0]);
16740       HASH[1] = safe_add(b, HASH[1]);
16741       HASH[2] = safe_add(c, HASH[2]);
16742       HASH[3] = safe_add(d, HASH[3]);
16743       HASH[4] = safe_add(e, HASH[4]);
16744       HASH[5] = safe_add(f, HASH[5]);
16745       HASH[6] = safe_add(g, HASH[6]);
16746       HASH[7] = safe_add(h, HASH[7]);
16747       }
16748       return HASH;
16749     }
16750
16751   },
16752
16753   /**
16754    * @class Hashes.SHA512
16755    * @param {config}
16756    *
16757    * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
16758    * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
16759    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
16760    * See http://pajhome.org.uk/crypt/md5 for details.
16761    */
16762   SHA512 : function (options) {
16763     /**
16764      * Private properties configuration variables. You may need to tweak these to be compatible with
16765      * the server-side, but the defaults work in most cases.
16766      * @see this.setUpperCase() method
16767      * @see this.setPad() method
16768      */
16769     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false , /* hexadecimal output case format. false - lowercase; true - uppercase  */
16770         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
16771         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
16772         sha512_k;
16773
16774     /* privileged (public) methods */
16775     this.hex = function (s) {
16776       return rstr2hex(rstr(s));
16777     };
16778     this.b64 = function (s) {
16779       return rstr2b64(rstr(s), b64pad);
16780     };
16781     this.any = function (s, e) {
16782       return rstr2any(rstr(s), e);
16783     };
16784     this.hex_hmac = function (k, d) {
16785       return rstr2hex(rstr_hmac(k, d));
16786     };
16787     this.b64_hmac = function (k, d) {
16788       return rstr2b64(rstr_hmac(k, d), b64pad);
16789     };
16790     this.any_hmac = function (k, d, e) {
16791       return rstr2any(rstr_hmac(k, d), e);
16792     };
16793     /**
16794      * Perform a simple self-test to see if the VM is working
16795      * @return {String} Hexadecimal hash sample
16796      * @public
16797      */
16798     this.vm_test = function () {
16799       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
16800     };
16801     /**
16802      * @description Enable/disable uppercase hexadecimal returned string
16803      * @param {boolean}
16804      * @return {Object} this
16805      * @public
16806      */
16807     this.setUpperCase = function (a) {
16808       if (typeof a === 'boolean') {
16809         hexcase = a;
16810       }
16811       return this;
16812     };
16813     /**
16814      * @description Defines a base64 pad string
16815      * @param {string} Pad
16816      * @return {Object} this
16817      * @public
16818      */
16819     this.setPad = function (a) {
16820       b64pad = a || b64pad;
16821       return this;
16822     };
16823     /**
16824      * @description Defines a base64 pad string
16825      * @param {boolean}
16826      * @return {Object} this
16827      * @public
16828      */
16829     this.setUTF8 = function (a) {
16830       if (typeof a === 'boolean') {
16831         utf8 = a;
16832       }
16833       return this;
16834     };
16835
16836     /* private methods */
16837
16838     /**
16839      * Calculate the SHA-512 of a raw string
16840      */
16841     function rstr(s) {
16842       s = (utf8) ? utf8Encode(s) : s;
16843       return binb2rstr(binb(rstr2binb(s), s.length * 8));
16844     }
16845     /*
16846      * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
16847      */
16848     function rstr_hmac(key, data) {
16849       key = (utf8) ? utf8Encode(key) : key;
16850       data = (utf8) ? utf8Encode(data) : data;
16851
16852       var hash, i = 0,
16853           bkey = rstr2binb(key),
16854           ipad = Array(32), opad = Array(32);
16855
16856       if (bkey.length > 32) { bkey = binb(bkey, key.length * 8); }
16857
16858       for (; i < 32; i+=1) {
16859         ipad[i] = bkey[i] ^ 0x36363636;
16860         opad[i] = bkey[i] ^ 0x5C5C5C5C;
16861       }
16862
16863       hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
16864       return binb2rstr(binb(opad.concat(hash), 1024 + 512));
16865     }
16866
16867     /**
16868      * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
16869      */
16870     function binb(x, len) {
16871       var j, i, l,
16872           W = new Array(80),
16873           hash = new Array(16),
16874           //Initial hash values
16875           H = [
16876             new int64(0x6a09e667, -205731576),
16877             new int64(-1150833019, -2067093701),
16878             new int64(0x3c6ef372, -23791573),
16879             new int64(-1521486534, 0x5f1d36f1),
16880             new int64(0x510e527f, -1377402159),
16881             new int64(-1694144372, 0x2b3e6c1f),
16882             new int64(0x1f83d9ab, -79577749),
16883             new int64(0x5be0cd19, 0x137e2179)
16884           ],
16885           T1 = new int64(0, 0),
16886           T2 = new int64(0, 0),
16887           a = new int64(0,0),
16888           b = new int64(0,0),
16889           c = new int64(0,0),
16890           d = new int64(0,0),
16891           e = new int64(0,0),
16892           f = new int64(0,0),
16893           g = new int64(0,0),
16894           h = new int64(0,0),
16895           //Temporary variables not specified by the document
16896           s0 = new int64(0, 0),
16897           s1 = new int64(0, 0),
16898           Ch = new int64(0, 0),
16899           Maj = new int64(0, 0),
16900           r1 = new int64(0, 0),
16901           r2 = new int64(0, 0),
16902           r3 = new int64(0, 0);
16903
16904       if (sha512_k === undefined) {
16905           //SHA512 constants
16906           sha512_k = [
16907             new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
16908             new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
16909             new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
16910             new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
16911             new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
16912             new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
16913             new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
16914             new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
16915             new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
16916             new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
16917             new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
16918             new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
16919             new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
16920             new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
16921             new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
16922             new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
16923             new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
16924             new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
16925             new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
16926             new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
16927             new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
16928             new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
16929             new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
16930             new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
16931             new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
16932             new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
16933             new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
16934             new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
16935             new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
16936             new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
16937             new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
16938             new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
16939             new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
16940             new int64(-354779690, -840897762), new int64(-176337025, -294727304),
16941             new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
16942             new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
16943             new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
16944             new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
16945             new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
16946             new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
16947           ];
16948       }
16949
16950       for (i=0; i<80; i+=1) {
16951         W[i] = new int64(0, 0);
16952       }
16953
16954       // append padding to the source string. The format is described in the FIPS.
16955       x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
16956       x[((len + 128 >> 10)<< 5) + 31] = len;
16957       l = x.length;
16958       for (i = 0; i<l; i+=32) { //32 dwords is the block size
16959         int64copy(a, H[0]);
16960         int64copy(b, H[1]);
16961         int64copy(c, H[2]);
16962         int64copy(d, H[3]);
16963         int64copy(e, H[4]);
16964         int64copy(f, H[5]);
16965         int64copy(g, H[6]);
16966         int64copy(h, H[7]);
16967
16968         for (j=0; j<16; j+=1) {
16969           W[j].h = x[i + 2*j];
16970           W[j].l = x[i + 2*j + 1];
16971         }
16972
16973         for (j=16; j<80; j+=1) {
16974           //sigma1
16975           int64rrot(r1, W[j-2], 19);
16976           int64revrrot(r2, W[j-2], 29);
16977           int64shr(r3, W[j-2], 6);
16978           s1.l = r1.l ^ r2.l ^ r3.l;
16979           s1.h = r1.h ^ r2.h ^ r3.h;
16980           //sigma0
16981           int64rrot(r1, W[j-15], 1);
16982           int64rrot(r2, W[j-15], 8);
16983           int64shr(r3, W[j-15], 7);
16984           s0.l = r1.l ^ r2.l ^ r3.l;
16985           s0.h = r1.h ^ r2.h ^ r3.h;
16986
16987           int64add4(W[j], s1, W[j-7], s0, W[j-16]);
16988         }
16989
16990         for (j = 0; j < 80; j+=1) {
16991           //Ch
16992           Ch.l = (e.l & f.l) ^ (~e.l & g.l);
16993           Ch.h = (e.h & f.h) ^ (~e.h & g.h);
16994
16995           //Sigma1
16996           int64rrot(r1, e, 14);
16997           int64rrot(r2, e, 18);
16998           int64revrrot(r3, e, 9);
16999           s1.l = r1.l ^ r2.l ^ r3.l;
17000           s1.h = r1.h ^ r2.h ^ r3.h;
17001
17002           //Sigma0
17003           int64rrot(r1, a, 28);
17004           int64revrrot(r2, a, 2);
17005           int64revrrot(r3, a, 7);
17006           s0.l = r1.l ^ r2.l ^ r3.l;
17007           s0.h = r1.h ^ r2.h ^ r3.h;
17008
17009           //Maj
17010           Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
17011           Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
17012
17013           int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
17014           int64add(T2, s0, Maj);
17015
17016           int64copy(h, g);
17017           int64copy(g, f);
17018           int64copy(f, e);
17019           int64add(e, d, T1);
17020           int64copy(d, c);
17021           int64copy(c, b);
17022           int64copy(b, a);
17023           int64add(a, T1, T2);
17024         }
17025         int64add(H[0], H[0], a);
17026         int64add(H[1], H[1], b);
17027         int64add(H[2], H[2], c);
17028         int64add(H[3], H[3], d);
17029         int64add(H[4], H[4], e);
17030         int64add(H[5], H[5], f);
17031         int64add(H[6], H[6], g);
17032         int64add(H[7], H[7], h);
17033       }
17034
17035       //represent the hash as an array of 32-bit dwords
17036       for (i=0; i<8; i+=1) {
17037         hash[2*i] = H[i].h;
17038         hash[2*i + 1] = H[i].l;
17039       }
17040       return hash;
17041     }
17042
17043     //A constructor for 64-bit numbers
17044     function int64(h, l) {
17045       this.h = h;
17046       this.l = l;
17047       //this.toString = int64toString;
17048     }
17049
17050     //Copies src into dst, assuming both are 64-bit numbers
17051     function int64copy(dst, src) {
17052       dst.h = src.h;
17053       dst.l = src.l;
17054     }
17055
17056     //Right-rotates a 64-bit number by shift
17057     //Won't handle cases of shift>=32
17058     //The function revrrot() is for that
17059     function int64rrot(dst, x, shift) {
17060       dst.l = (x.l >>> shift) | (x.h << (32-shift));
17061       dst.h = (x.h >>> shift) | (x.l << (32-shift));
17062     }
17063
17064     //Reverses the dwords of the source and then rotates right by shift.
17065     //This is equivalent to rotation by 32+shift
17066     function int64revrrot(dst, x, shift) {
17067       dst.l = (x.h >>> shift) | (x.l << (32-shift));
17068       dst.h = (x.l >>> shift) | (x.h << (32-shift));
17069     }
17070
17071     //Bitwise-shifts right a 64-bit number by shift
17072     //Won't handle shift>=32, but it's never needed in SHA512
17073     function int64shr(dst, x, shift) {
17074       dst.l = (x.l >>> shift) | (x.h << (32-shift));
17075       dst.h = (x.h >>> shift);
17076     }
17077
17078     //Adds two 64-bit numbers
17079     //Like the original implementation, does not rely on 32-bit operations
17080     function int64add(dst, x, y) {
17081        var w0 = (x.l & 0xffff) + (y.l & 0xffff);
17082        var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
17083        var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
17084        var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
17085        dst.l = (w0 & 0xffff) | (w1 << 16);
17086        dst.h = (w2 & 0xffff) | (w3 << 16);
17087     }
17088
17089     //Same, except with 4 addends. Works faster than adding them one by one.
17090     function int64add4(dst, a, b, c, d) {
17091        var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
17092        var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
17093        var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
17094        var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
17095        dst.l = (w0 & 0xffff) | (w1 << 16);
17096        dst.h = (w2 & 0xffff) | (w3 << 16);
17097     }
17098
17099     //Same, except with 5 addends
17100     function int64add5(dst, a, b, c, d, e) {
17101       var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
17102           w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
17103           w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
17104           w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
17105        dst.l = (w0 & 0xffff) | (w1 << 16);
17106        dst.h = (w2 & 0xffff) | (w3 << 16);
17107     }
17108   },
17109   /**
17110    * @class Hashes.RMD160
17111    * @constructor
17112    * @param {Object} [config]
17113    *
17114    * A JavaScript implementation of the RIPEMD-160 Algorithm
17115    * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
17116    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
17117    * See http://pajhome.org.uk/crypt/md5 for details.
17118    * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
17119    */
17120   RMD160 : function (options) {
17121     /**
17122      * Private properties configuration variables. You may need to tweak these to be compatible with
17123      * the server-side, but the defaults work in most cases.
17124      * @see this.setUpperCase() method
17125      * @see this.setPad() method
17126      */
17127     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,   /* hexadecimal output case format. false - lowercase; true - uppercase  */
17128         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
17129         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
17130         rmd160_r1 = [
17131            0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
17132            7,  4, 13,  1, 10,  6, 15,  3, 12,  0,  9,  5,  2, 14, 11,  8,
17133            3, 10, 14,  4,  9, 15,  8,  1,  2,  7,  0,  6, 13, 11,  5, 12,
17134            1,  9, 11, 10,  0,  8, 12,  4, 13,  3,  7, 15, 14,  5,  6,  2,
17135            4,  0,  5,  9,  7, 12,  2, 10, 14,  1,  3,  8, 11,  6, 15, 13
17136         ],
17137         rmd160_r2 = [
17138            5, 14,  7,  0,  9,  2, 11,  4, 13,  6, 15,  8,  1, 10,  3, 12,
17139            6, 11,  3,  7,  0, 13,  5, 10, 14, 15,  8, 12,  4,  9,  1,  2,
17140           15,  5,  1,  3,  7, 14,  6,  9, 11,  8, 12,  2, 10,  0,  4, 13,
17141            8,  6,  4,  1,  3, 11, 15,  0,  5, 12,  2, 13,  9,  7, 10, 14,
17142           12, 15, 10,  4,  1,  5,  8,  7,  6,  2, 13, 14,  0,  3,  9, 11
17143         ],
17144         rmd160_s1 = [
17145           11, 14, 15, 12,  5,  8,  7,  9, 11, 13, 14, 15,  6,  7,  9,  8,
17146            7,  6,  8, 13, 11,  9,  7, 15,  7, 12, 15,  9, 11,  7, 13, 12,
17147           11, 13,  6,  7, 14,  9, 13, 15, 14,  8, 13,  6,  5, 12,  7,  5,
17148           11, 12, 14, 15, 14, 15,  9,  8,  9, 14,  5,  6,  8,  6,  5, 12,
17149            9, 15,  5, 11,  6,  8, 13, 12,  5, 12, 13, 14, 11,  8,  5,  6
17150         ],
17151         rmd160_s2 = [
17152            8,  9,  9, 11, 13, 15, 15,  5,  7,  7,  8, 11, 14, 14, 12,  6,
17153            9, 13, 15,  7, 12,  8,  9, 11,  7,  7, 12,  7,  6, 15, 13, 11,
17154            9,  7, 15, 11,  8,  6,  6, 14, 12, 13,  5, 14, 13, 13,  7,  5,
17155           15,  5,  8, 11, 14, 14,  6, 14,  6,  9, 12,  9, 12,  5, 15,  8,
17156            8,  5, 12,  9, 12,  5, 14,  6,  8, 13,  6,  5, 15, 13, 11, 11
17157         ];
17158
17159     /* privileged (public) methods */
17160     this.hex = function (s) {
17161       return rstr2hex(rstr(s, utf8));
17162     };
17163     this.b64 = function (s) {
17164       return rstr2b64(rstr(s, utf8), b64pad);
17165     };
17166     this.any = function (s, e) {
17167       return rstr2any(rstr(s, utf8), e);
17168     };
17169     this.hex_hmac = function (k, d) {
17170       return rstr2hex(rstr_hmac(k, d));
17171     };
17172     this.b64_hmac = function (k, d) {
17173       return rstr2b64(rstr_hmac(k, d), b64pad);
17174     };
17175     this.any_hmac = function (k, d, e) {
17176       return rstr2any(rstr_hmac(k, d), e);
17177     };
17178     /**
17179      * Perform a simple self-test to see if the VM is working
17180      * @return {String} Hexadecimal hash sample
17181      * @public
17182      */
17183     this.vm_test = function () {
17184       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
17185     };
17186     /**
17187      * @description Enable/disable uppercase hexadecimal returned string
17188      * @param {boolean}
17189      * @return {Object} this
17190      * @public
17191      */
17192     this.setUpperCase = function (a) {
17193       if (typeof a === 'boolean' ) { hexcase = a; }
17194       return this;
17195     };
17196     /**
17197      * @description Defines a base64 pad string
17198      * @param {string} Pad
17199      * @return {Object} this
17200      * @public
17201      */
17202     this.setPad = function (a) {
17203       if (typeof a !== 'undefined' ) { b64pad = a; }
17204       return this;
17205     };
17206     /**
17207      * @description Defines a base64 pad string
17208      * @param {boolean}
17209      * @return {Object} this
17210      * @public
17211      */
17212     this.setUTF8 = function (a) {
17213       if (typeof a === 'boolean') { utf8 = a; }
17214       return this;
17215     };
17216
17217     /* private methods */
17218
17219     /**
17220      * Calculate the rmd160 of a raw string
17221      */
17222     function rstr(s) {
17223       s = (utf8) ? utf8Encode(s) : s;
17224       return binl2rstr(binl(rstr2binl(s), s.length * 8));
17225     }
17226
17227     /**
17228      * Calculate the HMAC-rmd160 of a key and some data (raw strings)
17229      */
17230     function rstr_hmac(key, data) {
17231       key = (utf8) ? utf8Encode(key) : key;
17232       data = (utf8) ? utf8Encode(data) : data;
17233       var i, hash,
17234           bkey = rstr2binl(key),
17235           ipad = Array(16), opad = Array(16);
17236
17237       if (bkey.length > 16) {
17238         bkey = binl(bkey, key.length * 8);
17239       }
17240
17241       for (i = 0; i < 16; i+=1) {
17242         ipad[i] = bkey[i] ^ 0x36363636;
17243         opad[i] = bkey[i] ^ 0x5C5C5C5C;
17244       }
17245       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
17246       return binl2rstr(binl(opad.concat(hash), 512 + 160));
17247     }
17248
17249     /**
17250      * Convert an array of little-endian words to a string
17251      */
17252     function binl2rstr(input) {
17253       var i, output = '', l = input.length * 32;
17254       for (i = 0; i < l; i += 8) {
17255         output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
17256       }
17257       return output;
17258     }
17259
17260     /**
17261      * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
17262      */
17263     function binl(x, len) {
17264       var T, j, i, l,
17265           h0 = 0x67452301,
17266           h1 = 0xefcdab89,
17267           h2 = 0x98badcfe,
17268           h3 = 0x10325476,
17269           h4 = 0xc3d2e1f0,
17270           A1, B1, C1, D1, E1,
17271           A2, B2, C2, D2, E2;
17272
17273       /* append padding */
17274       x[len >> 5] |= 0x80 << (len % 32);
17275       x[(((len + 64) >>> 9) << 4) + 14] = len;
17276       l = x.length;
17277
17278       for (i = 0; i < l; i+=16) {
17279         A1 = A2 = h0; B1 = B2 = h1; C1 = C2 = h2; D1 = D2 = h3; E1 = E2 = h4;
17280         for (j = 0; j <= 79; j+=1) {
17281           T = safe_add(A1, rmd160_f(j, B1, C1, D1));
17282           T = safe_add(T, x[i + rmd160_r1[j]]);
17283           T = safe_add(T, rmd160_K1(j));
17284           T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
17285           A1 = E1; E1 = D1; D1 = bit_rol(C1, 10); C1 = B1; B1 = T;
17286           T = safe_add(A2, rmd160_f(79-j, B2, C2, D2));
17287           T = safe_add(T, x[i + rmd160_r2[j]]);
17288           T = safe_add(T, rmd160_K2(j));
17289           T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
17290           A2 = E2; E2 = D2; D2 = bit_rol(C2, 10); C2 = B2; B2 = T;
17291         }
17292
17293         T = safe_add(h1, safe_add(C1, D2));
17294         h1 = safe_add(h2, safe_add(D1, E2));
17295         h2 = safe_add(h3, safe_add(E1, A2));
17296         h3 = safe_add(h4, safe_add(A1, B2));
17297         h4 = safe_add(h0, safe_add(B1, C2));
17298         h0 = T;
17299       }
17300       return [h0, h1, h2, h3, h4];
17301     }
17302
17303     // specific algorithm methods
17304     function rmd160_f(j, x, y, z) {
17305       return ( 0 <= j && j <= 15) ? (x ^ y ^ z) :
17306          (16 <= j && j <= 31) ? (x & y) | (~x & z) :
17307          (32 <= j && j <= 47) ? (x | ~y) ^ z :
17308          (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
17309          (64 <= j && j <= 79) ? x ^ (y | ~z) :
17310          'rmd160_f: j out of range';
17311     }
17312
17313     function rmd160_K1(j) {
17314       return ( 0 <= j && j <= 15) ? 0x00000000 :
17315          (16 <= j && j <= 31) ? 0x5a827999 :
17316          (32 <= j && j <= 47) ? 0x6ed9eba1 :
17317          (48 <= j && j <= 63) ? 0x8f1bbcdc :
17318          (64 <= j && j <= 79) ? 0xa953fd4e :
17319          'rmd160_K1: j out of range';
17320     }
17321
17322     function rmd160_K2(j){
17323       return ( 0 <= j && j <= 15) ? 0x50a28be6 :
17324          (16 <= j && j <= 31) ? 0x5c4dd124 :
17325          (32 <= j && j <= 47) ? 0x6d703ef3 :
17326          (48 <= j && j <= 63) ? 0x7a6d76e9 :
17327          (64 <= j && j <= 79) ? 0x00000000 :
17328          'rmd160_K2: j out of range';
17329     }
17330   }
17331 };
17332
17333   // exposes Hashes
17334   (function( window, undefined ) {
17335     var freeExports = false;
17336     if (typeof exports === 'object' ) {
17337       freeExports = exports;
17338       if (exports && typeof global === 'object' && global && global === global.global ) { window = global; }
17339     }
17340
17341     if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
17342       // define as an anonymous module, so, through path mapping, it can be aliased
17343       define(function () { return Hashes; });
17344     }
17345     else if ( freeExports ) {
17346       // in Node.js or RingoJS v0.8.0+
17347       if ( typeof module === 'object' && module && module.exports === freeExports ) {
17348         module.exports = Hashes;
17349       }
17350       // in Narwhal or RingoJS v0.7.0-
17351       else {
17352         freeExports.Hashes = Hashes;
17353       }
17354     }
17355     else {
17356       // in a browser or Rhino
17357       window.Hashes = Hashes;
17358     }
17359   }( this ));
17360 }()); // IIFE
17361
17362 })(window)
17363 },{}],2:[function(require,module,exports){
17364 'use strict';
17365
17366 var hashes = require('jshashes'),
17367     xtend = require('xtend'),
17368     sha1 = new hashes.SHA1();
17369
17370 var ohauth = {};
17371
17372 ohauth.qsString = function(obj) {
17373     return Object.keys(obj).sort().map(function(key) {
17374         return ohauth.percentEncode(key) + '=' +
17375             ohauth.percentEncode(obj[key]);
17376     }).join('&');
17377 };
17378
17379 ohauth.stringQs = function(str) {
17380     return str.split('&').reduce(function(obj, pair){
17381         var parts = pair.split('=');
17382         obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
17383             '' : decodeURIComponent(parts[1]);
17384         return obj;
17385     }, {});
17386 };
17387
17388 ohauth.rawxhr = function(method, url, data, headers, callback) {
17389     var xhr = new XMLHttpRequest(),
17390         twoHundred = /^20\d$/;
17391     xhr.onreadystatechange = function() {
17392         if (4 == xhr.readyState && 0 !== xhr.status) {
17393             if (twoHundred.test(xhr.status)) callback(null, xhr);
17394             else return callback(xhr, null);
17395         }
17396     };
17397     xhr.onerror = function(e) { return callback(e, null); };
17398     xhr.open(method, url, true);
17399     for (var h in headers) xhr.setRequestHeader(h, headers[h]);
17400     xhr.send(data);
17401 };
17402
17403 ohauth.xhr = function(method, url, auth, data, options, callback) {
17404     var headers = (options && options.header) || {
17405         'Content-Type': 'application/x-www-form-urlencoded'
17406     };
17407     headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
17408     ohauth.rawxhr(method, url, data, headers, callback);
17409 };
17410
17411 ohauth.nonce = function() {
17412     for (var o = ''; o.length < 6;) {
17413         o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
17414     }
17415     return o;
17416 };
17417
17418 ohauth.authHeader = function(obj) {
17419     return Object.keys(obj).sort().map(function(key) {
17420         return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
17421     }).join(', ');
17422 };
17423
17424 ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
17425
17426 ohauth.percentEncode = function(s) {
17427     return encodeURIComponent(s)
17428         .replace(/\!/g, '%21').replace(/\'/g, '%27')
17429         .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
17430 };
17431
17432 ohauth.baseString = function(method, url, params) {
17433     if (params.oauth_signature) delete params.oauth_signature;
17434     return [
17435         method,
17436         ohauth.percentEncode(url),
17437         ohauth.percentEncode(ohauth.qsString(params))].join('&');
17438 };
17439
17440 ohauth.signature = function(oauth_secret, token_secret, baseString) {
17441     return sha1.b64_hmac(
17442         ohauth.percentEncode(oauth_secret) + '&' +
17443         ohauth.percentEncode(token_secret),
17444         baseString);
17445 };
17446
17447 /**
17448  * Takes an options object for configuration (consumer_key,
17449  * consumer_secret, version, signature_method, token) and returns a
17450  * function that generates the Authorization header for given data.
17451  *
17452  * The returned function takes these parameters:
17453  * - method: GET/POST/...
17454  * - uri: full URI with protocol, port, path and query string
17455  * - extra_params: any extra parameters (that are passed in the POST data),
17456  *   can be an object or a from-urlencoded string.
17457  *
17458  * Returned function returns full OAuth header with "OAuth" string in it.
17459  */
17460
17461 ohauth.headerGenerator = function(options) {
17462     options = options || {};
17463     var consumer_key = options.consumer_key || '',
17464         consumer_secret = options.consumer_secret || '',
17465         signature_method = options.signature_method || 'HMAC-SHA1',
17466         version = options.version || '1.0',
17467         token = options.token || '';
17468
17469     return function(method, uri, extra_params) {
17470         method = method.toUpperCase();
17471         if (typeof extra_params === 'string' && extra_params.length > 0) {
17472             extra_params = ohauth.stringQs(extra_params);
17473         }
17474
17475         var uri_parts = uri.split('?', 2),
17476         base_uri = uri_parts[0];
17477
17478         var query_params = uri_parts.length === 2 ?
17479             ohauth.stringQs(uri_parts[1]) : {};
17480
17481         var oauth_params = {
17482             oauth_consumer_key: consumer_key,
17483             oauth_signature_method: signature_method,
17484             oauth_version: version,
17485             oauth_timestamp: ohauth.timestamp(),
17486             oauth_nonce: ohauth.nonce()
17487         };
17488
17489         if (token) oauth_params.oauth_token = token;
17490
17491         var all_params = xtend({}, oauth_params, query_params, extra_params),
17492             base_str = ohauth.baseString(method, base_uri, all_params);
17493
17494         oauth_params.oauth_signature = ohauth.signature(consumer_secret, token, base_str);
17495
17496         return 'OAuth ' + ohauth.authHeader(oauth_params);
17497     };
17498 };
17499
17500 module.exports = ohauth;
17501
17502 },{"jshashes":7,"xtend":4}],6:[function(require,module,exports){
17503 module.exports = Object.keys || require('./shim');
17504
17505
17506 },{"./shim":8}],8:[function(require,module,exports){
17507 (function () {
17508         "use strict";
17509
17510         // modified from https://github.com/kriskowal/es5-shim
17511         var has = Object.prototype.hasOwnProperty,
17512                 is = require('is'),
17513                 forEach = require('foreach'),
17514                 hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'),
17515                 dontEnums = [
17516                         "toString",
17517                         "toLocaleString",
17518                         "valueOf",
17519                         "hasOwnProperty",
17520                         "isPrototypeOf",
17521                         "propertyIsEnumerable",
17522                         "constructor"
17523                 ],
17524                 keysShim;
17525
17526         keysShim = function keys(object) {
17527                 if (!is.object(object) && !is.array(object)) {
17528                         throw new TypeError("Object.keys called on a non-object");
17529                 }
17530
17531                 var name, theKeys = [];
17532                 for (name in object) {
17533                         if (has.call(object, name)) {
17534                                 theKeys.push(name);
17535                         }
17536                 }
17537
17538                 if (hasDontEnumBug) {
17539                         forEach(dontEnums, function (dontEnum) {
17540                                 if (has.call(object, dontEnum)) {
17541                                         theKeys.push(dontEnum);
17542                                 }
17543                         });
17544                 }
17545                 return theKeys;
17546         };
17547
17548         module.exports = keysShim;
17549 }());
17550
17551
17552 },{"is":9,"foreach":10}],9:[function(require,module,exports){
17553
17554 /**!
17555  * is
17556  * the definitive JavaScript type testing library
17557  *
17558  * @copyright 2013 Enrico Marino
17559  * @license MIT
17560  */
17561
17562 var objProto = Object.prototype;
17563 var owns = objProto.hasOwnProperty;
17564 var toString = objProto.toString;
17565 var isActualNaN = function (value) {
17566   return value !== value;
17567 };
17568 var NON_HOST_TYPES = {
17569   "boolean": 1,
17570   "number": 1,
17571   "string": 1,
17572   "undefined": 1
17573 };
17574
17575 /**
17576  * Expose `is`
17577  */
17578
17579 var is = module.exports = {};
17580
17581 /**
17582  * Test general.
17583  */
17584
17585 /**
17586  * is.type
17587  * Test if `value` is a type of `type`.
17588  *
17589  * @param {Mixed} value value to test
17590  * @param {String} type type
17591  * @return {Boolean} true if `value` is a type of `type`, false otherwise
17592  * @api public
17593  */
17594
17595 is.a =
17596 is.type = function (value, type) {
17597   return typeof value === type;
17598 };
17599
17600 /**
17601  * is.defined
17602  * Test if `value` is defined.
17603  *
17604  * @param {Mixed} value value to test
17605  * @return {Boolean} true if 'value' is defined, false otherwise
17606  * @api public
17607  */
17608
17609 is.defined = function (value) {
17610   return value !== undefined;
17611 };
17612
17613 /**
17614  * is.empty
17615  * Test if `value` is empty.
17616  *
17617  * @param {Mixed} value value to test
17618  * @return {Boolean} true if `value` is empty, false otherwise
17619  * @api public
17620  */
17621
17622 is.empty = function (value) {
17623   var type = toString.call(value);
17624   var key;
17625
17626   if ('[object Array]' === type || '[object Arguments]' === type) {
17627     return value.length === 0;
17628   }
17629
17630   if ('[object Object]' === type) {
17631     for (key in value) if (owns.call(value, key)) return false;
17632     return true;
17633   }
17634
17635   if ('[object String]' === type) {
17636     return '' === value;
17637   }
17638
17639   return false;
17640 };
17641
17642 /**
17643  * is.equal
17644  * Test if `value` is equal to `other`.
17645  *
17646  * @param {Mixed} value value to test
17647  * @param {Mixed} other value to compare with
17648  * @return {Boolean} true if `value` is equal to `other`, false otherwise
17649  */
17650
17651 is.equal = function (value, other) {
17652   var type = toString.call(value)
17653   var key;
17654
17655   if (type !== toString.call(other)) {
17656     return false;
17657   }
17658
17659   if ('[object Object]' === type) {
17660     for (key in value) {
17661       if (!is.equal(value[key], other[key])) {
17662         return false;
17663       }
17664     }
17665     return true;
17666   }
17667
17668   if ('[object Array]' === type) {
17669     key = value.length;
17670     if (key !== other.length) {
17671       return false;
17672     }
17673     while (--key) {
17674       if (!is.equal(value[key], other[key])) {
17675         return false;
17676       }
17677     }
17678     return true;
17679   }
17680
17681   if ('[object Function]' === type) {
17682     return value.prototype === other.prototype;
17683   }
17684
17685   if ('[object Date]' === type) {
17686     return value.getTime() === other.getTime();
17687   }
17688
17689   return value === other;
17690 };
17691
17692 /**
17693  * is.hosted
17694  * Test if `value` is hosted by `host`.
17695  *
17696  * @param {Mixed} value to test
17697  * @param {Mixed} host host to test with
17698  * @return {Boolean} true if `value` is hosted by `host`, false otherwise
17699  * @api public
17700  */
17701
17702 is.hosted = function (value, host) {
17703   var type = typeof host[value];
17704   return type === 'object' ? !!host[value] : !NON_HOST_TYPES[type];
17705 };
17706
17707 /**
17708  * is.instance
17709  * Test if `value` is an instance of `constructor`.
17710  *
17711  * @param {Mixed} value value to test
17712  * @return {Boolean} true if `value` is an instance of `constructor`
17713  * @api public
17714  */
17715
17716 is.instance = is['instanceof'] = function (value, constructor) {
17717   return value instanceof constructor;
17718 };
17719
17720 /**
17721  * is.null
17722  * Test if `value` is null.
17723  *
17724  * @param {Mixed} value value to test
17725  * @return {Boolean} true if `value` is null, false otherwise
17726  * @api public
17727  */
17728
17729 is['null'] = function (value) {
17730   return value === null;
17731 };
17732
17733 /**
17734  * is.undefined
17735  * Test if `value` is undefined.
17736  *
17737  * @param {Mixed} value value to test
17738  * @return {Boolean} true if `value` is undefined, false otherwise
17739  * @api public
17740  */
17741
17742 is.undefined = function (value) {
17743   return value === undefined;
17744 };
17745
17746 /**
17747  * Test arguments.
17748  */
17749
17750 /**
17751  * is.arguments
17752  * Test if `value` is an arguments object.
17753  *
17754  * @param {Mixed} value value to test
17755  * @return {Boolean} true if `value` is an arguments object, false otherwise
17756  * @api public
17757  */
17758
17759 is.arguments = function (value) {
17760   var isStandardArguments = '[object Arguments]' === toString.call(value);
17761   var isOldArguments = !is.array(value) && is.arraylike(value) && is.object(value) && is.fn(value.callee);
17762   return isStandardArguments || isOldArguments;
17763 };
17764
17765 /**
17766  * Test array.
17767  */
17768
17769 /**
17770  * is.array
17771  * Test if 'value' is an array.
17772  *
17773  * @param {Mixed} value value to test
17774  * @return {Boolean} true if `value` is an array, false otherwise
17775  * @api public
17776  */
17777
17778 is.array = function (value) {
17779   return '[object Array]' === toString.call(value);
17780 };
17781
17782 /**
17783  * is.arguments.empty
17784  * Test if `value` is an empty arguments object.
17785  *
17786  * @param {Mixed} value value to test
17787  * @return {Boolean} true if `value` is an empty arguments object, false otherwise
17788  * @api public
17789  */
17790 is.arguments.empty = function (value) {
17791   return is.arguments(value) && value.length === 0;
17792 };
17793
17794 /**
17795  * is.array.empty
17796  * Test if `value` is an empty array.
17797  *
17798  * @param {Mixed} value value to test
17799  * @return {Boolean} true if `value` is an empty array, false otherwise
17800  * @api public
17801  */
17802 is.array.empty = function (value) {
17803   return is.array(value) && value.length === 0;
17804 };
17805
17806 /**
17807  * is.arraylike
17808  * Test if `value` is an arraylike object.
17809  *
17810  * @param {Mixed} value value to test
17811  * @return {Boolean} true if `value` is an arguments object, false otherwise
17812  * @api public
17813  */
17814
17815 is.arraylike = function (value) {
17816   return !!value && !is.boolean(value)
17817     && owns.call(value, 'length')
17818     && isFinite(value.length)
17819     && is.number(value.length)
17820     && value.length >= 0;
17821 };
17822
17823 /**
17824  * Test boolean.
17825  */
17826
17827 /**
17828  * is.boolean
17829  * Test if `value` is a boolean.
17830  *
17831  * @param {Mixed} value value to test
17832  * @return {Boolean} true if `value` is a boolean, false otherwise
17833  * @api public
17834  */
17835
17836 is.boolean = function (value) {
17837   return '[object Boolean]' === toString.call(value);
17838 };
17839
17840 /**
17841  * is.false
17842  * Test if `value` is false.
17843  *
17844  * @param {Mixed} value value to test
17845  * @return {Boolean} true if `value` is false, false otherwise
17846  * @api public
17847  */
17848
17849 is['false'] = function (value) {
17850   return is.boolean(value) && (value === false || value.valueOf() === false);
17851 };
17852
17853 /**
17854  * is.true
17855  * Test if `value` is true.
17856  *
17857  * @param {Mixed} value value to test
17858  * @return {Boolean} true if `value` is true, false otherwise
17859  * @api public
17860  */
17861
17862 is['true'] = function (value) {
17863   return is.boolean(value) && (value === true || value.valueOf() === true);
17864 };
17865
17866 /**
17867  * Test date.
17868  */
17869
17870 /**
17871  * is.date
17872  * Test if `value` is a date.
17873  *
17874  * @param {Mixed} value value to test
17875  * @return {Boolean} true if `value` is a date, false otherwise
17876  * @api public
17877  */
17878
17879 is.date = function (value) {
17880   return '[object Date]' === toString.call(value);
17881 };
17882
17883 /**
17884  * Test element.
17885  */
17886
17887 /**
17888  * is.element
17889  * Test if `value` is an html element.
17890  *
17891  * @param {Mixed} value value to test
17892  * @return {Boolean} true if `value` is an HTML Element, false otherwise
17893  * @api public
17894  */
17895
17896 is.element = function (value) {
17897   return value !== undefined
17898     && typeof HTMLElement !== 'undefined'
17899     && value instanceof HTMLElement
17900     && value.nodeType === 1;
17901 };
17902
17903 /**
17904  * Test error.
17905  */
17906
17907 /**
17908  * is.error
17909  * Test if `value` is an error object.
17910  *
17911  * @param {Mixed} value value to test
17912  * @return {Boolean} true if `value` is an error object, false otherwise
17913  * @api public
17914  */
17915
17916 is.error = function (value) {
17917   return '[object Error]' === toString.call(value);
17918 };
17919
17920 /**
17921  * Test function.
17922  */
17923
17924 /**
17925  * is.fn / is.function (deprecated)
17926  * Test if `value` is a function.
17927  *
17928  * @param {Mixed} value value to test
17929  * @return {Boolean} true if `value` is a function, false otherwise
17930  * @api public
17931  */
17932
17933 is.fn = is['function'] = function (value) {
17934   var isAlert = typeof window !== 'undefined' && value === window.alert;
17935   return isAlert || '[object Function]' === toString.call(value);
17936 };
17937
17938 /**
17939  * Test number.
17940  */
17941
17942 /**
17943  * is.number
17944  * Test if `value` is a number.
17945  *
17946  * @param {Mixed} value value to test
17947  * @return {Boolean} true if `value` is a number, false otherwise
17948  * @api public
17949  */
17950
17951 is.number = function (value) {
17952   return '[object Number]' === toString.call(value);
17953 };
17954
17955 /**
17956  * is.infinite
17957  * Test if `value` is positive or negative infinity.
17958  *
17959  * @param {Mixed} value value to test
17960  * @return {Boolean} true if `value` is positive or negative Infinity, false otherwise
17961  * @api public
17962  */
17963 is.infinite = function (value) {
17964   return value === Infinity || value === -Infinity;
17965 };
17966
17967 /**
17968  * is.decimal
17969  * Test if `value` is a decimal number.
17970  *
17971  * @param {Mixed} value value to test
17972  * @return {Boolean} true if `value` is a decimal number, false otherwise
17973  * @api public
17974  */
17975
17976 is.decimal = function (value) {
17977   return is.number(value) && !isActualNaN(value) && value % 1 !== 0;
17978 };
17979
17980 /**
17981  * is.divisibleBy
17982  * Test if `value` is divisible by `n`.
17983  *
17984  * @param {Number} value value to test
17985  * @param {Number} n dividend
17986  * @return {Boolean} true if `value` is divisible by `n`, false otherwise
17987  * @api public
17988  */
17989
17990 is.divisibleBy = function (value, n) {
17991   var isDividendInfinite = is.infinite(value);
17992   var isDivisorInfinite = is.infinite(n);
17993   var isNonZeroNumber = is.number(value) && !isActualNaN(value) && is.number(n) && !isActualNaN(n) && n !== 0;
17994   return isDividendInfinite || isDivisorInfinite || (isNonZeroNumber && value % n === 0);
17995 };
17996
17997 /**
17998  * is.int
17999  * Test if `value` is an integer.
18000  *
18001  * @param value to test
18002  * @return {Boolean} true if `value` is an integer, false otherwise
18003  * @api public
18004  */
18005
18006 is.int = function (value) {
18007   return is.number(value) && !isActualNaN(value) && value % 1 === 0;
18008 };
18009
18010 /**
18011  * is.maximum
18012  * Test if `value` is greater than 'others' values.
18013  *
18014  * @param {Number} value value to test
18015  * @param {Array} others values to compare with
18016  * @return {Boolean} true if `value` is greater than `others` values
18017  * @api public
18018  */
18019
18020 is.maximum = function (value, others) {
18021   if (isActualNaN(value)) {
18022     throw new TypeError('NaN is not a valid value');
18023   } else if (!is.arraylike(others)) {
18024     throw new TypeError('second argument must be array-like');
18025   }
18026   var len = others.length;
18027
18028   while (--len >= 0) {
18029     if (value < others[len]) {
18030       return false;
18031     }
18032   }
18033
18034   return true;
18035 };
18036
18037 /**
18038  * is.minimum
18039  * Test if `value` is less than `others` values.
18040  *
18041  * @param {Number} value value to test
18042  * @param {Array} others values to compare with
18043  * @return {Boolean} true if `value` is less than `others` values
18044  * @api public
18045  */
18046
18047 is.minimum = function (value, others) {
18048   if (isActualNaN(value)) {
18049     throw new TypeError('NaN is not a valid value');
18050   } else if (!is.arraylike(others)) {
18051     throw new TypeError('second argument must be array-like');
18052   }
18053   var len = others.length;
18054
18055   while (--len >= 0) {
18056     if (value > others[len]) {
18057       return false;
18058     }
18059   }
18060
18061   return true;
18062 };
18063
18064 /**
18065  * is.nan
18066  * Test if `value` is not a number.
18067  *
18068  * @param {Mixed} value value to test
18069  * @return {Boolean} true if `value` is not a number, false otherwise
18070  * @api public
18071  */
18072
18073 is.nan = function (value) {
18074   return !is.number(value) || value !== value;
18075 };
18076
18077 /**
18078  * is.even
18079  * Test if `value` is an even number.
18080  *
18081  * @param {Number} value value to test
18082  * @return {Boolean} true if `value` is an even number, false otherwise
18083  * @api public
18084  */
18085
18086 is.even = function (value) {
18087   return is.infinite(value) || (is.number(value) && value === value && value % 2 === 0);
18088 };
18089
18090 /**
18091  * is.odd
18092  * Test if `value` is an odd number.
18093  *
18094  * @param {Number} value value to test
18095  * @return {Boolean} true if `value` is an odd number, false otherwise
18096  * @api public
18097  */
18098
18099 is.odd = function (value) {
18100   return is.infinite(value) || (is.number(value) && value === value && value % 2 !== 0);
18101 };
18102
18103 /**
18104  * is.ge
18105  * Test if `value` is greater than or equal to `other`.
18106  *
18107  * @param {Number} value value to test
18108  * @param {Number} other value to compare with
18109  * @return {Boolean}
18110  * @api public
18111  */
18112
18113 is.ge = function (value, other) {
18114   if (isActualNaN(value) || isActualNaN(other)) {
18115     throw new TypeError('NaN is not a valid value');
18116   }
18117   return !is.infinite(value) && !is.infinite(other) && value >= other;
18118 };
18119
18120 /**
18121  * is.gt
18122  * Test if `value` is greater than `other`.
18123  *
18124  * @param {Number} value value to test
18125  * @param {Number} other value to compare with
18126  * @return {Boolean}
18127  * @api public
18128  */
18129
18130 is.gt = function (value, other) {
18131   if (isActualNaN(value) || isActualNaN(other)) {
18132     throw new TypeError('NaN is not a valid value');
18133   }
18134   return !is.infinite(value) && !is.infinite(other) && value > other;
18135 };
18136
18137 /**
18138  * is.le
18139  * Test if `value` is less than or equal to `other`.
18140  *
18141  * @param {Number} value value to test
18142  * @param {Number} other value to compare with
18143  * @return {Boolean} if 'value' is less than or equal to 'other'
18144  * @api public
18145  */
18146
18147 is.le = function (value, other) {
18148   if (isActualNaN(value) || isActualNaN(other)) {
18149     throw new TypeError('NaN is not a valid value');
18150   }
18151   return !is.infinite(value) && !is.infinite(other) && value <= other;
18152 };
18153
18154 /**
18155  * is.lt
18156  * Test if `value` is less than `other`.
18157  *
18158  * @param {Number} value value to test
18159  * @param {Number} other value to compare with
18160  * @return {Boolean} if `value` is less than `other`
18161  * @api public
18162  */
18163
18164 is.lt = function (value, other) {
18165   if (isActualNaN(value) || isActualNaN(other)) {
18166     throw new TypeError('NaN is not a valid value');
18167   }
18168   return !is.infinite(value) && !is.infinite(other) && value < other;
18169 };
18170
18171 /**
18172  * is.within
18173  * Test if `value` is within `start` and `finish`.
18174  *
18175  * @param {Number} value value to test
18176  * @param {Number} start lower bound
18177  * @param {Number} finish upper bound
18178  * @return {Boolean} true if 'value' is is within 'start' and 'finish'
18179  * @api public
18180  */
18181 is.within = function (value, start, finish) {
18182   if (isActualNaN(value) || isActualNaN(start) || isActualNaN(finish)) {
18183     throw new TypeError('NaN is not a valid value');
18184   } else if (!is.number(value) || !is.number(start) || !is.number(finish)) {
18185     throw new TypeError('all arguments must be numbers');
18186   }
18187   var isAnyInfinite = is.infinite(value) || is.infinite(start) || is.infinite(finish);
18188   return isAnyInfinite || (value >= start && value <= finish);
18189 };
18190
18191 /**
18192  * Test object.
18193  */
18194
18195 /**
18196  * is.object
18197  * Test if `value` is an object.
18198  *
18199  * @param {Mixed} value value to test
18200  * @return {Boolean} true if `value` is an object, false otherwise
18201  * @api public
18202  */
18203
18204 is.object = function (value) {
18205   return value && '[object Object]' === toString.call(value);
18206 };
18207
18208 /**
18209  * is.hash
18210  * Test if `value` is a hash - a plain object literal.
18211  *
18212  * @param {Mixed} value value to test
18213  * @return {Boolean} true if `value` is a hash, false otherwise
18214  * @api public
18215  */
18216
18217 is.hash = function (value) {
18218   return is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval;
18219 };
18220
18221 /**
18222  * Test regexp.
18223  */
18224
18225 /**
18226  * is.regexp
18227  * Test if `value` is a regular expression.
18228  *
18229  * @param {Mixed} value value to test
18230  * @return {Boolean} true if `value` is a regexp, false otherwise
18231  * @api public
18232  */
18233
18234 is.regexp = function (value) {
18235   return '[object RegExp]' === toString.call(value);
18236 };
18237
18238 /**
18239  * Test string.
18240  */
18241
18242 /**
18243  * is.string
18244  * Test if `value` is a string.
18245  *
18246  * @param {Mixed} value value to test
18247  * @return {Boolean} true if 'value' is a string, false otherwise
18248  * @api public
18249  */
18250
18251 is.string = function (value) {
18252   return '[object String]' === toString.call(value);
18253 };
18254
18255
18256 },{}],10:[function(require,module,exports){
18257
18258 var hasOwn = Object.prototype.hasOwnProperty;
18259 var toString = Object.prototype.toString;
18260
18261 module.exports = function forEach (obj, fn, ctx) {
18262     if (toString.call(fn) !== '[object Function]') {
18263         throw new TypeError('iterator must be a function');
18264     }
18265     var l = obj.length;
18266     if (l === +l) {
18267         for (var i = 0; i < l; i++) {
18268             fn.call(ctx, obj[i], i, obj);
18269         }
18270     } else {
18271         for (var k in obj) {
18272             if (hasOwn.call(obj, k)) {
18273                 fn.call(ctx, obj[k], k, obj);
18274             }
18275         }
18276     }
18277 };
18278
18279
18280 },{}]},{},[1])(1)
18281 });
18282 ;
18283 /*
18284  (c) 2015, Vladimir Agafonkin
18285  RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
18286  https://github.com/mourner/rbush
18287 */
18288
18289 (function () {
18290 'use strict';
18291
18292 function rbush(maxEntries, format) {
18293
18294     // jshint newcap: false, validthis: true
18295     if (!(this instanceof rbush)) return new rbush(maxEntries, format);
18296
18297     // max entries in a node is 9 by default; min node fill is 40% for best performance
18298     this._maxEntries = Math.max(4, maxEntries || 9);
18299     this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
18300
18301     if (format) {
18302         this._initFormat(format);
18303     }
18304
18305     this.clear();
18306 }
18307
18308 rbush.prototype = {
18309
18310     all: function () {
18311         return this._all(this.data, []);
18312     },
18313
18314     search: function (bbox) {
18315
18316         var node = this.data,
18317             result = [],
18318             toBBox = this.toBBox;
18319
18320         if (!intersects(bbox, node.bbox)) return result;
18321
18322         var nodesToSearch = [],
18323             i, len, child, childBBox;
18324
18325         while (node) {
18326             for (i = 0, len = node.children.length; i < len; i++) {
18327
18328                 child = node.children[i];
18329                 childBBox = node.leaf ? toBBox(child) : child.bbox;
18330
18331                 if (intersects(bbox, childBBox)) {
18332                     if (node.leaf) result.push(child);
18333                     else if (contains(bbox, childBBox)) this._all(child, result);
18334                     else nodesToSearch.push(child);
18335                 }
18336             }
18337             node = nodesToSearch.pop();
18338         }
18339
18340         return result;
18341     },
18342
18343     collides: function (bbox) {
18344
18345         var node = this.data,
18346             toBBox = this.toBBox;
18347
18348         if (!intersects(bbox, node.bbox)) return false;
18349
18350         var nodesToSearch = [],
18351             i, len, child, childBBox;
18352
18353         while (node) {
18354             for (i = 0, len = node.children.length; i < len; i++) {
18355
18356                 child = node.children[i];
18357                 childBBox = node.leaf ? toBBox(child) : child.bbox;
18358
18359                 if (intersects(bbox, childBBox)) {
18360                     if (node.leaf || contains(bbox, childBBox)) return true;
18361                     nodesToSearch.push(child);
18362                 }
18363             }
18364             node = nodesToSearch.pop();
18365         }
18366
18367         return false;
18368     },
18369
18370     load: function (data) {
18371         if (!(data && data.length)) return this;
18372
18373         if (data.length < this._minEntries) {
18374             for (var i = 0, len = data.length; i < len; i++) {
18375                 this.insert(data[i]);
18376             }
18377             return this;
18378         }
18379
18380         // recursively build the tree with the given data from stratch using OMT algorithm
18381         var node = this._build(data.slice(), 0, data.length - 1, 0);
18382
18383         if (!this.data.children.length) {
18384             // save as is if tree is empty
18385             this.data = node;
18386
18387         } else if (this.data.height === node.height) {
18388             // split root if trees have the same height
18389             this._splitRoot(this.data, node);
18390
18391         } else {
18392             if (this.data.height < node.height) {
18393                 // swap trees if inserted one is bigger
18394                 var tmpNode = this.data;
18395                 this.data = node;
18396                 node = tmpNode;
18397             }
18398
18399             // insert the small tree into the large tree at appropriate level
18400             this._insert(node, this.data.height - node.height - 1, true);
18401         }
18402
18403         return this;
18404     },
18405
18406     insert: function (item) {
18407         if (item) this._insert(item, this.data.height - 1);
18408         return this;
18409     },
18410
18411     clear: function () {
18412         this.data = {
18413             children: [],
18414             height: 1,
18415             bbox: empty(),
18416             leaf: true
18417         };
18418         return this;
18419     },
18420
18421     remove: function (item) {
18422         if (!item) return this;
18423
18424         var node = this.data,
18425             bbox = this.toBBox(item),
18426             path = [],
18427             indexes = [],
18428             i, parent, index, goingUp;
18429
18430         // depth-first iterative tree traversal
18431         while (node || path.length) {
18432
18433             if (!node) { // go up
18434                 node = path.pop();
18435                 parent = path[path.length - 1];
18436                 i = indexes.pop();
18437                 goingUp = true;
18438             }
18439
18440             if (node.leaf) { // check current node
18441                 index = node.children.indexOf(item);
18442
18443                 if (index !== -1) {
18444                     // item found, remove the item and condense tree upwards
18445                     node.children.splice(index, 1);
18446                     path.push(node);
18447                     this._condense(path);
18448                     return this;
18449                 }
18450             }
18451
18452             if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
18453                 path.push(node);
18454                 indexes.push(i);
18455                 i = 0;
18456                 parent = node;
18457                 node = node.children[0];
18458
18459             } else if (parent) { // go right
18460                 i++;
18461                 node = parent.children[i];
18462                 goingUp = false;
18463
18464             } else node = null; // nothing found
18465         }
18466
18467         return this;
18468     },
18469
18470     toBBox: function (item) { return item; },
18471
18472     compareMinX: function (a, b) { return a[0] - b[0]; },
18473     compareMinY: function (a, b) { return a[1] - b[1]; },
18474
18475     toJSON: function () { return this.data; },
18476
18477     fromJSON: function (data) {
18478         this.data = data;
18479         return this;
18480     },
18481
18482     _all: function (node, result) {
18483         var nodesToSearch = [];
18484         while (node) {
18485             if (node.leaf) result.push.apply(result, node.children);
18486             else nodesToSearch.push.apply(nodesToSearch, node.children);
18487
18488             node = nodesToSearch.pop();
18489         }
18490         return result;
18491     },
18492
18493     _build: function (items, left, right, height) {
18494
18495         var N = right - left + 1,
18496             M = this._maxEntries,
18497             node;
18498
18499         if (N <= M) {
18500             // reached leaf level; return leaf
18501             node = {
18502                 children: items.slice(left, right + 1),
18503                 height: 1,
18504                 bbox: null,
18505                 leaf: true
18506             };
18507             calcBBox(node, this.toBBox);
18508             return node;
18509         }
18510
18511         if (!height) {
18512             // target height of the bulk-loaded tree
18513             height = Math.ceil(Math.log(N) / Math.log(M));
18514
18515             // target number of root entries to maximize storage utilization
18516             M = Math.ceil(N / Math.pow(M, height - 1));
18517         }
18518
18519         node = {
18520             children: [],
18521             height: height,
18522             bbox: null,
18523             leaf: false
18524         };
18525
18526         // split the items into M mostly square tiles
18527
18528         var N2 = Math.ceil(N / M),
18529             N1 = N2 * Math.ceil(Math.sqrt(M)),
18530             i, j, right2, right3;
18531
18532         multiSelect(items, left, right, N1, this.compareMinX);
18533
18534         for (i = left; i <= right; i += N1) {
18535
18536             right2 = Math.min(i + N1 - 1, right);
18537
18538             multiSelect(items, i, right2, N2, this.compareMinY);
18539
18540             for (j = i; j <= right2; j += N2) {
18541
18542                 right3 = Math.min(j + N2 - 1, right2);
18543
18544                 // pack each entry recursively
18545                 node.children.push(this._build(items, j, right3, height - 1));
18546             }
18547         }
18548
18549         calcBBox(node, this.toBBox);
18550
18551         return node;
18552     },
18553
18554     _chooseSubtree: function (bbox, node, level, path) {
18555
18556         var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
18557
18558         while (true) {
18559             path.push(node);
18560
18561             if (node.leaf || path.length - 1 === level) break;
18562
18563             minArea = minEnlargement = Infinity;
18564
18565             for (i = 0, len = node.children.length; i < len; i++) {
18566                 child = node.children[i];
18567                 area = bboxArea(child.bbox);
18568                 enlargement = enlargedArea(bbox, child.bbox) - area;
18569
18570                 // choose entry with the least area enlargement
18571                 if (enlargement < minEnlargement) {
18572                     minEnlargement = enlargement;
18573                     minArea = area < minArea ? area : minArea;
18574                     targetNode = child;
18575
18576                 } else if (enlargement === minEnlargement) {
18577                     // otherwise choose one with the smallest area
18578                     if (area < minArea) {
18579                         minArea = area;
18580                         targetNode = child;
18581                     }
18582                 }
18583             }
18584
18585             node = targetNode;
18586         }
18587
18588         return node;
18589     },
18590
18591     _insert: function (item, level, isNode) {
18592
18593         var toBBox = this.toBBox,
18594             bbox = isNode ? item.bbox : toBBox(item),
18595             insertPath = [];
18596
18597         // find the best node for accommodating the item, saving all nodes along the path too
18598         var node = this._chooseSubtree(bbox, this.data, level, insertPath);
18599
18600         // put the item into the node
18601         node.children.push(item);
18602         extend(node.bbox, bbox);
18603
18604         // split on node overflow; propagate upwards if necessary
18605         while (level >= 0) {
18606             if (insertPath[level].children.length > this._maxEntries) {
18607                 this._split(insertPath, level);
18608                 level--;
18609             } else break;
18610         }
18611
18612         // adjust bboxes along the insertion path
18613         this._adjustParentBBoxes(bbox, insertPath, level);
18614     },
18615
18616     // split overflowed node into two
18617     _split: function (insertPath, level) {
18618
18619         var node = insertPath[level],
18620             M = node.children.length,
18621             m = this._minEntries;
18622
18623         this._chooseSplitAxis(node, m, M);
18624
18625         var splitIndex = this._chooseSplitIndex(node, m, M);
18626
18627         var newNode = {
18628             children: node.children.splice(splitIndex, node.children.length - splitIndex),
18629             height: node.height,
18630             bbox: null,
18631             leaf: false
18632         };
18633
18634         if (node.leaf) newNode.leaf = true;
18635
18636         calcBBox(node, this.toBBox);
18637         calcBBox(newNode, this.toBBox);
18638
18639         if (level) insertPath[level - 1].children.push(newNode);
18640         else this._splitRoot(node, newNode);
18641     },
18642
18643     _splitRoot: function (node, newNode) {
18644         // split root node
18645         this.data = {
18646             children: [node, newNode],
18647             height: node.height + 1,
18648             bbox: null,
18649             leaf: false
18650         };
18651         calcBBox(this.data, this.toBBox);
18652     },
18653
18654     _chooseSplitIndex: function (node, m, M) {
18655
18656         var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
18657
18658         minOverlap = minArea = Infinity;
18659
18660         for (i = m; i <= M - m; i++) {
18661             bbox1 = distBBox(node, 0, i, this.toBBox);
18662             bbox2 = distBBox(node, i, M, this.toBBox);
18663
18664             overlap = intersectionArea(bbox1, bbox2);
18665             area = bboxArea(bbox1) + bboxArea(bbox2);
18666
18667             // choose distribution with minimum overlap
18668             if (overlap < minOverlap) {
18669                 minOverlap = overlap;
18670                 index = i;
18671
18672                 minArea = area < minArea ? area : minArea;
18673
18674             } else if (overlap === minOverlap) {
18675                 // otherwise choose distribution with minimum area
18676                 if (area < minArea) {
18677                     minArea = area;
18678                     index = i;
18679                 }
18680             }
18681         }
18682
18683         return index;
18684     },
18685
18686     // sorts node children by the best axis for split
18687     _chooseSplitAxis: function (node, m, M) {
18688
18689         var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
18690             compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
18691             xMargin = this._allDistMargin(node, m, M, compareMinX),
18692             yMargin = this._allDistMargin(node, m, M, compareMinY);
18693
18694         // if total distributions margin value is minimal for x, sort by minX,
18695         // otherwise it's already sorted by minY
18696         if (xMargin < yMargin) node.children.sort(compareMinX);
18697     },
18698
18699     // total margin of all possible split distributions where each node is at least m full
18700     _allDistMargin: function (node, m, M, compare) {
18701
18702         node.children.sort(compare);
18703
18704         var toBBox = this.toBBox,
18705             leftBBox = distBBox(node, 0, m, toBBox),
18706             rightBBox = distBBox(node, M - m, M, toBBox),
18707             margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
18708             i, child;
18709
18710         for (i = m; i < M - m; i++) {
18711             child = node.children[i];
18712             extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
18713             margin += bboxMargin(leftBBox);
18714         }
18715
18716         for (i = M - m - 1; i >= m; i--) {
18717             child = node.children[i];
18718             extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
18719             margin += bboxMargin(rightBBox);
18720         }
18721
18722         return margin;
18723     },
18724
18725     _adjustParentBBoxes: function (bbox, path, level) {
18726         // adjust bboxes along the given tree path
18727         for (var i = level; i >= 0; i--) {
18728             extend(path[i].bbox, bbox);
18729         }
18730     },
18731
18732     _condense: function (path) {
18733         // go through the path, removing empty nodes and updating bboxes
18734         for (var i = path.length - 1, siblings; i >= 0; i--) {
18735             if (path[i].children.length === 0) {
18736                 if (i > 0) {
18737                     siblings = path[i - 1].children;
18738                     siblings.splice(siblings.indexOf(path[i]), 1);
18739
18740                 } else this.clear();
18741
18742             } else calcBBox(path[i], this.toBBox);
18743         }
18744     },
18745
18746     _initFormat: function (format) {
18747         // data format (minX, minY, maxX, maxY accessors)
18748
18749         // uses eval-type function compilation instead of just accepting a toBBox function
18750         // because the algorithms are very sensitive to sorting functions performance,
18751         // so they should be dead simple and without inner calls
18752
18753         // jshint evil: true
18754
18755         var compareArr = ['return a', ' - b', ';'];
18756
18757         this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
18758         this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
18759
18760         this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
18761     }
18762 };
18763
18764
18765 // calculate node's bbox from bboxes of its children
18766 function calcBBox(node, toBBox) {
18767     node.bbox = distBBox(node, 0, node.children.length, toBBox);
18768 }
18769
18770 // min bounding rectangle of node children from k to p-1
18771 function distBBox(node, k, p, toBBox) {
18772     var bbox = empty();
18773
18774     for (var i = k, child; i < p; i++) {
18775         child = node.children[i];
18776         extend(bbox, node.leaf ? toBBox(child) : child.bbox);
18777     }
18778
18779     return bbox;
18780 }
18781
18782 function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
18783
18784 function extend(a, b) {
18785     a[0] = Math.min(a[0], b[0]);
18786     a[1] = Math.min(a[1], b[1]);
18787     a[2] = Math.max(a[2], b[2]);
18788     a[3] = Math.max(a[3], b[3]);
18789     return a;
18790 }
18791
18792 function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
18793 function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
18794
18795 function bboxArea(a)   { return (a[2] - a[0]) * (a[3] - a[1]); }
18796 function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
18797
18798 function enlargedArea(a, b) {
18799     return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
18800            (Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
18801 }
18802
18803 function intersectionArea(a, b) {
18804     var minX = Math.max(a[0], b[0]),
18805         minY = Math.max(a[1], b[1]),
18806         maxX = Math.min(a[2], b[2]),
18807         maxY = Math.min(a[3], b[3]);
18808
18809     return Math.max(0, maxX - minX) *
18810            Math.max(0, maxY - minY);
18811 }
18812
18813 function contains(a, b) {
18814     return a[0] <= b[0] &&
18815            a[1] <= b[1] &&
18816            b[2] <= a[2] &&
18817            b[3] <= a[3];
18818 }
18819
18820 function intersects(a, b) {
18821     return b[0] <= a[2] &&
18822            b[1] <= a[3] &&
18823            b[2] >= a[0] &&
18824            b[3] >= a[1];
18825 }
18826
18827 // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
18828 // combines selection algorithm with binary divide & conquer approach
18829
18830 function multiSelect(arr, left, right, n, compare) {
18831     var stack = [left, right],
18832         mid;
18833
18834     while (stack.length) {
18835         right = stack.pop();
18836         left = stack.pop();
18837
18838         if (right - left <= n) continue;
18839
18840         mid = left + Math.ceil((right - left) / n / 2) * n;
18841         select(arr, left, right, mid, compare);
18842
18843         stack.push(left, mid, mid, right);
18844     }
18845 }
18846
18847 // Floyd-Rivest selection algorithm:
18848 // sort an array between left and right (inclusive) so that the smallest k elements come first (unordered)
18849 function select(arr, left, right, k, compare) {
18850     var n, i, z, s, sd, newLeft, newRight, t, j;
18851
18852     while (right > left) {
18853         if (right - left > 600) {
18854             n = right - left + 1;
18855             i = k - left + 1;
18856             z = Math.log(n);
18857             s = 0.5 * Math.exp(2 * z / 3);
18858             sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1);
18859             newLeft = Math.max(left, Math.floor(k - i * s / n + sd));
18860             newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd));
18861             select(arr, newLeft, newRight, k, compare);
18862         }
18863
18864         t = arr[k];
18865         i = left;
18866         j = right;
18867
18868         swap(arr, left, k);
18869         if (compare(arr[right], t) > 0) swap(arr, left, right);
18870
18871         while (i < j) {
18872             swap(arr, i, j);
18873             i++;
18874             j--;
18875             while (compare(arr[i], t) < 0) i++;
18876             while (compare(arr[j], t) > 0) j--;
18877         }
18878
18879         if (compare(arr[left], t) === 0) swap(arr, left, j);
18880         else {
18881             j++;
18882             swap(arr, j, right);
18883         }
18884
18885         if (j <= k) left = j + 1;
18886         if (k <= j) right = j - 1;
18887     }
18888 }
18889
18890 function swap(arr, i, j) {
18891     var tmp = arr[i];
18892     arr[i] = arr[j];
18893     arr[j] = tmp;
18894 }
18895
18896
18897 // export as AMD/CommonJS module or global variable
18898 if (typeof define === 'function' && define.amd) define('rbush', function () { return rbush; });
18899 else if (typeof module !== 'undefined') module.exports = rbush;
18900 else if (typeof self !== 'undefined') self.rbush = rbush;
18901 else window.rbush = rbush;
18902
18903 })();
18904 (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;
18905 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){
18906 module.exports = element;
18907 module.exports.pair = pair;
18908 module.exports.format = format;
18909 module.exports.formatPair = formatPair;
18910
18911 function element(x, dims) {
18912     return search(x, dims).val;
18913 }
18914
18915 function formatPair(x) {
18916     return format(x.lat, 'lat') + ' ' + format(x.lon, 'lon');
18917 }
18918
18919 // Is 0 North or South?
18920 function format(x, dim) {
18921     var dirs = {
18922             lat: ['N', 'S'],
18923             lon: ['E', 'W']
18924         }[dim] || '',
18925         dir = dirs[x >= 0 ? 0 : 1],
18926         abs = Math.abs(x),
18927         whole = Math.floor(abs),
18928         fraction = abs - whole,
18929         fractionMinutes = fraction * 60,
18930         minutes = Math.floor(fractionMinutes),
18931         seconds = Math.floor((fractionMinutes - minutes) * 60);
18932
18933     return whole + '° ' +
18934         (minutes ? minutes + "' " : '') +
18935         (seconds ? seconds + '" ' : '') + dir;
18936 }
18937
18938 function search(x, dims, r) {
18939     if (!dims) dims = 'NSEW';
18940     if (typeof x !== 'string') return { val: null, regex: r };
18941     r = r || /[\s\,]*([\-|\—|\―]?[0-9.]+)°? *(?:([0-9.]+)['’′‘] *)?(?:([0-9.]+)(?:''|"|”|″) *)?([NSEW])?/gi;
18942     var m = r.exec(x);
18943     if (!m) return { val: null, regex: r };
18944     else if (m[4] && dims.indexOf(m[4]) === -1) return { val: null, regex: r };
18945     else return {
18946         val: (((m[1]) ? parseFloat(m[1]) : 0) +
18947             ((m[2] ? parseFloat(m[2]) / 60 : 0)) +
18948             ((m[3] ? parseFloat(m[3]) / 3600 : 0))) *
18949             ((m[4] && m[4] === 'S' || m[4] === 'W') ? -1 : 1),
18950         regex: r,
18951         raw: m[0],
18952         dim: m[4]
18953     };
18954 }
18955
18956 function pair(x, dims) {
18957     x = x.trim();
18958     var one = search(x, dims);
18959     if (one.val === null) return null;
18960     var two = search(x, dims, one.regex);
18961     if (two.val === null) return null;
18962     // null if one/two are not contiguous.
18963     if (one.raw + two.raw !== x) return null;
18964     if (one.dim) return swapdim(one.val, two.val, one.dim);
18965     else return [one.val, two.val];
18966 }
18967
18968 function swapdim(a, b, dim) {
18969     if (dim == 'N' || dim == 'S') return [a, b];
18970     if (dim == 'W' || dim == 'E') return [b, a];
18971 }
18972
18973 },{}]},{},[1])
18974 (1)
18975 });
18976 ;toGeoJSON = (function() {
18977     'use strict';
18978
18979     var removeSpace = (/\s*/g),
18980         trimSpace = (/^\s*|\s*$/g),
18981         splitSpace = (/\s+/);
18982     // generate a short, numeric hash of a string
18983     function okhash(x) {
18984         if (!x || !x.length) return 0;
18985         for (var i = 0, h = 0; i < x.length; i++) {
18986             h = ((h << 5) - h) + x.charCodeAt(i) | 0;
18987         } return h;
18988     }
18989     // all Y children of X
18990     function get(x, y) { return x.getElementsByTagName(y); }
18991     function attr(x, y) { return x.getAttribute(y); }
18992     function attrf(x, y) { return parseFloat(attr(x, y)); }
18993     // one Y child of X, if any, otherwise null
18994     function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
18995     // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
18996     function norm(el) { if (el.normalize) { el.normalize(); } return el; }
18997     // cast array x into numbers
18998     function numarray(x) {
18999         for (var j = 0, o = []; j < x.length; j++) o[j] = parseFloat(x[j]);
19000         return o;
19001     }
19002     function clean(x) {
19003         var o = {};
19004         for (var i in x) if (x[i]) o[i] = x[i];
19005         return o;
19006     }
19007     // get the content of a text node, if any
19008     function nodeVal(x) { if (x) {norm(x);} return x && x.firstChild && x.firstChild.nodeValue; }
19009     // get one coordinate from a coordinate array, if any
19010     function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
19011     // get all coordinates from a coordinate array as [[],[]]
19012     function coord(v) {
19013         var coords = v.replace(trimSpace, '').split(splitSpace),
19014             o = [];
19015         for (var i = 0; i < coords.length; i++) {
19016             o.push(coord1(coords[i]));
19017         }
19018         return o;
19019     }
19020     function coordPair(x) { return [attrf(x, 'lon'), attrf(x, 'lat')]; }
19021
19022     // create a new feature collection parent object
19023     function fc() {
19024         return {
19025             type: 'FeatureCollection',
19026             features: []
19027         };
19028     }
19029
19030     var styleSupport = false;
19031     if (typeof XMLSerializer !== 'undefined') {
19032         var serializer = new XMLSerializer();
19033         styleSupport = true;
19034     }
19035     function xml2str(str) { return serializer.serializeToString(str); }
19036
19037     var t = {
19038         kml: function(doc, o) {
19039             o = o || {};
19040
19041             var gj = fc(),
19042                 // styleindex keeps track of hashed styles in order to match features
19043                 styleIndex = {},
19044                 // atomic geospatial types supported by KML - MultiGeometry is
19045                 // handled separately
19046                 geotypes = ['Polygon', 'LineString', 'Point', 'Track'],
19047                 // all root placemarks in the file
19048                 placemarks = get(doc, 'Placemark'),
19049                 styles = get(doc, 'Style');
19050
19051             if (styleSupport) for (var k = 0; k < styles.length; k++) {
19052                 styleIndex['#' + attr(styles[k], 'id')] = okhash(xml2str(styles[k])).toString(16);
19053             }
19054             for (var j = 0; j < placemarks.length; j++) {
19055                 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
19056             }
19057             function gxCoord(v) { return numarray(v.split(' ')); }
19058             function gxCoords(root) {
19059                 var elems = get(root, 'coord', 'gx'), coords = [];
19060                 for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
19061                 return coords;
19062             }
19063             function getGeometry(root) {
19064                 var geomNode, geomNodes, i, j, k, geoms = [];
19065                 if (get1(root, 'MultiGeometry')) return getGeometry(get1(root, 'MultiGeometry'));
19066                 if (get1(root, 'MultiTrack')) return getGeometry(get1(root, 'MultiTrack'));
19067                 for (i = 0; i < geotypes.length; i++) {
19068                     geomNodes = get(root, geotypes[i]);
19069                     if (geomNodes) {
19070                         for (j = 0; j < geomNodes.length; j++) {
19071                             geomNode = geomNodes[j];
19072                             if (geotypes[i] == 'Point') {
19073                                 geoms.push({
19074                                     type: 'Point',
19075                                     coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
19076                                 });
19077                             } else if (geotypes[i] == 'LineString') {
19078                                 geoms.push({
19079                                     type: 'LineString',
19080                                     coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
19081                                 });
19082                             } else if (geotypes[i] == 'Polygon') {
19083                                 var rings = get(geomNode, 'LinearRing'),
19084                                     coords = [];
19085                                 for (k = 0; k < rings.length; k++) {
19086                                     coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
19087                                 }
19088                                 geoms.push({
19089                                     type: 'Polygon',
19090                                     coordinates: coords
19091                                 });
19092                             } else if (geotypes[i] == 'Track') {
19093                                 geoms.push({
19094                                     type: 'LineString',
19095                                     coordinates: gxCoords(geomNode)
19096                                 });
19097                             }
19098                         }
19099                     }
19100                 }
19101                 return geoms;
19102             }
19103             function getPlacemark(root) {
19104                 var geoms = getGeometry(root), i, properties = {},
19105                     name = nodeVal(get1(root, 'name')),
19106                     styleUrl = nodeVal(get1(root, 'styleUrl')),
19107                     description = nodeVal(get1(root, 'description')),
19108                     extendedData = get1(root, 'ExtendedData');
19109
19110                 if (!geoms.length) return [];
19111                 if (name) properties.name = name;
19112                 if (styleUrl && styleIndex[styleUrl]) {
19113                     properties.styleUrl = styleUrl;
19114                     properties.styleHash = styleIndex[styleUrl];
19115                 }
19116                 if (description) properties.description = description;
19117                 if (extendedData) {
19118                     var datas = get(extendedData, 'Data'),
19119                         simpleDatas = get(extendedData, 'SimpleData');
19120
19121                     for (i = 0; i < datas.length; i++) {
19122                         properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
19123                     }
19124                     for (i = 0; i < simpleDatas.length; i++) {
19125                         properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
19126                     }
19127                 }
19128                 return [{
19129                     type: 'Feature',
19130                     geometry: (geoms.length === 1) ? geoms[0] : {
19131                         type: 'GeometryCollection',
19132                         geometries: geoms
19133                     },
19134                     properties: properties
19135                 }];
19136             }
19137             return gj;
19138         },
19139         gpx: function(doc, o) {
19140             var i,
19141                 tracks = get(doc, 'trk'),
19142                 routes = get(doc, 'rte'),
19143                 waypoints = get(doc, 'wpt'),
19144                 // a feature collection
19145                 gj = fc();
19146             for (i = 0; i < tracks.length; i++) {
19147                 gj.features.push(getLinestring(tracks[i], 'trkpt'));
19148             }
19149             for (i = 0; i < routes.length; i++) {
19150                 gj.features.push(getLinestring(routes[i], 'rtept'));
19151             }
19152             for (i = 0; i < waypoints.length; i++) {
19153                 gj.features.push(getPoint(waypoints[i]));
19154             }
19155             function getLinestring(node, pointname) {
19156                 var j, pts = get(node, pointname), line = [];
19157                 for (j = 0; j < pts.length; j++) {
19158                     line.push(coordPair(pts[j]));
19159                 }
19160                 return {
19161                     type: 'Feature',
19162                     properties: getProperties(node),
19163                     geometry: {
19164                         type: 'LineString',
19165                         coordinates: line
19166                     }
19167                 };
19168             }
19169             function getPoint(node) {
19170                 var prop = getProperties(node);
19171                 prop.ele = nodeVal(get1(node, 'ele'));
19172                 prop.sym = nodeVal(get1(node, 'sym'));
19173                 return {
19174                     type: 'Feature',
19175                     properties: prop,
19176                     geometry: {
19177                         type: 'Point',
19178                         coordinates: coordPair(node)
19179                     }
19180                 };
19181             }
19182             function getProperties(node) {
19183                 var meta = ['name', 'desc', 'author', 'copyright', 'link',
19184                             'time', 'keywords'],
19185                     prop = {},
19186                     k;
19187                 for (k = 0; k < meta.length; k++) {
19188                     prop[meta[k]] = nodeVal(get1(node, meta[k]));
19189                 }
19190                 return clean(prop);
19191             }
19192             return gj;
19193         }
19194     };
19195     return t;
19196 })();
19197
19198 if (typeof module !== 'undefined') module.exports = toGeoJSON;
19199 /**
19200  * marked - a markdown parser
19201  * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
19202  * https://github.com/chjj/marked
19203  */
19204
19205 ;(function() {
19206
19207 /**
19208  * Block-Level Grammar
19209  */
19210
19211 var block = {
19212   newline: /^\n+/,
19213   code: /^( {4}[^\n]+\n*)+/,
19214   fences: noop,
19215   hr: /^( *[-*_]){3,} *(?:\n+|$)/,
19216   heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
19217   nptable: noop,
19218   lheading: /^([^\n]+)\n *(=|-){3,} *\n*/,
19219   blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
19220   list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
19221   html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
19222   def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
19223   table: noop,
19224   paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
19225   text: /^[^\n]+/
19226 };
19227
19228 block.bullet = /(?:[*+-]|\d+\.)/;
19229 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
19230 block.item = replace(block.item, 'gm')
19231   (/bull/g, block.bullet)
19232   ();
19233
19234 block.list = replace(block.list)
19235   (/bull/g, block.bullet)
19236   ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)
19237   ();
19238
19239 block._tag = '(?!(?:'
19240   + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
19241   + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
19242   + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b';
19243
19244 block.html = replace(block.html)
19245   ('comment', /<!--[\s\S]*?-->/)
19246   ('closed', /<(tag)[\s\S]+?<\/\1>/)
19247   ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
19248   (/tag/g, block._tag)
19249   ();
19250
19251 block.paragraph = replace(block.paragraph)
19252   ('hr', block.hr)
19253   ('heading', block.heading)
19254   ('lheading', block.lheading)
19255   ('blockquote', block.blockquote)
19256   ('tag', '<' + block._tag)
19257   ('def', block.def)
19258   ();
19259
19260 /**
19261  * Normal Block Grammar
19262  */
19263
19264 block.normal = merge({}, block);
19265
19266 /**
19267  * GFM Block Grammar
19268  */
19269
19270 block.gfm = merge({}, block.normal, {
19271   fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
19272   paragraph: /^/
19273 });
19274
19275 block.gfm.paragraph = replace(block.paragraph)
19276   ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|')
19277   ();
19278
19279 /**
19280  * GFM + Tables Block Grammar
19281  */
19282
19283 block.tables = merge({}, block.gfm, {
19284   nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
19285   table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
19286 });
19287
19288 /**
19289  * Block Lexer
19290  */
19291
19292 function Lexer(options) {
19293   this.tokens = [];
19294   this.tokens.links = {};
19295   this.options = options || marked.defaults;
19296   this.rules = block.normal;
19297
19298   if (this.options.gfm) {
19299     if (this.options.tables) {
19300       this.rules = block.tables;
19301     } else {
19302       this.rules = block.gfm;
19303     }
19304   }
19305 }
19306
19307 /**
19308  * Expose Block Rules
19309  */
19310
19311 Lexer.rules = block;
19312
19313 /**
19314  * Static Lex Method
19315  */
19316
19317 Lexer.lex = function(src, options) {
19318   var lexer = new Lexer(options);
19319   return lexer.lex(src);
19320 };
19321
19322 /**
19323  * Preprocessing
19324  */
19325
19326 Lexer.prototype.lex = function(src) {
19327   src = src
19328     .replace(/\r\n|\r/g, '\n')
19329     .replace(/\t/g, '    ')
19330     .replace(/\u00a0/g, ' ')
19331     .replace(/\u2424/g, '\n');
19332
19333   return this.token(src, true);
19334 };
19335
19336 /**
19337  * Lexing
19338  */
19339
19340 Lexer.prototype.token = function(src, top) {
19341   var src = src.replace(/^ +$/gm, '')
19342     , next
19343     , loose
19344     , cap
19345     , bull
19346     , b
19347     , item
19348     , space
19349     , i
19350     , l;
19351
19352   while (src) {
19353     // newline
19354     if (cap = this.rules.newline.exec(src)) {
19355       src = src.substring(cap[0].length);
19356       if (cap[0].length > 1) {
19357         this.tokens.push({
19358           type: 'space'
19359         });
19360       }
19361     }
19362
19363     // code
19364     if (cap = this.rules.code.exec(src)) {
19365       src = src.substring(cap[0].length);
19366       cap = cap[0].replace(/^ {4}/gm, '');
19367       this.tokens.push({
19368         type: 'code',
19369         text: !this.options.pedantic
19370           ? cap.replace(/\n+$/, '')
19371           : cap
19372       });
19373       continue;
19374     }
19375
19376     // fences (gfm)
19377     if (cap = this.rules.fences.exec(src)) {
19378       src = src.substring(cap[0].length);
19379       this.tokens.push({
19380         type: 'code',
19381         lang: cap[2],
19382         text: cap[3]
19383       });
19384       continue;
19385     }
19386
19387     // heading
19388     if (cap = this.rules.heading.exec(src)) {
19389       src = src.substring(cap[0].length);
19390       this.tokens.push({
19391         type: 'heading',
19392         depth: cap[1].length,
19393         text: cap[2]
19394       });
19395       continue;
19396     }
19397
19398     // table no leading pipe (gfm)
19399     if (top && (cap = this.rules.nptable.exec(src))) {
19400       src = src.substring(cap[0].length);
19401
19402       item = {
19403         type: 'table',
19404         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19405         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19406         cells: cap[3].replace(/\n$/, '').split('\n')
19407       };
19408
19409       for (i = 0; i < item.align.length; i++) {
19410         if (/^ *-+: *$/.test(item.align[i])) {
19411           item.align[i] = 'right';
19412         } else if (/^ *:-+: *$/.test(item.align[i])) {
19413           item.align[i] = 'center';
19414         } else if (/^ *:-+ *$/.test(item.align[i])) {
19415           item.align[i] = 'left';
19416         } else {
19417           item.align[i] = null;
19418         }
19419       }
19420
19421       for (i = 0; i < item.cells.length; i++) {
19422         item.cells[i] = item.cells[i].split(/ *\| */);
19423       }
19424
19425       this.tokens.push(item);
19426
19427       continue;
19428     }
19429
19430     // lheading
19431     if (cap = this.rules.lheading.exec(src)) {
19432       src = src.substring(cap[0].length);
19433       this.tokens.push({
19434         type: 'heading',
19435         depth: cap[2] === '=' ? 1 : 2,
19436         text: cap[1]
19437       });
19438       continue;
19439     }
19440
19441     // hr
19442     if (cap = this.rules.hr.exec(src)) {
19443       src = src.substring(cap[0].length);
19444       this.tokens.push({
19445         type: 'hr'
19446       });
19447       continue;
19448     }
19449
19450     // blockquote
19451     if (cap = this.rules.blockquote.exec(src)) {
19452       src = src.substring(cap[0].length);
19453
19454       this.tokens.push({
19455         type: 'blockquote_start'
19456       });
19457
19458       cap = cap[0].replace(/^ *> ?/gm, '');
19459
19460       // Pass `top` to keep the current
19461       // "toplevel" state. This is exactly
19462       // how markdown.pl works.
19463       this.token(cap, top);
19464
19465       this.tokens.push({
19466         type: 'blockquote_end'
19467       });
19468
19469       continue;
19470     }
19471
19472     // list
19473     if (cap = this.rules.list.exec(src)) {
19474       src = src.substring(cap[0].length);
19475       bull = cap[2];
19476
19477       this.tokens.push({
19478         type: 'list_start',
19479         ordered: bull.length > 1
19480       });
19481
19482       // Get each top-level item.
19483       cap = cap[0].match(this.rules.item);
19484
19485       next = false;
19486       l = cap.length;
19487       i = 0;
19488
19489       for (; i < l; i++) {
19490         item = cap[i];
19491
19492         // Remove the list item's bullet
19493         // so it is seen as the next token.
19494         space = item.length;
19495         item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19496
19497         // Outdent whatever the
19498         // list item contains. Hacky.
19499         if (~item.indexOf('\n ')) {
19500           space -= item.length;
19501           item = !this.options.pedantic
19502             ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19503             : item.replace(/^ {1,4}/gm, '');
19504         }
19505
19506         // Determine whether the next list item belongs here.
19507         // Backpedal if it does not belong in this list.
19508         if (this.options.smartLists && i !== l - 1) {
19509           b = block.bullet.exec(cap[i+1])[0];
19510           if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19511             src = cap.slice(i + 1).join('\n') + src;
19512             i = l - 1;
19513           }
19514         }
19515
19516         // Determine whether item is loose or not.
19517         // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19518         // for discount behavior.
19519         loose = next || /\n\n(?!\s*$)/.test(item);
19520         if (i !== l - 1) {
19521           next = item[item.length-1] === '\n';
19522           if (!loose) loose = next;
19523         }
19524
19525         this.tokens.push({
19526           type: loose
19527             ? 'loose_item_start'
19528             : 'list_item_start'
19529         });
19530
19531         // Recurse.
19532         this.token(item, false);
19533
19534         this.tokens.push({
19535           type: 'list_item_end'
19536         });
19537       }
19538
19539       this.tokens.push({
19540         type: 'list_end'
19541       });
19542
19543       continue;
19544     }
19545
19546     // html
19547     if (cap = this.rules.html.exec(src)) {
19548       src = src.substring(cap[0].length);
19549       this.tokens.push({
19550         type: this.options.sanitize
19551           ? 'paragraph'
19552           : 'html',
19553         pre: cap[1] === 'pre' || cap[1] === 'script',
19554         text: cap[0]
19555       });
19556       continue;
19557     }
19558
19559     // def
19560     if (top && (cap = this.rules.def.exec(src))) {
19561       src = src.substring(cap[0].length);
19562       this.tokens.links[cap[1].toLowerCase()] = {
19563         href: cap[2],
19564         title: cap[3]
19565       };
19566       continue;
19567     }
19568
19569     // table (gfm)
19570     if (top && (cap = this.rules.table.exec(src))) {
19571       src = src.substring(cap[0].length);
19572
19573       item = {
19574         type: 'table',
19575         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19576         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19577         cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19578       };
19579
19580       for (i = 0; i < item.align.length; i++) {
19581         if (/^ *-+: *$/.test(item.align[i])) {
19582           item.align[i] = 'right';
19583         } else if (/^ *:-+: *$/.test(item.align[i])) {
19584           item.align[i] = 'center';
19585         } else if (/^ *:-+ *$/.test(item.align[i])) {
19586           item.align[i] = 'left';
19587         } else {
19588           item.align[i] = null;
19589         }
19590       }
19591
19592       for (i = 0; i < item.cells.length; i++) {
19593         item.cells[i] = item.cells[i]
19594           .replace(/^ *\| *| *\| *$/g, '')
19595           .split(/ *\| */);
19596       }
19597
19598       this.tokens.push(item);
19599
19600       continue;
19601     }
19602
19603     // top-level paragraph
19604     if (top && (cap = this.rules.paragraph.exec(src))) {
19605       src = src.substring(cap[0].length);
19606       this.tokens.push({
19607         type: 'paragraph',
19608         text: cap[1][cap[1].length-1] === '\n'
19609           ? cap[1].slice(0, -1)
19610           : cap[1]
19611       });
19612       continue;
19613     }
19614
19615     // text
19616     if (cap = this.rules.text.exec(src)) {
19617       // Top-level should never reach here.
19618       src = src.substring(cap[0].length);
19619       this.tokens.push({
19620         type: 'text',
19621         text: cap[0]
19622       });
19623       continue;
19624     }
19625
19626     if (src) {
19627       throw new
19628         Error('Infinite loop on byte: ' + src.charCodeAt(0));
19629     }
19630   }
19631
19632   return this.tokens;
19633 };
19634
19635 /**
19636  * Inline-Level Grammar
19637  */
19638
19639 var inline = {
19640   escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19641   autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19642   url: noop,
19643   tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19644   link: /^!?\[(inside)\]\(href\)/,
19645   reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19646   nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19647   strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19648   em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19649   code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19650   br: /^ {2,}\n(?!\s*$)/,
19651   del: noop,
19652   text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19653 };
19654
19655 inline._inside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
19656 inline._href = /\s*<?([^\s]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19657
19658 inline.link = replace(inline.link)
19659   ('inside', inline._inside)
19660   ('href', inline._href)
19661   ();
19662
19663 inline.reflink = replace(inline.reflink)
19664   ('inside', inline._inside)
19665   ();
19666
19667 /**
19668  * Normal Inline Grammar
19669  */
19670
19671 inline.normal = merge({}, inline);
19672
19673 /**
19674  * Pedantic Inline Grammar
19675  */
19676
19677 inline.pedantic = merge({}, inline.normal, {
19678   strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19679   em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19680 });
19681
19682 /**
19683  * GFM Inline Grammar
19684  */
19685
19686 inline.gfm = merge({}, inline.normal, {
19687   escape: replace(inline.escape)('])', '~|])')(),
19688   url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19689   del: /^~~(?=\S)([\s\S]*?\S)~~/,
19690   text: replace(inline.text)
19691     (']|', '~]|')
19692     ('|', '|https?://|')
19693     ()
19694 });
19695
19696 /**
19697  * GFM + Line Breaks Inline Grammar
19698  */
19699
19700 inline.breaks = merge({}, inline.gfm, {
19701   br: replace(inline.br)('{2,}', '*')(),
19702   text: replace(inline.gfm.text)('{2,}', '*')()
19703 });
19704
19705 /**
19706  * Inline Lexer & Compiler
19707  */
19708
19709 function InlineLexer(links, options) {
19710   this.options = options || marked.defaults;
19711   this.links = links;
19712   this.rules = inline.normal;
19713
19714   if (!this.links) {
19715     throw new
19716       Error('Tokens array requires a `links` property.');
19717   }
19718
19719   if (this.options.gfm) {
19720     if (this.options.breaks) {
19721       this.rules = inline.breaks;
19722     } else {
19723       this.rules = inline.gfm;
19724     }
19725   } else if (this.options.pedantic) {
19726     this.rules = inline.pedantic;
19727   }
19728 }
19729
19730 /**
19731  * Expose Inline Rules
19732  */
19733
19734 InlineLexer.rules = inline;
19735
19736 /**
19737  * Static Lexing/Compiling Method
19738  */
19739
19740 InlineLexer.output = function(src, links, options) {
19741   var inline = new InlineLexer(links, options);
19742   return inline.output(src);
19743 };
19744
19745 /**
19746  * Lexing/Compiling
19747  */
19748
19749 InlineLexer.prototype.output = function(src) {
19750   var out = ''
19751     , link
19752     , text
19753     , href
19754     , cap;
19755
19756   while (src) {
19757     // escape
19758     if (cap = this.rules.escape.exec(src)) {
19759       src = src.substring(cap[0].length);
19760       out += cap[1];
19761       continue;
19762     }
19763
19764     // autolink
19765     if (cap = this.rules.autolink.exec(src)) {
19766       src = src.substring(cap[0].length);
19767       if (cap[2] === '@') {
19768         text = cap[1][6] === ':'
19769           ? this.mangle(cap[1].substring(7))
19770           : this.mangle(cap[1]);
19771         href = this.mangle('mailto:') + text;
19772       } else {
19773         text = escape(cap[1]);
19774         href = text;
19775       }
19776       out += '<a href="'
19777         + href
19778         + '">'
19779         + text
19780         + '</a>';
19781       continue;
19782     }
19783
19784     // url (gfm)
19785     if (cap = this.rules.url.exec(src)) {
19786       src = src.substring(cap[0].length);
19787       text = escape(cap[1]);
19788       href = text;
19789       out += '<a href="'
19790         + href
19791         + '">'
19792         + text
19793         + '</a>';
19794       continue;
19795     }
19796
19797     // tag
19798     if (cap = this.rules.tag.exec(src)) {
19799       src = src.substring(cap[0].length);
19800       out += this.options.sanitize
19801         ? escape(cap[0])
19802         : cap[0];
19803       continue;
19804     }
19805
19806     // link
19807     if (cap = this.rules.link.exec(src)) {
19808       src = src.substring(cap[0].length);
19809       out += this.outputLink(cap, {
19810         href: cap[2],
19811         title: cap[3]
19812       });
19813       continue;
19814     }
19815
19816     // reflink, nolink
19817     if ((cap = this.rules.reflink.exec(src))
19818         || (cap = this.rules.nolink.exec(src))) {
19819       src = src.substring(cap[0].length);
19820       link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19821       link = this.links[link.toLowerCase()];
19822       if (!link || !link.href) {
19823         out += cap[0][0];
19824         src = cap[0].substring(1) + src;
19825         continue;
19826       }
19827       out += this.outputLink(cap, link);
19828       continue;
19829     }
19830
19831     // strong
19832     if (cap = this.rules.strong.exec(src)) {
19833       src = src.substring(cap[0].length);
19834       out += '<strong>'
19835         + this.output(cap[2] || cap[1])
19836         + '</strong>';
19837       continue;
19838     }
19839
19840     // em
19841     if (cap = this.rules.em.exec(src)) {
19842       src = src.substring(cap[0].length);
19843       out += '<em>'
19844         + this.output(cap[2] || cap[1])
19845         + '</em>';
19846       continue;
19847     }
19848
19849     // code
19850     if (cap = this.rules.code.exec(src)) {
19851       src = src.substring(cap[0].length);
19852       out += '<code>'
19853         + escape(cap[2], true)
19854         + '</code>';
19855       continue;
19856     }
19857
19858     // br
19859     if (cap = this.rules.br.exec(src)) {
19860       src = src.substring(cap[0].length);
19861       out += '<br>';
19862       continue;
19863     }
19864
19865     // del (gfm)
19866     if (cap = this.rules.del.exec(src)) {
19867       src = src.substring(cap[0].length);
19868       out += '<del>'
19869         + this.output(cap[1])
19870         + '</del>';
19871       continue;
19872     }
19873
19874     // text
19875     if (cap = this.rules.text.exec(src)) {
19876       src = src.substring(cap[0].length);
19877       out += escape(cap[0]);
19878       continue;
19879     }
19880
19881     if (src) {
19882       throw new
19883         Error('Infinite loop on byte: ' + src.charCodeAt(0));
19884     }
19885   }
19886
19887   return out;
19888 };
19889
19890 /**
19891  * Compile Link
19892  */
19893
19894 InlineLexer.prototype.outputLink = function(cap, link) {
19895   if (cap[0][0] !== '!') {
19896     return '<a href="'
19897       + escape(link.href)
19898       + '"'
19899       + (link.title
19900       ? ' title="'
19901       + escape(link.title)
19902       + '"'
19903       : '')
19904       + '>'
19905       + this.output(cap[1])
19906       + '</a>';
19907   } else {
19908     return '<img src="'
19909       + escape(link.href)
19910       + '" alt="'
19911       + escape(cap[1])
19912       + '"'
19913       + (link.title
19914       ? ' title="'
19915       + escape(link.title)
19916       + '"'
19917       : '')
19918       + '>';
19919   }
19920 };
19921
19922 /**
19923  * Smartypants Transformations
19924  */
19925
19926 InlineLexer.prototype.smartypants = function(text) {
19927   if (!this.options.smartypants) return text;
19928   return text
19929     .replace(/--/g, '—')
19930     .replace(/'([^']*)'/g, '‘$1’')
19931     .replace(/"([^"]*)"/g, '“$1”')
19932     .replace(/\.{3}/g, '…');
19933 };
19934
19935 /**
19936  * Mangle Links
19937  */
19938
19939 InlineLexer.prototype.mangle = function(text) {
19940   var out = ''
19941     , l = text.length
19942     , i = 0
19943     , ch;
19944
19945   for (; i < l; i++) {
19946     ch = text.charCodeAt(i);
19947     if (Math.random() > 0.5) {
19948       ch = 'x' + ch.toString(16);
19949     }
19950     out += '&#' + ch + ';';
19951   }
19952
19953   return out;
19954 };
19955
19956 /**
19957  * Parsing & Compiling
19958  */
19959
19960 function Parser(options) {
19961   this.tokens = [];
19962   this.token = null;
19963   this.options = options || marked.defaults;
19964 }
19965
19966 /**
19967  * Static Parse Method
19968  */
19969
19970 Parser.parse = function(src, options) {
19971   var parser = new Parser(options);
19972   return parser.parse(src);
19973 };
19974
19975 /**
19976  * Parse Loop
19977  */
19978
19979 Parser.prototype.parse = function(src) {
19980   this.inline = new InlineLexer(src.links, this.options);
19981   this.tokens = src.reverse();
19982
19983   var out = '';
19984   while (this.next()) {
19985     out += this.tok();
19986   }
19987
19988   return out;
19989 };
19990
19991 /**
19992  * Next Token
19993  */
19994
19995 Parser.prototype.next = function() {
19996   return this.token = this.tokens.pop();
19997 };
19998
19999 /**
20000  * Preview Next Token
20001  */
20002
20003 Parser.prototype.peek = function() {
20004   return this.tokens[this.tokens.length-1] || 0;
20005 };
20006
20007 /**
20008  * Parse Text Tokens
20009  */
20010
20011 Parser.prototype.parseText = function() {
20012   var body = this.token.text;
20013
20014   while (this.peek().type === 'text') {
20015     body += '\n' + this.next().text;
20016   }
20017
20018   return this.inline.output(body);
20019 };
20020
20021 /**
20022  * Parse Current Token
20023  */
20024
20025 Parser.prototype.tok = function() {
20026   switch (this.token.type) {
20027     case 'space': {
20028       return '';
20029     }
20030     case 'hr': {
20031       return '<hr>\n';
20032     }
20033     case 'heading': {
20034       return '<h'
20035         + this.token.depth
20036         + '>'
20037         + this.inline.output(this.token.text)
20038         + '</h'
20039         + this.token.depth
20040         + '>\n';
20041     }
20042     case 'code': {
20043       if (this.options.highlight) {
20044         var code = this.options.highlight(this.token.text, this.token.lang);
20045         if (code != null && code !== this.token.text) {
20046           this.token.escaped = true;
20047           this.token.text = code;
20048         }
20049       }
20050
20051       if (!this.token.escaped) {
20052         this.token.text = escape(this.token.text, true);
20053       }
20054
20055       return '<pre><code'
20056         + (this.token.lang
20057         ? ' class="'
20058         + this.options.langPrefix
20059         + this.token.lang
20060         + '"'
20061         : '')
20062         + '>'
20063         + this.token.text
20064         + '</code></pre>\n';
20065     }
20066     case 'table': {
20067       var body = ''
20068         , heading
20069         , i
20070         , row
20071         , cell
20072         , j;
20073
20074       // header
20075       body += '<thead>\n<tr>\n';
20076       for (i = 0; i < this.token.header.length; i++) {
20077         heading = this.inline.output(this.token.header[i]);
20078         body += this.token.align[i]
20079           ? '<th align="' + this.token.align[i] + '">' + heading + '</th>\n'
20080           : '<th>' + heading + '</th>\n';
20081       }
20082       body += '</tr>\n</thead>\n';
20083
20084       // body
20085       body += '<tbody>\n'
20086       for (i = 0; i < this.token.cells.length; i++) {
20087         row = this.token.cells[i];
20088         body += '<tr>\n';
20089         for (j = 0; j < row.length; j++) {
20090           cell = this.inline.output(row[j]);
20091           body += this.token.align[j]
20092             ? '<td align="' + this.token.align[j] + '">' + cell + '</td>\n'
20093             : '<td>' + cell + '</td>\n';
20094         }
20095         body += '</tr>\n';
20096       }
20097       body += '</tbody>\n';
20098
20099       return '<table>\n'
20100         + body
20101         + '</table>\n';
20102     }
20103     case 'blockquote_start': {
20104       var body = '';
20105
20106       while (this.next().type !== 'blockquote_end') {
20107         body += this.tok();
20108       }
20109
20110       return '<blockquote>\n'
20111         + body
20112         + '</blockquote>\n';
20113     }
20114     case 'list_start': {
20115       var type = this.token.ordered ? 'ol' : 'ul'
20116         , body = '';
20117
20118       while (this.next().type !== 'list_end') {
20119         body += this.tok();
20120       }
20121
20122       return '<'
20123         + type
20124         + '>\n'
20125         + body
20126         + '</'
20127         + type
20128         + '>\n';
20129     }
20130     case 'list_item_start': {
20131       var body = '';
20132
20133       while (this.next().type !== 'list_item_end') {
20134         body += this.token.type === 'text'
20135           ? this.parseText()
20136           : this.tok();
20137       }
20138
20139       return '<li>'
20140         + body
20141         + '</li>\n';
20142     }
20143     case 'loose_item_start': {
20144       var body = '';
20145
20146       while (this.next().type !== 'list_item_end') {
20147         body += this.tok();
20148       }
20149
20150       return '<li>'
20151         + body
20152         + '</li>\n';
20153     }
20154     case 'html': {
20155       return !this.token.pre && !this.options.pedantic
20156         ? this.inline.output(this.token.text)
20157         : this.token.text;
20158     }
20159     case 'paragraph': {
20160       return '<p>'
20161         + this.inline.output(this.token.text)
20162         + '</p>\n';
20163     }
20164     case 'text': {
20165       return '<p>'
20166         + this.parseText()
20167         + '</p>\n';
20168     }
20169   }
20170 };
20171
20172 /**
20173  * Helpers
20174  */
20175
20176 function escape(html, encode) {
20177   return html
20178     .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
20179     .replace(/</g, '&lt;')
20180     .replace(/>/g, '&gt;')
20181     .replace(/"/g, '&quot;')
20182     .replace(/'/g, '&#39;');
20183 }
20184
20185 function replace(regex, opt) {
20186   regex = regex.source;
20187   opt = opt || '';
20188   return function self(name, val) {
20189     if (!name) return new RegExp(regex, opt);
20190     val = val.source || val;
20191     val = val.replace(/(^|[^\[])\^/g, '$1');
20192     regex = regex.replace(name, val);
20193     return self;
20194   };
20195 }
20196
20197 function noop() {}
20198 noop.exec = noop;
20199
20200 function merge(obj) {
20201   var i = 1
20202     , target
20203     , key;
20204
20205   for (; i < arguments.length; i++) {
20206     target = arguments[i];
20207     for (key in target) {
20208       if (Object.prototype.hasOwnProperty.call(target, key)) {
20209         obj[key] = target[key];
20210       }
20211     }
20212   }
20213
20214   return obj;
20215 }
20216
20217 /**
20218  * Marked
20219  */
20220
20221 function marked(src, opt, callback) {
20222   if (callback || typeof opt === 'function') {
20223     if (!callback) {
20224       callback = opt;
20225       opt = null;
20226     }
20227
20228     if (opt) opt = merge({}, marked.defaults, opt);
20229
20230     var tokens = Lexer.lex(tokens, opt)
20231       , highlight = opt.highlight
20232       , pending = 0
20233       , l = tokens.length
20234       , i = 0;
20235
20236     if (!highlight || highlight.length < 3) {
20237       return callback(null, Parser.parse(tokens, opt));
20238     }
20239
20240     var done = function() {
20241       delete opt.highlight;
20242       var out = Parser.parse(tokens, opt);
20243       opt.highlight = highlight;
20244       return callback(null, out);
20245     };
20246
20247     for (; i < l; i++) {
20248       (function(token) {
20249         if (token.type !== 'code') return;
20250         pending++;
20251         return highlight(token.text, token.lang, function(err, code) {
20252           if (code == null || code === token.text) {
20253             return --pending || done();
20254           }
20255           token.text = code;
20256           token.escaped = true;
20257           --pending || done();
20258         });
20259       })(tokens[i]);
20260     }
20261
20262     return;
20263   }
20264   try {
20265     if (opt) opt = merge({}, marked.defaults, opt);
20266     return Parser.parse(Lexer.lex(src, opt), opt);
20267   } catch (e) {
20268     e.message += '\nPlease report this to https://github.com/chjj/marked.';
20269     if ((opt || marked.defaults).silent) {
20270       return '<p>An error occured:</p><pre>'
20271         + escape(e.message + '', true)
20272         + '</pre>';
20273     }
20274     throw e;
20275   }
20276 }
20277
20278 /**
20279  * Options
20280  */
20281
20282 marked.options =
20283 marked.setOptions = function(opt) {
20284   merge(marked.defaults, opt);
20285   return marked;
20286 };
20287
20288 marked.defaults = {
20289   gfm: true,
20290   tables: true,
20291   breaks: false,
20292   pedantic: false,
20293   sanitize: false,
20294   smartLists: false,
20295   silent: false,
20296   highlight: null,
20297   langPrefix: 'lang-'
20298 };
20299
20300 /**
20301  * Expose
20302  */
20303
20304 marked.Parser = Parser;
20305 marked.parser = Parser.parse;
20306
20307 marked.Lexer = Lexer;
20308 marked.lexer = Lexer.lex;
20309
20310 marked.InlineLexer = InlineLexer;
20311 marked.inlineLexer = InlineLexer.output;
20312
20313 marked.parse = marked;
20314
20315 if (typeof exports === 'object') {
20316   module.exports = marked;
20317 } else if (typeof define === 'function' && define.amd) {
20318   define(function() { return marked; });
20319 } else {
20320   this.marked = marked;
20321 }
20322
20323 }).call(function() {
20324   return this || (typeof window !== 'undefined' ? window : global);
20325 }());
20326 (function () {
20327 'use strict';
20328 window.iD = function () {
20329     window.locale.en = iD.data.en;
20330     window.locale.current('en');
20331
20332     var dispatch = d3.dispatch('enter', 'exit', 'change'),
20333         context = {};
20334
20335     // https://github.com/openstreetmap/iD/issues/772
20336     // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
20337     var storage;
20338     try { storage = localStorage; } catch (e) {}  // eslint-disable-line no-empty
20339     storage = storage || (function() {
20340         var s = {};
20341         return {
20342             getItem: function(k) { return s[k]; },
20343             setItem: function(k, v) { s[k] = v; },
20344             removeItem: function(k) { delete s[k]; }
20345         };
20346     })();
20347
20348     context.storage = function(k, v) {
20349         try {
20350             if (arguments.length === 1) return storage.getItem(k);
20351             else if (v === null) storage.removeItem(k);
20352             else storage.setItem(k, v);
20353         } catch(e) {
20354             // localstorage quota exceeded
20355             /* eslint-disable no-console */
20356             if (typeof console !== 'undefined') console.error('localStorage quota exceeded');
20357             /* eslint-enable no-console */
20358         }
20359     };
20360
20361
20362     /* Straight accessors. Avoid using these if you can. */
20363     var ui, connection, history;
20364     context.ui = function() { return ui; };
20365     context.connection = function() { return connection; };
20366     context.history = function() { return history; };
20367
20368
20369     /* Connection */
20370     function entitiesLoaded(err, result) {
20371         if (!err) history.merge(result.data, result.extent);
20372     }
20373
20374     context.preauth = function(options) {
20375         connection.switch(options);
20376         return context;
20377     };
20378
20379     context.loadTiles = function(projection, dimensions, callback) {
20380         function done(err, result) {
20381             entitiesLoaded(err, result);
20382             if (callback) callback(err, result);
20383         }
20384         connection.loadTiles(projection, dimensions, done);
20385     };
20386
20387     context.loadEntity = function(id, callback) {
20388         function done(err, result) {
20389             entitiesLoaded(err, result);
20390             if (callback) callback(err, result);
20391         }
20392         connection.loadEntity(id, done);
20393     };
20394
20395     context.zoomToEntity = function(id, zoomTo) {
20396         if (zoomTo !== false) {
20397             this.loadEntity(id, function(err, result) {
20398                 if (err) return;
20399                 var entity = _.find(result.data, function(e) { return e.id === id; });
20400                 if (entity) { map.zoomTo(entity); }
20401             });
20402         }
20403
20404         map.on('drawn.zoomToEntity', function() {
20405             if (!context.hasEntity(id)) return;
20406             map.on('drawn.zoomToEntity', null);
20407             context.on('enter.zoomToEntity', null);
20408             context.enter(iD.modes.Select(context, [id]));
20409         });
20410
20411         context.on('enter.zoomToEntity', function() {
20412             if (mode.id !== 'browse') {
20413                 map.on('drawn.zoomToEntity', null);
20414                 context.on('enter.zoomToEntity', null);
20415             }
20416         });
20417     };
20418
20419     var minEditableZoom = 16;
20420     context.minEditableZoom = function(_) {
20421         if (!arguments.length) return minEditableZoom;
20422         minEditableZoom = _;
20423         connection.tileZoom(_);
20424         return context;
20425     };
20426
20427
20428     /* History */
20429     var inIntro = false;
20430     context.inIntro = function(_) {
20431         if (!arguments.length) return inIntro;
20432         inIntro = _;
20433         return context;
20434     };
20435
20436     context.save = function() {
20437         if (inIntro || (mode && mode.id === 'save') || d3.select('.modal').size()) return;
20438         history.save();
20439         if (history.hasChanges()) return t('save.unsaved_changes');
20440     };
20441
20442     context.flush = function() {
20443         context.debouncedSave.cancel();
20444         connection.flush();
20445         features.reset();
20446         history.reset();
20447         _.each(iD.services, function(service) {
20448             var reset = service().reset;
20449             if (reset) reset(context);
20450         });
20451         return context;
20452     };
20453
20454
20455     /* Graph */
20456     context.hasEntity = function(id) {
20457         return history.graph().hasEntity(id);
20458     };
20459     context.entity = function(id) {
20460         return history.graph().entity(id);
20461     };
20462     context.childNodes = function(way) {
20463         return history.graph().childNodes(way);
20464     };
20465     context.geometry = function(id) {
20466         return context.entity(id).geometry(history.graph());
20467     };
20468
20469
20470     /* Modes */
20471     var mode;
20472     context.mode = function() {
20473         return mode;
20474     };
20475     context.enter = function(newMode) {
20476         if (mode) {
20477             mode.exit();
20478             dispatch.exit(mode);
20479         }
20480
20481         mode = newMode;
20482         mode.enter();
20483         dispatch.enter(mode);
20484     };
20485
20486     context.selectedIDs = function() {
20487         if (mode && mode.selectedIDs) {
20488             return mode.selectedIDs();
20489         } else {
20490             return [];
20491         }
20492     };
20493
20494
20495     /* Behaviors */
20496     context.install = function(behavior) {
20497         context.surface().call(behavior);
20498     };
20499     context.uninstall = function(behavior) {
20500         context.surface().call(behavior.off);
20501     };
20502
20503
20504     /* Copy/Paste */
20505     var copyIDs = [], copyGraph;
20506     context.copyGraph = function() { return copyGraph; };
20507     context.copyIDs = function(_) {
20508         if (!arguments.length) return copyIDs;
20509         copyIDs = _;
20510         copyGraph = history.graph();
20511         return context;
20512     };
20513
20514
20515     /* Background */
20516     var background;
20517     context.background = function() { return background; };
20518
20519
20520     /* Features */
20521     var features;
20522     context.features = function() { return features; };
20523     context.hasHiddenConnections = function(id) {
20524         var graph = history.graph(),
20525             entity = graph.entity(id);
20526         return features.hasHiddenConnections(entity, graph);
20527     };
20528
20529
20530     /* Map */
20531     var map;
20532     context.map = function() { return map; };
20533     context.layers = function() { return map.layers; };
20534     context.surface = function() { return map.surface; };
20535     context.editable = function() { return map.editable(); };
20536
20537     context.surfaceRect = function() {
20538         // Work around a bug in Firefox.
20539         //   http://stackoverflow.com/questions/18153989/
20540         //   https://bugzilla.mozilla.org/show_bug.cgi?id=530985
20541         return context.surface().node().parentNode.getBoundingClientRect();
20542     };
20543
20544
20545     /* Debug */
20546     var debugTile = false, debugCollision = false;
20547     context.debugTile = function(_) {
20548         if (!arguments.length) return debugTile;
20549         debugTile = _;
20550         dispatch.change();
20551         return context;
20552     };
20553     context.debugCollision = function(_) {
20554         if (!arguments.length) return debugCollision;
20555         debugCollision = _;
20556         dispatch.change();
20557         return context;
20558     };
20559
20560
20561     /* Presets */
20562     var presets;
20563     context.presets = function(_) {
20564         if (!arguments.length) return presets;
20565         presets.load(_);
20566         iD.areaKeys = presets.areaKeys();
20567         return context;
20568     };
20569
20570
20571     /* Imagery */
20572     context.imagery = function(_) {
20573         background.load(_);
20574         return context;
20575     };
20576
20577
20578     /* Container */
20579     var container, embed;
20580     context.container = function(_) {
20581         if (!arguments.length) return container;
20582         container = _;
20583         container.classed('id-container', true);
20584         return context;
20585     };
20586     context.embed = function(_) {
20587         if (!arguments.length) return embed;
20588         embed = _;
20589         return context;
20590     };
20591
20592
20593     /* Taginfo */
20594     var taginfo;
20595     context.taginfo = function(_) {
20596         if (!arguments.length) return taginfo;
20597         taginfo = _;
20598         return context;
20599     };
20600
20601
20602     /* Assets */
20603     var assetPath = '';
20604     context.assetPath = function(_) {
20605         if (!arguments.length) return assetPath;
20606         assetPath = _;
20607         return context;
20608     };
20609
20610     var assetMap = {};
20611     context.assetMap = function(_) {
20612         if (!arguments.length) return assetMap;
20613         assetMap = _;
20614         return context;
20615     };
20616
20617     context.asset = function(_) {
20618         var filename = assetPath + _;
20619         return assetMap[filename] || filename;
20620     };
20621
20622     context.imagePath = function(_) {
20623         return context.asset('img/' + _);
20624     };
20625
20626     var locale, localePath;
20627     context.locale = function(loc, path) {
20628         locale = loc;
20629         localePath = path;
20630
20631         // Also set iD.detect().locale (unless we detected 'en-us' and openstreetmap wants 'en')..
20632         if (!(loc.toLowerCase() === 'en' && iD.detect().locale.toLowerCase() === 'en-us')) {
20633             iD.detect().locale = loc;
20634         }
20635
20636         return context;
20637     };
20638
20639     context.loadLocale = function(cb) {
20640         if (locale && locale !== 'en' && iD.data.locales.indexOf(locale) !== -1) {
20641             localePath = localePath || context.asset('locales/' + locale + '.json');
20642             d3.json(localePath, function(err, result) {
20643                 window.locale[locale] = result;
20644                 window.locale.current(locale);
20645                 cb();
20646             });
20647         } else {
20648             cb();
20649         }
20650     };
20651
20652
20653     /* Init */
20654
20655     context.projection = iD.geo.RawMercator();
20656
20657     locale = iD.detect().locale;
20658     if (locale && iD.data.locales.indexOf(locale) === -1) {
20659         locale = locale.split('-')[0];
20660     }
20661
20662     history = iD.History(context);
20663     context.graph = history.graph;
20664     context.changes = history.changes;
20665     context.intersects = history.intersects;
20666
20667     // Debounce save, since it's a synchronous localStorage write,
20668     // and history changes can happen frequently (e.g. when dragging).
20669     context.debouncedSave = _.debounce(context.save, 350);
20670     function withDebouncedSave(fn) {
20671         return function() {
20672             var result = fn.apply(history, arguments);
20673             context.debouncedSave();
20674             return result;
20675         };
20676     }
20677
20678     context.perform = withDebouncedSave(history.perform);
20679     context.replace = withDebouncedSave(history.replace);
20680     context.pop = withDebouncedSave(history.pop);
20681     context.overwrite = withDebouncedSave(history.overwrite);
20682     context.undo = withDebouncedSave(history.undo);
20683     context.redo = withDebouncedSave(history.redo);
20684
20685     ui = iD.ui(context);
20686
20687     connection = iD.Connection();
20688
20689     background = iD.Background(context);
20690
20691     features = iD.Features(context);
20692
20693     map = iD.Map(context);
20694     context.mouse = map.mouse;
20695     context.extent = map.extent;
20696     context.pan = map.pan;
20697     context.zoomIn = map.zoomIn;
20698     context.zoomOut = map.zoomOut;
20699     context.zoomInFurther = map.zoomInFurther;
20700     context.zoomOutFurther = map.zoomOutFurther;
20701     context.redrawEnable = map.redrawEnable;
20702
20703     presets = iD.presets();
20704
20705     return d3.rebind(context, dispatch, 'on');
20706 };
20707
20708
20709 iD.version = '1.9.6';
20710
20711 (function() {
20712     var detected = {};
20713
20714     var ua = navigator.userAgent,
20715         m = null;
20716
20717     m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i);   // Edge
20718     if (m !== null) {
20719         detected.browser = m[1];
20720         detected.version = m[2];
20721     }
20722     if (!detected.browser) {
20723         m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i);   // IE11
20724         if (m !== null) {
20725             detected.browser = 'msie';
20726             detected.version = m[1];
20727         }
20728     }
20729     if (!detected.browser) {
20730         m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i);   // Opera 15+
20731         if (m !== null) {
20732             detected.browser = 'Opera';
20733             detected.version = m[2];
20734         }
20735     }
20736     if (!detected.browser) {
20737         m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
20738         if (m !== null) {
20739             detected.browser = m[1];
20740             detected.version = m[2];
20741             m = ua.match(/version\/([\.\d]+)/i);
20742             if (m !== null) detected.version = m[1];
20743         }
20744     }
20745     if (!detected.browser) {
20746         detected.browser = navigator.appName;
20747         detected.version = navigator.appVersion;
20748     }
20749
20750     // keep major.minor version only..
20751     detected.version = detected.version.split(/\W/).slice(0,2).join('.');
20752
20753     if (detected.browser.toLowerCase() === 'msie') {
20754         detected.ie = true;
20755         detected.browser = 'Internet Explorer';
20756         detected.support = parseFloat(detected.version) >= 11;
20757     } else {
20758         detected.ie = false;
20759         detected.support = true;
20760     }
20761
20762     // Added due to incomplete svg style support. See #715
20763     detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 );
20764
20765     detected.locale = (navigator.languages && navigator.languages.length)
20766         ? navigator.languages[0] : (navigator.language || navigator.userLanguage || 'en-US');
20767
20768     detected.filedrop = (window.FileReader && 'ondrop' in window);
20769
20770     function nav(x) {
20771         return navigator.userAgent.indexOf(x) !== -1;
20772     }
20773
20774     if (nav('Win')) {
20775         detected.os = 'win';
20776         detected.platform = 'Windows';
20777     }
20778     else if (nav('Mac')) {
20779         detected.os = 'mac';
20780         detected.platform = 'Macintosh';
20781     }
20782     else if (nav('X11') || nav('Linux')) {
20783         detected.os = 'linux';
20784         detected.platform = 'Linux';
20785     }
20786     else {
20787         detected.os = 'win';
20788         detected.platform = 'Unknown';
20789     }
20790
20791     iD.detect = function() { return detected; };
20792 })();
20793 iD.services = {};
20794 iD.services.mapillary = function() {
20795     var mapillary = {},
20796         dispatch = d3.dispatch('loadedImages', 'loadedSigns'),
20797         apibase = 'https://a.mapillary.com/v2/',
20798         viewercss = 'https://npmcdn.com/mapillary-js@1.3.0/dist/mapillary-js.min.css',
20799         viewerjs = 'https://npmcdn.com/mapillary-js@1.3.0/dist/mapillary-js.min.js',
20800         clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi',
20801         maxResults = 1000,
20802         maxPages = 10,
20803         tileZoom = 14;
20804
20805
20806     function loadSignStyles(context) {
20807         d3.select('head').selectAll('#traffico')
20808             .data([0])
20809             .enter()
20810             .append('link')
20811             .attr('id', 'traffico')
20812             .attr('rel', 'stylesheet')
20813             .attr('href', context.asset('traffico/stylesheets/traffico.css'));
20814     }
20815
20816     function loadSignDefs(context) {
20817         if (iD.services.mapillary.sign_defs) return;
20818         iD.services.mapillary.sign_defs = {};
20819
20820         _.each(['au', 'br', 'ca', 'de', 'us'], function(region) {
20821             d3.json(context.asset('traffico/string-maps/' + region + '-map.json'), function(err, data) {
20822                 if (err) return;
20823                 if (region === 'de') region = 'eu';
20824                 iD.services.mapillary.sign_defs[region] = data;
20825             });
20826         });
20827     }
20828
20829     function loadViewer() {
20830         // mapillary-wrap
20831         var wrap = d3.select('#content').selectAll('.mapillary-wrap')
20832             .data([0]);
20833
20834         var enter = wrap.enter().append('div')
20835             .attr('class', 'mapillary-wrap')
20836             .classed('al', true)       // 'al'=left,  'ar'=right
20837             .classed('hidden', true);
20838
20839         enter.append('button')
20840             .attr('class', 'thumb-hide')
20841             .on('click', function () { mapillary.hideViewer(); })
20842             .append('div')
20843             .call(iD.svg.Icon('#icon-close'));
20844
20845         enter.append('div')
20846             .attr('id', 'mly')
20847             .attr('class', 'mly-wrapper')
20848             .classed('active', false);
20849
20850         // mapillary-viewercss
20851         d3.select('head').selectAll('#mapillary-viewercss')
20852             .data([0])
20853             .enter()
20854             .append('link')
20855             .attr('id', 'mapillary-viewercss')
20856             .attr('rel', 'stylesheet')
20857             .attr('href', viewercss);
20858
20859         // mapillary-viewerjs
20860         d3.select('head').selectAll('#mapillary-viewerjs')
20861             .data([0])
20862             .enter()
20863             .append('script')
20864             .attr('id', 'mapillary-viewerjs')
20865             .attr('src', viewerjs);
20866     }
20867
20868     function initViewer(imageKey, context) {
20869
20870         function nodeChanged(d) {
20871             var clicks = iD.services.mapillary.clicks;
20872             var index = clicks.indexOf(d.key);
20873             if (index > -1) {    // nodechange initiated from clicking on a marker..
20874                 clicks.splice(index, 1);
20875             } else {             // nodechange initiated from the Mapillary viewer controls..
20876                 var loc = d.apiNavImIm ? [d.apiNavImIm.lon, d.apiNavImIm.lat] : [d.latLon.lon, d.latLon.lat];
20877                 context.map().centerEase(loc);
20878                 mapillary.setSelectedImage(d.key, false);
20879             }
20880         }
20881
20882         if (Mapillary && imageKey) {
20883             var opts = {
20884                 baseImageSize: 320,
20885                 cover: false,
20886                 cache: true,
20887                 debug: false,
20888                 imagePlane: true,
20889                 loading: true,
20890                 sequence: true
20891             };
20892
20893             var viewer = new Mapillary.Viewer('mly', clientId, imageKey, opts);
20894             viewer.on('nodechanged', nodeChanged);
20895             viewer.on('loadingchanged', mapillary.setViewerLoading);
20896             iD.services.mapillary.viewer = viewer;
20897         }
20898     }
20899
20900     function abortRequest(i) {
20901         i.abort();
20902     }
20903
20904     function nearNullIsland(x, y, z) {
20905         if (z >= 7) {
20906             var center = Math.pow(2, z - 1),
20907                 width = Math.pow(2, z - 6),
20908                 min = center - (width / 2),
20909                 max = center + (width / 2) - 1;
20910             return x >= min && x <= max && y >= min && y <= max;
20911         }
20912         return false;
20913     }
20914
20915     function getTiles(projection, dimensions) {
20916         var s = projection.scale() * 2 * Math.PI,
20917             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
20918             ts = 256 * Math.pow(2, z - tileZoom),
20919             origin = [
20920                 s / 2 - projection.translate()[0],
20921                 s / 2 - projection.translate()[1]];
20922
20923         return d3.geo.tile()
20924             .scaleExtent([tileZoom, tileZoom])
20925             .scale(s)
20926             .size(dimensions)
20927             .translate(projection.translate())()
20928             .map(function(tile) {
20929                 var x = tile[0] * ts - origin[0],
20930                     y = tile[1] * ts - origin[1];
20931
20932                 return {
20933                     id: tile.toString(),
20934                     extent: iD.geo.Extent(
20935                         projection.invert([x, y + ts]),
20936                         projection.invert([x + ts, y]))
20937                 };
20938             });
20939     }
20940
20941
20942     function loadTiles(which, url, projection, dimensions) {
20943         var tiles = getTiles(projection, dimensions).filter(function(t) {
20944               var xyz = t.id.split(',');
20945               return !nearNullIsland(xyz[0], xyz[1], xyz[2]);
20946             });
20947
20948         _.filter(which.inflight, function(v, k) {
20949             var wanted = _.find(tiles, function(tile) { return k === (tile.id + ',0'); });
20950             if (!wanted) delete which.inflight[k];
20951             return !wanted;
20952         }).map(abortRequest);
20953
20954         tiles.forEach(function(tile) {
20955             loadTilePage(which, url, tile, 0);
20956         });
20957     }
20958
20959     function loadTilePage(which, url, tile, page) {
20960         var cache = iD.services.mapillary.cache[which],
20961             id = tile.id + ',' + String(page),
20962             rect = tile.extent.rectangle();
20963
20964         if (cache.loaded[id] || cache.inflight[id]) return;
20965
20966         cache.inflight[id] = d3.json(url +
20967             iD.util.qsString({
20968                 geojson: 'true',
20969                 limit: maxResults,
20970                 page: page,
20971                 client_id: clientId,
20972                 min_lon: rect[0],
20973                 min_lat: rect[1],
20974                 max_lon: rect[2],
20975                 max_lat: rect[3]
20976             }), function(err, data) {
20977                 cache.loaded[id] = true;
20978                 delete cache.inflight[id];
20979                 if (err || !data.features || !data.features.length) return;
20980
20981                 var features = [],
20982                     nextPage = page + 1,
20983                     feature, loc, d;
20984
20985                 for (var i = 0; i < data.features.length; i++) {
20986                     feature = data.features[i];
20987                     loc = feature.geometry.coordinates;
20988                     d = { key: feature.properties.key, loc: loc };
20989                     if (which === 'images') d.ca = feature.properties.ca;
20990                     if (which === 'signs') d.signs = feature.properties.rects;
20991
20992                     features.push([loc[0], loc[1], loc[0], loc[1], d]);
20993                 }
20994
20995                 cache.rtree.load(features);
20996
20997                 if (which === 'images') dispatch.loadedImages();
20998                 if (which === 'signs') dispatch.loadedSigns();
20999
21000                 if (data.features.length === maxResults && nextPage < maxPages) {
21001                     loadTilePage(which, url, tile, nextPage);
21002                 }
21003             }
21004         );
21005     }
21006
21007     mapillary.loadImages = function(projection, dimensions) {
21008         var url = apibase + 'search/im/geojson?';
21009         loadTiles('images', url, projection, dimensions);
21010     };
21011
21012     mapillary.loadSigns = function(context, projection, dimensions) {
21013         var url = apibase + 'search/im/geojson/or?';
21014         loadSignStyles(context);
21015         loadSignDefs(context);
21016         loadTiles('signs', url, projection, dimensions);
21017     };
21018
21019     mapillary.loadViewer = function() {
21020         loadViewer();
21021     };
21022
21023
21024     // partition viewport into `psize` x `psize` regions
21025     function partitionViewport(psize, projection, dimensions) {
21026         psize = psize || 16;
21027         var cols = d3.range(0, dimensions[0], psize),
21028             rows = d3.range(0, dimensions[1], psize),
21029             partitions = [];
21030
21031         rows.forEach(function(y) {
21032             cols.forEach(function(x) {
21033                 var min = [x, y + psize],
21034                     max = [x + psize, y];
21035                 partitions.push(
21036                     iD.geo.Extent(projection.invert(min), projection.invert(max)));
21037             });
21038         });
21039
21040         return partitions;
21041     }
21042
21043     // no more than `limit` results per partition.
21044     function searchLimited(psize, limit, projection, dimensions, rtree) {
21045         limit = limit || 3;
21046
21047         var partitions = partitionViewport(psize, projection, dimensions);
21048         return _.flatten(_.compact(_.map(partitions, function(extent) {
21049             return rtree.search(extent.rectangle())
21050                 .slice(0, limit)
21051                 .map(function(d) { return d[4]; });
21052         })));
21053     }
21054
21055     mapillary.images = function(projection, dimensions) {
21056         var psize = 16, limit = 3;
21057         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.images.rtree);
21058     };
21059
21060     mapillary.signs = function(projection, dimensions) {
21061         var psize = 32, limit = 3;
21062         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.signs.rtree);
21063     };
21064
21065     mapillary.signsSupported = function() {
21066         var detected = iD.detect();
21067         return (!(detected.ie || detected.browser.toLowerCase() === 'safari'));
21068     };
21069
21070     mapillary.signHTML = function(d) {
21071         if (!iD.services.mapillary.sign_defs) return;
21072
21073         var detectionPackage = d.signs[0].package,
21074             type = d.signs[0].type,
21075             country = detectionPackage.split('_')[1];
21076
21077         return iD.services.mapillary.sign_defs[country][type];
21078     };
21079
21080     mapillary.showViewer = function() {
21081         d3.select('#content')
21082             .selectAll('.mapillary-wrap')
21083             .classed('hidden', false)
21084             .selectAll('.mly-wrapper')
21085             .classed('active', true);
21086
21087         return mapillary;
21088     };
21089
21090     mapillary.hideViewer = function() {
21091         d3.select('#content')
21092             .selectAll('.mapillary-wrap')
21093             .classed('hidden', true)
21094             .selectAll('.mly-wrapper')
21095             .classed('active', false);
21096
21097         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
21098             .classed('selected', false);
21099
21100         iD.services.mapillary.image = null;
21101
21102         return mapillary;
21103     };
21104
21105     mapillary.setViewerLoading = function(loading) {
21106         var canvas = d3.select('#content')
21107             .selectAll('.mly-wrapper canvas');
21108
21109         if (canvas.empty()) return;   // viewer not loaded yet
21110
21111         var cover = d3.select('#content')
21112             .selectAll('.mly-wrapper .Cover');
21113
21114         cover.classed('CoverDone', !loading);
21115
21116         var button = cover.selectAll('.CoverButton')
21117             .data(loading ? [0] : []);
21118
21119         button.enter()
21120             .append('div')
21121             .attr('class', 'CoverButton')
21122             .append('div')
21123             .attr('class', 'uil-ripple-css')
21124             .append('div');
21125
21126         button.exit()
21127             .remove();
21128
21129         return mapillary;
21130     };
21131
21132     mapillary.updateViewer = function(imageKey, context) {
21133         if (!iD.services.mapillary) return;
21134         if (!imageKey) return;
21135
21136         if (!iD.services.mapillary.viewer) {
21137             initViewer(imageKey, context);
21138         } else {
21139             iD.services.mapillary.viewer.moveToKey(imageKey);
21140         }
21141
21142         return mapillary;
21143     };
21144
21145     mapillary.getSelectedImage = function() {
21146         if (!iD.services.mapillary) return null;
21147         return iD.services.mapillary.image;
21148     };
21149
21150     mapillary.setSelectedImage = function(imageKey, fromClick) {
21151         if (!iD.services.mapillary) return null;
21152
21153         iD.services.mapillary.image = imageKey;
21154         if (fromClick) {
21155             iD.services.mapillary.clicks.push(imageKey);
21156         }
21157
21158         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
21159             .classed('selected', function(d) { return d.key === imageKey; });
21160
21161         return mapillary;
21162     };
21163
21164     mapillary.reset = function() {
21165         var cache = iD.services.mapillary.cache;
21166
21167         if (cache) {
21168             _.forEach(cache.images.inflight, abortRequest);
21169             _.forEach(cache.signs.inflight, abortRequest);
21170         }
21171
21172         iD.services.mapillary.cache = {
21173             images: { inflight: {}, loaded: {}, rtree: rbush() },
21174             signs:  { inflight: {}, loaded: {}, rtree: rbush() }
21175         };
21176
21177         iD.services.mapillary.image = null;
21178         iD.services.mapillary.clicks = [];
21179
21180         return mapillary;
21181     };
21182
21183
21184     if (!iD.services.mapillary.cache) {
21185         mapillary.reset();
21186     }
21187
21188     return d3.rebind(mapillary, dispatch, 'on');
21189 };
21190 iD.services.nominatim  = function() {
21191     var nominatim = {},
21192         endpoint = 'https://nominatim.openstreetmap.org/reverse?';
21193
21194
21195     nominatim.countryCode = function(location, callback) {
21196         var cache = iD.services.nominatim.cache,
21197             countryCodes = cache.search([location[0], location[1], location[0], location[1]]);
21198
21199         if (countryCodes.length > 0)
21200             return callback(null, countryCodes[0][4]);
21201
21202         d3.json(endpoint +
21203             iD.util.qsString({
21204                 format: 'json',
21205                 addressdetails: 1,
21206                 lat: location[1],
21207                 lon: location[0]
21208             }), function(err, result) {
21209                 if (err)
21210                     return callback(err);
21211                 else if (result && result.error)
21212                     return callback(result.error);
21213
21214                 var extent = iD.geo.Extent(location).padByMeters(1000);
21215
21216                 cache.insert(extent.rectangle().concat(result.address.country_code));
21217
21218                 callback(null, result.address.country_code);
21219             });
21220     };
21221
21222     nominatim.reset = function() {
21223         iD.services.nominatim.cache = rbush();
21224         return nominatim;
21225     };
21226
21227
21228     if (!iD.services.nominatim.cache) {
21229         nominatim.reset();
21230     }
21231
21232     return nominatim;
21233 };
21234 iD.services.taginfo = function() {
21235     var taginfo = {},
21236         endpoint = 'https://taginfo.openstreetmap.org/api/4/',
21237         tag_sorts = {
21238             point: 'count_nodes',
21239             vertex: 'count_nodes',
21240             area: 'count_ways',
21241             line: 'count_ways'
21242         },
21243         tag_filters = {
21244             point: 'nodes',
21245             vertex: 'nodes',
21246             area: 'ways',
21247             line: 'ways'
21248         };
21249
21250
21251     function sets(parameters, n, o) {
21252         if (parameters.geometry && o[parameters.geometry]) {
21253             parameters[n] = o[parameters.geometry];
21254         }
21255         return parameters;
21256     }
21257
21258     function setFilter(parameters) {
21259         return sets(parameters, 'filter', tag_filters);
21260     }
21261
21262     function setSort(parameters) {
21263         return sets(parameters, 'sortname', tag_sorts);
21264     }
21265
21266     function clean(parameters) {
21267         return _.omit(parameters, 'geometry', 'debounce');
21268     }
21269
21270     function filterKeys(type) {
21271         var count_type = type ? 'count_' + type : 'count_all';
21272         return function(d) {
21273             return parseFloat(d[count_type]) > 2500 || d.in_wiki;
21274         };
21275     }
21276
21277     function filterMultikeys() {
21278         return function(d) {
21279             return (d.key.match(/:/g) || []).length === 1;  // exactly one ':'
21280         };
21281     }
21282
21283     function filterValues() {
21284         return function(d) {
21285             if (d.value.match(/[A-Z*;,]/) !== null) return false;  // exclude some punctuation, uppercase letters
21286             return parseFloat(d.fraction) > 0.0 || d.in_wiki;
21287         };
21288     }
21289
21290     function valKey(d) {
21291         return {
21292             value: d.key,
21293             title: d.key
21294         };
21295     }
21296
21297     function valKeyDescription(d) {
21298         return {
21299             value: d.value,
21300             title: d.description || d.value
21301         };
21302     }
21303
21304     // sort keys with ':' lower than keys without ':'
21305     function sortKeys(a, b) {
21306         return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
21307             : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
21308             : 0;
21309     }
21310
21311     var debounced = _.debounce(d3.json, 100, true);
21312
21313     function request(url, debounce, callback) {
21314         var cache = iD.services.taginfo.cache;
21315
21316         if (cache[url]) {
21317             callback(null, cache[url]);
21318         } else if (debounce) {
21319             debounced(url, done);
21320         } else {
21321             d3.json(url, done);
21322         }
21323
21324         function done(err, data) {
21325             if (!err) cache[url] = data;
21326             callback(err, data);
21327         }
21328     }
21329
21330     taginfo.keys = function(parameters, callback) {
21331         var debounce = parameters.debounce;
21332         parameters = clean(setSort(parameters));
21333         request(endpoint + 'keys/all?' +
21334             iD.util.qsString(_.extend({
21335                 rp: 10,
21336                 sortname: 'count_all',
21337                 sortorder: 'desc',
21338                 page: 1
21339             }, parameters)), debounce, function(err, d) {
21340                 if (err) return callback(err);
21341                 var f = filterKeys(parameters.filter);
21342                 callback(null, d.data.filter(f).sort(sortKeys).map(valKey));
21343             });
21344     };
21345
21346     taginfo.multikeys = function(parameters, callback) {
21347         var debounce = parameters.debounce;
21348         parameters = clean(setSort(parameters));
21349         request(endpoint + 'keys/all?' +
21350             iD.util.qsString(_.extend({
21351                 rp: 25,
21352                 sortname: 'count_all',
21353                 sortorder: 'desc',
21354                 page: 1
21355             }, parameters)), debounce, function(err, d) {
21356                 if (err) return callback(err);
21357                 var f = filterMultikeys();
21358                 callback(null, d.data.filter(f).map(valKey));
21359             });
21360     };
21361
21362     taginfo.values = function(parameters, callback) {
21363         var debounce = parameters.debounce;
21364         parameters = clean(setSort(setFilter(parameters)));
21365         request(endpoint + 'key/values?' +
21366             iD.util.qsString(_.extend({
21367                 rp: 25,
21368                 sortname: 'count_all',
21369                 sortorder: 'desc',
21370                 page: 1
21371             }, parameters)), debounce, function(err, d) {
21372                 if (err) return callback(err);
21373                 var f = filterValues();
21374                 callback(null, d.data.filter(f).map(valKeyDescription));
21375             });
21376     };
21377
21378     taginfo.docs = function(parameters, callback) {
21379         var debounce = parameters.debounce;
21380         parameters = clean(setSort(parameters));
21381
21382         var path = 'key/wiki_pages?';
21383         if (parameters.value) path = 'tag/wiki_pages?';
21384         else if (parameters.rtype) path = 'relation/wiki_pages?';
21385
21386         request(endpoint + path + iD.util.qsString(parameters), debounce, function(err, d) {
21387             if (err) return callback(err);
21388             callback(null, d.data);
21389         });
21390     };
21391
21392     taginfo.endpoint = function(_) {
21393         if (!arguments.length) return endpoint;
21394         endpoint = _;
21395         return taginfo;
21396     };
21397
21398     taginfo.reset = function() {
21399         iD.services.taginfo.cache = {};
21400         return taginfo;
21401     };
21402
21403
21404     if (!iD.services.taginfo.cache) {
21405         taginfo.reset();
21406     }
21407
21408     return taginfo;
21409 };
21410 iD.services.wikidata = function() {
21411     var wiki = {},
21412         endpoint = 'https://www.wikidata.org/w/api.php?';
21413
21414     // Given a Wikipedia language and article title, return an array of
21415     // corresponding Wikidata entities.
21416     wiki.itemsByTitle = function(lang, title, callback) {
21417         lang = lang || 'en';
21418         d3.jsonp(endpoint + iD.util.qsString({
21419             action: 'wbgetentities',
21420             format: 'json',
21421             sites: lang.replace(/-/g, '_') + 'wiki',
21422             titles: title,
21423             languages: 'en', // shrink response by filtering to one language
21424             callback: '{callback}'
21425         }), function(data) {
21426             callback(title, data.entities || {});
21427         });
21428     };
21429
21430     return wiki;
21431 };
21432 iD.services.wikipedia = function() {
21433     var wiki = {},
21434         endpoint = 'https://en.wikipedia.org/w/api.php?';
21435
21436     wiki.search = function(lang, query, callback) {
21437         lang = lang || 'en';
21438         d3.jsonp(endpoint.replace('en', lang) +
21439             iD.util.qsString({
21440                 action: 'query',
21441                 list: 'search',
21442                 srlimit: '10',
21443                 srinfo: 'suggestion',
21444                 format: 'json',
21445                 callback: '{callback}',
21446                 srsearch: query
21447             }), function(data) {
21448                 if (!data.query) return;
21449                 callback(query, data.query.search.map(function(d) {
21450                     return d.title;
21451                 }));
21452             });
21453     };
21454
21455     wiki.suggestions = function(lang, query, callback) {
21456         lang = lang || 'en';
21457         d3.jsonp(endpoint.replace('en', lang) +
21458             iD.util.qsString({
21459                 action: 'opensearch',
21460                 namespace: 0,
21461                 suggest: '',
21462                 format: 'json',
21463                 callback: '{callback}',
21464                 search: query
21465             }), function(d) {
21466                 callback(d[0], d[1]);
21467             });
21468     };
21469
21470     wiki.translations = function(lang, title, callback) {
21471         d3.jsonp(endpoint.replace('en', lang) +
21472             iD.util.qsString({
21473                 action: 'query',
21474                 prop: 'langlinks',
21475                 format: 'json',
21476                 callback: '{callback}',
21477                 lllimit: 500,
21478                 titles: title
21479             }), function(d) {
21480                 var list = d.query.pages[Object.keys(d.query.pages)[0]],
21481                     translations = {};
21482                 if (list && list.langlinks) {
21483                     list.langlinks.forEach(function(d) {
21484                         translations[d.lang] = d['*'];
21485                     });
21486                     callback(translations);
21487                 }
21488             });
21489     };
21490
21491     return wiki;
21492 };
21493 iD.util = {};
21494
21495 iD.util.tagText = function(entity) {
21496     return d3.entries(entity.tags).map(function(e) {
21497         return e.key + '=' + e.value;
21498     }).join(', ');
21499 };
21500
21501 iD.util.entitySelector = function(ids) {
21502     return ids.length ? '.' + ids.join(',.') : 'nothing';
21503 };
21504
21505 iD.util.entityOrMemberSelector = function(ids, graph) {
21506     var s = iD.util.entitySelector(ids);
21507
21508     ids.forEach(function(id) {
21509         var entity = graph.hasEntity(id);
21510         if (entity && entity.type === 'relation') {
21511             entity.members.forEach(function(member) {
21512                 s += ',.' + member.id;
21513             });
21514         }
21515     });
21516
21517     return s;
21518 };
21519
21520 iD.util.displayName = function(entity) {
21521     var localeName = 'name:' + iD.detect().locale.toLowerCase().split('-')[0];
21522     return entity.tags[localeName] || entity.tags.name || entity.tags.ref;
21523 };
21524
21525 iD.util.displayType = function(id) {
21526     return {
21527         n: t('inspector.node'),
21528         w: t('inspector.way'),
21529         r: t('inspector.relation')
21530     }[id.charAt(0)];
21531 };
21532
21533 iD.util.stringQs = function(str) {
21534     return str.split('&').reduce(function(obj, pair){
21535         var parts = pair.split('=');
21536         if (parts.length === 2) {
21537             obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
21538         }
21539         return obj;
21540     }, {});
21541 };
21542
21543 iD.util.qsString = function(obj, noencode) {
21544     function softEncode(s) {
21545       // encode everything except special characters used in certain hash parameters:
21546       // "/" in map states, ":", ",", {" and "}" in background
21547       return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
21548     }
21549     return Object.keys(obj).sort().map(function(key) {
21550         return encodeURIComponent(key) + '=' + (
21551             noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
21552     }).join('&');
21553 };
21554
21555 iD.util.prefixDOMProperty = function(property) {
21556     var prefixes = ['webkit', 'ms', 'moz', 'o'],
21557         i = -1,
21558         n = prefixes.length,
21559         s = document.body;
21560
21561     if (property in s)
21562         return property;
21563
21564     property = property.substr(0, 1).toUpperCase() + property.substr(1);
21565
21566     while (++i < n)
21567         if (prefixes[i] + property in s)
21568             return prefixes[i] + property;
21569
21570     return false;
21571 };
21572
21573 iD.util.prefixCSSProperty = function(property) {
21574     var prefixes = ['webkit', 'ms', 'Moz', 'O'],
21575         i = -1,
21576         n = prefixes.length,
21577         s = document.body.style;
21578
21579     if (property.toLowerCase() in s)
21580         return property.toLowerCase();
21581
21582     while (++i < n)
21583         if (prefixes[i] + property in s)
21584             return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
21585
21586     return false;
21587 };
21588
21589
21590 iD.util.setTransform = function(el, x, y, scale) {
21591     var prop = iD.util.transformProperty = iD.util.transformProperty || iD.util.prefixCSSProperty('Transform'),
21592         translate = iD.detect().opera ?
21593             'translate('   + x + 'px,' + y + 'px)' :
21594             'translate3d(' + x + 'px,' + y + 'px,0)';
21595     return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
21596 };
21597
21598 iD.util.getStyle = function(selector) {
21599     for (var i = 0; i < document.styleSheets.length; i++) {
21600         var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules || [];
21601         for (var k = 0; k < rules.length; k++) {
21602             var selectorText = rules[k].selectorText && rules[k].selectorText.split(', ');
21603             if (_.includes(selectorText, selector)) {
21604                 return rules[k];
21605             }
21606         }
21607     }
21608 };
21609
21610 iD.util.editDistance = function(a, b) {
21611     if (a.length === 0) return b.length;
21612     if (b.length === 0) return a.length;
21613     var matrix = [];
21614     for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
21615     for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
21616     for (i = 1; i <= b.length; i++) {
21617         for (j = 1; j <= a.length; j++) {
21618             if (b.charAt(i-1) === a.charAt(j-1)) {
21619                 matrix[i][j] = matrix[i-1][j-1];
21620             } else {
21621                 matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
21622                     Math.min(matrix[i][j-1] + 1, // insertion
21623                     matrix[i-1][j] + 1)); // deletion
21624             }
21625         }
21626     }
21627     return matrix[b.length][a.length];
21628 };
21629
21630 // a d3.mouse-alike which
21631 // 1. Only works on HTML elements, not SVG
21632 // 2. Does not cause style recalculation
21633 iD.util.fastMouse = function(container) {
21634     var rect = container.getBoundingClientRect(),
21635         rectLeft = rect.left,
21636         rectTop = rect.top,
21637         clientLeft = +container.clientLeft,
21638         clientTop = +container.clientTop;
21639     return function(e) {
21640         return [
21641             e.clientX - rectLeft - clientLeft,
21642             e.clientY - rectTop - clientTop];
21643     };
21644 };
21645
21646 /* eslint-disable no-proto */
21647 iD.util.getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; };
21648 /* eslint-enable no-proto */
21649
21650 iD.util.asyncMap = function(inputs, func, callback) {
21651     var remaining = inputs.length,
21652         results = [],
21653         errors = [];
21654
21655     inputs.forEach(function(d, i) {
21656         func(d, function done(err, data) {
21657             errors[i] = err;
21658             results[i] = data;
21659             remaining--;
21660             if (!remaining) callback(errors, results);
21661         });
21662     });
21663 };
21664
21665 // wraps an index to an interval [0..length-1]
21666 iD.util.wrap = function(index, length) {
21667     if (index < 0)
21668         index += Math.ceil(-index/length)*length;
21669     return index % length;
21670 };
21671 // A per-domain session mutex backed by a cookie and dead man's
21672 // switch. If the session crashes, the mutex will auto-release
21673 // after 5 seconds.
21674
21675 iD.util.SessionMutex = function(name) {
21676     var mutex = {},
21677         intervalID;
21678
21679     function renew() {
21680         var expires = new Date();
21681         expires.setSeconds(expires.getSeconds() + 5);
21682         document.cookie = name + '=1; expires=' + expires.toUTCString();
21683     }
21684
21685     mutex.lock = function() {
21686         if (intervalID) return true;
21687         var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
21688         if (cookie) return false;
21689         renew();
21690         intervalID = window.setInterval(renew, 4000);
21691         return true;
21692     };
21693
21694     mutex.unlock = function() {
21695         if (!intervalID) return;
21696         document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
21697         clearInterval(intervalID);
21698         intervalID = null;
21699     };
21700
21701     mutex.locked = function() {
21702         return !!intervalID;
21703     };
21704
21705     return mutex;
21706 };
21707 iD.util.SuggestNames = function(preset, suggestions) {
21708     preset = preset.id.split('/', 2);
21709     var k = preset[0],
21710         v = preset[1];
21711
21712     return function(value, callback) {
21713         var result = [];
21714         if (value && value.length > 2) {
21715             if (suggestions[k] && suggestions[k][v]) {
21716                 for (var sugg in suggestions[k][v]) {
21717                     var dist = iD.util.editDistance(value, sugg.substring(0, value.length));
21718                     if (dist < 3) {
21719                         result.push({
21720                             title: sugg,
21721                             value: sugg,
21722                             dist: dist
21723                         });
21724                     }
21725                 }
21726             }
21727             result.sort(function(a, b) {
21728                 return a.dist - b.dist;
21729             });
21730         }
21731         result = result.slice(0,3);
21732         callback(result);
21733     };
21734 };
21735 iD.geo = {};
21736
21737 iD.geo.roundCoords = function(c) {
21738     return [Math.floor(c[0]), Math.floor(c[1])];
21739 };
21740
21741 iD.geo.interp = function(p1, p2, t) {
21742     return [p1[0] + (p2[0] - p1[0]) * t,
21743             p1[1] + (p2[1] - p1[1]) * t];
21744 };
21745
21746 // 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product.
21747 // Returns a positive value, if OAB makes a counter-clockwise turn,
21748 // negative for clockwise turn, and zero if the points are collinear.
21749 iD.geo.cross = function(o, a, b) {
21750     return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
21751 };
21752
21753 // http://jsperf.com/id-dist-optimization
21754 iD.geo.euclideanDistance = function(a, b) {
21755     var x = a[0] - b[0], y = a[1] - b[1];
21756     return Math.sqrt((x * x) + (y * y));
21757 };
21758
21759 // using WGS84 polar radius (6356752.314245179 m)
21760 // const = 2 * PI * r / 360
21761 iD.geo.latToMeters = function(dLat) {
21762     return dLat * 110946.257617;
21763 };
21764
21765 // using WGS84 equatorial radius (6378137.0 m)
21766 // const = 2 * PI * r / 360
21767 iD.geo.lonToMeters = function(dLon, atLat) {
21768     return Math.abs(atLat) >= 90 ? 0 :
21769         dLon * 111319.490793 * Math.abs(Math.cos(atLat * (Math.PI/180)));
21770 };
21771
21772 // using WGS84 polar radius (6356752.314245179 m)
21773 // const = 2 * PI * r / 360
21774 iD.geo.metersToLat = function(m) {
21775     return m / 110946.257617;
21776 };
21777
21778 // using WGS84 equatorial radius (6378137.0 m)
21779 // const = 2 * PI * r / 360
21780 iD.geo.metersToLon = function(m, atLat) {
21781     return Math.abs(atLat) >= 90 ? 0 :
21782         m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180)));
21783 };
21784
21785 iD.geo.offsetToMeters = function(offset) {
21786     var equatRadius = 6356752.314245179,
21787         polarRadius = 6378137.0,
21788         tileSize = 256;
21789
21790     return [
21791         offset[0] * 2 * Math.PI * equatRadius / tileSize,
21792         -offset[1] * 2 * Math.PI * polarRadius / tileSize
21793     ];
21794 };
21795
21796 iD.geo.metersToOffset = function(meters) {
21797     var equatRadius = 6356752.314245179,
21798         polarRadius = 6378137.0,
21799         tileSize = 256;
21800
21801     return [
21802         meters[0] * tileSize / (2 * Math.PI * equatRadius),
21803         -meters[1] * tileSize / (2 * Math.PI * polarRadius)
21804     ];
21805 };
21806
21807 // Equirectangular approximation of spherical distances on Earth
21808 iD.geo.sphericalDistance = function(a, b) {
21809     var x = iD.geo.lonToMeters(a[0] - b[0], (a[1] + b[1]) / 2),
21810         y = iD.geo.latToMeters(a[1] - b[1]);
21811     return Math.sqrt((x * x) + (y * y));
21812 };
21813
21814 iD.geo.edgeEqual = function(a, b) {
21815     return (a[0] === b[0] && a[1] === b[1]) ||
21816         (a[0] === b[1] && a[1] === b[0]);
21817 };
21818
21819 // Return the counterclockwise angle in the range (-pi, pi)
21820 // between the positive X axis and the line intersecting a and b.
21821 iD.geo.angle = function(a, b, projection) {
21822     a = projection(a.loc);
21823     b = projection(b.loc);
21824     return Math.atan2(b[1] - a[1], b[0] - a[0]);
21825 };
21826
21827 // Choose the edge with the minimal distance from `point` to its orthogonal
21828 // projection onto that edge, if such a projection exists, or the distance to
21829 // the closest vertex on that edge. Returns an object with the `index` of the
21830 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
21831 iD.geo.chooseEdge = function(nodes, point, projection) {
21832     var dist = iD.geo.euclideanDistance,
21833         points = nodes.map(function(n) { return projection(n.loc); }),
21834         min = Infinity,
21835         idx, loc;
21836
21837     function dot(p, q) {
21838         return p[0] * q[0] + p[1] * q[1];
21839     }
21840
21841     for (var i = 0; i < points.length - 1; i++) {
21842         var o = points[i],
21843             s = [points[i + 1][0] - o[0],
21844                  points[i + 1][1] - o[1]],
21845             v = [point[0] - o[0],
21846                  point[1] - o[1]],
21847             proj = dot(v, s) / dot(s, s),
21848             p;
21849
21850         if (proj < 0) {
21851             p = o;
21852         } else if (proj > 1) {
21853             p = points[i + 1];
21854         } else {
21855             p = [o[0] + proj * s[0], o[1] + proj * s[1]];
21856         }
21857
21858         var d = dist(p, point);
21859         if (d < min) {
21860             min = d;
21861             idx = i + 1;
21862             loc = projection.invert(p);
21863         }
21864     }
21865
21866     return {
21867         index: idx,
21868         distance: min,
21869         loc: loc
21870     };
21871 };
21872
21873 // Return the intersection point of 2 line segments.
21874 // From https://github.com/pgkelley4/line-segments-intersect
21875 // This uses the vector cross product approach described below:
21876 //  http://stackoverflow.com/a/565282/786339
21877 iD.geo.lineIntersection = function(a, b) {
21878     function subtractPoints(point1, point2) {
21879         return [point1[0] - point2[0], point1[1] - point2[1]];
21880     }
21881     function crossProduct(point1, point2) {
21882         return point1[0] * point2[1] - point1[1] * point2[0];
21883     }
21884
21885     var p = [a[0][0], a[0][1]],
21886         p2 = [a[1][0], a[1][1]],
21887         q = [b[0][0], b[0][1]],
21888         q2 = [b[1][0], b[1][1]],
21889         r = subtractPoints(p2, p),
21890         s = subtractPoints(q2, q),
21891         uNumerator = crossProduct(subtractPoints(q, p), r),
21892         denominator = crossProduct(r, s);
21893
21894     if (uNumerator && denominator) {
21895         var u = uNumerator / denominator,
21896             t = crossProduct(subtractPoints(q, p), s) / denominator;
21897
21898         if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
21899             return iD.geo.interp(p, p2, t);
21900         }
21901     }
21902
21903     return null;
21904 };
21905
21906 iD.geo.pathIntersections = function(path1, path2) {
21907     var intersections = [];
21908     for (var i = 0; i < path1.length - 1; i++) {
21909         for (var j = 0; j < path2.length - 1; j++) {
21910             var a = [ path1[i], path1[i+1] ],
21911                 b = [ path2[j], path2[j+1] ],
21912                 hit = iD.geo.lineIntersection(a, b);
21913             if (hit) intersections.push(hit);
21914         }
21915     }
21916     return intersections;
21917 };
21918
21919 // Return whether point is contained in polygon.
21920 //
21921 // `point` should be a 2-item array of coordinates.
21922 // `polygon` should be an array of 2-item arrays of coordinates.
21923 //
21924 // From https://github.com/substack/point-in-polygon.
21925 // ray-casting algorithm based on
21926 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
21927 //
21928 iD.geo.pointInPolygon = function(point, polygon) {
21929     var x = point[0],
21930         y = point[1],
21931         inside = false;
21932
21933     for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
21934         var xi = polygon[i][0], yi = polygon[i][1];
21935         var xj = polygon[j][0], yj = polygon[j][1];
21936
21937         var intersect = ((yi > y) !== (yj > y)) &&
21938             (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
21939         if (intersect) inside = !inside;
21940     }
21941
21942     return inside;
21943 };
21944
21945 iD.geo.polygonContainsPolygon = function(outer, inner) {
21946     return _.every(inner, function(point) {
21947         return iD.geo.pointInPolygon(point, outer);
21948     });
21949 };
21950
21951 iD.geo.polygonIntersectsPolygon = function(outer, inner, checkSegments) {
21952     function testSegments(outer, inner) {
21953         for (var i = 0; i < outer.length - 1; i++) {
21954             for (var j = 0; j < inner.length - 1; j++) {
21955                 var a = [ outer[i], outer[i+1] ],
21956                     b = [ inner[j], inner[j+1] ];
21957                 if (iD.geo.lineIntersection(a, b)) return true;
21958             }
21959         }
21960         return false;
21961     }
21962
21963     function testPoints(outer, inner) {
21964         return _.some(inner, function(point) {
21965             return iD.geo.pointInPolygon(point, outer);
21966         });
21967     }
21968
21969    return testPoints(outer, inner) || (!!checkSegments && testSegments(outer, inner));
21970 };
21971
21972 iD.geo.pathLength = function(path) {
21973     var length = 0,
21974         dx, dy;
21975     for (var i = 0; i < path.length - 1; i++) {
21976         dx = path[i][0] - path[i + 1][0];
21977         dy = path[i][1] - path[i + 1][1];
21978         length += Math.sqrt(dx * dx + dy * dy);
21979     }
21980     return length;
21981 };
21982 iD.geo.Extent = function geoExtent(min, max) {
21983     if (!(this instanceof iD.geo.Extent)) return new iD.geo.Extent(min, max);
21984     if (min instanceof iD.geo.Extent) {
21985         return min;
21986     } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
21987         this[0] = min[0];
21988         this[1] = min[1];
21989     } else {
21990         this[0] = min        || [ Infinity,  Infinity];
21991         this[1] = max || min || [-Infinity, -Infinity];
21992     }
21993 };
21994
21995 iD.geo.Extent.prototype = new Array(2);
21996
21997 _.extend(iD.geo.Extent.prototype, {
21998     equals: function (obj) {
21999         return this[0][0] === obj[0][0] &&
22000             this[0][1] === obj[0][1] &&
22001             this[1][0] === obj[1][0] &&
22002             this[1][1] === obj[1][1];
22003     },
22004
22005     extend: function(obj) {
22006         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
22007         return iD.geo.Extent([Math.min(obj[0][0], this[0][0]),
22008                               Math.min(obj[0][1], this[0][1])],
22009                              [Math.max(obj[1][0], this[1][0]),
22010                               Math.max(obj[1][1], this[1][1])]);
22011     },
22012
22013     _extend: function(extent) {
22014         this[0][0] = Math.min(extent[0][0], this[0][0]);
22015         this[0][1] = Math.min(extent[0][1], this[0][1]);
22016         this[1][0] = Math.max(extent[1][0], this[1][0]);
22017         this[1][1] = Math.max(extent[1][1], this[1][1]);
22018     },
22019
22020     area: function() {
22021         return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
22022     },
22023
22024     center: function() {
22025         return [(this[0][0] + this[1][0]) / 2,
22026                 (this[0][1] + this[1][1]) / 2];
22027     },
22028
22029     rectangle: function() {
22030         return [this[0][0], this[0][1], this[1][0], this[1][1]];
22031     },
22032
22033     polygon: function() {
22034         return [
22035             [this[0][0], this[0][1]],
22036             [this[0][0], this[1][1]],
22037             [this[1][0], this[1][1]],
22038             [this[1][0], this[0][1]],
22039             [this[0][0], this[0][1]]
22040         ];
22041     },
22042
22043     contains: function(obj) {
22044         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
22045         return obj[0][0] >= this[0][0] &&
22046                obj[0][1] >= this[0][1] &&
22047                obj[1][0] <= this[1][0] &&
22048                obj[1][1] <= this[1][1];
22049     },
22050
22051     intersects: function(obj) {
22052         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
22053         return obj[0][0] <= this[1][0] &&
22054                obj[0][1] <= this[1][1] &&
22055                obj[1][0] >= this[0][0] &&
22056                obj[1][1] >= this[0][1];
22057     },
22058
22059     intersection: function(obj) {
22060         if (!this.intersects(obj)) return new iD.geo.Extent();
22061         return new iD.geo.Extent([Math.max(obj[0][0], this[0][0]),
22062                                   Math.max(obj[0][1], this[0][1])],
22063                                  [Math.min(obj[1][0], this[1][0]),
22064                                   Math.min(obj[1][1], this[1][1])]);
22065     },
22066
22067     percentContainedIn: function(obj) {
22068         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
22069         var a1 = this.intersection(obj).area(),
22070             a2 = this.area();
22071
22072         if (a1 === Infinity || a2 === Infinity || a1 === 0 || a2 === 0) {
22073             return 0;
22074         } else {
22075             return a1 / a2;
22076         }
22077     },
22078
22079     padByMeters: function(meters) {
22080         var dLat = iD.geo.metersToLat(meters),
22081             dLon = iD.geo.metersToLon(meters, this.center()[1]);
22082         return iD.geo.Extent(
22083                 [this[0][0] - dLon, this[0][1] - dLat],
22084                 [this[1][0] + dLon, this[1][1] + dLat]);
22085     },
22086
22087     toParam: function() {
22088         return this.rectangle().join(',');
22089     }
22090
22091 });
22092 iD.geo.Turn = function(turn) {
22093     if (!(this instanceof iD.geo.Turn))
22094         return new iD.geo.Turn(turn);
22095     _.extend(this, turn);
22096 };
22097
22098 iD.geo.Intersection = function(graph, vertexId) {
22099     var vertex = graph.entity(vertexId),
22100         parentWays = graph.parentWays(vertex),
22101         coincident = [],
22102         highways = {};
22103
22104     function addHighway(way, adjacentNodeId) {
22105         if (highways[adjacentNodeId]) {
22106             coincident.push(adjacentNodeId);
22107         } else {
22108             highways[adjacentNodeId] = way;
22109         }
22110     }
22111
22112     // Pre-split ways that would need to be split in
22113     // order to add a restriction. The real split will
22114     // happen when the restriction is added.
22115     parentWays.forEach(function(way) {
22116         if (!way.tags.highway || way.isArea() || way.isDegenerate())
22117             return;
22118
22119         var isFirst = (vertexId === way.first()),
22120             isLast = (vertexId === way.last()),
22121             isAffix = (isFirst || isLast),
22122             isClosingNode = (isFirst && isLast);
22123
22124         if (isAffix && !isClosingNode) {
22125             var index = (isFirst ? 1 : way.nodes.length - 2);
22126             addHighway(way, way.nodes[index]);
22127
22128         } else {
22129             var splitIndex, wayA, wayB, indexA, indexB;
22130             if (isClosingNode) {
22131                 splitIndex = Math.ceil(way.nodes.length / 2);  // split at midpoint
22132                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
22133                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
22134                 indexA = 1;
22135                 indexB = way.nodes.length - 2;
22136             } else {
22137                 splitIndex = _.indexOf(way.nodes, vertex.id, 1);  // split at vertexid
22138                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
22139                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
22140                 indexA = splitIndex - 1;
22141                 indexB = splitIndex + 1;
22142             }
22143             graph = graph.replace(wayA).replace(wayB);
22144             addHighway(wayA, way.nodes[indexA]);
22145             addHighway(wayB, way.nodes[indexB]);
22146         }
22147     });
22148
22149     // remove any ways from this intersection that are coincident
22150     // (i.e. any adjacent node used by more than one intersecting way)
22151     coincident.forEach(function (n) {
22152         delete highways[n];
22153     });
22154
22155
22156     var intersection = {
22157         highways: highways,
22158         ways: _.values(highways),
22159         graph: graph
22160     };
22161
22162     intersection.adjacentNodeId = function(fromWayId) {
22163         return _.find(_.keys(highways), function(k) {
22164             return highways[k].id === fromWayId;
22165         });
22166     };
22167
22168     intersection.turns = function(fromNodeId) {
22169         var start = highways[fromNodeId];
22170         if (!start)
22171             return [];
22172
22173         if (start.first() === vertex.id && start.tags.oneway === 'yes')
22174             return [];
22175         if (start.last() === vertex.id && start.tags.oneway === '-1')
22176             return [];
22177
22178         function withRestriction(turn) {
22179             graph.parentRelations(graph.entity(turn.from.way)).forEach(function(relation) {
22180                 if (relation.tags.type !== 'restriction')
22181                     return;
22182
22183                 var f = relation.memberByRole('from'),
22184                     t = relation.memberByRole('to'),
22185                     v = relation.memberByRole('via');
22186
22187                 if (f && f.id === turn.from.way &&
22188                     v && v.id === turn.via.node &&
22189                     t && t.id === turn.to.way) {
22190                     turn.restriction = relation.id;
22191                 } else if (/^only_/.test(relation.tags.restriction) &&
22192                     f && f.id === turn.from.way &&
22193                     v && v.id === turn.via.node &&
22194                     t && t.id !== turn.to.way) {
22195                     turn.restriction = relation.id;
22196                     turn.indirect_restriction = true;
22197                 }
22198             });
22199
22200             return iD.geo.Turn(turn);
22201         }
22202
22203         var from = {
22204                 node: fromNodeId,
22205                 way: start.id.split(/-(a|b)/)[0]
22206             },
22207             via = { node: vertex.id },
22208             turns = [];
22209
22210         _.each(highways, function(end, adjacentNodeId) {
22211             if (end === start)
22212                 return;
22213
22214             // backward
22215             if (end.first() !== vertex.id && end.tags.oneway !== 'yes') {
22216                 turns.push(withRestriction({
22217                     from: from,
22218                     via: via,
22219                     to: {
22220                         node: adjacentNodeId,
22221                         way: end.id.split(/-(a|b)/)[0]
22222                     }
22223                 }));
22224             }
22225
22226             // forward
22227             if (end.last() !== vertex.id && end.tags.oneway !== '-1') {
22228                 turns.push(withRestriction({
22229                     from: from,
22230                     via: via,
22231                     to: {
22232                         node: adjacentNodeId,
22233                         way: end.id.split(/-(a|b)/)[0]
22234                     }
22235                 }));
22236             }
22237
22238         });
22239
22240         // U-turn
22241         if (start.tags.oneway !== 'yes' && start.tags.oneway !== '-1') {
22242             turns.push(withRestriction({
22243                 from: from,
22244                 via: via,
22245                 to: from,
22246                 u: true
22247             }));
22248         }
22249
22250         return turns;
22251     };
22252
22253     return intersection;
22254 };
22255
22256
22257 iD.geo.inferRestriction = function(graph, from, via, to, projection) {
22258     var fromWay = graph.entity(from.way),
22259         fromNode = graph.entity(from.node),
22260         toWay = graph.entity(to.way),
22261         toNode = graph.entity(to.node),
22262         viaNode = graph.entity(via.node),
22263         fromOneWay = (fromWay.tags.oneway === 'yes' && fromWay.last() === via.node) ||
22264             (fromWay.tags.oneway === '-1' && fromWay.first() === via.node),
22265         toOneWay = (toWay.tags.oneway === 'yes' && toWay.first() === via.node) ||
22266             (toWay.tags.oneway === '-1' && toWay.last() === via.node),
22267         angle = iD.geo.angle(viaNode, fromNode, projection) -
22268                 iD.geo.angle(viaNode, toNode, projection);
22269
22270     angle = angle * 180 / Math.PI;
22271
22272     while (angle < 0)
22273         angle += 360;
22274
22275     if (fromNode === toNode)
22276         return 'no_u_turn';
22277     if ((angle < 23 || angle > 336) && fromOneWay && toOneWay)
22278         return 'no_u_turn';
22279     if (angle < 158)
22280         return 'no_right_turn';
22281     if (angle > 202)
22282         return 'no_left_turn';
22283
22284     return 'no_straight_on';
22285 };
22286 // For fixing up rendering of multipolygons with tags on the outer member.
22287 // https://github.com/openstreetmap/iD/issues/613
22288 iD.geo.isSimpleMultipolygonOuterMember = function(entity, graph) {
22289     if (entity.type !== 'way')
22290         return false;
22291
22292     var parents = graph.parentRelations(entity);
22293     if (parents.length !== 1)
22294         return false;
22295
22296     var parent = parents[0];
22297     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
22298         return false;
22299
22300     var members = parent.members, member;
22301     for (var i = 0; i < members.length; i++) {
22302         member = members[i];
22303         if (member.id === entity.id && member.role && member.role !== 'outer')
22304             return false; // Not outer member
22305         if (member.id !== entity.id && (!member.role || member.role === 'outer'))
22306             return false; // Not a simple multipolygon
22307     }
22308
22309     return parent;
22310 };
22311
22312 iD.geo.simpleMultipolygonOuterMember = function(entity, graph) {
22313     if (entity.type !== 'way')
22314         return false;
22315
22316     var parents = graph.parentRelations(entity);
22317     if (parents.length !== 1)
22318         return false;
22319
22320     var parent = parents[0];
22321     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
22322         return false;
22323
22324     var members = parent.members, member, outerMember;
22325     for (var i = 0; i < members.length; i++) {
22326         member = members[i];
22327         if (!member.role || member.role === 'outer') {
22328             if (outerMember)
22329                 return false; // Not a simple multipolygon
22330             outerMember = member;
22331         }
22332     }
22333
22334     return outerMember && graph.hasEntity(outerMember.id);
22335 };
22336
22337 // Join `array` into sequences of connecting ways.
22338 //
22339 // Segments which share identical start/end nodes will, as much as possible,
22340 // be connected with each other.
22341 //
22342 // The return value is a nested array. Each constituent array contains elements
22343 // of `array` which have been determined to connect. Each consitituent array
22344 // also has a `nodes` property whose value is an ordered array of member nodes,
22345 // with appropriate order reversal and start/end coordinate de-duplication.
22346 //
22347 // Members of `array` must have, at minimum, `type` and `id` properties.
22348 // Thus either an array of `iD.Way`s or a relation member array may be
22349 // used.
22350 //
22351 // If an member has a `tags` property, its tags will be reversed via
22352 // `iD.actions.Reverse` in the output.
22353 //
22354 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
22355 // false) and non-way members are ignored.
22356 //
22357 iD.geo.joinWays = function(array, graph) {
22358     var joined = [], member, current, nodes, first, last, i, how, what;
22359
22360     array = array.filter(function(member) {
22361         return member.type === 'way' && graph.hasEntity(member.id);
22362     });
22363
22364     function resolve(member) {
22365         return graph.childNodes(graph.entity(member.id));
22366     }
22367
22368     function reverse(member) {
22369         return member.tags ? iD.actions.Reverse(member.id, {reverseOneway: true})(graph).entity(member.id) : member;
22370     }
22371
22372     while (array.length) {
22373         member = array.shift();
22374         current = [member];
22375         current.nodes = nodes = resolve(member).slice();
22376         joined.push(current);
22377
22378         while (array.length && _.first(nodes) !== _.last(nodes)) {
22379             first = _.first(nodes);
22380             last  = _.last(nodes);
22381
22382             for (i = 0; i < array.length; i++) {
22383                 member = array[i];
22384                 what = resolve(member);
22385
22386                 if (last === _.first(what)) {
22387                     how  = nodes.push;
22388                     what = what.slice(1);
22389                     break;
22390                 } else if (last === _.last(what)) {
22391                     how  = nodes.push;
22392                     what = what.slice(0, -1).reverse();
22393                     member = reverse(member);
22394                     break;
22395                 } else if (first === _.last(what)) {
22396                     how  = nodes.unshift;
22397                     what = what.slice(0, -1);
22398                     break;
22399                 } else if (first === _.first(what)) {
22400                     how  = nodes.unshift;
22401                     what = what.slice(1).reverse();
22402                     member = reverse(member);
22403                     break;
22404                 } else {
22405                     what = how = null;
22406                 }
22407             }
22408
22409             if (!what)
22410                 break; // No more joinable ways.
22411
22412             how.apply(current, [member]);
22413             how.apply(nodes, what);
22414
22415             array.splice(i, 1);
22416         }
22417     }
22418
22419     return joined;
22420 };
22421 /*
22422     Bypasses features of D3's default projection stream pipeline that are unnecessary:
22423     * Antimeridian clipping
22424     * Spherical rotation
22425     * Resampling
22426 */
22427 iD.geo.RawMercator = function () {
22428     var project = d3.geo.mercator.raw,
22429         k = 512 / Math.PI, // scale
22430         x = 0, y = 0, // translate
22431         clipExtent = [[0, 0], [0, 0]];
22432
22433     function projection(point) {
22434         point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
22435         return [point[0] * k + x, y - point[1] * k];
22436     }
22437
22438     projection.invert = function(point) {
22439         point = project.invert((point[0] - x) / k, (y - point[1]) / k);
22440         return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
22441     };
22442
22443     projection.scale = function(_) {
22444         if (!arguments.length) return k;
22445         k = +_;
22446         return projection;
22447     };
22448
22449     projection.translate = function(_) {
22450         if (!arguments.length) return [x, y];
22451         x = +_[0];
22452         y = +_[1];
22453         return projection;
22454     };
22455
22456     projection.clipExtent = function(_) {
22457         if (!arguments.length) return clipExtent;
22458         clipExtent = _;
22459         return projection;
22460     };
22461
22462     projection.stream = d3.geo.transform({
22463         point: function(x, y) {
22464             x = projection([x, y]);
22465             this.stream.point(x[0], x[1]);
22466         }
22467     }).stream;
22468
22469     return projection;
22470 };
22471 iD.actions = {};
22472 iD.actions.AddEntity = function(way) {
22473     return function(graph) {
22474         return graph.replace(way);
22475     };
22476 };
22477 iD.actions.AddMember = function(relationId, member, memberIndex) {
22478     return function(graph) {
22479         var relation = graph.entity(relationId);
22480
22481         if (isNaN(memberIndex) && member.type === 'way') {
22482             var members = relation.indexedMembers();
22483             members.push(member);
22484
22485             var joined = iD.geo.joinWays(members, graph);
22486             for (var i = 0; i < joined.length; i++) {
22487                 var segment = joined[i];
22488                 for (var j = 0; j < segment.length && segment.length >= 2; j++) {
22489                     if (segment[j] !== member)
22490                         continue;
22491
22492                     if (j === 0) {
22493                         memberIndex = segment[j + 1].index;
22494                     } else if (j === segment.length - 1) {
22495                         memberIndex = segment[j - 1].index + 1;
22496                     } else {
22497                         memberIndex = Math.min(segment[j - 1].index + 1, segment[j + 1].index + 1);
22498                     }
22499                 }
22500             }
22501         }
22502
22503         return graph.replace(relation.addMember(member, memberIndex));
22504     };
22505 };
22506 iD.actions.AddMidpoint = function(midpoint, node) {
22507     return function(graph) {
22508         graph = graph.replace(node.move(midpoint.loc));
22509
22510         var parents = _.intersection(
22511             graph.parentWays(graph.entity(midpoint.edge[0])),
22512             graph.parentWays(graph.entity(midpoint.edge[1])));
22513
22514         parents.forEach(function(way) {
22515             for (var i = 0; i < way.nodes.length - 1; i++) {
22516                 if (iD.geo.edgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
22517                     graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
22518
22519                     // Add only one midpoint on doubled-back segments,
22520                     // turning them into self-intersections.
22521                     return;
22522                 }
22523             }
22524         });
22525
22526         return graph;
22527     };
22528 };
22529 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
22530 iD.actions.AddVertex = function(wayId, nodeId, index) {
22531     return function(graph) {
22532         return graph.replace(graph.entity(wayId).addNode(nodeId, index));
22533     };
22534 };
22535 iD.actions.ChangeMember = function(relationId, member, memberIndex) {
22536     return function(graph) {
22537         return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
22538     };
22539 };
22540 iD.actions.ChangePreset = function(entityId, oldPreset, newPreset) {
22541     return function(graph) {
22542         var entity = graph.entity(entityId),
22543             geometry = entity.geometry(graph),
22544             tags = entity.tags;
22545
22546         if (oldPreset) tags = oldPreset.removeTags(tags, geometry);
22547         if (newPreset) tags = newPreset.applyTags(tags, geometry);
22548
22549         return graph.replace(entity.update({tags: tags}));
22550     };
22551 };
22552 iD.actions.ChangeTags = function(entityId, tags) {
22553     return function(graph) {
22554         var entity = graph.entity(entityId);
22555         return graph.replace(entity.update({tags: tags}));
22556     };
22557 };
22558 iD.actions.Circularize = function(wayId, projection, maxAngle) {
22559     maxAngle = (maxAngle || 20) * Math.PI / 180;
22560
22561     var action = function(graph) {
22562         var way = graph.entity(wayId);
22563
22564         if (!way.isConvex(graph)) {
22565             graph = action.makeConvex(graph);
22566         }
22567
22568         var nodes = _.uniq(graph.childNodes(way)),
22569             keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }),
22570             points = nodes.map(function(n) { return projection(n.loc); }),
22571             keyPoints = keyNodes.map(function(n) { return projection(n.loc); }),
22572             centroid = (points.length === 2) ? iD.geo.interp(points[0], points[1], 0.5) : d3.geom.polygon(points).centroid(),
22573             radius = d3.median(points, function(p) { return iD.geo.euclideanDistance(centroid, p); }),
22574             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
22575             ids;
22576
22577         // we need atleast two key nodes for the algorithm to work
22578         if (!keyNodes.length) {
22579             keyNodes = [nodes[0]];
22580             keyPoints = [points[0]];
22581         }
22582
22583         if (keyNodes.length === 1) {
22584             var index = nodes.indexOf(keyNodes[0]),
22585                 oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
22586
22587             keyNodes.push(nodes[oppositeIndex]);
22588             keyPoints.push(points[oppositeIndex]);
22589         }
22590
22591         // key points and nodes are those connected to the ways,
22592         // they are projected onto the circle, inbetween nodes are moved
22593         // to constant intervals between key nodes, extra inbetween nodes are
22594         // added if necessary.
22595         for (var i = 0; i < keyPoints.length; i++) {
22596             var nextKeyNodeIndex = (i + 1) % keyNodes.length,
22597                 startNode = keyNodes[i],
22598                 endNode = keyNodes[nextKeyNodeIndex],
22599                 startNodeIndex = nodes.indexOf(startNode),
22600                 endNodeIndex = nodes.indexOf(endNode),
22601                 numberNewPoints = -1,
22602                 indexRange = endNodeIndex - startNodeIndex,
22603                 distance, totalAngle, eachAngle, startAngle, endAngle,
22604                 angle, loc, node, j,
22605                 inBetweenNodes = [];
22606
22607             if (indexRange < 0) {
22608                 indexRange += nodes.length;
22609             }
22610
22611             // position this key node
22612             distance = iD.geo.euclideanDistance(centroid, keyPoints[i]);
22613             if (distance === 0) { distance = 1e-4; }
22614             keyPoints[i] = [
22615                 centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
22616                 centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
22617             graph = graph.replace(keyNodes[i].move(projection.invert(keyPoints[i])));
22618
22619             // figure out the between delta angle we want to match to
22620             startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
22621             endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
22622             totalAngle = endAngle - startAngle;
22623
22624             // detects looping around -pi/pi
22625             if (totalAngle * sign > 0) {
22626                 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
22627             }
22628
22629             do {
22630                 numberNewPoints++;
22631                 eachAngle = totalAngle / (indexRange + numberNewPoints);
22632             } while (Math.abs(eachAngle) > maxAngle);
22633
22634             // move existing points
22635             for (j = 1; j < indexRange; j++) {
22636                 angle = startAngle + j * eachAngle;
22637                 loc = projection.invert([
22638                     centroid[0] + Math.cos(angle)*radius,
22639                     centroid[1] + Math.sin(angle)*radius]);
22640
22641                 node = nodes[(j + startNodeIndex) % nodes.length].move(loc);
22642                 graph = graph.replace(node);
22643             }
22644
22645             // add new inbetween nodes if necessary
22646             for (j = 0; j < numberNewPoints; j++) {
22647                 angle = startAngle + (indexRange + j) * eachAngle;
22648                 loc = projection.invert([
22649                     centroid[0] + Math.cos(angle) * radius,
22650                     centroid[1] + Math.sin(angle) * radius]);
22651
22652                 node = iD.Node({loc: loc});
22653                 graph = graph.replace(node);
22654
22655                 nodes.splice(endNodeIndex + j, 0, node);
22656                 inBetweenNodes.push(node.id);
22657             }
22658
22659             // Check for other ways that share these keyNodes..
22660             // If keyNodes are adjacent in both ways,
22661             // we can add inBetween nodes to that shared way too..
22662             if (indexRange === 1 && inBetweenNodes.length) {
22663                 var startIndex1 = way.nodes.lastIndexOf(startNode.id),
22664                     endIndex1 = way.nodes.lastIndexOf(endNode.id),
22665                     wayDirection1 = (endIndex1 - startIndex1);
22666                 if (wayDirection1 < -1) { wayDirection1 = 1; }
22667
22668                 /* eslint-disable no-loop-func */
22669                 _.each(_.without(graph.parentWays(keyNodes[i]), way), function(sharedWay) {
22670                     if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
22671                         var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id),
22672                             endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id),
22673                             wayDirection2 = (endIndex2 - startIndex2),
22674                             insertAt = endIndex2;
22675                         if (wayDirection2 < -1) { wayDirection2 = 1; }
22676
22677                         if (wayDirection1 !== wayDirection2) {
22678                             inBetweenNodes.reverse();
22679                             insertAt = startIndex2;
22680                         }
22681                         for (j = 0; j < inBetweenNodes.length; j++) {
22682                             sharedWay = sharedWay.addNode(inBetweenNodes[j], insertAt + j);
22683                         }
22684                         graph = graph.replace(sharedWay);
22685                     }
22686                 });
22687                 /* eslint-enable no-loop-func */
22688             }
22689
22690         }
22691
22692         // update the way to have all the new nodes
22693         ids = nodes.map(function(n) { return n.id; });
22694         ids.push(ids[0]);
22695
22696         way = way.update({nodes: ids});
22697         graph = graph.replace(way);
22698
22699         return graph;
22700     };
22701
22702     action.makeConvex = function(graph) {
22703         var way = graph.entity(wayId),
22704             nodes = _.uniq(graph.childNodes(way)),
22705             points = nodes.map(function(n) { return projection(n.loc); }),
22706             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
22707             hull = d3.geom.hull(points);
22708
22709         // D3 convex hulls go counterclockwise..
22710         if (sign === -1) {
22711             nodes.reverse();
22712             points.reverse();
22713         }
22714
22715         for (var i = 0; i < hull.length - 1; i++) {
22716             var startIndex = points.indexOf(hull[i]),
22717                 endIndex = points.indexOf(hull[i+1]),
22718                 indexRange = (endIndex - startIndex);
22719
22720             if (indexRange < 0) {
22721                 indexRange += nodes.length;
22722             }
22723
22724             // move interior nodes to the surface of the convex hull..
22725             for (var j = 1; j < indexRange; j++) {
22726                 var point = iD.geo.interp(hull[i], hull[i+1], j / indexRange),
22727                     node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
22728                 graph = graph.replace(node);
22729             }
22730         }
22731         return graph;
22732     };
22733
22734     action.disabled = function(graph) {
22735         if (!graph.entity(wayId).isClosed())
22736             return 'not_closed';
22737     };
22738
22739     return action;
22740 };
22741 // Connect the ways at the given nodes.
22742 //
22743 // The last node will survive. All other nodes will be replaced with
22744 // the surviving node in parent ways, and then removed.
22745 //
22746 // Tags and relation memberships of of non-surviving nodes are merged
22747 // to the survivor.
22748 //
22749 // This is the inverse of `iD.actions.Disconnect`.
22750 //
22751 // Reference:
22752 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
22753 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
22754 //
22755 iD.actions.Connect = function(nodeIds) {
22756     return function(graph) {
22757         var survivor = graph.entity(_.last(nodeIds));
22758
22759         for (var i = 0; i < nodeIds.length - 1; i++) {
22760             var node = graph.entity(nodeIds[i]);
22761
22762             /* eslint-disable no-loop-func */
22763             graph.parentWays(node).forEach(function(parent) {
22764                 if (!parent.areAdjacent(node.id, survivor.id)) {
22765                     graph = graph.replace(parent.replaceNode(node.id, survivor.id));
22766                 }
22767             });
22768
22769             graph.parentRelations(node).forEach(function(parent) {
22770                 graph = graph.replace(parent.replaceMember(node, survivor));
22771             });
22772             /* eslint-enable no-loop-func */
22773
22774             survivor = survivor.mergeTags(node.tags);
22775             graph = iD.actions.DeleteNode(node.id)(graph);
22776         }
22777
22778         graph = graph.replace(survivor);
22779
22780         return graph;
22781     };
22782 };
22783 iD.actions.CopyEntities = function(ids, fromGraph) {
22784     var copies = {};
22785
22786     var action = function(graph) {
22787         ids.forEach(function(id) {
22788             fromGraph.entity(id).copy(fromGraph, copies);
22789         });
22790
22791         for (var id in copies) {
22792             graph = graph.replace(copies[id]);
22793         }
22794
22795         return graph;
22796     };
22797
22798     action.copies = function() {
22799         return copies;
22800     };
22801
22802     return action;
22803 };
22804 iD.actions.DeleteMember = function(relationId, memberIndex) {
22805     return function(graph) {
22806         var relation = graph.entity(relationId)
22807             .removeMember(memberIndex);
22808
22809         graph = graph.replace(relation);
22810
22811         if (relation.isDegenerate())
22812             graph = iD.actions.DeleteRelation(relation.id)(graph);
22813
22814         return graph;
22815     };
22816 };
22817 iD.actions.DeleteMultiple = function(ids) {
22818     var actions = {
22819         way: iD.actions.DeleteWay,
22820         node: iD.actions.DeleteNode,
22821         relation: iD.actions.DeleteRelation
22822     };
22823
22824     var action = function(graph) {
22825         ids.forEach(function(id) {
22826             if (graph.hasEntity(id)) { // It may have been deleted aready.
22827                 graph = actions[graph.entity(id).type](id)(graph);
22828             }
22829         });
22830
22831         return graph;
22832     };
22833
22834     action.disabled = function(graph) {
22835         for (var i = 0; i < ids.length; i++) {
22836             var id = ids[i],
22837                 disabled = actions[graph.entity(id).type](id).disabled(graph);
22838             if (disabled) return disabled;
22839         }
22840     };
22841
22842     return action;
22843 };
22844 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
22845 iD.actions.DeleteNode = function(nodeId) {
22846     var action = function(graph) {
22847         var node = graph.entity(nodeId);
22848
22849         graph.parentWays(node)
22850             .forEach(function(parent) {
22851                 parent = parent.removeNode(nodeId);
22852                 graph = graph.replace(parent);
22853
22854                 if (parent.isDegenerate()) {
22855                     graph = iD.actions.DeleteWay(parent.id)(graph);
22856                 }
22857             });
22858
22859         graph.parentRelations(node)
22860             .forEach(function(parent) {
22861                 parent = parent.removeMembersWithID(nodeId);
22862                 graph = graph.replace(parent);
22863
22864                 if (parent.isDegenerate()) {
22865                     graph = iD.actions.DeleteRelation(parent.id)(graph);
22866                 }
22867             });
22868
22869         return graph.remove(node);
22870     };
22871
22872     action.disabled = function() {
22873         return false;
22874     };
22875
22876     return action;
22877 };
22878 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
22879 iD.actions.DeleteRelation = function(relationId) {
22880     function deleteEntity(entity, graph) {
22881         return !graph.parentWays(entity).length &&
22882             !graph.parentRelations(entity).length &&
22883             !entity.hasInterestingTags();
22884     }
22885
22886     var action = function(graph) {
22887         var relation = graph.entity(relationId);
22888
22889         graph.parentRelations(relation)
22890             .forEach(function(parent) {
22891                 parent = parent.removeMembersWithID(relationId);
22892                 graph = graph.replace(parent);
22893
22894                 if (parent.isDegenerate()) {
22895                     graph = iD.actions.DeleteRelation(parent.id)(graph);
22896                 }
22897             });
22898
22899         _.uniq(_.map(relation.members, 'id')).forEach(function(memberId) {
22900             graph = graph.replace(relation.removeMembersWithID(memberId));
22901
22902             var entity = graph.entity(memberId);
22903             if (deleteEntity(entity, graph)) {
22904                 graph = iD.actions.DeleteMultiple([memberId])(graph);
22905             }
22906         });
22907
22908         return graph.remove(relation);
22909     };
22910
22911     action.disabled = function(graph) {
22912         if (!graph.entity(relationId).isComplete(graph))
22913             return 'incomplete_relation';
22914     };
22915
22916     return action;
22917 };
22918 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
22919 iD.actions.DeleteWay = function(wayId) {
22920     function deleteNode(node, graph) {
22921         return !graph.parentWays(node).length &&
22922             !graph.parentRelations(node).length &&
22923             !node.hasInterestingTags();
22924     }
22925
22926     var action = function(graph) {
22927         var way = graph.entity(wayId);
22928
22929         graph.parentRelations(way)
22930             .forEach(function(parent) {
22931                 parent = parent.removeMembersWithID(wayId);
22932                 graph = graph.replace(parent);
22933
22934                 if (parent.isDegenerate()) {
22935                     graph = iD.actions.DeleteRelation(parent.id)(graph);
22936                 }
22937             });
22938
22939         _.uniq(way.nodes).forEach(function(nodeId) {
22940             graph = graph.replace(way.removeNode(nodeId));
22941
22942             var node = graph.entity(nodeId);
22943             if (deleteNode(node, graph)) {
22944                 graph = graph.remove(node);
22945             }
22946         });
22947
22948         return graph.remove(way);
22949     };
22950
22951     action.disabled = function(graph) {
22952         var disabled = false;
22953
22954         graph.parentRelations(graph.entity(wayId)).forEach(function(parent) {
22955             var type = parent.tags.type,
22956                 role = parent.memberById(wayId).role || 'outer';
22957             if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
22958                 disabled = 'part_of_relation';
22959             }
22960         });
22961
22962         return disabled;
22963     };
22964
22965     return action;
22966 };
22967 iD.actions.DeprecateTags = function(entityId) {
22968     return function(graph) {
22969         var entity = graph.entity(entityId),
22970             newtags = _.clone(entity.tags),
22971             change = false,
22972             rule;
22973
22974         // This handles deprecated tags with a single condition
22975         for (var i = 0; i < iD.data.deprecated.length; i++) {
22976
22977             rule = iD.data.deprecated[i];
22978             var match = _.toPairs(rule.old)[0],
22979                 replacements = rule.replace ? _.toPairs(rule.replace) : null;
22980
22981             if (entity.tags[match[0]] && match[1] === '*') {
22982
22983                 var value = entity.tags[match[0]];
22984                 if (replacements && !newtags[replacements[0][0]]) {
22985                     newtags[replacements[0][0]] = value;
22986                 }
22987                 delete newtags[match[0]];
22988                 change = true;
22989
22990             } else if (entity.tags[match[0]] === match[1]) {
22991                 newtags = _.assign({}, rule.replace || {}, _.omit(newtags, match[0]));
22992                 change = true;
22993             }
22994         }
22995
22996         if (change) {
22997             return graph.replace(entity.update({tags: newtags}));
22998         } else {
22999             return graph;
23000         }
23001     };
23002 };
23003 iD.actions.DiscardTags = function(difference) {
23004     return function(graph) {
23005         function discardTags(entity) {
23006             if (!_.isEmpty(entity.tags)) {
23007                 var tags = {};
23008                 _.each(entity.tags, function(v, k) {
23009                     if (v) tags[k] = v;
23010                 });
23011
23012                 graph = graph.replace(entity.update({
23013                     tags: _.omit(tags, iD.data.discarded)
23014                 }));
23015             }
23016         }
23017
23018         difference.modified().forEach(discardTags);
23019         difference.created().forEach(discardTags);
23020
23021         return graph;
23022     };
23023 };
23024 // Disconect the ways at the given node.
23025 //
23026 // Optionally, disconnect only the given ways.
23027 //
23028 // For testing convenience, accepts an ID to assign to the (first) new node.
23029 // Normally, this will be undefined and the way will automatically
23030 // be assigned a new ID.
23031 //
23032 // This is the inverse of `iD.actions.Connect`.
23033 //
23034 // Reference:
23035 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
23036 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
23037 //
23038 iD.actions.Disconnect = function(nodeId, newNodeId) {
23039     var wayIds;
23040
23041     var action = function(graph) {
23042         var node = graph.entity(nodeId),
23043             connections = action.connections(graph);
23044
23045         connections.forEach(function(connection) {
23046             var way = graph.entity(connection.wayID),
23047                 newNode = iD.Node({id: newNodeId, loc: node.loc, tags: node.tags});
23048
23049             graph = graph.replace(newNode);
23050             if (connection.index === 0 && way.isArea()) {
23051                 // replace shared node with shared node..
23052                 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
23053             } else {
23054                 // replace shared node with multiple new nodes..
23055                 graph = graph.replace(way.updateNode(newNode.id, connection.index));
23056             }
23057         });
23058
23059         return graph;
23060     };
23061
23062     action.connections = function(graph) {
23063         var candidates = [],
23064             keeping = false,
23065             parentWays = graph.parentWays(graph.entity(nodeId));
23066
23067         parentWays.forEach(function(way) {
23068             if (wayIds && wayIds.indexOf(way.id) === -1) {
23069                 keeping = true;
23070                 return;
23071             }
23072             if (way.isArea() && (way.nodes[0] === nodeId)) {
23073                 candidates.push({wayID: way.id, index: 0});
23074             } else {
23075                 way.nodes.forEach(function(waynode, index) {
23076                     if (waynode === nodeId) {
23077                         candidates.push({wayID: way.id, index: index});
23078                     }
23079                 });
23080             }
23081         });
23082
23083         return keeping ? candidates : candidates.slice(1);
23084     };
23085
23086     action.disabled = function(graph) {
23087         var connections = action.connections(graph);
23088         if (connections.length === 0 || (wayIds && wayIds.length !== connections.length))
23089             return 'not_connected';
23090
23091         var parentWays = graph.parentWays(graph.entity(nodeId)),
23092             seenRelationIds = {},
23093             sharedRelation;
23094
23095         parentWays.forEach(function(way) {
23096             if (wayIds && wayIds.indexOf(way.id) === -1)
23097                 return;
23098
23099             var relations = graph.parentRelations(way);
23100             relations.forEach(function(relation) {
23101                 if (relation.id in seenRelationIds) {
23102                     sharedRelation = relation;
23103                 } else {
23104                     seenRelationIds[relation.id] = true;
23105                 }
23106             });
23107         });
23108
23109         if (sharedRelation)
23110             return 'relation';
23111     };
23112
23113     action.limitWays = function(_) {
23114         if (!arguments.length) return wayIds;
23115         wayIds = _;
23116         return action;
23117     };
23118
23119     return action;
23120 };
23121 // Join ways at the end node they share.
23122 //
23123 // This is the inverse of `iD.actions.Split`.
23124 //
23125 // Reference:
23126 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
23127 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
23128 //
23129 iD.actions.Join = function(ids) {
23130
23131     function groupEntitiesByGeometry(graph) {
23132         var entities = ids.map(function(id) { return graph.entity(id); });
23133         return _.extend({line: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
23134     }
23135
23136     var action = function(graph) {
23137         var ways = ids.map(graph.entity, graph),
23138             survivor = ways[0];
23139
23140         // Prefer to keep an existing way.
23141         for (var i = 0; i < ways.length; i++) {
23142             if (!ways[i].isNew()) {
23143                 survivor = ways[i];
23144                 break;
23145             }
23146         }
23147
23148         var joined = iD.geo.joinWays(ways, graph)[0];
23149
23150         survivor = survivor.update({nodes: _.map(joined.nodes, 'id')});
23151         graph = graph.replace(survivor);
23152
23153         joined.forEach(function(way) {
23154             if (way.id === survivor.id)
23155                 return;
23156
23157             graph.parentRelations(way).forEach(function(parent) {
23158                 graph = graph.replace(parent.replaceMember(way, survivor));
23159             });
23160
23161             survivor = survivor.mergeTags(way.tags);
23162
23163             graph = graph.replace(survivor);
23164             graph = iD.actions.DeleteWay(way.id)(graph);
23165         });
23166
23167         return graph;
23168     };
23169
23170     action.disabled = function(graph) {
23171         var geometries = groupEntitiesByGeometry(graph);
23172         if (ids.length < 2 || ids.length !== geometries.line.length)
23173             return 'not_eligible';
23174
23175         var joined = iD.geo.joinWays(ids.map(graph.entity, graph), graph);
23176         if (joined.length > 1)
23177             return 'not_adjacent';
23178
23179         var nodeIds = _.map(joined[0].nodes, 'id').slice(1, -1),
23180             relation,
23181             tags = {},
23182             conflicting = false;
23183
23184         joined[0].forEach(function(way) {
23185             var parents = graph.parentRelations(way);
23186             parents.forEach(function(parent) {
23187                 if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; }))
23188                     relation = parent;
23189             });
23190
23191             for (var k in way.tags) {
23192                 if (!(k in tags)) {
23193                     tags[k] = way.tags[k];
23194                 } else if (tags[k] && iD.interestingTag(k) && tags[k] !== way.tags[k]) {
23195                     conflicting = true;
23196                 }
23197             }
23198         });
23199
23200         if (relation)
23201             return 'restriction';
23202
23203         if (conflicting)
23204             return 'conflicting_tags';
23205     };
23206
23207     return action;
23208 };
23209 iD.actions.Merge = function(ids) {
23210     function groupEntitiesByGeometry(graph) {
23211         var entities = ids.map(function(id) { return graph.entity(id); });
23212         return _.extend({point: [], area: [], line: [], relation: []},
23213             _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
23214     }
23215
23216     var action = function(graph) {
23217         var geometries = groupEntitiesByGeometry(graph),
23218             target = geometries.area[0] || geometries.line[0],
23219             points = geometries.point;
23220
23221         points.forEach(function(point) {
23222             target = target.mergeTags(point.tags);
23223
23224             graph.parentRelations(point).forEach(function(parent) {
23225                 graph = graph.replace(parent.replaceMember(point, target));
23226             });
23227
23228             graph = graph.remove(point);
23229         });
23230
23231         graph = graph.replace(target);
23232
23233         return graph;
23234     };
23235
23236     action.disabled = function(graph) {
23237         var geometries = groupEntitiesByGeometry(graph);
23238         if (geometries.point.length === 0 ||
23239             (geometries.area.length + geometries.line.length) !== 1 ||
23240             geometries.relation.length !== 0)
23241             return 'not_eligible';
23242     };
23243
23244     return action;
23245 };
23246 iD.actions.MergePolygon = function(ids, newRelationId) {
23247
23248     function groupEntities(graph) {
23249         var entities = ids.map(function (id) { return graph.entity(id); });
23250         return _.extend({
23251                 closedWay: [],
23252                 multipolygon: [],
23253                 other: []
23254             }, _.groupBy(entities, function(entity) {
23255                 if (entity.type === 'way' && entity.isClosed()) {
23256                     return 'closedWay';
23257                 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
23258                     return 'multipolygon';
23259                 } else {
23260                     return 'other';
23261                 }
23262             }));
23263     }
23264
23265     var action = function(graph) {
23266         var entities = groupEntities(graph);
23267
23268         // An array representing all the polygons that are part of the multipolygon.
23269         //
23270         // Each element is itself an array of objects with an id property, and has a
23271         // locs property which is an array of the locations forming the polygon.
23272         var polygons = entities.multipolygon.reduce(function(polygons, m) {
23273             return polygons.concat(iD.geo.joinWays(m.members, graph));
23274         }, []).concat(entities.closedWay.map(function(d) {
23275             var member = [{id: d.id}];
23276             member.nodes = graph.childNodes(d);
23277             return member;
23278         }));
23279
23280         // contained is an array of arrays of boolean values,
23281         // where contained[j][k] is true iff the jth way is
23282         // contained by the kth way.
23283         var contained = polygons.map(function(w, i) {
23284             return polygons.map(function(d, n) {
23285                 if (i === n) return null;
23286                 return iD.geo.polygonContainsPolygon(
23287                     _.map(d.nodes, 'loc'),
23288                     _.map(w.nodes, 'loc'));
23289             });
23290         });
23291
23292         // Sort all polygons as either outer or inner ways
23293         var members = [],
23294             outer = true;
23295
23296         while (polygons.length) {
23297             extractUncontained(polygons);
23298             polygons = polygons.filter(isContained);
23299             contained = contained.filter(isContained).map(filterContained);
23300         }
23301
23302         function isContained(d, i) {
23303             return _.some(contained[i]);
23304         }
23305
23306         function filterContained(d) {
23307             return d.filter(isContained);
23308         }
23309
23310         function extractUncontained(polygons) {
23311             polygons.forEach(function(d, i) {
23312                 if (!isContained(d, i)) {
23313                     d.forEach(function(member) {
23314                         members.push({
23315                             type: 'way',
23316                             id: member.id,
23317                             role: outer ? 'outer' : 'inner'
23318                         });
23319                     });
23320                 }
23321             });
23322             outer = !outer;
23323         }
23324
23325         // Move all tags to one relation
23326         var relation = entities.multipolygon[0] ||
23327             iD.Relation({ id: newRelationId, tags: { type: 'multipolygon' }});
23328
23329         entities.multipolygon.slice(1).forEach(function(m) {
23330             relation = relation.mergeTags(m.tags);
23331             graph = graph.remove(m);
23332         });
23333
23334         entities.closedWay.forEach(function(way) {
23335             function isThisOuter(m) {
23336                 return m.id === way.id && m.role !== 'inner';
23337             }
23338             if (members.some(isThisOuter)) {
23339                 relation = relation.mergeTags(way.tags);
23340                 graph = graph.replace(way.update({ tags: {} }));
23341             }
23342         });
23343
23344         return graph.replace(relation.update({
23345             members: members,
23346             tags: _.omit(relation.tags, 'area')
23347         }));
23348     };
23349
23350     action.disabled = function(graph) {
23351         var entities = groupEntities(graph);
23352         if (entities.other.length > 0 ||
23353             entities.closedWay.length + entities.multipolygon.length < 2)
23354             return 'not_eligible';
23355         if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); }))
23356             return 'incomplete_relation';
23357     };
23358
23359     return action;
23360 };
23361 iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser) {
23362     var option = 'safe',  // 'safe', 'force_local', 'force_remote'
23363         conflicts = [];
23364
23365     function user(d) {
23366         return _.isFunction(formatUser) ? formatUser(d) : d;
23367     }
23368
23369
23370     function mergeLocation(remote, target) {
23371         function pointEqual(a, b) {
23372             var epsilon = 1e-6;
23373             return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon);
23374         }
23375
23376         if (option === 'force_local' || pointEqual(target.loc, remote.loc)) {
23377             return target;
23378         }
23379         if (option === 'force_remote') {
23380             return target.update({loc: remote.loc});
23381         }
23382
23383         conflicts.push(t('merge_remote_changes.conflict.location', { user: user(remote.user) }));
23384         return target;
23385     }
23386
23387
23388     function mergeNodes(base, remote, target) {
23389         if (option === 'force_local' || _.isEqual(target.nodes, remote.nodes)) {
23390             return target;
23391         }
23392         if (option === 'force_remote') {
23393             return target.update({nodes: remote.nodes});
23394         }
23395
23396         var ccount = conflicts.length,
23397             o = base.nodes || [],
23398             a = target.nodes || [],
23399             b = remote.nodes || [],
23400             nodes = [],
23401             hunks = Diff3.diff3_merge(a, o, b, true);
23402
23403         for (var i = 0; i < hunks.length; i++) {
23404             var hunk = hunks[i];
23405             if (hunk.ok) {
23406                 nodes.push.apply(nodes, hunk.ok);
23407             } else {
23408                 // for all conflicts, we can assume c.a !== c.b
23409                 // because `diff3_merge` called with `true` option to exclude false conflicts..
23410                 var c = hunk.conflict;
23411                 if (_.isEqual(c.o, c.a)) {  // only changed remotely
23412                     nodes.push.apply(nodes, c.b);
23413                 } else if (_.isEqual(c.o, c.b)) {  // only changed locally
23414                     nodes.push.apply(nodes, c.a);
23415                 } else {       // changed both locally and remotely
23416                     conflicts.push(t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) }));
23417                     break;
23418                 }
23419             }
23420         }
23421
23422         return (conflicts.length === ccount) ? target.update({nodes: nodes}) : target;
23423     }
23424
23425
23426     function mergeChildren(targetWay, children, updates, graph) {
23427         function isUsed(node, targetWay) {
23428             var parentWays = _.map(graph.parentWays(node), 'id');
23429             return node.hasInterestingTags() ||
23430                 _.without(parentWays, targetWay.id).length > 0 ||
23431                 graph.parentRelations(node).length > 0;
23432         }
23433
23434         var ccount = conflicts.length;
23435
23436         for (var i = 0; i < children.length; i++) {
23437             var id = children[i],
23438                 node = graph.hasEntity(id);
23439
23440             // remove unused childNodes..
23441             if (targetWay.nodes.indexOf(id) === -1) {
23442                 if (node && !isUsed(node, targetWay)) {
23443                     updates.removeIds.push(id);
23444                 }
23445                 continue;
23446             }
23447
23448             // restore used childNodes..
23449             var local = localGraph.hasEntity(id),
23450                 remote = remoteGraph.hasEntity(id),
23451                 target;
23452
23453             if (option === 'force_remote' && remote && remote.visible) {
23454                 updates.replacements.push(remote);
23455
23456             } else if (option === 'force_local' && local) {
23457                 target = iD.Entity(local);
23458                 if (remote) {
23459                     target = target.update({ version: remote.version });
23460                 }
23461                 updates.replacements.push(target);
23462
23463             } else if (option === 'safe' && local && remote && local.version !== remote.version) {
23464                 target = iD.Entity(local, { version: remote.version });
23465                 if (remote.visible) {
23466                     target = mergeLocation(remote, target);
23467                 } else {
23468                     conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
23469                 }
23470
23471                 if (conflicts.length !== ccount) break;
23472                 updates.replacements.push(target);
23473             }
23474         }
23475
23476         return targetWay;
23477     }
23478
23479
23480     function updateChildren(updates, graph) {
23481         for (var i = 0; i < updates.replacements.length; i++) {
23482             graph = graph.replace(updates.replacements[i]);
23483         }
23484         if (updates.removeIds.length) {
23485             graph = iD.actions.DeleteMultiple(updates.removeIds)(graph);
23486         }
23487         return graph;
23488     }
23489
23490
23491     function mergeMembers(remote, target) {
23492         if (option === 'force_local' || _.isEqual(target.members, remote.members)) {
23493             return target;
23494         }
23495         if (option === 'force_remote') {
23496             return target.update({members: remote.members});
23497         }
23498
23499         conflicts.push(t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) }));
23500         return target;
23501     }
23502
23503
23504     function mergeTags(base, remote, target) {
23505         function ignoreKey(k) {
23506             return _.includes(iD.data.discarded, k);
23507         }
23508
23509         if (option === 'force_local' || _.isEqual(target.tags, remote.tags)) {
23510             return target;
23511         }
23512         if (option === 'force_remote') {
23513             return target.update({tags: remote.tags});
23514         }
23515
23516         var ccount = conflicts.length,
23517             o = base.tags || {},
23518             a = target.tags || {},
23519             b = remote.tags || {},
23520             keys = _.reject(_.union(_.keys(o), _.keys(a), _.keys(b)), ignoreKey),
23521             tags = _.clone(a),
23522             changed = false;
23523
23524         for (var i = 0; i < keys.length; i++) {
23525             var k = keys[i];
23526
23527             if (o[k] !== b[k] && a[k] !== b[k]) {    // changed remotely..
23528                 if (o[k] !== a[k]) {      // changed locally..
23529                     conflicts.push(t('merge_remote_changes.conflict.tags',
23530                         { tag: k, local: a[k], remote: b[k], user: user(remote.user) }));
23531
23532                 } else {                  // unchanged locally, accept remote change..
23533                     if (b.hasOwnProperty(k)) {
23534                         tags[k] = b[k];
23535                     } else {
23536                         delete tags[k];
23537                     }
23538                     changed = true;
23539                 }
23540             }
23541         }
23542
23543         return (changed && conflicts.length === ccount) ? target.update({tags: tags}) : target;
23544     }
23545
23546
23547     //  `graph.base()` is the common ancestor of the two graphs.
23548     //  `localGraph` contains user's edits up to saving
23549     //  `remoteGraph` contains remote edits to modified nodes
23550     //  `graph` must be a descendent of `localGraph` and may include
23551     //      some conflict resolution actions performed on it.
23552     //
23553     //                  --- ... --- `localGraph` -- ... -- `graph`
23554     //                 /
23555     //  `graph.base()` --- ... --- `remoteGraph`
23556     //
23557     var action = function(graph) {
23558         var updates = { replacements: [], removeIds: [] },
23559             base = graph.base().entities[id],
23560             local = localGraph.entity(id),
23561             remote = remoteGraph.entity(id),
23562             target = iD.Entity(local, { version: remote.version });
23563
23564         // delete/undelete
23565         if (!remote.visible) {
23566             if (option === 'force_remote') {
23567                 return iD.actions.DeleteMultiple([id])(graph);
23568
23569             } else if (option === 'force_local') {
23570                 if (target.type === 'way') {
23571                     target = mergeChildren(target, _.uniq(local.nodes), updates, graph);
23572                     graph = updateChildren(updates, graph);
23573                 }
23574                 return graph.replace(target);
23575
23576             } else {
23577                 conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
23578                 return graph;  // do nothing
23579             }
23580         }
23581
23582         // merge
23583         if (target.type === 'node') {
23584             target = mergeLocation(remote, target);
23585
23586         } else if (target.type === 'way') {
23587             // pull in any child nodes that may not be present locally..
23588             graph.rebase(remoteGraph.childNodes(remote), [graph], false);
23589             target = mergeNodes(base, remote, target);
23590             target = mergeChildren(target, _.union(local.nodes, remote.nodes), updates, graph);
23591
23592         } else if (target.type === 'relation') {
23593             target = mergeMembers(remote, target);
23594         }
23595
23596         target = mergeTags(base, remote, target);
23597
23598         if (!conflicts.length) {
23599             graph = updateChildren(updates, graph).replace(target);
23600         }
23601
23602         return graph;
23603     };
23604
23605     action.withOption = function(opt) {
23606         option = opt;
23607         return action;
23608     };
23609
23610     action.conflicts = function() {
23611         return conflicts;
23612     };
23613
23614     return action;
23615 };
23616 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
23617 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
23618 iD.actions.Move = function(moveIds, tryDelta, projection, cache) {
23619     var delta = tryDelta;
23620
23621     function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; }
23622     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
23623
23624     function setupCache(graph) {
23625         function canMove(nodeId) {
23626             var parents = _.map(graph.parentWays(graph.entity(nodeId)), 'id');
23627             if (parents.length < 3) return true;
23628
23629             // Don't move a vertex where >2 ways meet, unless all parentWays are moving too..
23630             var parentsMoving = _.all(parents, function(id) { return cache.moving[id]; });
23631             if (!parentsMoving) delete cache.moving[nodeId];
23632
23633             return parentsMoving;
23634         }
23635
23636         function cacheEntities(ids) {
23637             _.each(ids, function(id) {
23638                 if (cache.moving[id]) return;
23639                 cache.moving[id] = true;
23640
23641                 var entity = graph.hasEntity(id);
23642                 if (!entity) return;
23643
23644                 if (entity.type === 'node') {
23645                     cache.nodes.push(id);
23646                     cache.startLoc[id] = entity.loc;
23647                 } else if (entity.type === 'way') {
23648                     cache.ways.push(id);
23649                     cacheEntities(entity.nodes);
23650                 } else {
23651                     cacheEntities(_.map(entity.members, 'id'));
23652                 }
23653             });
23654         }
23655
23656         function cacheIntersections(ids) {
23657             function isEndpoint(way, id) { return !way.isClosed() && !!way.affix(id); }
23658
23659             _.each(ids, function(id) {
23660                 // consider only intersections with 1 moved and 1 unmoved way.
23661                 _.each(graph.childNodes(graph.entity(id)), function(node) {
23662                     var parents = graph.parentWays(node);
23663                     if (parents.length !== 2) return;
23664
23665                     var moved = graph.entity(id),
23666                         unmoved = _.find(parents, function(way) { return !cache.moving[way.id]; });
23667                     if (!unmoved) return;
23668
23669                     // exclude ways that are overly connected..
23670                     if (_.intersection(moved.nodes, unmoved.nodes).length > 2) return;
23671
23672                     if (moved.isArea() || unmoved.isArea()) return;
23673
23674                     cache.intersection[node.id] = {
23675                         nodeId: node.id,
23676                         movedId: moved.id,
23677                         unmovedId: unmoved.id,
23678                         movedIsEP: isEndpoint(moved, node.id),
23679                         unmovedIsEP: isEndpoint(unmoved, node.id)
23680                     };
23681                 });
23682             });
23683         }
23684
23685
23686         if (!cache) {
23687             cache = {};
23688         }
23689         if (!cache.ok) {
23690             cache.moving = {};
23691             cache.intersection = {};
23692             cache.replacedVertex = {};
23693             cache.startLoc = {};
23694             cache.nodes = [];
23695             cache.ways = [];
23696
23697             cacheEntities(moveIds);
23698             cacheIntersections(cache.ways);
23699             cache.nodes = _.filter(cache.nodes, canMove);
23700
23701             cache.ok = true;
23702         }
23703     }
23704
23705
23706     // Place a vertex where the moved vertex used to be, to preserve way shape..
23707     function replaceMovedVertex(nodeId, wayId, graph, delta) {
23708         var way = graph.entity(wayId),
23709             moved = graph.entity(nodeId),
23710             movedIndex = way.nodes.indexOf(nodeId),
23711             len, prevIndex, nextIndex;
23712
23713         if (way.isClosed()) {
23714             len = way.nodes.length - 1;
23715             prevIndex = (movedIndex + len - 1) % len;
23716             nextIndex = (movedIndex + len + 1) % len;
23717         } else {
23718             len = way.nodes.length;
23719             prevIndex = movedIndex - 1;
23720             nextIndex = movedIndex + 1;
23721         }
23722
23723         var prev = graph.hasEntity(way.nodes[prevIndex]),
23724             next = graph.hasEntity(way.nodes[nextIndex]);
23725
23726         // Don't add orig vertex at endpoint..
23727         if (!prev || !next) return graph;
23728
23729         var key = wayId + '_' + nodeId,
23730             orig = cache.replacedVertex[key];
23731         if (!orig) {
23732             orig = iD.Node();
23733             cache.replacedVertex[key] = orig;
23734             cache.startLoc[orig.id] = cache.startLoc[nodeId];
23735         }
23736
23737         var start, end;
23738         if (delta) {
23739             start = projection(cache.startLoc[nodeId]);
23740             end = projection.invert(vecAdd(start, delta));
23741         } else {
23742             end = cache.startLoc[nodeId];
23743         }
23744         orig = orig.move(end);
23745
23746         var angle = Math.abs(iD.geo.angle(orig, prev, projection) -
23747                 iD.geo.angle(orig, next, projection)) * 180 / Math.PI;
23748
23749         // Don't add orig vertex if it would just make a straight line..
23750         if (angle > 175 && angle < 185) return graph;
23751
23752         // Don't add orig vertex if another point is already nearby (within 10m)
23753         if (iD.geo.sphericalDistance(prev.loc, orig.loc) < 10 ||
23754             iD.geo.sphericalDistance(orig.loc, next.loc) < 10) return graph;
23755
23756         // moving forward or backward along way?
23757         var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection),
23758             p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection),
23759             d1 = iD.geo.pathLength(p1),
23760             d2 = iD.geo.pathLength(p2),
23761             insertAt = (d1 < d2) ? movedIndex : nextIndex;
23762
23763         // moving around closed loop?
23764         if (way.isClosed() && insertAt === 0) insertAt = len;
23765
23766         way = way.addNode(orig.id, insertAt);
23767         return graph.replace(orig).replace(way);
23768     }
23769
23770     // Reorder nodes around intersections that have moved..
23771     function unZorroIntersection(intersection, graph) {
23772         var vertex = graph.entity(intersection.nodeId),
23773             way1 = graph.entity(intersection.movedId),
23774             way2 = graph.entity(intersection.unmovedId),
23775             isEP1 = intersection.movedIsEP,
23776             isEP2 = intersection.unmovedIsEP;
23777
23778         // don't move the vertex if it is the endpoint of both ways.
23779         if (isEP1 && isEP2) return graph;
23780
23781         var nodes1 = _.without(graph.childNodes(way1), vertex),
23782             nodes2 = _.without(graph.childNodes(way2), vertex);
23783
23784         if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
23785         if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
23786
23787         var edge1 = !isEP1 && iD.geo.chooseEdge(nodes1, projection(vertex.loc), projection),
23788             edge2 = !isEP2 && iD.geo.chooseEdge(nodes2, projection(vertex.loc), projection),
23789             loc;
23790
23791         // snap vertex to nearest edge (or some point between them)..
23792         if (!isEP1 && !isEP2) {
23793             var epsilon = 1e-4, maxIter = 10;
23794             for (var i = 0; i < maxIter; i++) {
23795                 loc = iD.geo.interp(edge1.loc, edge2.loc, 0.5);
23796                 edge1 = iD.geo.chooseEdge(nodes1, projection(loc), projection);
23797                 edge2 = iD.geo.chooseEdge(nodes2, projection(loc), projection);
23798                 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
23799             }
23800         } else if (!isEP1) {
23801             loc = edge1.loc;
23802         } else {
23803             loc = edge2.loc;
23804         }
23805
23806         graph = graph.replace(vertex.move(loc));
23807
23808         // if zorro happened, reorder nodes..
23809         if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
23810             way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
23811             graph = graph.replace(way1);
23812         }
23813         if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
23814             way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
23815             graph = graph.replace(way2);
23816         }
23817
23818         return graph;
23819     }
23820
23821
23822     function cleanupIntersections(graph) {
23823         _.each(cache.intersection, function(obj) {
23824             graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, delta);
23825             graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
23826             graph = unZorroIntersection(obj, graph);
23827         });
23828
23829         return graph;
23830     }
23831
23832     // check if moving way endpoint can cross an unmoved way, if so limit delta..
23833     function limitDelta(graph) {
23834         _.each(cache.intersection, function(obj) {
23835             if (!obj.movedIsEP) return;
23836
23837             var node = graph.entity(obj.nodeId),
23838                 start = projection(node.loc),
23839                 end = vecAdd(start, delta),
23840                 movedNodes = graph.childNodes(graph.entity(obj.movedId)),
23841                 movedPath = _.map(_.map(movedNodes, 'loc'),
23842                     function(loc) { return vecAdd(projection(loc), delta); }),
23843                 unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId)),
23844                 unmovedPath = _.map(_.map(unmovedNodes, 'loc'), projection),
23845                 hits = iD.geo.pathIntersections(movedPath, unmovedPath);
23846
23847             for (var i = 0; i < hits.length; i++) {
23848                 if (_.isEqual(hits[i], end)) continue;
23849                 var edge = iD.geo.chooseEdge(unmovedNodes, end, projection);
23850                 delta = vecSub(projection(edge.loc), start);
23851             }
23852         });
23853     }
23854
23855
23856     var action = function(graph) {
23857         if (delta[0] === 0 && delta[1] === 0) return graph;
23858
23859         setupCache(graph);
23860
23861         if (!_.isEmpty(cache.intersection)) {
23862             limitDelta(graph);
23863         }
23864
23865         _.each(cache.nodes, function(id) {
23866             var node = graph.entity(id),
23867                 start = projection(node.loc),
23868                 end = vecAdd(start, delta);
23869             graph = graph.replace(node.move(projection.invert(end)));
23870         });
23871
23872         if (!_.isEmpty(cache.intersection)) {
23873             graph = cleanupIntersections(graph);
23874         }
23875
23876         return graph;
23877     };
23878
23879     action.disabled = function(graph) {
23880         function incompleteRelation(id) {
23881             var entity = graph.entity(id);
23882             return entity.type === 'relation' && !entity.isComplete(graph);
23883         }
23884
23885         if (_.some(moveIds, incompleteRelation))
23886             return 'incomplete_relation';
23887     };
23888
23889     action.delta = function() {
23890         return delta;
23891     };
23892
23893     return action;
23894 };
23895 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
23896 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
23897 iD.actions.MoveNode = function(nodeId, loc) {
23898     return function(graph) {
23899         return graph.replace(graph.entity(nodeId).move(loc));
23900     };
23901 };
23902 iD.actions.Noop = function() {
23903     return function(graph) {
23904         return graph;
23905     };
23906 };
23907 /*
23908  * Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
23909  */
23910
23911 iD.actions.Orthogonalize = function(wayId, projection) {
23912     var threshold = 12, // degrees within right or straight to alter
23913         lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180),
23914         upperThreshold = Math.cos(threshold * Math.PI / 180);
23915
23916     var action = function(graph) {
23917         var way = graph.entity(wayId),
23918             nodes = graph.childNodes(way),
23919             points = _.uniq(nodes).map(function(n) { return projection(n.loc); }),
23920             corner = {i: 0, dotp: 1},
23921             epsilon = 1e-4,
23922             i, j, score, motions;
23923
23924         if (nodes.length === 4) {
23925             for (i = 0; i < 1000; i++) {
23926                 motions = points.map(calcMotion);
23927                 points[corner.i] = addPoints(points[corner.i],motions[corner.i]);
23928                 score = corner.dotp;
23929                 if (score < epsilon) {
23930                     break;
23931                 }
23932             }
23933
23934             graph = graph.replace(graph.entity(nodes[corner.i].id)
23935                 .move(projection.invert(points[corner.i])));
23936         } else {
23937             var best,
23938                 originalPoints = _.clone(points);
23939             score = Infinity;
23940
23941             for (i = 0; i < 1000; i++) {
23942                 motions = points.map(calcMotion);
23943                 for (j = 0; j < motions.length; j++) {
23944                     points[j] = addPoints(points[j],motions[j]);
23945                 }
23946                 var newScore = squareness(points);
23947                 if (newScore < score) {
23948                     best = _.clone(points);
23949                     score = newScore;
23950                 }
23951                 if (score < epsilon) {
23952                     break;
23953                 }
23954             }
23955
23956             points = best;
23957
23958             for (i = 0; i < points.length; i++) {
23959                 // only move the points that actually moved
23960                 if (originalPoints[i][0] !== points[i][0] || originalPoints[i][1] !== points[i][1]) {
23961                     graph = graph.replace(graph.entity(nodes[i].id)
23962                         .move(projection.invert(points[i])));
23963                 }
23964             }
23965
23966             // remove empty nodes on straight sections
23967             for (i = 0; i < points.length; i++) {
23968                 var node = nodes[i];
23969
23970                 if (graph.parentWays(node).length > 1 ||
23971                     graph.parentRelations(node).length ||
23972                     node.hasInterestingTags()) {
23973
23974                     continue;
23975                 }
23976
23977                 var dotp = normalizedDotProduct(i, points);
23978                 if (dotp < -1 + epsilon) {
23979                     graph = iD.actions.DeleteNode(nodes[i].id)(graph);
23980                 }
23981             }
23982         }
23983
23984         return graph;
23985
23986         function calcMotion(b, i, array) {
23987             var a = array[(i - 1 + array.length) % array.length],
23988                 c = array[(i + 1) % array.length],
23989                 p = subtractPoints(a, b),
23990                 q = subtractPoints(c, b),
23991                 scale, dotp;
23992
23993             scale = 2 * Math.min(iD.geo.euclideanDistance(p, [0, 0]), iD.geo.euclideanDistance(q, [0, 0]));
23994             p = normalizePoint(p, 1.0);
23995             q = normalizePoint(q, 1.0);
23996
23997             dotp = filterDotProduct(p[0] * q[0] + p[1] * q[1]);
23998
23999             // nasty hack to deal with almost-straight segments (angle is closer to 180 than to 90/270).
24000             if (array.length > 3) {
24001                 if (dotp < -0.707106781186547) {
24002                     dotp += 1.0;
24003                 }
24004             } else if (dotp && Math.abs(dotp) < corner.dotp) {
24005                 corner.i = i;
24006                 corner.dotp = Math.abs(dotp);
24007             }
24008
24009             return normalizePoint(addPoints(p, q), 0.1 * dotp * scale);
24010         }
24011     };
24012
24013     function squareness(points) {
24014         return points.reduce(function(sum, val, i, array) {
24015             var dotp = normalizedDotProduct(i, array);
24016
24017             dotp = filterDotProduct(dotp);
24018             return sum + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
24019         }, 0);
24020     }
24021
24022     function normalizedDotProduct(i, points) {
24023         var a = points[(i - 1 + points.length) % points.length],
24024             b = points[i],
24025             c = points[(i + 1) % points.length],
24026             p = subtractPoints(a, b),
24027             q = subtractPoints(c, b);
24028
24029         p = normalizePoint(p, 1.0);
24030         q = normalizePoint(q, 1.0);
24031
24032         return p[0] * q[0] + p[1] * q[1];
24033     }
24034
24035     function subtractPoints(a, b) {
24036         return [a[0] - b[0], a[1] - b[1]];
24037     }
24038
24039     function addPoints(a, b) {
24040         return [a[0] + b[0], a[1] + b[1]];
24041     }
24042
24043     function normalizePoint(point, scale) {
24044         var vector = [0, 0];
24045         var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]);
24046         if (length !== 0) {
24047             vector[0] = point[0] / length;
24048             vector[1] = point[1] / length;
24049         }
24050
24051         vector[0] *= scale;
24052         vector[1] *= scale;
24053
24054         return vector;
24055     }
24056
24057     function filterDotProduct(dotp) {
24058         if (lowerThreshold > Math.abs(dotp) || Math.abs(dotp) > upperThreshold) {
24059             return dotp;
24060         }
24061
24062         return 0;
24063     }
24064
24065     action.disabled = function(graph) {
24066         var way = graph.entity(wayId),
24067             nodes = graph.childNodes(way),
24068             points = _.uniq(nodes).map(function(n) { return projection(n.loc); });
24069
24070         if (squareness(points)) {
24071             return false;
24072         }
24073
24074         return 'not_squarish';
24075     };
24076
24077     return action;
24078 };
24079 // Create a restriction relation for `turn`, which must have the following structure:
24080 //
24081 //     {
24082 //         from: { node: <node ID>, way: <way ID> },
24083 //         via:  { node: <node ID> },
24084 //         to:   { node: <node ID>, way: <way ID> },
24085 //         restriction: <'no_right_turn', 'no_left_turn', etc.>
24086 //     }
24087 //
24088 // This specifies a restriction of type `restriction` when traveling from
24089 // `from.node` in `from.way` toward `to.node` in `to.way` via `via.node`.
24090 // (The action does not check that these entities form a valid intersection.)
24091 //
24092 // If `restriction` is not provided, it is automatically determined by
24093 // iD.geo.inferRestriction.
24094 //
24095 // If necessary, the `from` and `to` ways are split. In these cases, `from.node`
24096 // and `to.node` are used to determine which portion of the split ways become
24097 // members of the restriction.
24098 //
24099 // For testing convenience, accepts an ID to assign to the new relation.
24100 // Normally, this will be undefined and the relation will automatically
24101 // be assigned a new ID.
24102 //
24103 iD.actions.RestrictTurn = function(turn, projection, restrictionId) {
24104     return function(graph) {
24105         var from = graph.entity(turn.from.way),
24106             via  = graph.entity(turn.via.node),
24107             to   = graph.entity(turn.to.way);
24108
24109         function isClosingNode(way, nodeId) {
24110             return nodeId === way.first() && nodeId === way.last();
24111         }
24112
24113         function split(toOrFrom) {
24114             var newID = toOrFrom.newID || iD.Way().id;
24115             graph = iD.actions.Split(via.id, [newID])
24116                 .limitWays([toOrFrom.way])(graph);
24117
24118             var a = graph.entity(newID),
24119                 b = graph.entity(toOrFrom.way);
24120
24121             if (a.nodes.indexOf(toOrFrom.node) !== -1) {
24122                 return [a, b];
24123             } else {
24124                 return [b, a];
24125             }
24126         }
24127
24128         if (!from.affix(via.id) || isClosingNode(from, via.id)) {
24129             if (turn.from.node === turn.to.node) {
24130                 // U-turn
24131                 from = to = split(turn.from)[0];
24132             } else if (turn.from.way === turn.to.way) {
24133                 // Straight-on or circular
24134                 var s = split(turn.from);
24135                 from = s[0];
24136                 to   = s[1];
24137             } else {
24138                 // Other
24139                 from = split(turn.from)[0];
24140             }
24141         }
24142
24143         if (!to.affix(via.id) || isClosingNode(to, via.id)) {
24144             to = split(turn.to)[0];
24145         }
24146
24147         return graph.replace(iD.Relation({
24148             id: restrictionId,
24149             tags: {
24150                 type: 'restriction',
24151                 restriction: turn.restriction ||
24152                     iD.geo.inferRestriction(
24153                         graph,
24154                         turn.from,
24155                         turn.via,
24156                         turn.to,
24157                         projection)
24158             },
24159             members: [
24160                 {id: from.id, type: 'way',  role: 'from'},
24161                 {id: via.id,  type: 'node', role: 'via'},
24162                 {id: to.id,   type: 'way',  role: 'to'}
24163             ]
24164         }));
24165     };
24166 };
24167 /*
24168   Order the nodes of a way in reverse order and reverse any direction dependent tags
24169   other than `oneway`. (We assume that correcting a backwards oneway is the primary
24170   reason for reversing a way.)
24171
24172   The following transforms are performed:
24173
24174     Keys:
24175           *:right=* ⟺ *:left=*
24176         *:forward=* ⟺ *:backward=*
24177        direction=up ⟺ direction=down
24178          incline=up ⟺ incline=down
24179             *=right ⟺ *=left
24180
24181     Relation members:
24182        role=forward ⟺ role=backward
24183          role=north ⟺ role=south
24184           role=east ⟺ role=west
24185
24186    In addition, numeric-valued `incline` tags are negated.
24187
24188    The JOSM implementation was used as a guide, but transformations that were of unclear benefit
24189    or adjusted tags that don't seem to be used in practice were omitted.
24190
24191    References:
24192       http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
24193       http://wiki.openstreetmap.org/wiki/Key:direction#Steps
24194       http://wiki.openstreetmap.org/wiki/Key:incline
24195       http://wiki.openstreetmap.org/wiki/Route#Members
24196       http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
24197  */
24198 iD.actions.Reverse = function(wayId, options) {
24199     var replacements = [
24200             [/:right$/, ':left'], [/:left$/, ':right'],
24201             [/:forward$/, ':backward'], [/:backward$/, ':forward']
24202         ],
24203         numeric = /^([+\-]?)(?=[\d.])/,
24204         roleReversals = {
24205             forward: 'backward',
24206             backward: 'forward',
24207             north: 'south',
24208             south: 'north',
24209             east: 'west',
24210             west: 'east'
24211         };
24212
24213     function reverseKey(key) {
24214         for (var i = 0; i < replacements.length; ++i) {
24215             var replacement = replacements[i];
24216             if (replacement[0].test(key)) {
24217                 return key.replace(replacement[0], replacement[1]);
24218             }
24219         }
24220         return key;
24221     }
24222
24223     function reverseValue(key, value) {
24224         if (key === 'incline' && numeric.test(value)) {
24225             return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
24226         } else if (key === 'incline' || key === 'direction') {
24227             return {up: 'down', down: 'up'}[value] || value;
24228         } else if (options && options.reverseOneway && key === 'oneway') {
24229             return {yes: '-1', '1': '-1', '-1': 'yes'}[value] || value;
24230         } else {
24231             return {left: 'right', right: 'left'}[value] || value;
24232         }
24233     }
24234
24235     return function(graph) {
24236         var way = graph.entity(wayId),
24237             nodes = way.nodes.slice().reverse(),
24238             tags = {}, key, role;
24239
24240         for (key in way.tags) {
24241             tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
24242         }
24243
24244         graph.parentRelations(way).forEach(function(relation) {
24245             relation.members.forEach(function(member, index) {
24246                 if (member.id === way.id && (role = roleReversals[member.role])) {
24247                     relation = relation.updateMember({role: role}, index);
24248                     graph = graph.replace(relation);
24249                 }
24250             });
24251         });
24252
24253         return graph.replace(way.update({nodes: nodes, tags: tags}));
24254     };
24255 };
24256 iD.actions.Revert = function(id) {
24257
24258     var action = function(graph) {
24259         var entity = graph.hasEntity(id),
24260             base = graph.base().entities[id];
24261
24262         if (entity && !base) {    // entity will be removed..
24263             if (entity.type === 'node') {
24264                 graph.parentWays(entity)
24265                     .forEach(function(parent) {
24266                         parent = parent.removeNode(id);
24267                         graph = graph.replace(parent);
24268
24269                         if (parent.isDegenerate()) {
24270                             graph = iD.actions.DeleteWay(parent.id)(graph);
24271                         }
24272                     });
24273             }
24274
24275             graph.parentRelations(entity)
24276                 .forEach(function(parent) {
24277                     parent = parent.removeMembersWithID(id);
24278                     graph = graph.replace(parent);
24279
24280                     if (parent.isDegenerate()) {
24281                         graph = iD.actions.DeleteRelation(parent.id)(graph);
24282                     }
24283                 });
24284         }
24285
24286         return graph.revert(id);
24287     };
24288
24289     return action;
24290 };
24291 iD.actions.RotateWay = function(wayId, pivot, angle, projection) {
24292     return function(graph) {
24293         return graph.update(function(graph) {
24294             var way = graph.entity(wayId);
24295
24296             _.uniq(way.nodes).forEach(function(id) {
24297
24298                 var node = graph.entity(id),
24299                     point = projection(node.loc),
24300                     radial = [0,0];
24301
24302                 radial[0] = point[0] - pivot[0];
24303                 radial[1] = point[1] - pivot[1];
24304
24305                 point = [
24306                     radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + pivot[0],
24307                     radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + pivot[1]
24308                 ];
24309
24310                 graph = graph.replace(node.move(projection.invert(point)));
24311
24312             });
24313
24314         });
24315     };
24316 };
24317 // Split a way at the given node.
24318 //
24319 // Optionally, split only the given ways, if multiple ways share
24320 // the given node.
24321 //
24322 // This is the inverse of `iD.actions.Join`.
24323 //
24324 // For testing convenience, accepts an ID to assign to the new way.
24325 // Normally, this will be undefined and the way will automatically
24326 // be assigned a new ID.
24327 //
24328 // Reference:
24329 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
24330 //
24331 iD.actions.Split = function(nodeId, newWayIds) {
24332     var wayIds;
24333
24334     // if the way is closed, we need to search for a partner node
24335     // to split the way at.
24336     //
24337     // The following looks for a node that is both far away from
24338     // the initial node in terms of way segment length and nearby
24339     // in terms of beeline-distance. This assures that areas get
24340     // split on the most "natural" points (independent of the number
24341     // of nodes).
24342     // For example: bone-shaped areas get split across their waist
24343     // line, circles across the diameter.
24344     function splitArea(nodes, idxA, graph) {
24345         var lengths = new Array(nodes.length),
24346             length,
24347             i,
24348             best = 0,
24349             idxB;
24350
24351         function wrap(index) {
24352             return iD.util.wrap(index, nodes.length);
24353         }
24354
24355         function dist(nA, nB) {
24356             return iD.geo.sphericalDistance(graph.entity(nA).loc, graph.entity(nB).loc);
24357         }
24358
24359         // calculate lengths
24360         length = 0;
24361         for (i = wrap(idxA+1); i !== idxA; i = wrap(i+1)) {
24362             length += dist(nodes[i], nodes[wrap(i-1)]);
24363             lengths[i] = length;
24364         }
24365
24366         length = 0;
24367         for (i = wrap(idxA-1); i !== idxA; i = wrap(i-1)) {
24368             length += dist(nodes[i], nodes[wrap(i+1)]);
24369             if (length < lengths[i])
24370                 lengths[i] = length;
24371         }
24372
24373         // determine best opposite node to split
24374         for (i = 0; i < nodes.length; i++) {
24375             var cost = lengths[i] / dist(nodes[idxA], nodes[i]);
24376             if (cost > best) {
24377                 idxB = i;
24378                 best = cost;
24379             }
24380         }
24381
24382         return idxB;
24383     }
24384
24385     function split(graph, wayA, newWayId) {
24386         var wayB = iD.Way({id: newWayId, tags: wayA.tags}),
24387             nodesA,
24388             nodesB,
24389             isArea = wayA.isArea(),
24390             isOuter = iD.geo.isSimpleMultipolygonOuterMember(wayA, graph);
24391
24392         if (wayA.isClosed()) {
24393             var nodes = wayA.nodes.slice(0, -1),
24394                 idxA = _.indexOf(nodes, nodeId),
24395                 idxB = splitArea(nodes, idxA, graph);
24396
24397             if (idxB < idxA) {
24398                 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
24399                 nodesB = nodes.slice(idxB, idxA + 1);
24400             } else {
24401                 nodesA = nodes.slice(idxA, idxB + 1);
24402                 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
24403             }
24404         } else {
24405             var idx = _.indexOf(wayA.nodes, nodeId, 1);
24406             nodesA = wayA.nodes.slice(0, idx + 1);
24407             nodesB = wayA.nodes.slice(idx);
24408         }
24409
24410         wayA = wayA.update({nodes: nodesA});
24411         wayB = wayB.update({nodes: nodesB});
24412
24413         graph = graph.replace(wayA);
24414         graph = graph.replace(wayB);
24415
24416         graph.parentRelations(wayA).forEach(function(relation) {
24417             if (relation.isRestriction()) {
24418                 var via = relation.memberByRole('via');
24419                 if (via && wayB.contains(via.id)) {
24420                     relation = relation.updateMember({id: wayB.id}, relation.memberById(wayA.id).index);
24421                     graph = graph.replace(relation);
24422                 }
24423             } else {
24424                 if (relation === isOuter) {
24425                     graph = graph.replace(relation.mergeTags(wayA.tags));
24426                     graph = graph.replace(wayA.update({tags: {}}));
24427                     graph = graph.replace(wayB.update({tags: {}}));
24428                 }
24429
24430                 var member = {
24431                     id: wayB.id,
24432                     type: 'way',
24433                     role: relation.memberById(wayA.id).role
24434                 };
24435
24436                 graph = iD.actions.AddMember(relation.id, member)(graph);
24437             }
24438         });
24439
24440         if (!isOuter && isArea) {
24441             var multipolygon = iD.Relation({
24442                 tags: _.extend({}, wayA.tags, {type: 'multipolygon'}),
24443                 members: [
24444                     {id: wayA.id, role: 'outer', type: 'way'},
24445                     {id: wayB.id, role: 'outer', type: 'way'}
24446                 ]});
24447
24448             graph = graph.replace(multipolygon);
24449             graph = graph.replace(wayA.update({tags: {}}));
24450             graph = graph.replace(wayB.update({tags: {}}));
24451         }
24452
24453         return graph;
24454     }
24455
24456     var action = function(graph) {
24457         var candidates = action.ways(graph);
24458         for (var i = 0; i < candidates.length; i++) {
24459             graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
24460         }
24461         return graph;
24462     };
24463
24464     action.ways = function(graph) {
24465         var node = graph.entity(nodeId),
24466             parents = graph.parentWays(node),
24467             hasLines = _.some(parents, function(parent) { return parent.geometry(graph) === 'line'; });
24468
24469         return parents.filter(function(parent) {
24470             if (wayIds && wayIds.indexOf(parent.id) === -1)
24471                 return false;
24472
24473             if (!wayIds && hasLines && parent.geometry(graph) !== 'line')
24474                 return false;
24475
24476             if (parent.isClosed()) {
24477                 return true;
24478             }
24479
24480             for (var i = 1; i < parent.nodes.length - 1; i++) {
24481                 if (parent.nodes[i] === nodeId) {
24482                     return true;
24483                 }
24484             }
24485
24486             return false;
24487         });
24488     };
24489
24490     action.disabled = function(graph) {
24491         var candidates = action.ways(graph);
24492         if (candidates.length === 0 || (wayIds && wayIds.length !== candidates.length))
24493             return 'not_eligible';
24494     };
24495
24496     action.limitWays = function(_) {
24497         if (!arguments.length) return wayIds;
24498         wayIds = _;
24499         return action;
24500     };
24501
24502     return action;
24503 };
24504 /*
24505  * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
24506  */
24507
24508 iD.actions.Straighten = function(wayId, projection) {
24509     function positionAlongWay(n, s, e) {
24510         return ((n[0] - s[0]) * (e[0] - s[0]) + (n[1] - s[1]) * (e[1] - s[1]))/
24511                 (Math.pow(e[0] - s[0], 2) + Math.pow(e[1] - s[1], 2));
24512     }
24513
24514     var action = function(graph) {
24515         var way = graph.entity(wayId),
24516             nodes = graph.childNodes(way),
24517             points = nodes.map(function(n) { return projection(n.loc); }),
24518             startPoint = points[0],
24519             endPoint = points[points.length-1],
24520             toDelete = [],
24521             i;
24522
24523         for (i = 1; i < points.length-1; i++) {
24524             var node = nodes[i],
24525                 point = points[i];
24526
24527             if (graph.parentWays(node).length > 1 ||
24528                 graph.parentRelations(node).length ||
24529                 node.hasInterestingTags()) {
24530
24531                 var u = positionAlongWay(point, startPoint, endPoint),
24532                     p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
24533                     p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]);
24534
24535                 graph = graph.replace(graph.entity(node.id)
24536                     .move(projection.invert([p0, p1])));
24537             } else {
24538                 // safe to delete
24539                 if (toDelete.indexOf(node) === -1) {
24540                     toDelete.push(node);
24541                 }
24542             }
24543         }
24544
24545         for (i = 0; i < toDelete.length; i++) {
24546             graph = iD.actions.DeleteNode(toDelete[i].id)(graph);
24547         }
24548
24549         return graph;
24550     };
24551
24552     action.disabled = function(graph) {
24553         // check way isn't too bendy
24554         var way = graph.entity(wayId),
24555             nodes = graph.childNodes(way),
24556             points = nodes.map(function(n) { return projection(n.loc); }),
24557             startPoint = points[0],
24558             endPoint = points[points.length-1],
24559             threshold = 0.2 * Math.sqrt(Math.pow(startPoint[0] - endPoint[0], 2) + Math.pow(startPoint[1] - endPoint[1], 2)),
24560             i;
24561
24562         if (threshold === 0) {
24563             return 'too_bendy';
24564         }
24565
24566         for (i = 1; i < points.length-1; i++) {
24567             var point = points[i],
24568                 u = positionAlongWay(point, startPoint, endPoint),
24569                 p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
24570                 p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]),
24571                 dist = Math.sqrt(Math.pow(p0 - point[0], 2) + Math.pow(p1 - point[1], 2));
24572
24573             // to bendy if point is off by 20% of total start/end distance in projected space
24574             if (isNaN(dist) || dist > threshold) {
24575                 return 'too_bendy';
24576             }
24577         }
24578     };
24579
24580     return action;
24581 };
24582 // Remove the effects of `turn.restriction` on `turn`, which must have the
24583 // following structure:
24584 //
24585 //     {
24586 //         from: { node: <node ID>, way: <way ID> },
24587 //         via:  { node: <node ID> },
24588 //         to:   { node: <node ID>, way: <way ID> },
24589 //         restriction: <relation ID>
24590 //     }
24591 //
24592 // In the simple case, `restriction` is a reference to a `no_*` restriction
24593 // on the turn itself. In this case, it is simply deleted.
24594 //
24595 // The more complex case is where `restriction` references an `only_*`
24596 // restriction on a different turn in the same intersection. In that case,
24597 // that restriction is also deleted, but at the same time restrictions on
24598 // the turns other than the first two are created.
24599 //
24600 iD.actions.UnrestrictTurn = function(turn) {
24601     return function(graph) {
24602         return iD.actions.DeleteRelation(turn.restriction)(graph);
24603     };
24604 };
24605 iD.behavior = {};
24606 iD.behavior.AddWay = function(context) {
24607     var event = d3.dispatch('start', 'startFromWay', 'startFromNode'),
24608         draw = iD.behavior.Draw(context);
24609
24610     var addWay = function(surface) {
24611         draw.on('click', event.start)
24612             .on('clickWay', event.startFromWay)
24613             .on('clickNode', event.startFromNode)
24614             .on('cancel', addWay.cancel)
24615             .on('finish', addWay.cancel);
24616
24617         context.map()
24618             .dblclickEnable(false);
24619
24620         surface.call(draw);
24621     };
24622
24623     addWay.off = function(surface) {
24624         surface.call(draw.off);
24625     };
24626
24627     addWay.cancel = function() {
24628         window.setTimeout(function() {
24629             context.map().dblclickEnable(true);
24630         }, 1000);
24631
24632         context.enter(iD.modes.Browse(context));
24633     };
24634
24635     addWay.tail = function(text) {
24636         draw.tail(text);
24637         return addWay;
24638     };
24639
24640     return d3.rebind(addWay, event, 'on');
24641 };
24642 iD.behavior.Breathe = function() {
24643     var duration = 800,
24644         selector = '.selected.shadow, .selected .shadow',
24645         selected = d3.select(null),
24646         classed = '',
24647         params = {},
24648         done;
24649
24650     function reset(selection) {
24651         selection
24652             .style('stroke-opacity', null)
24653             .style('stroke-width', null)
24654             .style('fill-opacity', null)
24655             .style('r', null);
24656     }
24657
24658     function setAnimationParams(transition, fromTo) {
24659         transition
24660             .style('stroke-opacity', function(d) { return params[d.id][fromTo].opacity; })
24661             .style('stroke-width', function(d) { return params[d.id][fromTo].width; })
24662             .style('fill-opacity', function(d) { return params[d.id][fromTo].opacity; })
24663             .style('r', function(d) { return params[d.id][fromTo].width; });
24664     }
24665
24666     function calcAnimationParams(selection) {
24667         selection
24668             .call(reset)
24669             .each(function(d) {
24670                 var s = d3.select(this),
24671                     tag = s.node().tagName,
24672                     p = {'from': {}, 'to': {}},
24673                     opacity, width;
24674
24675                 // determine base opacity and width
24676                 if (tag === 'circle') {
24677                     opacity = parseFloat(s.style('fill-opacity') || 0.5);
24678                     width = parseFloat(s.style('r') || 15.5);
24679                 } else {
24680                     opacity = parseFloat(s.style('stroke-opacity') || 0.7);
24681                     width = parseFloat(s.style('stroke-width') || 10);
24682                 }
24683
24684                 // calculate from/to interpolation params..
24685                 p.tag = tag;
24686                 p.from.opacity = opacity * 0.6;
24687                 p.to.opacity = opacity * 1.25;
24688                 p.from.width = width * 0.9;
24689                 p.to.width = width * (tag === 'circle' ? 1.5 : 1.25);
24690                 params[d.id] = p;
24691             });
24692     }
24693
24694     function run(surface, fromTo) {
24695         var toFrom = (fromTo === 'from' ? 'to': 'from'),
24696             currSelected = surface.selectAll(selector),
24697             currClassed = surface.attr('class'),
24698             n = 0;
24699
24700         if (done || currSelected.empty()) {
24701             selected.call(reset);
24702             return;
24703         }
24704
24705         if (!_.isEqual(currSelected, selected) || currClassed !== classed) {
24706             selected.call(reset);
24707             classed = currClassed;
24708             selected = currSelected.call(calcAnimationParams);
24709         }
24710
24711         selected
24712             .transition()
24713             .call(setAnimationParams, fromTo)
24714             .duration(duration)
24715             .each(function() { ++n; })
24716             .each('end', function() {
24717                 if (!--n) {  // call once
24718                     surface.call(run, toFrom);
24719                 }
24720             });
24721     }
24722
24723     var breathe = function(surface) {
24724         done = false;
24725         d3.timer(function() {
24726             if (done) return true;
24727
24728             var currSelected = surface.selectAll(selector);
24729             if (currSelected.empty()) return false;
24730
24731             surface.call(run, 'from');
24732             return true;
24733         }, 200);
24734     };
24735
24736     breathe.off = function() {
24737         done = true;
24738         d3.timer.flush();
24739         selected
24740             .transition()
24741             .call(reset)
24742             .duration(0);
24743     };
24744
24745     return breathe;
24746 };
24747 iD.behavior.Copy = function(context) {
24748     var keybinding = d3.keybinding('copy');
24749
24750     function groupEntities(ids, graph) {
24751         var entities = ids.map(function (id) { return graph.entity(id); });
24752         return _.extend({relation: [], way: [], node: []},
24753             _.groupBy(entities, function(entity) { return entity.type; }));
24754     }
24755
24756     function getDescendants(id, graph, descendants) {
24757         var entity = graph.entity(id),
24758             i, children;
24759
24760         descendants = descendants || {};
24761
24762         if (entity.type === 'relation') {
24763             children = _.map(entity.members, 'id');
24764         } else if (entity.type === 'way') {
24765             children = entity.nodes;
24766         } else {
24767             children = [];
24768         }
24769
24770         for (i = 0; i < children.length; i++) {
24771             if (!descendants[children[i]]) {
24772                 descendants[children[i]] = true;
24773                 descendants = getDescendants(children[i], graph, descendants);
24774             }
24775         }
24776
24777         return descendants;
24778     }
24779
24780     function doCopy() {
24781         d3.event.preventDefault();
24782         if (context.inIntro()) return;
24783
24784         var graph = context.graph(),
24785             selected = groupEntities(context.selectedIDs(), graph),
24786             canCopy = [],
24787             skip = {},
24788             i, entity;
24789
24790         for (i = 0; i < selected.relation.length; i++) {
24791             entity = selected.relation[i];
24792             if (!skip[entity.id] && entity.isComplete(graph)) {
24793                 canCopy.push(entity.id);
24794                 skip = getDescendants(entity.id, graph, skip);
24795             }
24796         }
24797         for (i = 0; i < selected.way.length; i++) {
24798             entity = selected.way[i];
24799             if (!skip[entity.id]) {
24800                 canCopy.push(entity.id);
24801                 skip = getDescendants(entity.id, graph, skip);
24802             }
24803         }
24804         for (i = 0; i < selected.node.length; i++) {
24805             entity = selected.node[i];
24806             if (!skip[entity.id]) {
24807                 canCopy.push(entity.id);
24808             }
24809         }
24810
24811         context.copyIDs(canCopy);
24812     }
24813
24814     function copy() {
24815         keybinding.on(iD.ui.cmd('⌘C'), doCopy);
24816         d3.select(document).call(keybinding);
24817         return copy;
24818     }
24819
24820     copy.off = function() {
24821         d3.select(document).call(keybinding.off);
24822     };
24823
24824     return copy;
24825 };
24826 /*
24827     `iD.behavior.drag` is like `d3.behavior.drag`, with the following differences:
24828
24829     * The `origin` function is expected to return an [x, y] tuple rather than an
24830       {x, y} object.
24831     * The events are `start`, `move`, and `end`.
24832       (https://github.com/mbostock/d3/issues/563)
24833     * The `start` event is not dispatched until the first cursor movement occurs.
24834       (https://github.com/mbostock/d3/pull/368)
24835     * The `move` event has a `point` and `delta` [x, y] tuple properties rather
24836       than `x`, `y`, `dx`, and `dy` properties.
24837     * The `end` event is not dispatched if no movement occurs.
24838     * An `off` function is available that unbinds the drag's internal event handlers.
24839     * Delegation is supported via the `delegate` function.
24840
24841  */
24842 iD.behavior.drag = function() {
24843     function d3_eventCancel() {
24844       d3.event.stopPropagation();
24845       d3.event.preventDefault();
24846     }
24847
24848     var event = d3.dispatch('start', 'move', 'end'),
24849         origin = null,
24850         selector = '',
24851         filter = null,
24852         event_, target, surface;
24853
24854     event.of = function(thiz, argumentz) {
24855       return function(e1) {
24856         var e0 = e1.sourceEvent = d3.event;
24857         e1.target = drag;
24858         d3.event = e1;
24859         try {
24860           event[e1.type].apply(thiz, argumentz);
24861         } finally {
24862           d3.event = e0;
24863         }
24864       };
24865     };
24866
24867     var d3_event_userSelectProperty = iD.util.prefixCSSProperty('UserSelect'),
24868         d3_event_userSelectSuppress = d3_event_userSelectProperty ?
24869             function () {
24870                 var selection = d3.selection(),
24871                     select = selection.style(d3_event_userSelectProperty);
24872                 selection.style(d3_event_userSelectProperty, 'none');
24873                 return function () {
24874                     selection.style(d3_event_userSelectProperty, select);
24875                 };
24876             } :
24877             function (type) {
24878                 var w = d3.select(window).on('selectstart.' + type, d3_eventCancel);
24879                 return function () {
24880                     w.on('selectstart.' + type, null);
24881                 };
24882             };
24883
24884     function mousedown() {
24885         target = this;
24886         event_ = event.of(target, arguments);
24887         var eventTarget = d3.event.target,
24888             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
24889             offset,
24890             origin_ = point(),
24891             started = false,
24892             selectEnable = d3_event_userSelectSuppress(touchId !== null ? 'drag-' + touchId : 'drag');
24893
24894         var w = d3.select(window)
24895             .on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', dragmove)
24896             .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', dragend, true);
24897
24898         if (origin) {
24899             offset = origin.apply(target, arguments);
24900             offset = [offset[0] - origin_[0], offset[1] - origin_[1]];
24901         } else {
24902             offset = [0, 0];
24903         }
24904
24905         if (touchId === null) d3.event.stopPropagation();
24906
24907         function point() {
24908             var p = target.parentNode || surface;
24909             return touchId !== null ? d3.touches(p).filter(function(p) {
24910                 return p.identifier === touchId;
24911             })[0] : d3.mouse(p);
24912         }
24913
24914         function dragmove() {
24915
24916             var p = point(),
24917                 dx = p[0] - origin_[0],
24918                 dy = p[1] - origin_[1];
24919             
24920             if (dx === 0 && dy === 0)
24921                 return;
24922
24923             if (!started) {
24924                 started = true;
24925                 event_({
24926                     type: 'start'
24927                 });
24928             }
24929
24930             origin_ = p;
24931             d3_eventCancel();
24932
24933             event_({
24934                 type: 'move',
24935                 point: [p[0] + offset[0],  p[1] + offset[1]],
24936                 delta: [dx, dy]
24937             });
24938         }
24939
24940         function dragend() {
24941             if (started) {
24942                 event_({
24943                     type: 'end'
24944                 });
24945
24946                 d3_eventCancel();
24947                 if (d3.event.target === eventTarget) w.on('click.drag', click, true);
24948             }
24949
24950             w.on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', null)
24951                 .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', null);
24952             selectEnable();
24953         }
24954
24955         function click() {
24956             d3_eventCancel();
24957             w.on('click.drag', null);
24958         }
24959     }
24960
24961     function drag(selection) {
24962         var matchesSelector = iD.util.prefixDOMProperty('matchesSelector'),
24963             delegate = mousedown;
24964
24965         if (selector) {
24966             delegate = function() {
24967                 var root = this,
24968                     target = d3.event.target;
24969                 for (; target && target !== root; target = target.parentNode) {
24970                     if (target[matchesSelector](selector) &&
24971                             (!filter || filter(target.__data__))) {
24972                         return mousedown.call(target, target.__data__);
24973                     }
24974                 }
24975             };
24976         }
24977
24978         selection.on('mousedown.drag' + selector, delegate)
24979             .on('touchstart.drag' + selector, delegate);
24980     }
24981
24982     drag.off = function(selection) {
24983         selection.on('mousedown.drag' + selector, null)
24984             .on('touchstart.drag' + selector, null);
24985     };
24986
24987     drag.delegate = function(_) {
24988         if (!arguments.length) return selector;
24989         selector = _;
24990         return drag;
24991     };
24992
24993     drag.filter = function(_) {
24994         if (!arguments.length) return origin;
24995         filter = _;
24996         return drag;
24997     };
24998
24999     drag.origin = function (_) {
25000         if (!arguments.length) return origin;
25001         origin = _;
25002         return drag;
25003     };
25004
25005     drag.cancel = function() {
25006         d3.select(window)
25007             .on('mousemove.drag', null)
25008             .on('mouseup.drag', null);
25009         return drag;
25010     };
25011
25012     drag.target = function() {
25013         if (!arguments.length) return target;
25014         target = arguments[0];
25015         event_ = event.of(target, Array.prototype.slice.call(arguments, 1));
25016         return drag;
25017     };
25018
25019     drag.surface = function() {
25020         if (!arguments.length) return surface;
25021         surface = arguments[0];
25022         return drag;
25023     };
25024
25025     return d3.rebind(drag, event, 'on');
25026 };
25027 iD.behavior.Draw = function(context) {
25028     var event = d3.dispatch('move', 'click', 'clickWay',
25029             'clickNode', 'undo', 'cancel', 'finish'),
25030         keybinding = d3.keybinding('draw'),
25031         hover = iD.behavior.Hover(context)
25032             .altDisables(true)
25033             .on('hover', context.ui().sidebar.hover),
25034         tail = iD.behavior.Tail(),
25035         edit = iD.behavior.Edit(context),
25036         closeTolerance = 4,
25037         tolerance = 12,
25038         mouseLeave = false,
25039         lastMouse = null,
25040         cached = iD.behavior.Draw;
25041
25042     function datum() {
25043         if (d3.event.altKey) return {};
25044
25045         if (d3.event.type === 'keydown') {
25046             return (lastMouse && lastMouse.target.__data__) || {};
25047         } else {
25048             return d3.event.target.__data__ || {};
25049         }
25050     }
25051
25052     function mousedown() {
25053
25054         function point() {
25055             var p = context.container().node();
25056             return touchId !== null ? d3.touches(p).filter(function(p) {
25057                 return p.identifier === touchId;
25058             })[0] : d3.mouse(p);
25059         }
25060
25061         var element = d3.select(this),
25062             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
25063             t1 = +new Date(),
25064             p1 = point();
25065
25066         element.on('mousemove.draw', null);
25067
25068         d3.select(window).on('mouseup.draw', function() {
25069             var t2 = +new Date(),
25070                 p2 = point(),
25071                 dist = iD.geo.euclideanDistance(p1, p2);
25072
25073             element.on('mousemove.draw', mousemove);
25074             d3.select(window).on('mouseup.draw', null);
25075
25076             if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) {
25077                 // Prevent a quick second click
25078                 d3.select(window).on('click.draw-block', function() {
25079                     d3.event.stopPropagation();
25080                 }, true);
25081
25082                 context.map().dblclickEnable(false);
25083
25084                 window.setTimeout(function() {
25085                     context.map().dblclickEnable(true);
25086                     d3.select(window).on('click.draw-block', null);
25087                 }, 500);
25088
25089                 click();
25090             }
25091         });
25092     }
25093
25094     function mousemove() {
25095         lastMouse = d3.event;
25096         event.move(datum());
25097     }
25098
25099     function mouseenter() {
25100         mouseLeave = false;
25101     }
25102
25103     function mouseleave() {
25104         mouseLeave = true;
25105     }
25106
25107     function click() {
25108         var d = datum();
25109         if (d.type === 'way') {
25110             var dims = context.map().dimensions(),
25111                 mouse = context.mouse(),
25112                 pad = 5,
25113                 trySnap = mouse[0] > pad && mouse[0] < dims[0] - pad &&
25114                     mouse[1] > pad && mouse[1] < dims[1] - pad;
25115
25116             if (trySnap) {
25117                 var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection),
25118                     edge = [d.nodes[choice.index - 1], d.nodes[choice.index]];
25119                 event.clickWay(choice.loc, edge);
25120             } else {
25121                 event.click(context.map().mouseCoordinates());
25122             }
25123
25124         } else if (d.type === 'node') {
25125             event.clickNode(d);
25126
25127         } else {
25128             event.click(context.map().mouseCoordinates());
25129         }
25130     }
25131
25132     function space() {
25133         var currSpace = context.mouse();
25134         if (cached.disableSpace && cached.lastSpace) {
25135             var dist = iD.geo.euclideanDistance(cached.lastSpace, currSpace);
25136             if (dist > tolerance) {
25137                 cached.disableSpace = false;
25138             }
25139         }
25140
25141         if (cached.disableSpace || mouseLeave || !lastMouse) return;
25142
25143         // user must move mouse or release space bar to allow another click
25144         cached.lastSpace = currSpace;
25145         cached.disableSpace = true;
25146
25147         d3.select(window).on('keyup.space-block', function() {
25148             cached.disableSpace = false;
25149             d3.select(window).on('keyup.space-block', null);
25150         });
25151
25152         d3.event.preventDefault();
25153         click();
25154     }
25155
25156     function backspace() {
25157         d3.event.preventDefault();
25158         event.undo();
25159     }
25160
25161     function del() {
25162         d3.event.preventDefault();
25163         event.cancel();
25164     }
25165
25166     function ret() {
25167         d3.event.preventDefault();
25168         event.finish();
25169     }
25170
25171     function draw(selection) {
25172         context.install(hover);
25173         context.install(edit);
25174
25175         if (!context.inIntro() && !cached.usedTails[tail.text()]) {
25176             context.install(tail);
25177         }
25178
25179         keybinding
25180             .on('⌫', backspace)
25181             .on('⌦', del)
25182             .on('⎋', ret)
25183             .on('↩', ret)
25184             .on('space', space)
25185             .on('⌥space', space);
25186
25187         selection
25188             .on('mouseenter.draw', mouseenter)
25189             .on('mouseleave.draw', mouseleave)
25190             .on('mousedown.draw', mousedown)
25191             .on('mousemove.draw', mousemove);
25192
25193         d3.select(document)
25194             .call(keybinding);
25195
25196         return draw;
25197     }
25198
25199     draw.off = function(selection) {
25200         context.ui().sidebar.hover.cancel();
25201         context.uninstall(hover);
25202         context.uninstall(edit);
25203
25204         if (!context.inIntro() && !cached.usedTails[tail.text()]) {
25205             context.uninstall(tail);
25206             cached.usedTails[tail.text()] = true;
25207         }
25208
25209         selection
25210             .on('mouseenter.draw', null)
25211             .on('mouseleave.draw', null)
25212             .on('mousedown.draw', null)
25213             .on('mousemove.draw', null);
25214
25215         d3.select(window)
25216             .on('mouseup.draw', null);
25217             // note: keyup.space-block, click.draw-block should remain
25218
25219         d3.select(document)
25220             .call(keybinding.off);
25221     };
25222
25223     draw.tail = function(_) {
25224         tail.text(_);
25225         return draw;
25226     };
25227
25228     return d3.rebind(draw, event, 'on');
25229 };
25230
25231 iD.behavior.Draw.usedTails = {};
25232 iD.behavior.Draw.disableSpace = false;
25233 iD.behavior.Draw.lastSpace = null;
25234 iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
25235     var way = context.entity(wayId),
25236         isArea = context.geometry(wayId) === 'area',
25237         finished = false,
25238         annotation = t((way.isDegenerate() ?
25239             'operations.start.annotation.' :
25240             'operations.continue.annotation.') + context.geometry(wayId)),
25241         draw = iD.behavior.Draw(context);
25242
25243     var startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0,
25244         start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}),
25245         end = iD.Node({loc: context.map().mouseCoordinates()}),
25246         segment = iD.Way({
25247             nodes: typeof index === 'undefined' ? [start.id, end.id] : [end.id, start.id],
25248             tags: _.clone(way.tags)
25249         });
25250
25251     var f = context[way.isDegenerate() ? 'replace' : 'perform'];
25252     if (isArea) {
25253         f(iD.actions.AddEntity(end),
25254             iD.actions.AddVertex(wayId, end.id, index));
25255     } else {
25256         f(iD.actions.AddEntity(start),
25257             iD.actions.AddEntity(end),
25258             iD.actions.AddEntity(segment));
25259     }
25260
25261     function move(datum) {
25262         var loc;
25263
25264         if (datum.type === 'node' && datum.id !== end.id) {
25265             loc = datum.loc;
25266
25267         } else if (datum.type === 'way' && datum.id !== segment.id) {
25268             var dims = context.map().dimensions(),
25269                 mouse = context.mouse(),
25270                 pad = 5,
25271                 trySnap = mouse[0] > pad && mouse[0] < dims[0] - pad &&
25272                     mouse[1] > pad && mouse[1] < dims[1] - pad;
25273
25274             if (trySnap) {
25275                 loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc;
25276             }
25277         }
25278
25279         if (!loc) {
25280             loc = context.map().mouseCoordinates();
25281         }
25282
25283         context.replace(iD.actions.MoveNode(end.id, loc));
25284     }
25285
25286     function undone() {
25287         finished = true;
25288         context.enter(iD.modes.Browse(context));
25289     }
25290
25291     function setActiveElements() {
25292         var active = isArea ? [wayId, end.id] : [segment.id, start.id, end.id];
25293         context.surface().selectAll(iD.util.entitySelector(active))
25294             .classed('active', true);
25295     }
25296
25297     var drawWay = function(surface) {
25298         draw.on('move', move)
25299             .on('click', drawWay.add)
25300             .on('clickWay', drawWay.addWay)
25301             .on('clickNode', drawWay.addNode)
25302             .on('undo', context.undo)
25303             .on('cancel', drawWay.cancel)
25304             .on('finish', drawWay.finish);
25305
25306         context.map()
25307             .dblclickEnable(false)
25308             .on('drawn.draw', setActiveElements);
25309
25310         setActiveElements();
25311
25312         surface.call(draw);
25313
25314         context.history()
25315             .on('undone.draw', undone);
25316     };
25317
25318     drawWay.off = function(surface) {
25319         if (!finished)
25320             context.pop();
25321
25322         context.map()
25323             .on('drawn.draw', null);
25324
25325         surface.call(draw.off)
25326             .selectAll('.active')
25327             .classed('active', false);
25328
25329         context.history()
25330             .on('undone.draw', null);
25331     };
25332
25333     function ReplaceTemporaryNode(newNode) {
25334         return function(graph) {
25335             if (isArea) {
25336                 return graph
25337                     .replace(way.addNode(newNode.id, index))
25338                     .remove(end);
25339
25340             } else {
25341                 return graph
25342                     .replace(graph.entity(wayId).addNode(newNode.id, index))
25343                     .remove(end)
25344                     .remove(segment)
25345                     .remove(start);
25346             }
25347         };
25348     }
25349
25350     // Accept the current position of the temporary node and continue drawing.
25351     drawWay.add = function(loc) {
25352
25353         // prevent duplicate nodes
25354         var last = context.hasEntity(way.nodes[way.nodes.length - (isArea ? 2 : 1)]);
25355         if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return;
25356
25357         var newNode = iD.Node({loc: loc});
25358
25359         context.replace(
25360             iD.actions.AddEntity(newNode),
25361             ReplaceTemporaryNode(newNode),
25362             annotation);
25363
25364         finished = true;
25365         context.enter(mode);
25366     };
25367
25368     // Connect the way to an existing way.
25369     drawWay.addWay = function(loc, edge) {
25370         var previousEdge = startIndex ?
25371             [way.nodes[startIndex], way.nodes[startIndex - 1]] :
25372             [way.nodes[0], way.nodes[1]];
25373
25374         // Avoid creating duplicate segments
25375         if (!isArea && iD.geo.edgeEqual(edge, previousEdge))
25376             return;
25377
25378         var newNode = iD.Node({ loc: loc });
25379
25380         context.perform(
25381             iD.actions.AddMidpoint({ loc: loc, edge: edge}, newNode),
25382             ReplaceTemporaryNode(newNode),
25383             annotation);
25384
25385         finished = true;
25386         context.enter(mode);
25387     };
25388
25389     // Connect the way to an existing node and continue drawing.
25390     drawWay.addNode = function(node) {
25391
25392         // Avoid creating duplicate segments
25393         if (way.areAdjacent(node.id, way.nodes[way.nodes.length - 1])) return;
25394
25395         context.perform(
25396             ReplaceTemporaryNode(node),
25397             annotation);
25398
25399         finished = true;
25400         context.enter(mode);
25401     };
25402
25403     // Finish the draw operation, removing the temporary node. If the way has enough
25404     // nodes to be valid, it's selected. Otherwise, return to browse mode.
25405     drawWay.finish = function() {
25406         context.pop();
25407         finished = true;
25408
25409         window.setTimeout(function() {
25410             context.map().dblclickEnable(true);
25411         }, 1000);
25412
25413         if (context.hasEntity(wayId)) {
25414             context.enter(
25415                 iD.modes.Select(context, [wayId])
25416                     .suppressMenu(true)
25417                     .newFeature(true));
25418         } else {
25419             context.enter(iD.modes.Browse(context));
25420         }
25421     };
25422
25423     // Cancel the draw operation and return to browse, deleting everything drawn.
25424     drawWay.cancel = function() {
25425         context.perform(
25426             d3.functor(baseGraph),
25427             t('operations.cancel_draw.annotation'));
25428
25429         window.setTimeout(function() {
25430             context.map().dblclickEnable(true);
25431         }, 1000);
25432
25433         finished = true;
25434         context.enter(iD.modes.Browse(context));
25435     };
25436
25437     drawWay.tail = function(text) {
25438         draw.tail(text);
25439         return drawWay;
25440     };
25441
25442     return drawWay;
25443 };
25444 iD.behavior.Edit = function(context) {
25445     function edit() {
25446         context.map()
25447             .minzoom(context.minEditableZoom());
25448     }
25449
25450     edit.off = function() {
25451         context.map()
25452             .minzoom(0);
25453     };
25454
25455     return edit;
25456 };
25457 iD.behavior.Hash = function(context) {
25458     var s0 = null, // cached location.hash
25459         lat = 90 - 1e-8; // allowable latitude range
25460
25461     var parser = function(map, s) {
25462         var q = iD.util.stringQs(s);
25463         var args = (q.map || '').split('/').map(Number);
25464         if (args.length < 3 || args.some(isNaN)) {
25465             return true; // replace bogus hash
25466         } else if (s !== formatter(map).slice(1)) {
25467             map.centerZoom([args[1],
25468                 Math.min(lat, Math.max(-lat, args[2]))], args[0]);
25469         }
25470     };
25471
25472     var formatter = function(map) {
25473         var mode = context.mode(),
25474             center = map.center(),
25475             zoom = map.zoom(),
25476             precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
25477             q = _.omit(iD.util.stringQs(location.hash.substring(1)), 'comment'),
25478             newParams = {};
25479
25480         if (mode && mode.id === 'browse') {
25481             delete q.id;
25482         } else {
25483             var selected = context.selectedIDs().filter(function(id) {
25484                 return !context.entity(id).isNew();
25485             });
25486             if (selected.length) {
25487                 newParams.id = selected.join(',');
25488             }
25489         }
25490
25491         newParams.map = zoom.toFixed(2) +
25492                 '/' + center[0].toFixed(precision) +
25493                 '/' + center[1].toFixed(precision);
25494
25495         return '#' + iD.util.qsString(_.assign(q, newParams), true);
25496     };
25497
25498     function update() {
25499         if (context.inIntro()) return;
25500         var s1 = formatter(context.map());
25501         if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map!
25502     }
25503
25504     var throttledUpdate = _.throttle(update, 500);
25505
25506     function hashchange() {
25507         if (location.hash === s0) return; // ignore spurious hashchange events
25508         if (parser(context.map(), (s0 = location.hash).substring(1))) {
25509             update(); // replace bogus hash
25510         }
25511     }
25512
25513     function hash() {
25514         context.map()
25515             .on('move.hash', throttledUpdate);
25516
25517         context
25518             .on('enter.hash', throttledUpdate);
25519
25520         d3.select(window)
25521             .on('hashchange.hash', hashchange);
25522
25523         if (location.hash) {
25524             var q = iD.util.stringQs(location.hash.substring(1));
25525             if (q.id) context.zoomToEntity(q.id.split(',')[0], !q.map);
25526             if (q.comment) context.storage('comment', q.comment);
25527             hashchange();
25528             if (q.map) hash.hadHash = true;
25529         }
25530     }
25531
25532     hash.off = function() {
25533         throttledUpdate.cancel();
25534
25535         context.map()
25536             .on('move.hash', null);
25537
25538         context
25539             .on('enter.hash', null);
25540
25541         d3.select(window)
25542             .on('hashchange.hash', null);
25543
25544         location.hash = '';
25545     };
25546
25547     return hash;
25548 };
25549 /*
25550    The hover behavior adds the `.hover` class on mouseover to all elements to which
25551    the identical datum is bound, and removes it on mouseout.
25552
25553    The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
25554    representation may consist of several elements scattered throughout the DOM hierarchy.
25555    Only one of these elements can have the :hover pseudo-class, but all of them will
25556    have the .hover class.
25557  */
25558 iD.behavior.Hover = function() {
25559     var dispatch = d3.dispatch('hover'),
25560         selection,
25561         altDisables,
25562         target;
25563
25564     function keydown() {
25565         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
25566             dispatch.hover(null);
25567             selection.selectAll('.hover')
25568                 .classed('hover-suppressed', true)
25569                 .classed('hover', false);
25570         }
25571     }
25572
25573     function keyup() {
25574         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
25575             dispatch.hover(target ? target.id : null);
25576             selection.selectAll('.hover-suppressed')
25577                 .classed('hover-suppressed', false)
25578                 .classed('hover', true);
25579         }
25580     }
25581
25582     var hover = function(__) {
25583         selection = __;
25584
25585         function enter(d) {
25586             if (d === target) return;
25587
25588             target = d;
25589
25590             selection.selectAll('.hover')
25591                 .classed('hover', false);
25592             selection.selectAll('.hover-suppressed')
25593                 .classed('hover-suppressed', false);
25594
25595             if (target instanceof iD.Entity) {
25596                 var selector = '.' + target.id;
25597
25598                 if (target.type === 'relation') {
25599                     target.members.forEach(function(member) {
25600                         selector += ', .' + member.id;
25601                     });
25602                 }
25603
25604                 var suppressed = altDisables && d3.event && d3.event.altKey;
25605
25606                 selection.selectAll(selector)
25607                     .classed(suppressed ? 'hover-suppressed' : 'hover', true);
25608
25609                 dispatch.hover(target.id);
25610             } else {
25611                 dispatch.hover(null);
25612             }
25613         }
25614
25615         var down;
25616
25617         function mouseover() {
25618             if (down) return;
25619             var target = d3.event.target;
25620             enter(target ? target.__data__ : null);
25621         }
25622
25623         function mouseout() {
25624             if (down) return;
25625             var target = d3.event.relatedTarget;
25626             enter(target ? target.__data__ : null);
25627         }
25628
25629         function mousedown() {
25630             down = true;
25631             d3.select(window)
25632                 .on('mouseup.hover', mouseup);
25633         }
25634
25635         function mouseup() {
25636             down = false;
25637         }
25638
25639         selection
25640             .on('mouseover.hover', mouseover)
25641             .on('mouseout.hover', mouseout)
25642             .on('mousedown.hover', mousedown)
25643             .on('mouseup.hover', mouseup);
25644
25645         d3.select(window)
25646             .on('keydown.hover', keydown)
25647             .on('keyup.hover', keyup);
25648     };
25649
25650     hover.off = function(selection) {
25651         selection.selectAll('.hover')
25652             .classed('hover', false);
25653         selection.selectAll('.hover-suppressed')
25654             .classed('hover-suppressed', false);
25655
25656         selection
25657             .on('mouseover.hover', null)
25658             .on('mouseout.hover', null)
25659             .on('mousedown.hover', null)
25660             .on('mouseup.hover', null);
25661
25662         d3.select(window)
25663             .on('keydown.hover', null)
25664             .on('keyup.hover', null)
25665             .on('mouseup.hover', null);
25666     };
25667
25668     hover.altDisables = function(_) {
25669         if (!arguments.length) return altDisables;
25670         altDisables = _;
25671         return hover;
25672     };
25673
25674     return d3.rebind(hover, dispatch, 'on');
25675 };
25676 iD.behavior.Lasso = function(context) {
25677
25678     var behavior = function(selection) {
25679         var lasso;
25680
25681         function mousedown() {
25682             var button = 0;  // left
25683             if (d3.event.button === button && d3.event.shiftKey === true) {
25684                 lasso = null;
25685
25686                 selection
25687                     .on('mousemove.lasso', mousemove)
25688                     .on('mouseup.lasso', mouseup);
25689
25690                 d3.event.stopPropagation();
25691             }
25692         }
25693
25694         function mousemove() {
25695             if (!lasso) {
25696                 lasso = iD.ui.Lasso(context);
25697                 context.surface().call(lasso);
25698             }
25699
25700             lasso.p(context.mouse());
25701         }
25702
25703         function normalize(a, b) {
25704             return [
25705                 [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
25706                 [Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
25707         }
25708
25709         function lassoed() {
25710             if (!lasso) return [];
25711
25712             var graph = context.graph(),
25713                 bounds = lasso.extent().map(context.projection.invert),
25714                 extent = iD.geo.Extent(normalize(bounds[0], bounds[1]));
25715
25716             return _.map(context.intersects(extent).filter(function(entity) {
25717                 return entity.type === 'node' &&
25718                     iD.geo.pointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
25719                     !context.features().isHidden(entity, graph, entity.geometry(graph));
25720             }), 'id');
25721         }
25722
25723         function mouseup() {
25724             selection
25725                 .on('mousemove.lasso', null)
25726                 .on('mouseup.lasso', null);
25727
25728             if (!lasso) return;
25729
25730             var ids = lassoed();
25731             lasso.close();
25732
25733             if (ids.length) {
25734                 context.enter(iD.modes.Select(context, ids));
25735             }
25736         }
25737
25738         selection
25739             .on('mousedown.lasso', mousedown);
25740     };
25741
25742     behavior.off = function(selection) {
25743         selection.on('mousedown.lasso', null);
25744     };
25745
25746     return behavior;
25747 };
25748 iD.behavior.Paste = function(context) {
25749     var keybinding = d3.keybinding('paste');
25750
25751     function omitTag(v, k) {
25752         return (
25753             k === 'phone' ||
25754             k === 'fax' ||
25755             k === 'email' ||
25756             k === 'website' ||
25757             k === 'url' ||
25758             k === 'note' ||
25759             k === 'description' ||
25760             k.indexOf('name') !== -1 ||
25761             k.indexOf('wiki') === 0 ||
25762             k.indexOf('addr:') === 0 ||
25763             k.indexOf('contact:') === 0
25764         );
25765     }
25766
25767     function doPaste() {
25768         d3.event.preventDefault();
25769         if (context.inIntro()) return;
25770
25771         var baseGraph = context.graph(),
25772             mouse = context.mouse(),
25773             projection = context.projection,
25774             viewport = iD.geo.Extent(projection.clipExtent()).polygon();
25775
25776         if (!iD.geo.pointInPolygon(mouse, viewport)) return;
25777
25778         var extent = iD.geo.Extent(),
25779             oldIDs = context.copyIDs(),
25780             oldGraph = context.copyGraph(),
25781             newIDs = [];
25782
25783         if (!oldIDs.length) return;
25784
25785         var action = iD.actions.CopyEntities(oldIDs, oldGraph);
25786         context.perform(action);
25787
25788         var copies = action.copies();
25789         for (var id in copies) {
25790             var oldEntity = oldGraph.entity(id),
25791                 newEntity = copies[id];
25792
25793             extent._extend(oldEntity.extent(oldGraph));
25794             newIDs.push(newEntity.id);
25795             context.perform(iD.actions.ChangeTags(newEntity.id, _.omit(newEntity.tags, omitTag)));
25796         }
25797
25798         // Put pasted objects where mouse pointer is..
25799         var center = projection(extent.center()),
25800             delta = [ mouse[0] - center[0], mouse[1] - center[1] ];
25801
25802         context.perform(iD.actions.Move(newIDs, delta, projection));
25803         context.enter(iD.modes.Move(context, newIDs, baseGraph));
25804     }
25805
25806     function paste() {
25807         keybinding.on(iD.ui.cmd('⌘V'), doPaste);
25808         d3.select(document).call(keybinding);
25809         return paste;
25810     }
25811
25812     paste.off = function() {
25813         d3.select(document).call(keybinding.off);
25814     };
25815
25816     return paste;
25817 };
25818 iD.behavior.Select = function(context) {
25819     function keydown() {
25820         if (d3.event && d3.event.shiftKey) {
25821             context.surface()
25822                 .classed('behavior-multiselect', true);
25823         }
25824     }
25825
25826     function keyup() {
25827         if (!d3.event || !d3.event.shiftKey) {
25828             context.surface()
25829                 .classed('behavior-multiselect', false);
25830         }
25831     }
25832
25833     function click() {
25834         var datum = d3.event.target.__data__,
25835             lasso = d3.select('#surface .lasso').node(),
25836             mode = context.mode();
25837
25838         if (!(datum instanceof iD.Entity)) {
25839             if (!d3.event.shiftKey && !lasso && mode.id !== 'browse')
25840                 context.enter(iD.modes.Browse(context));
25841
25842         } else if (!d3.event.shiftKey && !lasso) {
25843             // Avoid re-entering Select mode with same entity.
25844             if (context.selectedIDs().length !== 1 || context.selectedIDs()[0] !== datum.id) {
25845                 context.enter(iD.modes.Select(context, [datum.id]));
25846             } else {
25847                 mode.suppressMenu(false).reselect();
25848             }
25849         } else if (context.selectedIDs().indexOf(datum.id) >= 0) {
25850             var selectedIDs = _.without(context.selectedIDs(), datum.id);
25851             context.enter(selectedIDs.length ?
25852                 iD.modes.Select(context, selectedIDs) :
25853                 iD.modes.Browse(context));
25854
25855         } else {
25856             context.enter(iD.modes.Select(context, context.selectedIDs().concat([datum.id])));
25857         }
25858     }
25859
25860     var behavior = function(selection) {
25861         d3.select(window)
25862             .on('keydown.select', keydown)
25863             .on('keyup.select', keyup);
25864
25865         selection.on('click.select', click);
25866
25867         keydown();
25868     };
25869
25870     behavior.off = function(selection) {
25871         d3.select(window)
25872             .on('keydown.select', null)
25873             .on('keyup.select', null);
25874
25875         selection.on('click.select', null);
25876
25877         keyup();
25878     };
25879
25880     return behavior;
25881 };
25882 iD.behavior.Tail = function() {
25883     var text,
25884         container,
25885         xmargin = 25,
25886         tooltipSize = [0, 0],
25887         selectionSize = [0, 0];
25888
25889     function tail(selection) {
25890         if (!text) return;
25891
25892         d3.select(window)
25893             .on('resize.tail', function() { selectionSize = selection.dimensions(); });
25894
25895         function show() {
25896             container.style('display', 'block');
25897             tooltipSize = container.dimensions();
25898         }
25899
25900         function mousemove() {
25901             if (container.style('display') === 'none') show();
25902             var xoffset = ((d3.event.clientX + tooltipSize[0] + xmargin) > selectionSize[0]) ?
25903                 -tooltipSize[0] - xmargin : xmargin;
25904             container.classed('left', xoffset > 0);
25905             iD.util.setTransform(container, d3.event.clientX + xoffset, d3.event.clientY);
25906         }
25907
25908         function mouseleave() {
25909             if (d3.event.relatedTarget !== container.node()) {
25910                 container.style('display', 'none');
25911             }
25912         }
25913
25914         function mouseenter() {
25915             if (d3.event.relatedTarget !== container.node()) {
25916                 show();
25917             }
25918         }
25919
25920         container = d3.select(document.body)
25921             .append('div')
25922             .style('display', 'none')
25923             .attr('class', 'tail tooltip-inner');
25924
25925         container.append('div')
25926             .text(text);
25927
25928         selection
25929             .on('mousemove.tail', mousemove)
25930             .on('mouseenter.tail', mouseenter)
25931             .on('mouseleave.tail', mouseleave);
25932
25933         container
25934             .on('mousemove.tail', mousemove);
25935
25936         tooltipSize = container.dimensions();
25937         selectionSize = selection.dimensions();
25938     }
25939
25940     tail.off = function(selection) {
25941         if (!text) return;
25942
25943         container
25944             .on('mousemove.tail', null)
25945             .remove();
25946
25947         selection
25948             .on('mousemove.tail', null)
25949             .on('mouseenter.tail', null)
25950             .on('mouseleave.tail', null);
25951
25952         d3.select(window)
25953             .on('resize.tail', null);
25954     };
25955
25956     tail.text = function(_) {
25957         if (!arguments.length) return text;
25958         text = _;
25959         return tail;
25960     };
25961
25962     return tail;
25963 };
25964 iD.modes = {};
25965 iD.modes.AddArea = function(context) {
25966     var mode = {
25967         id: 'add-area',
25968         button: 'area',
25969         title: t('modes.add_area.title'),
25970         description: t('modes.add_area.description'),
25971         key: '3'
25972     };
25973
25974     var behavior = iD.behavior.AddWay(context)
25975             .tail(t('modes.add_area.tail'))
25976             .on('start', start)
25977             .on('startFromWay', startFromWay)
25978             .on('startFromNode', startFromNode),
25979         defaultTags = {area: 'yes'};
25980
25981     function start(loc) {
25982         var graph = context.graph(),
25983             node = iD.Node({loc: loc}),
25984             way = iD.Way({tags: defaultTags});
25985
25986         context.perform(
25987             iD.actions.AddEntity(node),
25988             iD.actions.AddEntity(way),
25989             iD.actions.AddVertex(way.id, node.id),
25990             iD.actions.AddVertex(way.id, node.id));
25991
25992         context.enter(iD.modes.DrawArea(context, way.id, graph));
25993     }
25994
25995     function startFromWay(loc, edge) {
25996         var graph = context.graph(),
25997             node = iD.Node({loc: loc}),
25998             way = iD.Way({tags: defaultTags});
25999
26000         context.perform(
26001             iD.actions.AddEntity(node),
26002             iD.actions.AddEntity(way),
26003             iD.actions.AddVertex(way.id, node.id),
26004             iD.actions.AddVertex(way.id, node.id),
26005             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
26006
26007         context.enter(iD.modes.DrawArea(context, way.id, graph));
26008     }
26009
26010     function startFromNode(node) {
26011         var graph = context.graph(),
26012             way = iD.Way({tags: defaultTags});
26013
26014         context.perform(
26015             iD.actions.AddEntity(way),
26016             iD.actions.AddVertex(way.id, node.id),
26017             iD.actions.AddVertex(way.id, node.id));
26018
26019         context.enter(iD.modes.DrawArea(context, way.id, graph));
26020     }
26021
26022     mode.enter = function() {
26023         context.install(behavior);
26024     };
26025
26026     mode.exit = function() {
26027         context.uninstall(behavior);
26028     };
26029
26030     return mode;
26031 };
26032 iD.modes.AddLine = function(context) {
26033     var mode = {
26034         id: 'add-line',
26035         button: 'line',
26036         title: t('modes.add_line.title'),
26037         description: t('modes.add_line.description'),
26038         key: '2'
26039     };
26040
26041     var behavior = iD.behavior.AddWay(context)
26042         .tail(t('modes.add_line.tail'))
26043         .on('start', start)
26044         .on('startFromWay', startFromWay)
26045         .on('startFromNode', startFromNode);
26046
26047     function start(loc) {
26048         var baseGraph = context.graph(),
26049             node = iD.Node({loc: loc}),
26050             way = iD.Way();
26051
26052         context.perform(
26053             iD.actions.AddEntity(node),
26054             iD.actions.AddEntity(way),
26055             iD.actions.AddVertex(way.id, node.id));
26056
26057         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
26058     }
26059
26060     function startFromWay(loc, edge) {
26061         var baseGraph = context.graph(),
26062             node = iD.Node({loc: loc}),
26063             way = iD.Way();
26064
26065         context.perform(
26066             iD.actions.AddEntity(node),
26067             iD.actions.AddEntity(way),
26068             iD.actions.AddVertex(way.id, node.id),
26069             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
26070
26071         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
26072     }
26073
26074     function startFromNode(node) {
26075         var baseGraph = context.graph(),
26076             way = iD.Way();
26077
26078         context.perform(
26079             iD.actions.AddEntity(way),
26080             iD.actions.AddVertex(way.id, node.id));
26081
26082         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
26083     }
26084
26085     mode.enter = function() {
26086         context.install(behavior);
26087     };
26088
26089     mode.exit = function() {
26090         context.uninstall(behavior);
26091     };
26092
26093     return mode;
26094 };
26095 iD.modes.AddPoint = function(context) {
26096     var mode = {
26097         id: 'add-point',
26098         button: 'point',
26099         title: t('modes.add_point.title'),
26100         description: t('modes.add_point.description'),
26101         key: '1'
26102     };
26103
26104     var behavior = iD.behavior.Draw(context)
26105         .tail(t('modes.add_point.tail'))
26106         .on('click', add)
26107         .on('clickWay', addWay)
26108         .on('clickNode', addNode)
26109         .on('cancel', cancel)
26110         .on('finish', cancel);
26111
26112     function add(loc) {
26113         var node = iD.Node({loc: loc});
26114
26115         context.perform(
26116             iD.actions.AddEntity(node),
26117             t('operations.add.annotation.point'));
26118
26119         context.enter(
26120             iD.modes.Select(context, [node.id])
26121                 .suppressMenu(true)
26122                 .newFeature(true));
26123     }
26124
26125     function addWay(loc) {
26126         add(loc);
26127     }
26128
26129     function addNode(node) {
26130         add(node.loc);
26131     }
26132
26133     function cancel() {
26134         context.enter(iD.modes.Browse(context));
26135     }
26136
26137     mode.enter = function() {
26138         context.install(behavior);
26139     };
26140
26141     mode.exit = function() {
26142         context.uninstall(behavior);
26143     };
26144
26145     return mode;
26146 };
26147 iD.modes.Browse = function(context) {
26148     var mode = {
26149         button: 'browse',
26150         id: 'browse',
26151         title: t('modes.browse.title'),
26152         description: t('modes.browse.description')
26153     }, sidebar;
26154
26155     var behaviors = [
26156         iD.behavior.Paste(context),
26157         iD.behavior.Hover(context)
26158             .on('hover', context.ui().sidebar.hover),
26159         iD.behavior.Select(context),
26160         iD.behavior.Lasso(context),
26161         iD.modes.DragNode(context).behavior];
26162
26163     mode.enter = function() {
26164         behaviors.forEach(function(behavior) {
26165             context.install(behavior);
26166         });
26167
26168         // Get focus on the body.
26169         if (document.activeElement && document.activeElement.blur) {
26170             document.activeElement.blur();
26171         }
26172
26173         if (sidebar) {
26174             context.ui().sidebar.show(sidebar);
26175         } else {
26176             context.ui().sidebar.select(null);
26177         }
26178     };
26179
26180     mode.exit = function() {
26181         context.ui().sidebar.hover.cancel();
26182         behaviors.forEach(function(behavior) {
26183             context.uninstall(behavior);
26184         });
26185
26186         if (sidebar) {
26187             context.ui().sidebar.hide();
26188         }
26189     };
26190
26191     mode.sidebar = function(_) {
26192         if (!arguments.length) return sidebar;
26193         sidebar = _;
26194         return mode;
26195     };
26196
26197     return mode;
26198 };
26199 iD.modes.DragNode = function(context) {
26200     var mode = {
26201         id: 'drag-node',
26202         button: 'browse'
26203     };
26204
26205     var nudgeInterval,
26206         activeIDs,
26207         wasMidpoint,
26208         cancelled,
26209         selectedIDs = [],
26210         hover = iD.behavior.Hover(context)
26211             .altDisables(true)
26212             .on('hover', context.ui().sidebar.hover),
26213         edit = iD.behavior.Edit(context);
26214
26215     function edge(point, size) {
26216         var pad = [30, 100, 30, 100];
26217         if (point[0] > size[0] - pad[0]) return [-10, 0];
26218         else if (point[0] < pad[2]) return [10, 0];
26219         else if (point[1] > size[1] - pad[1]) return [0, -10];
26220         else if (point[1] < pad[3]) return [0, 10];
26221         return null;
26222     }
26223
26224     function startNudge(nudge) {
26225         if (nudgeInterval) window.clearInterval(nudgeInterval);
26226         nudgeInterval = window.setInterval(function() {
26227             context.pan(nudge);
26228         }, 50);
26229     }
26230
26231     function stopNudge() {
26232         if (nudgeInterval) window.clearInterval(nudgeInterval);
26233         nudgeInterval = null;
26234     }
26235
26236     function moveAnnotation(entity) {
26237         return t('operations.move.annotation.' + entity.geometry(context.graph()));
26238     }
26239
26240     function connectAnnotation(entity) {
26241         return t('operations.connect.annotation.' + entity.geometry(context.graph()));
26242     }
26243
26244     function origin(entity) {
26245         return context.projection(entity.loc);
26246     }
26247
26248     function start(entity) {
26249         cancelled = d3.event.sourceEvent.shiftKey ||
26250             context.features().hasHiddenConnections(entity, context.graph());
26251
26252         if (cancelled) return behavior.cancel();
26253
26254         wasMidpoint = entity.type === 'midpoint';
26255         if (wasMidpoint) {
26256             var midpoint = entity;
26257             entity = iD.Node();
26258             context.perform(iD.actions.AddMidpoint(midpoint, entity));
26259
26260              var vertex = context.surface()
26261                 .selectAll('.' + entity.id);
26262              behavior.target(vertex.node(), entity);
26263
26264         } else {
26265             context.perform(
26266                 iD.actions.Noop());
26267         }
26268
26269         activeIDs = _.map(context.graph().parentWays(entity), 'id');
26270         activeIDs.push(entity.id);
26271
26272         context.enter(mode);
26273     }
26274
26275     function datum() {
26276         if (d3.event.sourceEvent.altKey) {
26277             return {};
26278         }
26279
26280         return d3.event.sourceEvent.target.__data__ || {};
26281     }
26282
26283     // via https://gist.github.com/shawnbot/4166283
26284     function childOf(p, c) {
26285         if (p === c) return false;
26286         while (c && c !== p) c = c.parentNode;
26287         return c === p;
26288     }
26289
26290     function move(entity) {
26291         if (cancelled) return;
26292         d3.event.sourceEvent.stopPropagation();
26293
26294         var nudge = childOf(context.container().node(),
26295             d3.event.sourceEvent.toElement) &&
26296             edge(d3.event.point, context.map().dimensions());
26297
26298         if (nudge) startNudge(nudge);
26299         else stopNudge();
26300
26301         var loc = context.projection.invert(d3.event.point);
26302
26303         var d = datum();
26304         if (d.type === 'node' && d.id !== entity.id) {
26305             loc = d.loc;
26306         } else if (d.type === 'way' && !d3.select(d3.event.sourceEvent.target).classed('fill')) {
26307             loc = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection).loc;
26308         }
26309
26310         context.replace(
26311             iD.actions.MoveNode(entity.id, loc),
26312             moveAnnotation(entity));
26313     }
26314
26315     function end(entity) {
26316         if (cancelled) return;
26317
26318         var d = datum();
26319
26320         if (d.type === 'way') {
26321             var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection);
26322             context.replace(
26323                 iD.actions.AddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity),
26324                 connectAnnotation(d));
26325
26326         } else if (d.type === 'node' && d.id !== entity.id) {
26327             context.replace(
26328                 iD.actions.Connect([d.id, entity.id]),
26329                 connectAnnotation(d));
26330
26331         } else if (wasMidpoint) {
26332             context.replace(
26333                 iD.actions.Noop(),
26334                 t('operations.add.annotation.vertex'));
26335
26336         } else {
26337             context.replace(
26338                 iD.actions.Noop(),
26339                 moveAnnotation(entity));
26340         }
26341
26342         var reselection = selectedIDs.filter(function(id) {
26343             return context.graph().hasEntity(id);
26344         });
26345
26346         if (reselection.length) {
26347             context.enter(
26348                 iD.modes.Select(context, reselection)
26349                     .suppressMenu(true));
26350         } else {
26351             context.enter(iD.modes.Browse(context));
26352         }
26353     }
26354
26355     function cancel() {
26356         behavior.cancel();
26357         context.enter(iD.modes.Browse(context));
26358     }
26359
26360     function setActiveElements() {
26361         context.surface().selectAll(iD.util.entitySelector(activeIDs))
26362             .classed('active', true);
26363     }
26364
26365     var behavior = iD.behavior.drag()
26366         .delegate('g.node, g.point, g.midpoint')
26367         .surface(context.surface().node())
26368         .origin(origin)
26369         .on('start', start)
26370         .on('move', move)
26371         .on('end', end);
26372
26373     mode.enter = function() {
26374         context.install(hover);
26375         context.install(edit);
26376
26377         context.history()
26378             .on('undone.drag-node', cancel);
26379
26380         context.map()
26381             .on('drawn.drag-node', setActiveElements);
26382
26383         setActiveElements();
26384     };
26385
26386     mode.exit = function() {
26387         context.ui().sidebar.hover.cancel();
26388         context.uninstall(hover);
26389         context.uninstall(edit);
26390
26391         context.history()
26392             .on('undone.drag-node', null);
26393
26394         context.map()
26395             .on('drawn.drag-node', null);
26396
26397         context.surface()
26398             .selectAll('.active')
26399             .classed('active', false);
26400
26401         stopNudge();
26402     };
26403
26404     mode.selectedIDs = function(_) {
26405         if (!arguments.length) return selectedIDs;
26406         selectedIDs = _;
26407         return mode;
26408     };
26409
26410     mode.behavior = behavior;
26411
26412     return mode;
26413 };
26414 iD.modes.DrawArea = function(context, wayId, baseGraph) {
26415     var mode = {
26416         button: 'area',
26417         id: 'draw-area'
26418     };
26419
26420     var behavior;
26421
26422     mode.enter = function() {
26423         var way = context.entity(wayId),
26424             headId = way.nodes[way.nodes.length - 2],
26425             tailId = way.first();
26426
26427         behavior = iD.behavior.DrawWay(context, wayId, -1, mode, baseGraph)
26428             .tail(t('modes.draw_area.tail'));
26429
26430         var addNode = behavior.addNode;
26431
26432         behavior.addNode = function(node) {
26433             if (node.id === headId || node.id === tailId) {
26434                 behavior.finish();
26435             } else {
26436                 addNode(node);
26437             }
26438         };
26439
26440         context.install(behavior);
26441     };
26442
26443     mode.exit = function() {
26444         context.uninstall(behavior);
26445     };
26446
26447     mode.selectedIDs = function() {
26448         return [wayId];
26449     };
26450
26451     return mode;
26452 };
26453 iD.modes.DrawLine = function(context, wayId, baseGraph, affix) {
26454     var mode = {
26455         button: 'line',
26456         id: 'draw-line'
26457     };
26458
26459     var behavior;
26460
26461     mode.enter = function() {
26462         var way = context.entity(wayId),
26463             index = (affix === 'prefix') ? 0 : undefined,
26464             headId = (affix === 'prefix') ? way.first() : way.last();
26465
26466         behavior = iD.behavior.DrawWay(context, wayId, index, mode, baseGraph)
26467             .tail(t('modes.draw_line.tail'));
26468
26469         var addNode = behavior.addNode;
26470
26471         behavior.addNode = function(node) {
26472             if (node.id === headId) {
26473                 behavior.finish();
26474             } else {
26475                 addNode(node);
26476             }
26477         };
26478
26479         context.install(behavior);
26480     };
26481
26482     mode.exit = function() {
26483         context.uninstall(behavior);
26484     };
26485
26486     mode.selectedIDs = function() {
26487         return [wayId];
26488     };
26489
26490     return mode;
26491 };
26492 iD.modes.Move = function(context, entityIDs, baseGraph) {
26493     var mode = {
26494         id: 'move',
26495         button: 'browse'
26496     };
26497
26498     var keybinding = d3.keybinding('move'),
26499         edit = iD.behavior.Edit(context),
26500         annotation = entityIDs.length === 1 ?
26501             t('operations.move.annotation.' + context.geometry(entityIDs[0])) :
26502             t('operations.move.annotation.multiple'),
26503         cache,
26504         origin,
26505         nudgeInterval;
26506
26507     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
26508
26509     function edge(point, size) {
26510         var pad = [30, 100, 30, 100];
26511         if (point[0] > size[0] - pad[0]) return [-10, 0];
26512         else if (point[0] < pad[2]) return [10, 0];
26513         else if (point[1] > size[1] - pad[1]) return [0, -10];
26514         else if (point[1] < pad[3]) return [0, 10];
26515         return null;
26516     }
26517
26518     function startNudge(nudge) {
26519         if (nudgeInterval) window.clearInterval(nudgeInterval);
26520         nudgeInterval = window.setInterval(function() {
26521             context.pan(nudge);
26522
26523             var currMouse = context.mouse(),
26524                 origMouse = context.projection(origin),
26525                 delta = vecSub(vecSub(currMouse, origMouse), nudge),
26526                 action = iD.actions.Move(entityIDs, delta, context.projection, cache);
26527
26528             context.overwrite(action, annotation);
26529
26530         }, 50);
26531     }
26532
26533     function stopNudge() {
26534         if (nudgeInterval) window.clearInterval(nudgeInterval);
26535         nudgeInterval = null;
26536     }
26537
26538     function move() {
26539         var currMouse = context.mouse(),
26540             origMouse = context.projection(origin),
26541             delta = vecSub(currMouse, origMouse),
26542             action = iD.actions.Move(entityIDs, delta, context.projection, cache);
26543
26544         context.overwrite(action, annotation);
26545
26546         var nudge = edge(currMouse, context.map().dimensions());
26547         if (nudge) startNudge(nudge);
26548         else stopNudge();
26549     }
26550
26551     function finish() {
26552         d3.event.stopPropagation();
26553         context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
26554         stopNudge();
26555     }
26556
26557     function cancel() {
26558         if (baseGraph) {
26559             while (context.graph() !== baseGraph) context.pop();
26560             context.enter(iD.modes.Browse(context));
26561         } else {
26562             context.pop();
26563             context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
26564         }
26565         stopNudge();
26566     }
26567
26568     function undone() {
26569         context.enter(iD.modes.Browse(context));
26570     }
26571
26572     mode.enter = function() {
26573         origin = context.map().mouseCoordinates();
26574         cache = {};
26575
26576         context.install(edit);
26577
26578         context.perform(
26579             iD.actions.Noop(),
26580             annotation);
26581
26582         context.surface()
26583             .on('mousemove.move', move)
26584             .on('click.move', finish);
26585
26586         context.history()
26587             .on('undone.move', undone);
26588
26589         keybinding
26590             .on('⎋', cancel)
26591             .on('↩', finish);
26592
26593         d3.select(document)
26594             .call(keybinding);
26595     };
26596
26597     mode.exit = function() {
26598         stopNudge();
26599
26600         context.uninstall(edit);
26601
26602         context.surface()
26603             .on('mousemove.move', null)
26604             .on('click.move', null);
26605
26606         context.history()
26607             .on('undone.move', null);
26608
26609         keybinding.off();
26610     };
26611
26612     return mode;
26613 };
26614 iD.modes.RotateWay = function(context, wayId) {
26615     var mode = {
26616         id: 'rotate-way',
26617         button: 'browse'
26618     };
26619
26620     var keybinding = d3.keybinding('rotate-way'),
26621         edit = iD.behavior.Edit(context);
26622
26623     mode.enter = function() {
26624         context.install(edit);
26625
26626         var annotation = t('operations.rotate.annotation.' + context.geometry(wayId)),
26627             way = context.graph().entity(wayId),
26628             nodes = _.uniq(context.graph().childNodes(way)),
26629             points = nodes.map(function(n) { return context.projection(n.loc); }),
26630             pivot = d3.geom.polygon(points).centroid(),
26631             angle;
26632
26633         context.perform(
26634             iD.actions.Noop(),
26635             annotation);
26636
26637         function rotate() {
26638
26639             var mousePoint = context.mouse(),
26640                 newAngle = Math.atan2(mousePoint[1] - pivot[1], mousePoint[0] - pivot[0]);
26641
26642             if (typeof angle === 'undefined') angle = newAngle;
26643
26644             context.replace(
26645                 iD.actions.RotateWay(wayId, pivot, newAngle - angle, context.projection),
26646                 annotation);
26647
26648             angle = newAngle;
26649         }
26650
26651         function finish() {
26652             d3.event.stopPropagation();
26653             context.enter(iD.modes.Select(context, [wayId])
26654                 .suppressMenu(true));
26655         }
26656
26657         function cancel() {
26658             context.pop();
26659             context.enter(iD.modes.Select(context, [wayId])
26660                 .suppressMenu(true));
26661         }
26662
26663         function undone() {
26664             context.enter(iD.modes.Browse(context));
26665         }
26666
26667         context.surface()
26668             .on('mousemove.rotate-way', rotate)
26669             .on('click.rotate-way', finish);
26670
26671         context.history()
26672             .on('undone.rotate-way', undone);
26673
26674         keybinding
26675             .on('⎋', cancel)
26676             .on('↩', finish);
26677
26678         d3.select(document)
26679             .call(keybinding);
26680     };
26681
26682     mode.exit = function() {
26683         context.uninstall(edit);
26684
26685         context.surface()
26686             .on('mousemove.rotate-way', null)
26687             .on('click.rotate-way', null);
26688
26689         context.history()
26690             .on('undone.rotate-way', null);
26691
26692         keybinding.off();
26693     };
26694
26695     return mode;
26696 };
26697 iD.modes.Save = function(context) {
26698     var ui = iD.ui.Commit(context)
26699             .on('cancel', cancel)
26700             .on('save', save);
26701
26702     function cancel() {
26703         context.enter(iD.modes.Browse(context));
26704     }
26705
26706     function save(e, tryAgain) {
26707         function withChildNodes(ids, graph) {
26708             return _.uniq(_.reduce(ids, function(result, id) {
26709                 var e = graph.entity(id);
26710                 if (e.type === 'way') {
26711                     try {
26712                         var cn = graph.childNodes(e);
26713                         result.push.apply(result, _.map(_.filter(cn, 'version'), 'id'));
26714                     } catch(err) {
26715                         /* eslint-disable no-console */
26716                         if (typeof console !== 'undefined') console.error(err);
26717                         /* eslint-enable no-console */
26718                     }
26719                 }
26720                 return result;
26721             }, _.clone(ids)));
26722         }
26723
26724         var loading = iD.ui.Loading(context).message(t('save.uploading')).blocking(true),
26725             history = context.history(),
26726             origChanges = history.changes(iD.actions.DiscardTags(history.difference())),
26727             localGraph = context.graph(),
26728             remoteGraph = iD.Graph(history.base(), true),
26729             modified = _.filter(history.difference().summary(), {changeType: 'modified'}),
26730             toCheck = _.map(_.map(modified, 'entity'), 'id'),
26731             toLoad = withChildNodes(toCheck, localGraph),
26732             conflicts = [],
26733             errors = [];
26734
26735         if (!tryAgain) history.perform(iD.actions.Noop());  // checkpoint
26736         context.container().call(loading);
26737
26738         if (toCheck.length) {
26739             context.connection().loadMultiple(toLoad, loaded);
26740         } else {
26741             finalize();
26742         }
26743
26744
26745         // Reload modified entities into an alternate graph and check for conflicts..
26746         function loaded(err, result) {
26747             if (errors.length) return;
26748
26749             if (err) {
26750                 errors.push({
26751                     msg: err.responseText,
26752                     details: [ t('save.status_code', { code: err.status }) ]
26753                 });
26754                 showErrors();
26755
26756             } else {
26757                 var loadMore = [];
26758                 _.each(result.data, function(entity) {
26759                     remoteGraph.replace(entity);
26760                     toLoad = _.without(toLoad, entity.id);
26761
26762                     // Because loadMultiple doesn't download /full like loadEntity,
26763                     // need to also load children that aren't already being checked..
26764                     if (!entity.visible) return;
26765                     if (entity.type === 'way') {
26766                         loadMore.push.apply(loadMore,
26767                             _.difference(entity.nodes, toCheck, toLoad, loadMore));
26768                     } else if (entity.type === 'relation' && entity.isMultipolygon()) {
26769                         loadMore.push.apply(loadMore,
26770                             _.difference(_.map(entity.members, 'id'), toCheck, toLoad, loadMore));
26771                     }
26772                 });
26773
26774                 if (loadMore.length) {
26775                     toLoad.push.apply(toLoad, loadMore);
26776                     context.connection().loadMultiple(loadMore, loaded);
26777                 }
26778
26779                 if (!toLoad.length) {
26780                     checkConflicts();
26781                 }
26782             }
26783         }
26784
26785
26786         function checkConflicts() {
26787             function choice(id, text, action) {
26788                 return { id: id, text: text, action: function() { history.replace(action); } };
26789             }
26790             function formatUser(d) {
26791                 return '<a href="' + context.connection().userURL(d) + '" target="_blank">' + d + '</a>';
26792             }
26793             function entityName(entity) {
26794                 return iD.util.displayName(entity) || (iD.util.displayType(entity.id) + ' ' + entity.id);
26795             }
26796
26797             function compareVersions(local, remote) {
26798                 if (local.version !== remote.version) return false;
26799
26800                 if (local.type === 'way') {
26801                     var children = _.union(local.nodes, remote.nodes);
26802
26803                     for (var i = 0; i < children.length; i++) {
26804                         var a = localGraph.hasEntity(children[i]),
26805                             b = remoteGraph.hasEntity(children[i]);
26806
26807                         if (a && b && a.version !== b.version) return false;
26808                     }
26809                 }
26810
26811                 return true;
26812             }
26813
26814             _.each(toCheck, function(id) {
26815                 var local = localGraph.entity(id),
26816                     remote = remoteGraph.entity(id);
26817
26818                 if (compareVersions(local, remote)) return;
26819
26820                 var action = iD.actions.MergeRemoteChanges,
26821                     merge = action(id, localGraph, remoteGraph, formatUser);
26822
26823                 history.replace(merge);
26824
26825                 var mergeConflicts = merge.conflicts();
26826                 if (!mergeConflicts.length) return;  // merged safely
26827
26828                 var forceLocal = action(id, localGraph, remoteGraph).withOption('force_local'),
26829                     forceRemote = action(id, localGraph, remoteGraph).withOption('force_remote'),
26830                     keepMine = t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore')),
26831                     keepTheirs = t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
26832
26833                 conflicts.push({
26834                     id: id,
26835                     name: entityName(local),
26836                     details: mergeConflicts,
26837                     chosen: 1,
26838                     choices: [
26839                         choice(id, keepMine, forceLocal),
26840                         choice(id, keepTheirs, forceRemote)
26841                     ]
26842                 });
26843             });
26844
26845             finalize();
26846         }
26847
26848
26849         function finalize() {
26850             if (conflicts.length) {
26851                 conflicts.sort(function(a,b) { return b.id.localeCompare(a.id); });
26852                 showConflicts();
26853             } else if (errors.length) {
26854                 showErrors();
26855             } else {
26856                 var changes = history.changes(iD.actions.DiscardTags(history.difference()));
26857                 if (changes.modified.length || changes.created.length || changes.deleted.length) {
26858                     context.connection().putChangeset(
26859                         changes,
26860                         e.comment,
26861                         history.imageryUsed(),
26862                         function(err, changeset_id) {
26863                             if (err) {
26864                                 errors.push({
26865                                     msg: err.responseText,
26866                                     details: [ t('save.status_code', { code: err.status }) ]
26867                                 });
26868                                 showErrors();
26869                             } else {
26870                                 history.clearSaved();
26871                                 success(e, changeset_id);
26872                                 // Add delay to allow for postgres replication #1646 #2678
26873                                 window.setTimeout(function() {
26874                                     loading.close();
26875                                     context.flush();
26876                                 }, 2500);
26877                             }
26878                         });
26879                 } else {        // changes were insignificant or reverted by user
26880                     loading.close();
26881                     context.flush();
26882                     cancel();
26883                 }
26884             }
26885         }
26886
26887
26888         function showConflicts() {
26889             var selection = context.container()
26890                 .select('#sidebar')
26891                 .append('div')
26892                 .attr('class','sidebar-component');
26893
26894             loading.close();
26895
26896             selection.call(iD.ui.Conflicts(context)
26897                 .list(conflicts)
26898                 .on('download', function() {
26899                     var data = JXON.stringify(context.connection().osmChangeJXON('CHANGEME', origChanges)),
26900                         win = window.open('data:text/xml,' + encodeURIComponent(data), '_blank');
26901                     win.focus();
26902                 })
26903                 .on('cancel', function() {
26904                     history.pop();
26905                     selection.remove();
26906                 })
26907                 .on('save', function() {
26908                     for (var i = 0; i < conflicts.length; i++) {
26909                         if (conflicts[i].chosen === 1) {  // user chose "keep theirs"
26910                             var entity = context.hasEntity(conflicts[i].id);
26911                             if (entity && entity.type === 'way') {
26912                                 var children = _.uniq(entity.nodes);
26913                                 for (var j = 0; j < children.length; j++) {
26914                                     history.replace(iD.actions.Revert(children[j]));
26915                                 }
26916                             }
26917                             history.replace(iD.actions.Revert(conflicts[i].id));
26918                         }
26919                     }
26920
26921                     selection.remove();
26922                     save(e, true);
26923                 })
26924             );
26925         }
26926
26927
26928         function showErrors() {
26929             var selection = iD.ui.confirm(context.container());
26930
26931             history.pop();
26932             loading.close();
26933
26934             selection
26935                 .select('.modal-section.header')
26936                 .append('h3')
26937                 .text(t('save.error'));
26938
26939             addErrors(selection, errors);
26940             selection.okButton();
26941         }
26942
26943
26944         function addErrors(selection, data) {
26945             var message = selection
26946                 .select('.modal-section.message-text');
26947
26948             var items = message
26949                 .selectAll('.error-container')
26950                 .data(data);
26951
26952             var enter = items.enter()
26953                 .append('div')
26954                 .attr('class', 'error-container');
26955
26956             enter
26957                 .append('a')
26958                 .attr('class', 'error-description')
26959                 .attr('href', '#')
26960                 .classed('hide-toggle', true)
26961                 .text(function(d) { return d.msg || t('save.unknown_error_details'); })
26962                 .on('click', function() {
26963                     var error = d3.select(this),
26964                         detail = d3.select(this.nextElementSibling),
26965                         exp = error.classed('expanded');
26966
26967                     detail.style('display', exp ? 'none' : 'block');
26968                     error.classed('expanded', !exp);
26969
26970                     d3.event.preventDefault();
26971                 });
26972
26973             var details = enter
26974                 .append('div')
26975                 .attr('class', 'error-detail-container')
26976                 .style('display', 'none');
26977
26978             details
26979                 .append('ul')
26980                 .attr('class', 'error-detail-list')
26981                 .selectAll('li')
26982                 .data(function(d) { return d.details || []; })
26983                 .enter()
26984                 .append('li')
26985                 .attr('class', 'error-detail-item')
26986                 .text(function(d) { return d; });
26987
26988             items.exit()
26989                 .remove();
26990         }
26991
26992     }
26993
26994
26995     function success(e, changeset_id) {
26996         context.enter(iD.modes.Browse(context)
26997             .sidebar(iD.ui.Success(context)
26998                 .changeset({
26999                     id: changeset_id,
27000                     comment: e.comment
27001                 })
27002                 .on('cancel', function() {
27003                     context.ui().sidebar.hide();
27004                 })));
27005     }
27006
27007     var mode = {
27008         id: 'save'
27009     };
27010
27011     mode.enter = function() {
27012         context.connection().authenticate(function(err) {
27013             if (err) {
27014                 cancel();
27015             } else {
27016                 context.ui().sidebar.show(ui);
27017             }
27018         });
27019     };
27020
27021     mode.exit = function() {
27022         context.ui().sidebar.hide();
27023     };
27024
27025     return mode;
27026 };
27027 iD.modes.Select = function(context, selectedIDs) {
27028     var mode = {
27029         id: 'select',
27030         button: 'browse'
27031     };
27032
27033     var keybinding = d3.keybinding('select'),
27034         timeout = null,
27035         behaviors = [
27036             iD.behavior.Copy(context),
27037             iD.behavior.Paste(context),
27038             iD.behavior.Breathe(context),
27039             iD.behavior.Hover(context),
27040             iD.behavior.Select(context),
27041             iD.behavior.Lasso(context),
27042             iD.modes.DragNode(context)
27043                 .selectedIDs(selectedIDs)
27044                 .behavior],
27045         inspector,
27046         radialMenu,
27047         newFeature = false,
27048         suppressMenu = false;
27049
27050     var wrap = context.container()
27051         .select('.inspector-wrap');
27052
27053
27054     function singular() {
27055         if (selectedIDs.length === 1) {
27056             return context.hasEntity(selectedIDs[0]);
27057         }
27058     }
27059
27060     function closeMenu() {
27061         if (radialMenu) {
27062             context.surface().call(radialMenu.close);
27063         }
27064     }
27065
27066     function positionMenu() {
27067         if (suppressMenu || !radialMenu) { return; }
27068
27069         var entity = singular();
27070         if (entity && context.geometry(entity.id) === 'relation') {
27071             suppressMenu = true;
27072         } else if (entity && entity.type === 'node') {
27073             radialMenu.center(context.projection(entity.loc));
27074         } else {
27075             var point = context.mouse(),
27076                 viewport = iD.geo.Extent(context.projection.clipExtent()).polygon();
27077             if (iD.geo.pointInPolygon(point, viewport)) {
27078                 radialMenu.center(point);
27079             } else {
27080                 suppressMenu = true;
27081             }
27082         }
27083     }
27084
27085     function showMenu() {
27086         closeMenu();
27087         if (!suppressMenu && radialMenu) {
27088             context.surface().call(radialMenu);
27089         }
27090     }
27091
27092     function toggleMenu() {
27093         if (d3.select('.radial-menu').empty()) {
27094             showMenu();
27095         } else {
27096             closeMenu();
27097         }
27098     }
27099
27100     mode.selectedIDs = function() {
27101         return selectedIDs;
27102     };
27103
27104     mode.reselect = function() {
27105         var surfaceNode = context.surface().node();
27106         if (surfaceNode.focus) { // FF doesn't support it
27107             surfaceNode.focus();
27108         }
27109
27110         positionMenu();
27111         showMenu();
27112     };
27113
27114     mode.newFeature = function(_) {
27115         if (!arguments.length) return newFeature;
27116         newFeature = _;
27117         return mode;
27118     };
27119
27120     mode.suppressMenu = function(_) {
27121         if (!arguments.length) return suppressMenu;
27122         suppressMenu = _;
27123         return mode;
27124     };
27125
27126     mode.enter = function() {
27127         function update() {
27128             closeMenu();
27129             if (_.some(selectedIDs, function(id) { return !context.hasEntity(id); })) {
27130                 // Exit mode if selected entity gets undone
27131                 context.enter(iD.modes.Browse(context));
27132             }
27133         }
27134
27135         function dblclick() {
27136             var target = d3.select(d3.event.target),
27137                 datum = target.datum();
27138
27139             if (datum instanceof iD.Way && !target.classed('fill')) {
27140                 var choice = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection),
27141                     node = iD.Node();
27142
27143                 var prev = datum.nodes[choice.index - 1],
27144                     next = datum.nodes[choice.index];
27145
27146                 context.perform(
27147                     iD.actions.AddMidpoint({loc: choice.loc, edge: [prev, next]}, node),
27148                     t('operations.add.annotation.vertex'));
27149
27150                 d3.event.preventDefault();
27151                 d3.event.stopPropagation();
27152             }
27153         }
27154
27155         function selectElements(drawn) {
27156             var entity = singular();
27157             if (entity && context.geometry(entity.id) === 'relation') {
27158                 suppressMenu = true;
27159                 return;
27160             }
27161
27162             var selection = context.surface()
27163                     .selectAll(iD.util.entityOrMemberSelector(selectedIDs, context.graph()));
27164
27165             if (selection.empty()) {
27166                 if (drawn) {  // Exit mode if selected DOM elements have disappeared..
27167                     context.enter(iD.modes.Browse(context));
27168                 }
27169             } else {
27170                 selection
27171                     .classed('selected', true);
27172             }
27173         }
27174
27175         function esc() {
27176             if (!context.inIntro()) {
27177                 context.enter(iD.modes.Browse(context));
27178             }
27179         }
27180
27181
27182         behaviors.forEach(function(behavior) {
27183             context.install(behavior);
27184         });
27185
27186         var operations = _.without(d3.values(iD.operations), iD.operations.Delete)
27187                 .map(function(o) { return o(selectedIDs, context); })
27188                 .filter(function(o) { return o.available(); });
27189
27190         operations.unshift(iD.operations.Delete(selectedIDs, context));
27191
27192         keybinding
27193             .on('⎋', esc, true)
27194             .on('space', toggleMenu);
27195
27196         operations.forEach(function(operation) {
27197             operation.keys.forEach(function(key) {
27198                 keybinding.on(key, function() {
27199                     if (!(context.inIntro() || operation.disabled())) {
27200                         operation();
27201                     }
27202                 });
27203             });
27204         });
27205
27206         d3.select(document)
27207             .call(keybinding);
27208
27209         radialMenu = iD.ui.RadialMenu(context, operations);
27210
27211         context.ui().sidebar
27212             .select(singular() ? singular().id : null, newFeature);
27213
27214         context.history()
27215             .on('undone.select', update)
27216             .on('redone.select', update);
27217
27218         context.map()
27219             .on('move.select', closeMenu)
27220             .on('drawn.select', selectElements);
27221
27222         selectElements();
27223
27224         var show = d3.event && !suppressMenu;
27225
27226         if (show) {
27227             positionMenu();
27228         }
27229
27230         timeout = window.setTimeout(function() {
27231             if (show) {
27232                 showMenu();
27233             }
27234
27235             context.surface()
27236                 .on('dblclick.select', dblclick);
27237         }, 200);
27238
27239         if (selectedIDs.length > 1) {
27240             var entities = iD.ui.SelectionList(context, selectedIDs);
27241             context.ui().sidebar.show(entities);
27242         }
27243     };
27244
27245     mode.exit = function() {
27246         if (timeout) window.clearTimeout(timeout);
27247
27248         if (inspector) wrap.call(inspector.close);
27249
27250         behaviors.forEach(function(behavior) {
27251             context.uninstall(behavior);
27252         });
27253
27254         keybinding.off();
27255         closeMenu();
27256         radialMenu = undefined;
27257
27258         context.history()
27259             .on('undone.select', null)
27260             .on('redone.select', null);
27261
27262         context.surface()
27263             .on('dblclick.select', null)
27264             .selectAll('.selected')
27265             .classed('selected', false);
27266
27267         context.map().on('drawn.select', null);
27268         context.ui().sidebar.hide();
27269     };
27270
27271     return mode;
27272 };
27273 iD.operations = {};
27274 iD.operations.Circularize = function(selectedIDs, context) {
27275     var entityId = selectedIDs[0],
27276         entity = context.entity(entityId),
27277         extent = entity.extent(context.graph()),
27278         geometry = context.geometry(entityId),
27279         action = iD.actions.Circularize(entityId, context.projection);
27280
27281     var operation = function() {
27282         var annotation = t('operations.circularize.annotation.' + geometry);
27283         context.perform(action, annotation);
27284     };
27285
27286     operation.available = function() {
27287         return selectedIDs.length === 1 &&
27288             entity.type === 'way' &&
27289             _.uniq(entity.nodes).length > 1;
27290     };
27291
27292     operation.disabled = function() {
27293         var reason;
27294         if (extent.percentContainedIn(context.extent()) < 0.8) {
27295             reason = 'too_large';
27296         } else if (context.hasHiddenConnections(entityId)) {
27297             reason = 'connected_to_hidden';
27298         }
27299         return action.disabled(context.graph()) || reason;
27300     };
27301
27302     operation.tooltip = function() {
27303         var disable = operation.disabled();
27304         return disable ?
27305             t('operations.circularize.' + disable) :
27306             t('operations.circularize.description.' + geometry);
27307     };
27308
27309     operation.id = 'circularize';
27310     operation.keys = [t('operations.circularize.key')];
27311     operation.title = t('operations.circularize.title');
27312
27313     return operation;
27314 };
27315 iD.operations.Continue = function(selectedIDs, context) {
27316     var graph = context.graph(),
27317         entities = selectedIDs.map(function(id) { return graph.entity(id); }),
27318         geometries = _.extend({line: [], vertex: []},
27319             _.groupBy(entities, function(entity) { return entity.geometry(graph); })),
27320         vertex = geometries.vertex[0];
27321
27322     function candidateWays() {
27323         return graph.parentWays(vertex).filter(function(parent) {
27324             return parent.geometry(graph) === 'line' &&
27325                 parent.affix(vertex.id) &&
27326                 (geometries.line.length === 0 || geometries.line[0] === parent);
27327         });
27328     }
27329
27330     var operation = function() {
27331         var candidate = candidateWays()[0];
27332         context.enter(iD.modes.DrawLine(
27333             context,
27334             candidate.id,
27335             context.graph(),
27336             candidate.affix(vertex.id)));
27337     };
27338
27339     operation.available = function() {
27340         return geometries.vertex.length === 1 && geometries.line.length <= 1 &&
27341             !context.features().hasHiddenConnections(vertex, context.graph());
27342     };
27343
27344     operation.disabled = function() {
27345         var candidates = candidateWays();
27346         if (candidates.length === 0)
27347             return 'not_eligible';
27348         if (candidates.length > 1)
27349             return 'multiple';
27350     };
27351
27352     operation.tooltip = function() {
27353         var disable = operation.disabled();
27354         return disable ?
27355             t('operations.continue.' + disable) :
27356             t('operations.continue.description');
27357     };
27358
27359     operation.id = 'continue';
27360     operation.keys = [t('operations.continue.key')];
27361     operation.title = t('operations.continue.title');
27362
27363     return operation;
27364 };
27365 iD.operations.Delete = function(selectedIDs, context) {
27366     var action = iD.actions.DeleteMultiple(selectedIDs);
27367
27368     var operation = function() {
27369         var annotation,
27370             nextSelectedID;
27371
27372         if (selectedIDs.length > 1) {
27373             annotation = t('operations.delete.annotation.multiple', {n: selectedIDs.length});
27374
27375         } else {
27376             var id = selectedIDs[0],
27377                 entity = context.entity(id),
27378                 geometry = context.geometry(id),
27379                 parents = context.graph().parentWays(entity),
27380                 parent = parents[0];
27381
27382             annotation = t('operations.delete.annotation.' + geometry);
27383
27384             // Select the next closest node in the way.
27385             if (geometry === 'vertex' && parents.length === 1 && parent.nodes.length > 2) {
27386                 var nodes = parent.nodes,
27387                     i = nodes.indexOf(id);
27388
27389                 if (i === 0) {
27390                     i++;
27391                 } else if (i === nodes.length - 1) {
27392                     i--;
27393                 } else {
27394                     var a = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc),
27395                         b = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
27396                     i = a < b ? i - 1 : i + 1;
27397                 }
27398
27399                 nextSelectedID = nodes[i];
27400             }
27401         }
27402
27403         if (nextSelectedID && context.hasEntity(nextSelectedID)) {
27404             context.enter(iD.modes.Select(context, [nextSelectedID]));
27405         } else {
27406             context.enter(iD.modes.Browse(context));
27407         }
27408
27409         context.perform(
27410             action,
27411             annotation);
27412     };
27413
27414     operation.available = function() {
27415         return true;
27416     };
27417
27418     operation.disabled = function() {
27419         var reason;
27420         if (_.some(selectedIDs, context.hasHiddenConnections)) {
27421             reason = 'connected_to_hidden';
27422         }
27423         return action.disabled(context.graph()) || reason;
27424     };
27425
27426     operation.tooltip = function() {
27427         var disable = operation.disabled();
27428         return disable ?
27429             t('operations.delete.' + disable) :
27430             t('operations.delete.description');
27431     };
27432
27433     operation.id = 'delete';
27434     operation.keys = [iD.ui.cmd('⌘⌫'), iD.ui.cmd('⌘⌦')];
27435     operation.title = t('operations.delete.title');
27436
27437     return operation;
27438 };
27439 iD.operations.Disconnect = function(selectedIDs, context) {
27440     var vertices = _.filter(selectedIDs, function vertex(entityId) {
27441         return context.geometry(entityId) === 'vertex';
27442     });
27443
27444     var entityId = vertices[0],
27445         action = iD.actions.Disconnect(entityId);
27446
27447     if (selectedIDs.length > 1) {
27448         action.limitWays(_.without(selectedIDs, entityId));
27449     }
27450
27451     var operation = function() {
27452         context.perform(action, t('operations.disconnect.annotation'));
27453     };
27454
27455     operation.available = function() {
27456         return vertices.length === 1;
27457     };
27458
27459     operation.disabled = function() {
27460         var reason;
27461         if (_.some(selectedIDs, context.hasHiddenConnections)) {
27462             reason = 'connected_to_hidden';
27463         }
27464         return action.disabled(context.graph()) || reason;
27465     };
27466
27467     operation.tooltip = function() {
27468         var disable = operation.disabled();
27469         return disable ?
27470             t('operations.disconnect.' + disable) :
27471             t('operations.disconnect.description');
27472     };
27473
27474     operation.id = 'disconnect';
27475     operation.keys = [t('operations.disconnect.key')];
27476     operation.title = t('operations.disconnect.title');
27477
27478     return operation;
27479 };
27480 iD.operations.Merge = function(selectedIDs, context) {
27481     var join = iD.actions.Join(selectedIDs),
27482         merge = iD.actions.Merge(selectedIDs),
27483         mergePolygon = iD.actions.MergePolygon(selectedIDs);
27484
27485     var operation = function() {
27486         var annotation = t('operations.merge.annotation', {n: selectedIDs.length}),
27487             action;
27488
27489         if (!join.disabled(context.graph())) {
27490             action = join;
27491         } else if (!merge.disabled(context.graph())) {
27492             action = merge;
27493         } else {
27494             action = mergePolygon;
27495         }
27496
27497         context.perform(action, annotation);
27498         context.enter(iD.modes.Select(context, selectedIDs.filter(function(id) { return context.hasEntity(id); }))
27499             .suppressMenu(true));
27500     };
27501
27502     operation.available = function() {
27503         return selectedIDs.length >= 2;
27504     };
27505
27506     operation.disabled = function() {
27507         return join.disabled(context.graph()) &&
27508             merge.disabled(context.graph()) &&
27509             mergePolygon.disabled(context.graph());
27510     };
27511
27512     operation.tooltip = function() {
27513         var j = join.disabled(context.graph()),
27514             m = merge.disabled(context.graph()),
27515             p = mergePolygon.disabled(context.graph());
27516
27517         if (j === 'restriction' && m && p)
27518             return t('operations.merge.restriction', {relation: context.presets().item('type/restriction').name()});
27519
27520         if (p === 'incomplete_relation' && j && m)
27521             return t('operations.merge.incomplete_relation');
27522
27523         if (j && m && p)
27524             return t('operations.merge.' + j);
27525
27526         return t('operations.merge.description');
27527     };
27528
27529     operation.id = 'merge';
27530     operation.keys = [t('operations.merge.key')];
27531     operation.title = t('operations.merge.title');
27532
27533     return operation;
27534 };
27535 iD.operations.Move = function(selectedIDs, context) {
27536     var extent = selectedIDs.reduce(function(extent, id) {
27537             return extent.extend(context.entity(id).extent(context.graph()));
27538         }, iD.geo.Extent());
27539
27540     var operation = function() {
27541         context.enter(iD.modes.Move(context, selectedIDs));
27542     };
27543
27544     operation.available = function() {
27545         return selectedIDs.length > 1 ||
27546             context.entity(selectedIDs[0]).type !== 'node';
27547     };
27548
27549     operation.disabled = function() {
27550         var reason;
27551         if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
27552             reason = 'too_large';
27553         } else if (_.some(selectedIDs, context.hasHiddenConnections)) {
27554             reason = 'connected_to_hidden';
27555         }
27556         return iD.actions.Move(selectedIDs).disabled(context.graph()) || reason;
27557     };
27558
27559     operation.tooltip = function() {
27560         var disable = operation.disabled();
27561         return disable ?
27562             t('operations.move.' + disable) :
27563             t('operations.move.description');
27564     };
27565
27566     operation.id = 'move';
27567     operation.keys = [t('operations.move.key')];
27568     operation.title = t('operations.move.title');
27569
27570     return operation;
27571 };
27572 iD.operations.Orthogonalize = function(selectedIDs, context) {
27573     var entityId = selectedIDs[0],
27574         entity = context.entity(entityId),
27575         extent = entity.extent(context.graph()),
27576         geometry = context.geometry(entityId),
27577         action = iD.actions.Orthogonalize(entityId, context.projection);
27578
27579     var operation = function() {
27580         var annotation = t('operations.orthogonalize.annotation.' + geometry);
27581         context.perform(action, annotation);
27582     };
27583
27584     operation.available = function() {
27585         return selectedIDs.length === 1 &&
27586             entity.type === 'way' &&
27587             entity.isClosed() &&
27588             _.uniq(entity.nodes).length > 2;
27589     };
27590
27591     operation.disabled = function() {
27592         var reason;
27593         if (extent.percentContainedIn(context.extent()) < 0.8) {
27594             reason = 'too_large';
27595         } else if (context.hasHiddenConnections(entityId)) {
27596             reason = 'connected_to_hidden';
27597         }
27598         return action.disabled(context.graph()) || reason;
27599     };
27600
27601     operation.tooltip = function() {
27602         var disable = operation.disabled();
27603         return disable ?
27604             t('operations.orthogonalize.' + disable) :
27605             t('operations.orthogonalize.description.' + geometry);
27606     };
27607
27608     operation.id = 'orthogonalize';
27609     operation.keys = [t('operations.orthogonalize.key')];
27610     operation.title = t('operations.orthogonalize.title');
27611
27612     return operation;
27613 };
27614 iD.operations.Reverse = function(selectedIDs, context) {
27615     var entityId = selectedIDs[0];
27616
27617     var operation = function() {
27618         context.perform(
27619             iD.actions.Reverse(entityId),
27620             t('operations.reverse.annotation'));
27621     };
27622
27623     operation.available = function() {
27624         return selectedIDs.length === 1 &&
27625             context.geometry(entityId) === 'line';
27626     };
27627
27628     operation.disabled = function() {
27629         return false;
27630     };
27631
27632     operation.tooltip = function() {
27633         return t('operations.reverse.description');
27634     };
27635
27636     operation.id = 'reverse';
27637     operation.keys = [t('operations.reverse.key')];
27638     operation.title = t('operations.reverse.title');
27639
27640     return operation;
27641 };
27642 iD.operations.Rotate = function(selectedIDs, context) {
27643     var entityId = selectedIDs[0],
27644         entity = context.entity(entityId),
27645         extent = entity.extent(context.graph()),
27646         geometry = context.geometry(entityId);
27647
27648     var operation = function() {
27649         context.enter(iD.modes.RotateWay(context, entityId));
27650     };
27651
27652     operation.available = function() {
27653         if (selectedIDs.length !== 1 || entity.type !== 'way')
27654             return false;
27655         if (geometry === 'area')
27656             return true;
27657         if (entity.isClosed() &&
27658             context.graph().parentRelations(entity).some(function(r) { return r.isMultipolygon(); }))
27659             return true;
27660         return false;
27661     };
27662
27663     operation.disabled = function() {
27664         if (extent.percentContainedIn(context.extent()) < 0.8) {
27665             return 'too_large';
27666         } else if (context.hasHiddenConnections(entityId)) {
27667             return 'connected_to_hidden';
27668         } else {
27669             return false;
27670         }
27671     };
27672
27673     operation.tooltip = function() {
27674         var disable = operation.disabled();
27675         return disable ?
27676             t('operations.rotate.' + disable) :
27677             t('operations.rotate.description');
27678     };
27679
27680     operation.id = 'rotate';
27681     operation.keys = [t('operations.rotate.key')];
27682     operation.title = t('operations.rotate.title');
27683
27684     return operation;
27685 };
27686 iD.operations.Split = function(selectedIDs, context) {
27687     var vertices = _.filter(selectedIDs, function vertex(entityId) {
27688         return context.geometry(entityId) === 'vertex';
27689     });
27690
27691     var entityId = vertices[0],
27692         action = iD.actions.Split(entityId);
27693
27694     if (selectedIDs.length > 1) {
27695         action.limitWays(_.without(selectedIDs, entityId));
27696     }
27697
27698     var operation = function() {
27699         var annotation;
27700
27701         var ways = action.ways(context.graph());
27702         if (ways.length === 1) {
27703             annotation = t('operations.split.annotation.' + context.geometry(ways[0].id));
27704         } else {
27705             annotation = t('operations.split.annotation.multiple', {n: ways.length});
27706         }
27707
27708         var difference = context.perform(action, annotation);
27709         context.enter(iD.modes.Select(context, difference.extantIDs()));
27710     };
27711
27712     operation.available = function() {
27713         return vertices.length === 1;
27714     };
27715
27716     operation.disabled = function() {
27717         var reason;
27718         if (_.some(selectedIDs, context.hasHiddenConnections)) {
27719             reason = 'connected_to_hidden';
27720         }
27721         return action.disabled(context.graph()) || reason;
27722     };
27723
27724     operation.tooltip = function() {
27725         var disable = operation.disabled();
27726         if (disable) {
27727             return t('operations.split.' + disable);
27728         }
27729
27730         var ways = action.ways(context.graph());
27731         if (ways.length === 1) {
27732             return t('operations.split.description.' + context.geometry(ways[0].id));
27733         } else {
27734             return t('operations.split.description.multiple');
27735         }
27736     };
27737
27738     operation.id = 'split';
27739     operation.keys = [t('operations.split.key')];
27740     operation.title = t('operations.split.title');
27741
27742     return operation;
27743 };
27744 iD.operations.Straighten = function(selectedIDs, context) {
27745     var entityId = selectedIDs[0],
27746         action = iD.actions.Straighten(entityId, context.projection);
27747
27748     function operation() {
27749         var annotation = t('operations.straighten.annotation');
27750         context.perform(action, annotation);
27751     }
27752
27753     operation.available = function() {
27754         var entity = context.entity(entityId);
27755         return selectedIDs.length === 1 &&
27756             entity.type === 'way' &&
27757             !entity.isClosed() &&
27758             _.uniq(entity.nodes).length > 2;
27759     };
27760
27761     operation.disabled = function() {
27762         var reason;
27763         if (context.hasHiddenConnections(entityId)) {
27764             reason = 'connected_to_hidden';
27765         }
27766         return action.disabled(context.graph()) || reason;
27767     };
27768
27769     operation.tooltip = function() {
27770         var disable = operation.disabled();
27771         return disable ?
27772             t('operations.straighten.' + disable) :
27773             t('operations.straighten.description');
27774     };
27775
27776     operation.id = 'straighten';
27777     operation.keys = [t('operations.straighten.key')];
27778     operation.title = t('operations.straighten.title');
27779
27780     return operation;
27781 };
27782 iD.Connection = function(useHttps) {
27783     if (typeof useHttps !== 'boolean') {
27784       useHttps = window.location.protocol === 'https:';
27785     }
27786
27787     var event = d3.dispatch('authenticating', 'authenticated', 'auth', 'loading', 'loaded'),
27788         protocol = useHttps ? 'https:' : 'http:',
27789         url = protocol + '//www.openstreetmap.org',
27790         connection = {},
27791         inflight = {},
27792         loadedTiles = {},
27793         tileZoom = 16,
27794         oauth = osmAuth({
27795             url: protocol + '//www.openstreetmap.org',
27796             oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
27797             oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
27798             loading: authenticating,
27799             done: authenticated
27800         }),
27801         ndStr = 'nd',
27802         tagStr = 'tag',
27803         memberStr = 'member',
27804         nodeStr = 'node',
27805         wayStr = 'way',
27806         relationStr = 'relation',
27807         userDetails,
27808         off;
27809
27810
27811     connection.changesetURL = function(changesetId) {
27812         return url + '/changeset/' + changesetId;
27813     };
27814
27815     connection.changesetsURL = function(center, zoom) {
27816         var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
27817         return url + '/history#map=' +
27818             Math.floor(zoom) + '/' +
27819             center[1].toFixed(precision) + '/' +
27820             center[0].toFixed(precision);
27821     };
27822
27823     connection.entityURL = function(entity) {
27824         return url + '/' + entity.type + '/' + entity.osmId();
27825     };
27826
27827     connection.userURL = function(username) {
27828         return url + '/user/' + username;
27829     };
27830
27831     connection.loadFromURL = function(url, callback) {
27832         function done(err, dom) {
27833             return callback(err, parse(dom));
27834         }
27835         return d3.xml(url).get(done);
27836     };
27837
27838     connection.loadEntity = function(id, callback) {
27839         var type = iD.Entity.id.type(id),
27840             osmID = iD.Entity.id.toOSM(id);
27841
27842         connection.loadFromURL(
27843             url + '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''),
27844             function(err, entities) {
27845                 if (callback) callback(err, {data: entities});
27846             });
27847     };
27848
27849     connection.loadEntityVersion = function(id, version, callback) {
27850         var type = iD.Entity.id.type(id),
27851             osmID = iD.Entity.id.toOSM(id);
27852
27853         connection.loadFromURL(
27854             url + '/api/0.6/' + type + '/' + osmID + '/' + version,
27855             function(err, entities) {
27856                 if (callback) callback(err, {data: entities});
27857             });
27858     };
27859
27860     connection.loadMultiple = function(ids, callback) {
27861         _.each(_.groupBy(_.uniq(ids), iD.Entity.id.type), function(v, k) {
27862             var type = k + 's',
27863                 osmIDs = _.map(v, iD.Entity.id.toOSM);
27864
27865             _.each(_.chunk(osmIDs, 150), function(arr) {
27866                 connection.loadFromURL(
27867                     url + '/api/0.6/' + type + '?' + type + '=' + arr.join(),
27868                     function(err, entities) {
27869                         if (callback) callback(err, {data: entities});
27870                     });
27871             });
27872         });
27873     };
27874
27875     function authenticating() {
27876         event.authenticating();
27877     }
27878
27879     function authenticated() {
27880         event.authenticated();
27881     }
27882
27883     function getLoc(attrs) {
27884         var lon = attrs.lon && attrs.lon.value,
27885             lat = attrs.lat && attrs.lat.value;
27886         return [parseFloat(lon), parseFloat(lat)];
27887     }
27888
27889     function getNodes(obj) {
27890         var elems = obj.getElementsByTagName(ndStr),
27891             nodes = new Array(elems.length);
27892         for (var i = 0, l = elems.length; i < l; i++) {
27893             nodes[i] = 'n' + elems[i].attributes.ref.value;
27894         }
27895         return nodes;
27896     }
27897
27898     function getTags(obj) {
27899         var elems = obj.getElementsByTagName(tagStr),
27900             tags = {};
27901         for (var i = 0, l = elems.length; i < l; i++) {
27902             var attrs = elems[i].attributes;
27903             tags[attrs.k.value] = attrs.v.value;
27904         }
27905         return tags;
27906     }
27907
27908     function getMembers(obj) {
27909         var elems = obj.getElementsByTagName(memberStr),
27910             members = new Array(elems.length);
27911         for (var i = 0, l = elems.length; i < l; i++) {
27912             var attrs = elems[i].attributes;
27913             members[i] = {
27914                 id: attrs.type.value[0] + attrs.ref.value,
27915                 type: attrs.type.value,
27916                 role: attrs.role.value
27917             };
27918         }
27919         return members;
27920     }
27921
27922     function getVisible(attrs) {
27923         return (!attrs.visible || attrs.visible.value !== 'false');
27924     }
27925
27926     var parsers = {
27927         node: function nodeData(obj) {
27928             var attrs = obj.attributes;
27929             return new iD.Node({
27930                 id: iD.Entity.id.fromOSM(nodeStr, attrs.id.value),
27931                 loc: getLoc(attrs),
27932                 version: attrs.version.value,
27933                 user: attrs.user && attrs.user.value,
27934                 tags: getTags(obj),
27935                 visible: getVisible(attrs)
27936             });
27937         },
27938
27939         way: function wayData(obj) {
27940             var attrs = obj.attributes;
27941             return new iD.Way({
27942                 id: iD.Entity.id.fromOSM(wayStr, attrs.id.value),
27943                 version: attrs.version.value,
27944                 user: attrs.user && attrs.user.value,
27945                 tags: getTags(obj),
27946                 nodes: getNodes(obj),
27947                 visible: getVisible(attrs)
27948             });
27949         },
27950
27951         relation: function relationData(obj) {
27952             var attrs = obj.attributes;
27953             return new iD.Relation({
27954                 id: iD.Entity.id.fromOSM(relationStr, attrs.id.value),
27955                 version: attrs.version.value,
27956                 user: attrs.user && attrs.user.value,
27957                 tags: getTags(obj),
27958                 members: getMembers(obj),
27959                 visible: getVisible(attrs)
27960             });
27961         }
27962     };
27963
27964     function parse(dom) {
27965         if (!dom || !dom.childNodes) return;
27966
27967         var root = dom.childNodes[0],
27968             children = root.childNodes,
27969             entities = [];
27970
27971         for (var i = 0, l = children.length; i < l; i++) {
27972             var child = children[i],
27973                 parser = parsers[child.nodeName];
27974             if (parser) {
27975                 entities.push(parser(child));
27976             }
27977         }
27978
27979         return entities;
27980     }
27981
27982     connection.authenticated = function() {
27983         return oauth.authenticated();
27984     };
27985
27986     // Generate Changeset XML. Returns a string.
27987     connection.changesetJXON = function(tags) {
27988         return {
27989             osm: {
27990                 changeset: {
27991                     tag: _.map(tags, function(value, key) {
27992                         return { '@k': key, '@v': value };
27993                     }),
27994                     '@version': 0.6,
27995                     '@generator': 'iD'
27996                 }
27997             }
27998         };
27999     };
28000
28001     // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
28002     // XML. Returns a string.
28003     connection.osmChangeJXON = function(changeset_id, changes) {
28004         function nest(x, order) {
28005             var groups = {};
28006             for (var i = 0; i < x.length; i++) {
28007                 var tagName = Object.keys(x[i])[0];
28008                 if (!groups[tagName]) groups[tagName] = [];
28009                 groups[tagName].push(x[i][tagName]);
28010             }
28011             var ordered = {};
28012             order.forEach(function(o) {
28013                 if (groups[o]) ordered[o] = groups[o];
28014             });
28015             return ordered;
28016         }
28017
28018         function rep(entity) {
28019             return entity.asJXON(changeset_id);
28020         }
28021
28022         return {
28023             osmChange: {
28024                 '@version': 0.6,
28025                 '@generator': 'iD',
28026                 'create': nest(changes.created.map(rep), ['node', 'way', 'relation']),
28027                 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
28028                 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true})
28029             }
28030         };
28031     };
28032
28033     connection.changesetTags = function(comment, imageryUsed) {
28034         var detected = iD.detect(),
28035             tags = {
28036                 created_by: 'iD ' + iD.version,
28037                 imagery_used: imageryUsed.join(';').substr(0, 255),
28038                 host: (window.location.origin + window.location.pathname).substr(0, 255),
28039                 locale: detected.locale
28040             };
28041
28042         if (comment) {
28043             tags.comment = comment.substr(0, 255);
28044         }
28045
28046         return tags;
28047     };
28048
28049     connection.putChangeset = function(changes, comment, imageryUsed, callback) {
28050         oauth.xhr({
28051                 method: 'PUT',
28052                 path: '/api/0.6/changeset/create',
28053                 options: { header: { 'Content-Type': 'text/xml' } },
28054                 content: JXON.stringify(connection.changesetJXON(connection.changesetTags(comment, imageryUsed)))
28055             }, function(err, changeset_id) {
28056                 if (err) return callback(err);
28057                 oauth.xhr({
28058                     method: 'POST',
28059                     path: '/api/0.6/changeset/' + changeset_id + '/upload',
28060                     options: { header: { 'Content-Type': 'text/xml' } },
28061                     content: JXON.stringify(connection.osmChangeJXON(changeset_id, changes))
28062                 }, function(err) {
28063                     if (err) return callback(err);
28064                     // POST was successful, safe to call the callback.
28065                     // Still attempt to close changeset, but ignore response because #2667
28066                     // Add delay to allow for postgres replication #1646 #2678
28067                     window.setTimeout(function() { callback(null, changeset_id); }, 2500);
28068                     oauth.xhr({
28069                         method: 'PUT',
28070                         path: '/api/0.6/changeset/' + changeset_id + '/close',
28071                         options: { header: { 'Content-Type': 'text/xml' } }
28072                     }, d3.functor(true));
28073                 });
28074             });
28075     };
28076
28077     connection.userDetails = function(callback) {
28078         if (userDetails) {
28079             callback(undefined, userDetails);
28080             return;
28081         }
28082
28083         function done(err, user_details) {
28084             if (err) return callback(err);
28085
28086             var u = user_details.getElementsByTagName('user')[0],
28087                 img = u.getElementsByTagName('img'),
28088                 image_url = '';
28089
28090             if (img && img[0] && img[0].getAttribute('href')) {
28091                 image_url = img[0].getAttribute('href');
28092             }
28093
28094             userDetails = {
28095                 display_name: u.attributes.display_name.value,
28096                 image_url: image_url,
28097                 id: u.attributes.id.value
28098             };
28099
28100             callback(undefined, userDetails);
28101         }
28102
28103         oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
28104     };
28105
28106     connection.userChangesets = function(callback) {
28107         connection.userDetails(function(err, user) {
28108             if (err) return callback(err);
28109
28110             function done(changesets) {
28111                 callback(undefined, Array.prototype.map.call(changesets.getElementsByTagName('changeset'),
28112                     function (changeset) {
28113                         return { tags: getTags(changeset) };
28114                     }));
28115             }
28116
28117             d3.xml(url + '/api/0.6/changesets?user=' + user.id).get()
28118                 .on('load', done)
28119                 .on('error', callback);
28120         });
28121     };
28122
28123     connection.status = function(callback) {
28124         function done(capabilities) {
28125             var apiStatus = capabilities.getElementsByTagName('status');
28126             callback(undefined, apiStatus[0].getAttribute('api'));
28127         }
28128         d3.xml(url + '/api/capabilities').get()
28129             .on('load', done)
28130             .on('error', callback);
28131     };
28132
28133     function abortRequest(i) { i.abort(); }
28134
28135     connection.tileZoom = function(_) {
28136         if (!arguments.length) return tileZoom;
28137         tileZoom = _;
28138         return connection;
28139     };
28140
28141     connection.loadTiles = function(projection, dimensions, callback) {
28142
28143         if (off) return;
28144
28145         var s = projection.scale() * 2 * Math.PI,
28146             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
28147             ts = 256 * Math.pow(2, z - tileZoom),
28148             origin = [
28149                 s / 2 - projection.translate()[0],
28150                 s / 2 - projection.translate()[1]];
28151
28152         var tiles = d3.geo.tile()
28153             .scaleExtent([tileZoom, tileZoom])
28154             .scale(s)
28155             .size(dimensions)
28156             .translate(projection.translate())()
28157             .map(function(tile) {
28158                 var x = tile[0] * ts - origin[0],
28159                     y = tile[1] * ts - origin[1];
28160
28161                 return {
28162                     id: tile.toString(),
28163                     extent: iD.geo.Extent(
28164                         projection.invert([x, y + ts]),
28165                         projection.invert([x + ts, y]))
28166                 };
28167             });
28168
28169         function bboxUrl(tile) {
28170             return url + '/api/0.6/map?bbox=' + tile.extent.toParam();
28171         }
28172
28173         _.filter(inflight, function(v, i) {
28174             var wanted = _.find(tiles, function(tile) {
28175                 return i === tile.id;
28176             });
28177             if (!wanted) delete inflight[i];
28178             return !wanted;
28179         }).map(abortRequest);
28180
28181         tiles.forEach(function(tile) {
28182             var id = tile.id;
28183
28184             if (loadedTiles[id] || inflight[id]) return;
28185
28186             if (_.isEmpty(inflight)) {
28187                 event.loading();
28188             }
28189
28190             inflight[id] = connection.loadFromURL(bboxUrl(tile), function(err, parsed) {
28191                 loadedTiles[id] = true;
28192                 delete inflight[id];
28193
28194                 if (callback) callback(err, _.extend({data: parsed}, tile));
28195
28196                 if (_.isEmpty(inflight)) {
28197                     event.loaded();
28198                 }
28199             });
28200         });
28201     };
28202
28203     connection.switch = function(options) {
28204         url = options.url;
28205         oauth.options(_.extend({
28206             loading: authenticating,
28207             done: authenticated
28208         }, options));
28209         event.auth();
28210         connection.flush();
28211         return connection;
28212     };
28213
28214     connection.toggle = function(_) {
28215         off = !_;
28216         return connection;
28217     };
28218
28219     connection.flush = function() {
28220         userDetails = undefined;
28221         _.forEach(inflight, abortRequest);
28222         loadedTiles = {};
28223         inflight = {};
28224         return connection;
28225     };
28226
28227     connection.loadedTiles = function(_) {
28228         if (!arguments.length) return loadedTiles;
28229         loadedTiles = _;
28230         return connection;
28231     };
28232
28233     connection.logout = function() {
28234         userDetails = undefined;
28235         oauth.logout();
28236         event.auth();
28237         return connection;
28238     };
28239
28240     connection.authenticate = function(callback) {
28241         userDetails = undefined;
28242         function done(err, res) {
28243             event.auth();
28244             if (callback) callback(err, res);
28245         }
28246         return oauth.authenticate(done);
28247     };
28248
28249     return d3.rebind(connection, event, 'on');
28250 };
28251 /*
28252     iD.Difference represents the difference between two graphs.
28253     It knows how to calculate the set of entities that were
28254     created, modified, or deleted, and also contains the logic
28255     for recursively extending a difference to the complete set
28256     of entities that will require a redraw, taking into account
28257     child and parent relationships.
28258  */
28259 iD.Difference = function(base, head) {
28260     var changes = {}, length = 0;
28261
28262     function changed(h, b) {
28263         return h !== b && !_.isEqual(_.omit(h, 'v'), _.omit(b, 'v'));
28264     }
28265
28266     _.each(head.entities, function(h, id) {
28267         var b = base.entities[id];
28268         if (changed(h, b)) {
28269             changes[id] = {base: b, head: h};
28270             length++;
28271         }
28272     });
28273
28274     _.each(base.entities, function(b, id) {
28275         var h = head.entities[id];
28276         if (!changes[id] && changed(h, b)) {
28277             changes[id] = {base: b, head: h};
28278             length++;
28279         }
28280     });
28281
28282     function addParents(parents, result) {
28283         for (var i = 0; i < parents.length; i++) {
28284             var parent = parents[i];
28285
28286             if (parent.id in result)
28287                 continue;
28288
28289             result[parent.id] = parent;
28290             addParents(head.parentRelations(parent), result);
28291         }
28292     }
28293
28294     var difference = {};
28295
28296     difference.length = function() {
28297         return length;
28298     };
28299
28300     difference.changes = function() {
28301         return changes;
28302     };
28303
28304     difference.extantIDs = function() {
28305         var result = [];
28306         _.each(changes, function(change, id) {
28307             if (change.head) result.push(id);
28308         });
28309         return result;
28310     };
28311
28312     difference.modified = function() {
28313         var result = [];
28314         _.each(changes, function(change) {
28315             if (change.base && change.head) result.push(change.head);
28316         });
28317         return result;
28318     };
28319
28320     difference.created = function() {
28321         var result = [];
28322         _.each(changes, function(change) {
28323             if (!change.base && change.head) result.push(change.head);
28324         });
28325         return result;
28326     };
28327
28328     difference.deleted = function() {
28329         var result = [];
28330         _.each(changes, function(change) {
28331             if (change.base && !change.head) result.push(change.base);
28332         });
28333         return result;
28334     };
28335
28336     difference.summary = function() {
28337         var relevant = {};
28338
28339         function addEntity(entity, graph, changeType) {
28340             relevant[entity.id] = {
28341                 entity: entity,
28342                 graph: graph,
28343                 changeType: changeType
28344             };
28345         }
28346
28347         function addParents(entity) {
28348             var parents = head.parentWays(entity);
28349             for (var j = parents.length - 1; j >= 0; j--) {
28350                 var parent = parents[j];
28351                 if (!(parent.id in relevant)) addEntity(parent, head, 'modified');
28352             }
28353         }
28354
28355         _.each(changes, function(change) {
28356             if (change.head && change.head.geometry(head) !== 'vertex') {
28357                 addEntity(change.head, head, change.base ? 'modified' : 'created');
28358
28359             } else if (change.base && change.base.geometry(base) !== 'vertex') {
28360                 addEntity(change.base, base, 'deleted');
28361
28362             } else if (change.base && change.head) { // modified vertex
28363                 var moved    = !_.isEqual(change.base.loc,  change.head.loc),
28364                     retagged = !_.isEqual(change.base.tags, change.head.tags);
28365
28366                 if (moved) {
28367                     addParents(change.head);
28368                 }
28369
28370                 if (retagged || (moved && change.head.hasInterestingTags())) {
28371                     addEntity(change.head, head, 'modified');
28372                 }
28373
28374             } else if (change.head && change.head.hasInterestingTags()) { // created vertex
28375                 addEntity(change.head, head, 'created');
28376
28377             } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
28378                 addEntity(change.base, base, 'deleted');
28379             }
28380         });
28381
28382         return d3.values(relevant);
28383     };
28384
28385     difference.complete = function(extent) {
28386         var result = {}, id, change;
28387
28388         for (id in changes) {
28389             change = changes[id];
28390
28391             var h = change.head,
28392                 b = change.base,
28393                 entity = h || b;
28394
28395             if (extent &&
28396                 (!h || !h.intersects(extent, head)) &&
28397                 (!b || !b.intersects(extent, base)))
28398                 continue;
28399
28400             result[id] = h;
28401
28402             if (entity.type === 'way') {
28403                 var nh = h ? h.nodes : [],
28404                     nb = b ? b.nodes : [],
28405                     diff, i;
28406
28407                 diff = _.difference(nh, nb);
28408                 for (i = 0; i < diff.length; i++) {
28409                     result[diff[i]] = head.hasEntity(diff[i]);
28410                 }
28411
28412                 diff = _.difference(nb, nh);
28413                 for (i = 0; i < diff.length; i++) {
28414                     result[diff[i]] = head.hasEntity(diff[i]);
28415                 }
28416             }
28417
28418             addParents(head.parentWays(entity), result);
28419             addParents(head.parentRelations(entity), result);
28420         }
28421
28422         return result;
28423     };
28424
28425     return difference;
28426 };
28427 iD.Entity = function(attrs) {
28428     // For prototypal inheritance.
28429     if (this instanceof iD.Entity) return;
28430
28431     // Create the appropriate subtype.
28432     if (attrs && attrs.type) {
28433         return iD.Entity[attrs.type].apply(this, arguments);
28434     } else if (attrs && attrs.id) {
28435         return iD.Entity[iD.Entity.id.type(attrs.id)].apply(this, arguments);
28436     }
28437
28438     // Initialize a generic Entity (used only in tests).
28439     return (new iD.Entity()).initialize(arguments);
28440 };
28441
28442 iD.Entity.id = function(type) {
28443     return iD.Entity.id.fromOSM(type, iD.Entity.id.next[type]--);
28444 };
28445
28446 iD.Entity.id.next = {node: -1, way: -1, relation: -1};
28447
28448 iD.Entity.id.fromOSM = function(type, id) {
28449     return type[0] + id;
28450 };
28451
28452 iD.Entity.id.toOSM = function(id) {
28453     return id.slice(1);
28454 };
28455
28456 iD.Entity.id.type = function(id) {
28457     return {'n': 'node', 'w': 'way', 'r': 'relation'}[id[0]];
28458 };
28459
28460 // A function suitable for use as the second argument to d3.selection#data().
28461 iD.Entity.key = function(entity) {
28462     return entity.id + 'v' + (entity.v || 0);
28463 };
28464
28465 iD.Entity.prototype = {
28466     tags: {},
28467
28468     initialize: function(sources) {
28469         for (var i = 0; i < sources.length; ++i) {
28470             var source = sources[i];
28471             for (var prop in source) {
28472                 if (Object.prototype.hasOwnProperty.call(source, prop)) {
28473                     if (source[prop] === undefined) {
28474                         delete this[prop];
28475                     } else {
28476                         this[prop] = source[prop];
28477                     }
28478                 }
28479             }
28480         }
28481
28482         if (!this.id && this.type) {
28483             this.id = iD.Entity.id(this.type);
28484         }
28485         if (!this.hasOwnProperty('visible')) {
28486             this.visible = true;
28487         }
28488
28489         if (iD.debug) {
28490             Object.freeze(this);
28491             Object.freeze(this.tags);
28492
28493             if (this.loc) Object.freeze(this.loc);
28494             if (this.nodes) Object.freeze(this.nodes);
28495             if (this.members) Object.freeze(this.members);
28496         }
28497
28498         return this;
28499     },
28500
28501     copy: function(resolver, copies) {
28502         if (copies[this.id])
28503             return copies[this.id];
28504
28505         var copy = iD.Entity(this, {id: undefined, user: undefined, version: undefined});
28506         copies[this.id] = copy;
28507
28508         return copy;
28509     },
28510
28511     osmId: function() {
28512         return iD.Entity.id.toOSM(this.id);
28513     },
28514
28515     isNew: function() {
28516         return this.osmId() < 0;
28517     },
28518
28519     update: function(attrs) {
28520         return iD.Entity(this, attrs, {v: 1 + (this.v || 0)});
28521     },
28522
28523     mergeTags: function(tags) {
28524         var merged = _.clone(this.tags), changed = false;
28525         for (var k in tags) {
28526             var t1 = merged[k],
28527                 t2 = tags[k];
28528             if (!t1) {
28529                 changed = true;
28530                 merged[k] = t2;
28531             } else if (t1 !== t2) {
28532                 changed = true;
28533                 merged[k] = _.union(t1.split(/;\s*/), t2.split(/;\s*/)).join(';');
28534             }
28535         }
28536         return changed ? this.update({tags: merged}) : this;
28537     },
28538
28539     intersects: function(extent, resolver) {
28540         return this.extent(resolver).intersects(extent);
28541     },
28542
28543     isUsed: function(resolver) {
28544         return _.without(Object.keys(this.tags), 'area').length > 0 ||
28545             resolver.parentRelations(this).length > 0;
28546     },
28547
28548     hasInterestingTags: function() {
28549         return _.keys(this.tags).some(iD.interestingTag);
28550     },
28551
28552     isHighwayIntersection: function() {
28553         return false;
28554     },
28555
28556     deprecatedTags: function() {
28557         var tags = _.toPairs(this.tags);
28558         var deprecated = {};
28559
28560         iD.data.deprecated.forEach(function(d) {
28561             var match = _.toPairs(d.old)[0];
28562             tags.forEach(function(t) {
28563                 if (t[0] === match[0] &&
28564                     (t[1] === match[1] || match[1] === '*')) {
28565                     deprecated[t[0]] = t[1];
28566                 }
28567             });
28568         });
28569
28570         return deprecated;
28571     }
28572 };
28573 iD.Graph = function(other, mutable) {
28574     if (!(this instanceof iD.Graph)) return new iD.Graph(other, mutable);
28575
28576     if (other instanceof iD.Graph) {
28577         var base = other.base();
28578         this.entities = _.assign(Object.create(base.entities), other.entities);
28579         this._parentWays = _.assign(Object.create(base.parentWays), other._parentWays);
28580         this._parentRels = _.assign(Object.create(base.parentRels), other._parentRels);
28581
28582     } else {
28583         this.entities = Object.create({});
28584         this._parentWays = Object.create({});
28585         this._parentRels = Object.create({});
28586         this.rebase(other || [], [this]);
28587     }
28588
28589     this.transients = {};
28590     this._childNodes = {};
28591     this.frozen = !mutable;
28592 };
28593
28594 iD.Graph.prototype = {
28595     hasEntity: function(id) {
28596         return this.entities[id];
28597     },
28598
28599     entity: function(id) {
28600         var entity = this.entities[id];
28601         if (!entity) {
28602             throw new Error('entity ' + id + ' not found');
28603         }
28604         return entity;
28605     },
28606
28607     transient: function(entity, key, fn) {
28608         var id = entity.id,
28609             transients = this.transients[id] ||
28610             (this.transients[id] = {});
28611
28612         if (transients[key] !== undefined) {
28613             return transients[key];
28614         }
28615
28616         transients[key] = fn.call(entity);
28617
28618         return transients[key];
28619     },
28620
28621     parentWays: function(entity) {
28622         var parents = this._parentWays[entity.id],
28623             result = [];
28624
28625         if (parents) {
28626             for (var i = 0; i < parents.length; i++) {
28627                 result.push(this.entity(parents[i]));
28628             }
28629         }
28630         return result;
28631     },
28632
28633     isPoi: function(entity) {
28634         var parentWays = this._parentWays[entity.id];
28635         return !parentWays || parentWays.length === 0;
28636     },
28637
28638     isShared: function(entity) {
28639         var parentWays = this._parentWays[entity.id];
28640         return parentWays && parentWays.length > 1;
28641     },
28642
28643     parentRelations: function(entity) {
28644         var parents = this._parentRels[entity.id],
28645             result = [];
28646
28647         if (parents) {
28648             for (var i = 0; i < parents.length; i++) {
28649                 result.push(this.entity(parents[i]));
28650             }
28651         }
28652         return result;
28653     },
28654
28655     childNodes: function(entity) {
28656         if (this._childNodes[entity.id]) return this._childNodes[entity.id];
28657         if (!entity.nodes) return [];
28658
28659         var nodes = [];
28660         for (var i = 0; i < entity.nodes.length; i++) {
28661             nodes[i] = this.entity(entity.nodes[i]);
28662         }
28663
28664         if (iD.debug) Object.freeze(nodes);
28665
28666         this._childNodes[entity.id] = nodes;
28667         return this._childNodes[entity.id];
28668     },
28669
28670     base: function() {
28671         return {
28672             'entities': iD.util.getPrototypeOf(this.entities),
28673             'parentWays': iD.util.getPrototypeOf(this._parentWays),
28674             'parentRels': iD.util.getPrototypeOf(this._parentRels)
28675         };
28676     },
28677
28678     // Unlike other graph methods, rebase mutates in place. This is because it
28679     // is used only during the history operation that merges newly downloaded
28680     // data into each state. To external consumers, it should appear as if the
28681     // graph always contained the newly downloaded data.
28682     rebase: function(entities, stack, force) {
28683         var base = this.base(),
28684             i, j, k, id;
28685
28686         for (i = 0; i < entities.length; i++) {
28687             var entity = entities[i];
28688
28689             if (!entity.visible || (!force && base.entities[entity.id]))
28690                 continue;
28691
28692             // Merging data into the base graph
28693             base.entities[entity.id] = entity;
28694             this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
28695
28696             // Restore provisionally-deleted nodes that are discovered to have an extant parent
28697             if (entity.type === 'way') {
28698                 for (j = 0; j < entity.nodes.length; j++) {
28699                     id = entity.nodes[j];
28700                     for (k = 1; k < stack.length; k++) {
28701                         var ents = stack[k].entities;
28702                         if (ents.hasOwnProperty(id) && ents[id] === undefined) {
28703                             delete ents[id];
28704                         }
28705                     }
28706                 }
28707             }
28708         }
28709
28710         for (i = 0; i < stack.length; i++) {
28711             stack[i]._updateRebased();
28712         }
28713     },
28714
28715     _updateRebased: function() {
28716         var base = this.base(),
28717             i, k, child, id, keys;
28718
28719         keys = Object.keys(this._parentWays);
28720         for (i = 0; i < keys.length; i++) {
28721             child = keys[i];
28722             if (base.parentWays[child]) {
28723                 for (k = 0; k < base.parentWays[child].length; k++) {
28724                     id = base.parentWays[child][k];
28725                     if (!this.entities.hasOwnProperty(id) && !_.includes(this._parentWays[child], id)) {
28726                         this._parentWays[child].push(id);
28727                     }
28728                 }
28729             }
28730         }
28731
28732         keys = Object.keys(this._parentRels);
28733         for (i = 0; i < keys.length; i++) {
28734             child = keys[i];
28735             if (base.parentRels[child]) {
28736                 for (k = 0; k < base.parentRels[child].length; k++) {
28737                     id = base.parentRels[child][k];
28738                     if (!this.entities.hasOwnProperty(id) && !_.includes(this._parentRels[child], id)) {
28739                         this._parentRels[child].push(id);
28740                     }
28741                 }
28742             }
28743         }
28744
28745         this.transients = {};
28746
28747         // this._childNodes is not updated, under the assumption that
28748         // ways are always downloaded with their child nodes.
28749     },
28750
28751     // Updates calculated properties (parentWays, parentRels) for the specified change
28752     _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
28753
28754         parentWays = parentWays || this._parentWays;
28755         parentRels = parentRels || this._parentRels;
28756
28757         var type = entity && entity.type || oldentity && oldentity.type,
28758             removed, added, ways, rels, i;
28759
28760
28761         if (type === 'way') {
28762
28763             // Update parentWays
28764             if (oldentity && entity) {
28765                 removed = _.difference(oldentity.nodes, entity.nodes);
28766                 added = _.difference(entity.nodes, oldentity.nodes);
28767             } else if (oldentity) {
28768                 removed = oldentity.nodes;
28769                 added = [];
28770             } else if (entity) {
28771                 removed = [];
28772                 added = entity.nodes;
28773             }
28774             for (i = 0; i < removed.length; i++) {
28775                 parentWays[removed[i]] = _.without(parentWays[removed[i]], oldentity.id);
28776             }
28777             for (i = 0; i < added.length; i++) {
28778                 ways = _.without(parentWays[added[i]], entity.id);
28779                 ways.push(entity.id);
28780                 parentWays[added[i]] = ways;
28781             }
28782
28783         } else if (type === 'relation') {
28784
28785             // Update parentRels
28786             if (oldentity && entity) {
28787                 removed = _.difference(oldentity.members, entity.members);
28788                 added = _.difference(entity.members, oldentity);
28789             } else if (oldentity) {
28790                 removed = oldentity.members;
28791                 added = [];
28792             } else if (entity) {
28793                 removed = [];
28794                 added = entity.members;
28795             }
28796             for (i = 0; i < removed.length; i++) {
28797                 parentRels[removed[i].id] = _.without(parentRels[removed[i].id], oldentity.id);
28798             }
28799             for (i = 0; i < added.length; i++) {
28800                 rels = _.without(parentRels[added[i].id], entity.id);
28801                 rels.push(entity.id);
28802                 parentRels[added[i].id] = rels;
28803             }
28804         }
28805     },
28806
28807     replace: function(entity) {
28808         if (this.entities[entity.id] === entity)
28809             return this;
28810
28811         return this.update(function() {
28812             this._updateCalculated(this.entities[entity.id], entity);
28813             this.entities[entity.id] = entity;
28814         });
28815     },
28816
28817     remove: function(entity) {
28818         return this.update(function() {
28819             this._updateCalculated(entity, undefined);
28820             this.entities[entity.id] = undefined;
28821         });
28822     },
28823
28824     revert: function(id) {
28825         var baseEntity = this.base().entities[id],
28826             headEntity = this.entities[id];
28827
28828         if (headEntity === baseEntity)
28829             return this;
28830
28831         return this.update(function() {
28832             this._updateCalculated(headEntity, baseEntity);
28833             delete this.entities[id];
28834         });
28835     },
28836
28837     update: function() {
28838         var graph = this.frozen ? iD.Graph(this, true) : this;
28839
28840         for (var i = 0; i < arguments.length; i++) {
28841             arguments[i].call(graph, graph);
28842         }
28843
28844         if (this.frozen) graph.frozen = true;
28845
28846         return graph;
28847     },
28848
28849     // Obliterates any existing entities
28850     load: function(entities) {
28851         var base = this.base();
28852         this.entities = Object.create(base.entities);
28853
28854         for (var i in entities) {
28855             this.entities[i] = entities[i];
28856             this._updateCalculated(base.entities[i], this.entities[i]);
28857         }
28858
28859         return this;
28860     }
28861 };
28862 iD.History = function(context) {
28863     var stack, index, tree,
28864         imageryUsed = ['Bing'],
28865         dispatch = d3.dispatch('change', 'undone', 'redone'),
28866         lock = iD.util.SessionMutex('lock');
28867
28868     function perform(actions) {
28869         actions = Array.prototype.slice.call(actions);
28870
28871         var annotation;
28872
28873         if (!_.isFunction(_.last(actions))) {
28874             annotation = actions.pop();
28875         }
28876
28877         var graph = stack[index].graph;
28878         for (var i = 0; i < actions.length; i++) {
28879             graph = actions[i](graph);
28880         }
28881
28882         return {
28883             graph: graph,
28884             annotation: annotation,
28885             imageryUsed: imageryUsed
28886         };
28887     }
28888
28889     function change(previous) {
28890         var difference = iD.Difference(previous, history.graph());
28891         dispatch.change(difference);
28892         return difference;
28893     }
28894
28895     // iD uses namespaced keys so multiple installations do not conflict
28896     function getKey(n) {
28897         return 'iD_' + window.location.origin + '_' + n;
28898     }
28899
28900     var history = {
28901         graph: function() {
28902             return stack[index].graph;
28903         },
28904
28905         base: function() {
28906             return stack[0].graph;
28907         },
28908
28909         merge: function(entities, extent) {
28910             stack[0].graph.rebase(entities, _.map(stack, 'graph'), false);
28911             tree.rebase(entities, false);
28912
28913             dispatch.change(undefined, extent);
28914         },
28915
28916         perform: function() {
28917             var previous = stack[index].graph;
28918
28919             stack = stack.slice(0, index + 1);
28920             stack.push(perform(arguments));
28921             index++;
28922
28923             return change(previous);
28924         },
28925
28926         replace: function() {
28927             var previous = stack[index].graph;
28928
28929             // assert(index == stack.length - 1)
28930             stack[index] = perform(arguments);
28931
28932             return change(previous);
28933         },
28934
28935         pop: function() {
28936             var previous = stack[index].graph;
28937
28938             if (index > 0) {
28939                 index--;
28940                 stack.pop();
28941                 return change(previous);
28942             }
28943         },
28944
28945         // Same as calling pop and then perform
28946         overwrite: function() {
28947             var previous = stack[index].graph;
28948
28949             if (index > 0) {
28950                 index--;
28951                 stack.pop();
28952             }
28953             stack = stack.slice(0, index + 1);
28954             stack.push(perform(arguments));
28955             index++;
28956
28957             return change(previous);
28958         },
28959
28960         undo: function() {
28961             var previous = stack[index].graph;
28962
28963             // Pop to the next annotated state.
28964             while (index > 0) {
28965                 index--;
28966                 if (stack[index].annotation) break;
28967             }
28968
28969             dispatch.undone();
28970             return change(previous);
28971         },
28972
28973         redo: function() {
28974             var previous = stack[index].graph;
28975
28976             while (index < stack.length - 1) {
28977                 index++;
28978                 if (stack[index].annotation) break;
28979             }
28980
28981             dispatch.redone();
28982             return change(previous);
28983         },
28984
28985         undoAnnotation: function() {
28986             var i = index;
28987             while (i >= 0) {
28988                 if (stack[i].annotation) return stack[i].annotation;
28989                 i--;
28990             }
28991         },
28992
28993         redoAnnotation: function() {
28994             var i = index + 1;
28995             while (i <= stack.length - 1) {
28996                 if (stack[i].annotation) return stack[i].annotation;
28997                 i++;
28998             }
28999         },
29000
29001         intersects: function(extent) {
29002             return tree.intersects(extent, stack[index].graph);
29003         },
29004
29005         difference: function() {
29006             var base = stack[0].graph,
29007                 head = stack[index].graph;
29008             return iD.Difference(base, head);
29009         },
29010
29011         changes: function(action) {
29012             var base = stack[0].graph,
29013                 head = stack[index].graph;
29014
29015             if (action) {
29016                 head = action(head);
29017             }
29018
29019             var difference = iD.Difference(base, head);
29020
29021             return {
29022                 modified: difference.modified(),
29023                 created: difference.created(),
29024                 deleted: difference.deleted()
29025             };
29026         },
29027
29028         validate: function(changes) {
29029             return _(iD.validations)
29030                 .map(function(fn) { return fn()(changes, stack[index].graph); })
29031                 .flatten()
29032                 .value();
29033         },
29034
29035         hasChanges: function() {
29036             return this.difference().length() > 0;
29037         },
29038
29039         imageryUsed: function(sources) {
29040             if (sources) {
29041                 imageryUsed = sources;
29042                 return history;
29043             } else {
29044                 return _(stack.slice(1, index + 1))
29045                     .map('imageryUsed')
29046                     .flatten()
29047                     .uniq()
29048                     .without(undefined, 'Custom')
29049                     .value();
29050             }
29051         },
29052
29053         reset: function() {
29054             stack = [{graph: iD.Graph()}];
29055             index = 0;
29056             tree = iD.Tree(stack[0].graph);
29057             dispatch.change();
29058             return history;
29059         },
29060
29061         toJSON: function() {
29062             if (!this.hasChanges()) return;
29063
29064             var allEntities = {},
29065                 baseEntities = {},
29066                 base = stack[0];
29067
29068             var s = stack.map(function(i) {
29069                 var modified = [], deleted = [];
29070
29071                 _.forEach(i.graph.entities, function(entity, id) {
29072                     if (entity) {
29073                         var key = iD.Entity.key(entity);
29074                         allEntities[key] = entity;
29075                         modified.push(key);
29076                     } else {
29077                         deleted.push(id);
29078                     }
29079
29080                     // make sure that the originals of changed or deleted entities get merged
29081                     // into the base of the stack after restoring the data from JSON.
29082                     if (id in base.graph.entities) {
29083                         baseEntities[id] = base.graph.entities[id];
29084                     }
29085                     // get originals of parent entities too
29086                     _.forEach(base.graph._parentWays[id], function(parentId) {
29087                         if (parentId in base.graph.entities) {
29088                             baseEntities[parentId] = base.graph.entities[parentId];
29089                         }
29090                     });
29091                 });
29092
29093                 var x = {};
29094
29095                 if (modified.length) x.modified = modified;
29096                 if (deleted.length) x.deleted = deleted;
29097                 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
29098                 if (i.annotation) x.annotation = i.annotation;
29099
29100                 return x;
29101             });
29102
29103             return JSON.stringify({
29104                 version: 3,
29105                 entities: _.values(allEntities),
29106                 baseEntities: _.values(baseEntities),
29107                 stack: s,
29108                 nextIDs: iD.Entity.id.next,
29109                 index: index
29110             });
29111         },
29112
29113         fromJSON: function(json, loadChildNodes) {
29114             var h = JSON.parse(json),
29115                 loadComplete = true;
29116
29117             iD.Entity.id.next = h.nextIDs;
29118             index = h.index;
29119
29120             if (h.version === 2 || h.version === 3) {
29121                 var allEntities = {};
29122
29123                 h.entities.forEach(function(entity) {
29124                     allEntities[iD.Entity.key(entity)] = iD.Entity(entity);
29125                 });
29126
29127                 if (h.version === 3) {
29128                     // This merges originals for changed entities into the base of
29129                     // the stack even if the current stack doesn't have them (for
29130                     // example when iD has been restarted in a different region)
29131                     var baseEntities = h.baseEntities.map(function(d) { return iD.Entity(d); });
29132                     stack[0].graph.rebase(baseEntities, _.map(stack, 'graph'), true);
29133                     tree.rebase(baseEntities, true);
29134
29135                     // When we restore a modified way, we also need to fetch any missing
29136                     // childnodes that would normally have been downloaded with it.. #2142
29137                     if (loadChildNodes) {
29138                         var missing =  _(baseEntities)
29139                                 .filter({ type: 'way' })
29140                                 .map('nodes')
29141                                 .flatten()
29142                                 .uniq()
29143                                 .reject(function(n) { return stack[0].graph.hasEntity(n); })
29144                                 .value();
29145
29146                         if (!_.isEmpty(missing)) {
29147                             loadComplete = false;
29148                             context.redrawEnable(false);
29149
29150                             var loading = iD.ui.Loading(context).blocking(true);
29151                             context.container().call(loading);
29152
29153                             var childNodesLoaded = function(err, result) {
29154                                 if (!err) {
29155                                     var visible = _.groupBy(result.data, 'visible');
29156                                     if (!_.isEmpty(visible.true)) {
29157                                         missing = _.difference(missing, _.map(visible.true, 'id'));
29158                                         stack[0].graph.rebase(visible.true, _.map(stack, 'graph'), true);
29159                                         tree.rebase(visible.true, true);
29160                                     }
29161
29162                                     // fetch older versions of nodes that were deleted..
29163                                     _.each(visible.false, function(entity) {
29164                                         context.connection()
29165                                             .loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
29166                                     });
29167                                 }
29168
29169                                 if (err || _.isEmpty(missing)) {
29170                                     loading.close();
29171                                     context.redrawEnable(true);
29172                                     dispatch.change();
29173                                 }
29174                             };
29175
29176                             context.connection().loadMultiple(missing, childNodesLoaded);
29177                         }
29178                     }
29179                 }
29180
29181                 stack = h.stack.map(function(d) {
29182                     var entities = {}, entity;
29183
29184                     if (d.modified) {
29185                         d.modified.forEach(function(key) {
29186                             entity = allEntities[key];
29187                             entities[entity.id] = entity;
29188                         });
29189                     }
29190
29191                     if (d.deleted) {
29192                         d.deleted.forEach(function(id) {
29193                             entities[id] = undefined;
29194                         });
29195                     }
29196
29197                     return {
29198                         graph: iD.Graph(stack[0].graph).load(entities),
29199                         annotation: d.annotation,
29200                         imageryUsed: d.imageryUsed
29201                     };
29202                 });
29203
29204             } else { // original version
29205                 stack = h.stack.map(function(d) {
29206                     var entities = {};
29207
29208                     for (var i in d.entities) {
29209                         var entity = d.entities[i];
29210                         entities[i] = entity === 'undefined' ? undefined : iD.Entity(entity);
29211                     }
29212
29213                     d.graph = iD.Graph(stack[0].graph).load(entities);
29214                     return d;
29215                 });
29216             }
29217
29218             if (loadComplete) {
29219                 dispatch.change();
29220             }
29221
29222             return history;
29223         },
29224
29225         save: function() {
29226             if (lock.locked()) context.storage(getKey('saved_history'), history.toJSON() || null);
29227             return history;
29228         },
29229
29230         clearSaved: function() {
29231             context.debouncedSave.cancel();
29232             if (lock.locked()) context.storage(getKey('saved_history'), null);
29233             return history;
29234         },
29235
29236         lock: function() {
29237             return lock.lock();
29238         },
29239
29240         unlock: function() {
29241             lock.unlock();
29242         },
29243
29244         // is iD not open in another window and it detects that
29245         // there's a history stored in localStorage that's recoverable?
29246         restorableChanges: function() {
29247             return lock.locked() && !!context.storage(getKey('saved_history'));
29248         },
29249
29250         // load history from a version stored in localStorage
29251         restore: function() {
29252             if (!lock.locked()) return;
29253
29254             var json = context.storage(getKey('saved_history'));
29255             if (json) history.fromJSON(json, true);
29256         },
29257
29258         _getKey: getKey
29259
29260     };
29261
29262     history.reset();
29263
29264     return d3.rebind(history, dispatch, 'on');
29265 };
29266 iD.Node = iD.Entity.node = function iD_Node() {
29267     if (!(this instanceof iD_Node)) {
29268         return (new iD_Node()).initialize(arguments);
29269     } else if (arguments.length) {
29270         this.initialize(arguments);
29271     }
29272 };
29273
29274 iD.Node.prototype = Object.create(iD.Entity.prototype);
29275
29276 _.extend(iD.Node.prototype, {
29277     type: 'node',
29278
29279     extent: function() {
29280         return new iD.geo.Extent(this.loc);
29281     },
29282
29283     geometry: function(graph) {
29284         return graph.transient(this, 'geometry', function() {
29285             return graph.isPoi(this) ? 'point' : 'vertex';
29286         });
29287     },
29288
29289     move: function(loc) {
29290         return this.update({loc: loc});
29291     },
29292
29293     isIntersection: function(resolver) {
29294         return resolver.transient(this, 'isIntersection', function() {
29295             return resolver.parentWays(this).filter(function(parent) {
29296                 return (parent.tags.highway ||
29297                     parent.tags.waterway ||
29298                     parent.tags.railway ||
29299                     parent.tags.aeroway) &&
29300                     parent.geometry(resolver) === 'line';
29301             }).length > 1;
29302         });
29303     },
29304
29305     isHighwayIntersection: function(resolver) {
29306         return resolver.transient(this, 'isHighwayIntersection', function() {
29307             return resolver.parentWays(this).filter(function(parent) {
29308                 return parent.tags.highway && parent.geometry(resolver) === 'line';
29309             }).length > 1;
29310         });
29311     },
29312
29313     asJXON: function(changeset_id) {
29314         var r = {
29315             node: {
29316                 '@id': this.osmId(),
29317                 '@lon': this.loc[0],
29318                 '@lat': this.loc[1],
29319                 '@version': (this.version || 0),
29320                 tag: _.map(this.tags, function(v, k) {
29321                     return { keyAttributes: { k: k, v: v } };
29322                 })
29323             }
29324         };
29325         if (changeset_id) r.node['@changeset'] = changeset_id;
29326         return r;
29327     },
29328
29329     asGeoJSON: function() {
29330         return {
29331             type: 'Point',
29332             coordinates: this.loc
29333         };
29334     }
29335 });
29336 iD.Relation = iD.Entity.relation = function iD_Relation() {
29337     if (!(this instanceof iD_Relation)) {
29338         return (new iD_Relation()).initialize(arguments);
29339     } else if (arguments.length) {
29340         this.initialize(arguments);
29341     }
29342 };
29343
29344 iD.Relation.prototype = Object.create(iD.Entity.prototype);
29345
29346 iD.Relation.creationOrder = function(a, b) {
29347     var aId = parseInt(iD.Entity.id.toOSM(a.id), 10);
29348     var bId = parseInt(iD.Entity.id.toOSM(b.id), 10);
29349
29350     if (aId < 0 || bId < 0) return aId - bId;
29351     return bId - aId;
29352 };
29353
29354 _.extend(iD.Relation.prototype, {
29355     type: 'relation',
29356     members: [],
29357
29358     copy: function(resolver, copies) {
29359         if (copies[this.id])
29360             return copies[this.id];
29361
29362         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
29363
29364         var members = this.members.map(function(member) {
29365             return _.extend({}, member, {id: resolver.entity(member.id).copy(resolver, copies).id});
29366         });
29367
29368         copy = copy.update({members: members});
29369         copies[this.id] = copy;
29370
29371         return copy;
29372     },
29373
29374     extent: function(resolver, memo) {
29375         return resolver.transient(this, 'extent', function() {
29376             if (memo && memo[this.id]) return iD.geo.Extent();
29377             memo = memo || {};
29378             memo[this.id] = true;
29379
29380             var extent = iD.geo.Extent();
29381             for (var i = 0; i < this.members.length; i++) {
29382                 var member = resolver.hasEntity(this.members[i].id);
29383                 if (member) {
29384                     extent._extend(member.extent(resolver, memo));
29385                 }
29386             }
29387             return extent;
29388         });
29389     },
29390
29391     geometry: function(graph) {
29392         return graph.transient(this, 'geometry', function() {
29393             return this.isMultipolygon() ? 'area' : 'relation';
29394         });
29395     },
29396
29397     isDegenerate: function() {
29398         return this.members.length === 0;
29399     },
29400
29401     // Return an array of members, each extended with an 'index' property whose value
29402     // is the member index.
29403     indexedMembers: function() {
29404         var result = new Array(this.members.length);
29405         for (var i = 0; i < this.members.length; i++) {
29406             result[i] = _.extend({}, this.members[i], {index: i});
29407         }
29408         return result;
29409     },
29410
29411     // Return the first member with the given role. A copy of the member object
29412     // is returned, extended with an 'index' property whose value is the member index.
29413     memberByRole: function(role) {
29414         for (var i = 0; i < this.members.length; i++) {
29415             if (this.members[i].role === role) {
29416                 return _.extend({}, this.members[i], {index: i});
29417             }
29418         }
29419     },
29420
29421     // Return the first member with the given id. A copy of the member object
29422     // is returned, extended with an 'index' property whose value is the member index.
29423     memberById: function(id) {
29424         for (var i = 0; i < this.members.length; i++) {
29425             if (this.members[i].id === id) {
29426                 return _.extend({}, this.members[i], {index: i});
29427             }
29428         }
29429     },
29430
29431     // Return the first member with the given id and role. A copy of the member object
29432     // is returned, extended with an 'index' property whose value is the member index.
29433     memberByIdAndRole: function(id, role) {
29434         for (var i = 0; i < this.members.length; i++) {
29435             if (this.members[i].id === id && this.members[i].role === role) {
29436                 return _.extend({}, this.members[i], {index: i});
29437             }
29438         }
29439     },
29440
29441     addMember: function(member, index) {
29442         var members = this.members.slice();
29443         members.splice(index === undefined ? members.length : index, 0, member);
29444         return this.update({members: members});
29445     },
29446
29447     updateMember: function(member, index) {
29448         var members = this.members.slice();
29449         members.splice(index, 1, _.extend({}, members[index], member));
29450         return this.update({members: members});
29451     },
29452
29453     removeMember: function(index) {
29454         var members = this.members.slice();
29455         members.splice(index, 1);
29456         return this.update({members: members});
29457     },
29458
29459     removeMembersWithID: function(id) {
29460         var members = _.reject(this.members, function(m) { return m.id === id; });
29461         return this.update({members: members});
29462     },
29463
29464     // Wherever a member appears with id `needle.id`, replace it with a member
29465     // with id `replacement.id`, type `replacement.type`, and the original role,
29466     // unless a member already exists with that id and role. Return an updated
29467     // relation.
29468     replaceMember: function(needle, replacement) {
29469         if (!this.memberById(needle.id))
29470             return this;
29471
29472         var members = [];
29473
29474         for (var i = 0; i < this.members.length; i++) {
29475             var member = this.members[i];
29476             if (member.id !== needle.id) {
29477                 members.push(member);
29478             } else if (!this.memberByIdAndRole(replacement.id, member.role)) {
29479                 members.push({id: replacement.id, type: replacement.type, role: member.role});
29480             }
29481         }
29482
29483         return this.update({members: members});
29484     },
29485
29486     asJXON: function(changeset_id) {
29487         var r = {
29488             relation: {
29489                 '@id': this.osmId(),
29490                 '@version': this.version || 0,
29491                 member: _.map(this.members, function(member) {
29492                     return { keyAttributes: { type: member.type, role: member.role, ref: iD.Entity.id.toOSM(member.id) } };
29493                 }),
29494                 tag: _.map(this.tags, function(v, k) {
29495                     return { keyAttributes: { k: k, v: v } };
29496                 })
29497             }
29498         };
29499         if (changeset_id) r.relation['@changeset'] = changeset_id;
29500         return r;
29501     },
29502
29503     asGeoJSON: function(resolver) {
29504         return resolver.transient(this, 'GeoJSON', function () {
29505             if (this.isMultipolygon()) {
29506                 return {
29507                     type: 'MultiPolygon',
29508                     coordinates: this.multipolygon(resolver)
29509                 };
29510             } else {
29511                 return {
29512                     type: 'FeatureCollection',
29513                     properties: this.tags,
29514                     features: this.members.map(function (member) {
29515                         return _.extend({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
29516                     })
29517                 };
29518             }
29519         });
29520     },
29521
29522     area: function(resolver) {
29523         return resolver.transient(this, 'area', function() {
29524             return d3.geo.area(this.asGeoJSON(resolver));
29525         });
29526     },
29527
29528     isMultipolygon: function() {
29529         return this.tags.type === 'multipolygon';
29530     },
29531
29532     isComplete: function(resolver) {
29533         for (var i = 0; i < this.members.length; i++) {
29534             if (!resolver.hasEntity(this.members[i].id)) {
29535                 return false;
29536             }
29537         }
29538         return true;
29539     },
29540
29541     isRestriction: function() {
29542         return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
29543     },
29544
29545     // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
29546     // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
29547     //
29548     // This corresponds to the structure needed for rendering a multipolygon path using a
29549     // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
29550     //
29551     // In the case of invalid geometries, this function will still return a result which
29552     // includes the nodes of all way members, but some Nds may be unclosed and some inner
29553     // rings not matched with the intended outer ring.
29554     //
29555     multipolygon: function(resolver) {
29556         var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
29557             inners = this.members.filter(function(m) { return 'inner' === m.role; });
29558
29559         outers = iD.geo.joinWays(outers, resolver);
29560         inners = iD.geo.joinWays(inners, resolver);
29561
29562         outers = outers.map(function(outer) { return _.map(outer.nodes, 'loc'); });
29563         inners = inners.map(function(inner) { return _.map(inner.nodes, 'loc'); });
29564
29565         var result = outers.map(function(o) {
29566             // Heuristic for detecting counterclockwise winding order. Assumes
29567             // that OpenStreetMap polygons are not hemisphere-spanning.
29568             return [d3.geo.area({type: 'Polygon', coordinates: [o]}) > 2 * Math.PI ? o.reverse() : o];
29569         });
29570
29571         function findOuter(inner) {
29572             var o, outer;
29573
29574             for (o = 0; o < outers.length; o++) {
29575                 outer = outers[o];
29576                 if (iD.geo.polygonContainsPolygon(outer, inner))
29577                     return o;
29578             }
29579
29580             for (o = 0; o < outers.length; o++) {
29581                 outer = outers[o];
29582                 if (iD.geo.polygonIntersectsPolygon(outer, inner))
29583                     return o;
29584             }
29585         }
29586
29587         for (var i = 0; i < inners.length; i++) {
29588             var inner = inners[i];
29589
29590             if (d3.geo.area({type: 'Polygon', coordinates: [inner]}) < 2 * Math.PI) {
29591                 inner = inner.reverse();
29592             }
29593
29594             var o = findOuter(inners[i]);
29595             if (o !== undefined)
29596                 result[o].push(inners[i]);
29597             else
29598                 result.push([inners[i]]); // Invalid geometry
29599         }
29600
29601         return result;
29602     }
29603 });
29604 iD.oneWayTags = {
29605     'aerialway': {
29606         'chair_lift': true,
29607         'mixed_lift': true,
29608         't-bar': true,
29609         'j-bar': true,
29610         'platter': true,
29611         'rope_tow': true,
29612         'magic_carpet': true,
29613         'yes': true
29614     },
29615     'highway': {
29616         'motorway': true,
29617         'motorway_link': true
29618     },
29619     'junction': {
29620         'roundabout': true
29621     },
29622     'man_made': {
29623         'piste:halfpipe': true
29624     },
29625     'piste:type': {
29626         'downhill': true,
29627         'sled': true,
29628         'yes': true
29629     },
29630     'waterway': {
29631         'river': true,
29632         'stream': true
29633     }
29634 };
29635
29636 iD.pavedTags = {
29637     'surface': {
29638         'paved': true,
29639         'asphalt': true,
29640         'concrete': true
29641     },
29642     'tracktype': {
29643         'grade1': true
29644     }
29645 };
29646
29647 iD.interestingTag = function (key) {
29648     return key !== 'attribution' &&
29649         key !== 'created_by' &&
29650         key !== 'source' &&
29651         key !== 'odbl' &&
29652         key.indexOf('tiger:') !== 0;
29653
29654 };
29655 iD.Tree = function(head) {
29656     var rtree = rbush(),
29657         rectangles = {};
29658
29659     function entityRectangle(entity) {
29660         var rect = entity.extent(head).rectangle();
29661         rect.id = entity.id;
29662         rectangles[entity.id] = rect;
29663         return rect;
29664     }
29665
29666     function updateParents(entity, insertions, memo) {
29667         head.parentWays(entity).forEach(function(way) {
29668             if (rectangles[way.id]) {
29669                 rtree.remove(rectangles[way.id]);
29670                 insertions[way.id] = way;
29671             }
29672             updateParents(way, insertions, memo);
29673         });
29674
29675         head.parentRelations(entity).forEach(function(relation) {
29676             if (memo[entity.id]) return;
29677             memo[entity.id] = true;
29678             if (rectangles[relation.id]) {
29679                 rtree.remove(rectangles[relation.id]);
29680                 insertions[relation.id] = relation;
29681             }
29682             updateParents(relation, insertions, memo);
29683         });
29684     }
29685
29686     var tree = {};
29687
29688     tree.rebase = function(entities, force) {
29689         var insertions = {};
29690
29691         for (var i = 0; i < entities.length; i++) {
29692             var entity = entities[i];
29693
29694             if (!entity.visible)
29695                 continue;
29696
29697             if (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id]) {
29698                 if (!force) {
29699                     continue;
29700                 } else if (rectangles[entity.id]) {
29701                     rtree.remove(rectangles[entity.id]);
29702                 }
29703             }
29704
29705             insertions[entity.id] = entity;
29706             updateParents(entity, insertions, {});
29707         }
29708
29709         rtree.load(_.map(insertions, entityRectangle));
29710
29711         return tree;
29712     };
29713
29714     tree.intersects = function(extent, graph) {
29715         if (graph !== head) {
29716             var diff = iD.Difference(head, graph),
29717                 insertions = {};
29718
29719             head = graph;
29720
29721             diff.deleted().forEach(function(entity) {
29722                 rtree.remove(rectangles[entity.id]);
29723                 delete rectangles[entity.id];
29724             });
29725
29726             diff.modified().forEach(function(entity) {
29727                 rtree.remove(rectangles[entity.id]);
29728                 insertions[entity.id] = entity;
29729                 updateParents(entity, insertions, {});
29730             });
29731
29732             diff.created().forEach(function(entity) {
29733                 insertions[entity.id] = entity;
29734             });
29735
29736             rtree.load(_.map(insertions, entityRectangle));
29737         }
29738
29739         return rtree.search(extent.rectangle()).map(function(rect) {
29740             return head.entity(rect.id);
29741         });
29742     };
29743
29744     return tree;
29745 };
29746 iD.Way = iD.Entity.way = function iD_Way() {
29747     if (!(this instanceof iD_Way)) {
29748         return (new iD_Way()).initialize(arguments);
29749     } else if (arguments.length) {
29750         this.initialize(arguments);
29751     }
29752 };
29753
29754 iD.Way.prototype = Object.create(iD.Entity.prototype);
29755
29756 _.extend(iD.Way.prototype, {
29757     type: 'way',
29758     nodes: [],
29759
29760     copy: function(resolver, copies) {
29761         if (copies[this.id])
29762             return copies[this.id];
29763
29764         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
29765
29766         var nodes = this.nodes.map(function(id) {
29767             return resolver.entity(id).copy(resolver, copies).id;
29768         });
29769
29770         copy = copy.update({nodes: nodes});
29771         copies[this.id] = copy;
29772
29773         return copy;
29774     },
29775
29776     extent: function(resolver) {
29777         return resolver.transient(this, 'extent', function() {
29778             var extent = iD.geo.Extent();
29779             for (var i = 0; i < this.nodes.length; i++) {
29780                 var node = resolver.hasEntity(this.nodes[i]);
29781                 if (node) {
29782                     extent._extend(node.extent());
29783                 }
29784             }
29785             return extent;
29786         });
29787     },
29788
29789     first: function() {
29790         return this.nodes[0];
29791     },
29792
29793     last: function() {
29794         return this.nodes[this.nodes.length - 1];
29795     },
29796
29797     contains: function(node) {
29798         return this.nodes.indexOf(node) >= 0;
29799     },
29800
29801     affix: function(node) {
29802         if (this.nodes[0] === node) return 'prefix';
29803         if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
29804     },
29805
29806     layer: function() {
29807         // explicit layer tag, clamp between -10, 10..
29808         if (this.tags.layer !== undefined) {
29809             return Math.max(-10, Math.min(+(this.tags.layer), 10));
29810         }
29811
29812         // implied layer tag..
29813         if (this.tags.location === 'overground') return 1;
29814         if (this.tags.location === 'underground') return -1;
29815         if (this.tags.location === 'underwater') return -10;
29816
29817         if (this.tags.power === 'line') return 10;
29818         if (this.tags.power === 'minor_line') return 10;
29819         if (this.tags.aerialway) return 10;
29820         if (this.tags.bridge) return 1;
29821         if (this.tags.cutting) return -1;
29822         if (this.tags.tunnel) return -1;
29823         if (this.tags.waterway) return -1;
29824         if (this.tags.man_made === 'pipeline') return -10;
29825         if (this.tags.boundary) return -10;
29826         return 0;
29827     },
29828
29829     isOneWay: function() {
29830         // explicit oneway tag..
29831         if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; }
29832         if (['no', '0'].indexOf(this.tags.oneway) !== -1) { return false; }
29833
29834         // implied oneway tag..
29835         for (var key in this.tags) {
29836             if (key in iD.oneWayTags && (this.tags[key] in iD.oneWayTags[key]))
29837                 return true;
29838         }
29839         return false;
29840     },
29841
29842     isClosed: function() {
29843         return this.nodes.length > 0 && this.first() === this.last();
29844     },
29845
29846     isConvex: function(resolver) {
29847         if (!this.isClosed() || this.isDegenerate()) return null;
29848
29849         var nodes = _.uniq(resolver.childNodes(this)),
29850             coords = _.map(nodes, 'loc'),
29851             curr = 0, prev = 0;
29852
29853         for (var i = 0; i < coords.length; i++) {
29854             var o = coords[(i+1) % coords.length],
29855                 a = coords[i],
29856                 b = coords[(i+2) % coords.length],
29857                 res = iD.geo.cross(o, a, b);
29858
29859             curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
29860             if (curr === 0) {
29861                 continue;
29862             } else if (prev && curr !== prev) {
29863                 return false;
29864             }
29865             prev = curr;
29866         }
29867         return true;
29868     },
29869
29870     isArea: function() {
29871         if (this.tags.area === 'yes')
29872             return true;
29873         if (!this.isClosed() || this.tags.area === 'no')
29874             return false;
29875         for (var key in this.tags)
29876             if (key in iD.areaKeys && !(this.tags[key] in iD.areaKeys[key]))
29877                 return true;
29878         return false;
29879     },
29880
29881     isDegenerate: function() {
29882         return _.uniq(this.nodes).length < (this.isArea() ? 3 : 2);
29883     },
29884
29885     areAdjacent: function(n1, n2) {
29886         for (var i = 0; i < this.nodes.length; i++) {
29887             if (this.nodes[i] === n1) {
29888                 if (this.nodes[i - 1] === n2) return true;
29889                 if (this.nodes[i + 1] === n2) return true;
29890             }
29891         }
29892         return false;
29893     },
29894
29895     geometry: function(graph) {
29896         return graph.transient(this, 'geometry', function() {
29897             return this.isArea() ? 'area' : 'line';
29898         });
29899     },
29900
29901     addNode: function(id, index) {
29902         var nodes = this.nodes.slice();
29903         nodes.splice(index === undefined ? nodes.length : index, 0, id);
29904         return this.update({nodes: nodes});
29905     },
29906
29907     updateNode: function(id, index) {
29908         var nodes = this.nodes.slice();
29909         nodes.splice(index, 1, id);
29910         return this.update({nodes: nodes});
29911     },
29912
29913     replaceNode: function(needle, replacement) {
29914         if (this.nodes.indexOf(needle) < 0)
29915             return this;
29916
29917         var nodes = this.nodes.slice();
29918         for (var i = 0; i < nodes.length; i++) {
29919             if (nodes[i] === needle) {
29920                 nodes[i] = replacement;
29921             }
29922         }
29923         return this.update({nodes: nodes});
29924     },
29925
29926     removeNode: function(id) {
29927         var nodes = [];
29928
29929         for (var i = 0; i < this.nodes.length; i++) {
29930             var node = this.nodes[i];
29931             if (node !== id && nodes[nodes.length - 1] !== node) {
29932                 nodes.push(node);
29933             }
29934         }
29935
29936         // Preserve circularity
29937         if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] !== nodes[0]) {
29938             nodes.push(nodes[0]);
29939         }
29940
29941         return this.update({nodes: nodes});
29942     },
29943
29944     asJXON: function(changeset_id) {
29945         var r = {
29946             way: {
29947                 '@id': this.osmId(),
29948                 '@version': this.version || 0,
29949                 nd: _.map(this.nodes, function(id) {
29950                     return { keyAttributes: { ref: iD.Entity.id.toOSM(id) } };
29951                 }),
29952                 tag: _.map(this.tags, function(v, k) {
29953                     return { keyAttributes: { k: k, v: v } };
29954                 })
29955             }
29956         };
29957         if (changeset_id) r.way['@changeset'] = changeset_id;
29958         return r;
29959     },
29960
29961     asGeoJSON: function(resolver) {
29962         return resolver.transient(this, 'GeoJSON', function() {
29963             var coordinates = _.map(resolver.childNodes(this), 'loc');
29964             if (this.isArea() && this.isClosed()) {
29965                 return {
29966                     type: 'Polygon',
29967                     coordinates: [coordinates]
29968                 };
29969             } else {
29970                 return {
29971                     type: 'LineString',
29972                     coordinates: coordinates
29973                 };
29974             }
29975         });
29976     },
29977
29978     area: function(resolver) {
29979         return resolver.transient(this, 'area', function() {
29980             var nodes = resolver.childNodes(this);
29981
29982             var json = {
29983                 type: 'Polygon',
29984                 coordinates: [_.map(nodes, 'loc')]
29985             };
29986
29987             if (!this.isClosed() && nodes.length) {
29988                 json.coordinates[0].push(nodes[0].loc);
29989             }
29990
29991             var area = d3.geo.area(json);
29992
29993             // Heuristic for detecting counterclockwise winding order. Assumes
29994             // that OpenStreetMap polygons are not hemisphere-spanning.
29995             if (area > 2 * Math.PI) {
29996                 json.coordinates[0] = json.coordinates[0].reverse();
29997                 area = d3.geo.area(json);
29998             }
29999
30000             return isNaN(area) ? 0 : area;
30001         });
30002     }
30003 });
30004 iD.Background = function(context) {
30005     var dispatch = d3.dispatch('change'),
30006         baseLayer = iD.TileLayer(context).projection(context.projection),
30007         overlayLayers = [],
30008         backgroundSources;
30009
30010
30011     function findSource(id) {
30012         return _.find(backgroundSources, function(d) {
30013             return d.id && d.id === id;
30014         });
30015     }
30016
30017
30018     function background(selection) {
30019         var base = selection.selectAll('.layer-background')
30020             .data([0]);
30021
30022         base.enter()
30023             .insert('div', '.layer-data')
30024             .attr('class', 'layer layer-background');
30025
30026         base.call(baseLayer);
30027
30028         var overlays = selection.selectAll('.layer-overlay')
30029             .data(overlayLayers, function(d) { return d.source().name(); });
30030
30031         overlays.enter()
30032             .insert('div', '.layer-data')
30033             .attr('class', 'layer layer-overlay');
30034
30035         overlays.each(function(layer) {
30036             d3.select(this).call(layer);
30037         });
30038
30039         overlays.exit()
30040             .remove();
30041     }
30042
30043
30044     background.updateImagery = function() {
30045         var b = background.baseLayerSource(),
30046             o = overlayLayers.map(function (d) { return d.source().id; }).join(','),
30047             meters = iD.geo.offsetToMeters(b.offset()),
30048             epsilon = 0.01,
30049             x = +meters[0].toFixed(2),
30050             y = +meters[1].toFixed(2),
30051             q = iD.util.stringQs(location.hash.substring(1));
30052
30053         var id = b.id;
30054         if (id === 'custom') {
30055             id = 'custom:' + b.template;
30056         }
30057
30058         if (id) {
30059             q.background = id;
30060         } else {
30061             delete q.background;
30062         }
30063
30064         if (o) {
30065             q.overlays = o;
30066         } else {
30067             delete q.overlays;
30068         }
30069
30070         if (Math.abs(x) > epsilon || Math.abs(y) > epsilon) {
30071             q.offset = x + ',' + y;
30072         } else {
30073             delete q.offset;
30074         }
30075
30076         location.replace('#' + iD.util.qsString(q, true));
30077
30078         var imageryUsed = [b.imageryUsed()];
30079
30080         overlayLayers.forEach(function (d) {
30081             var source = d.source();
30082             if (!source.isLocatorOverlay()) {
30083                 imageryUsed.push(source.imageryUsed());
30084             }
30085         });
30086
30087         var gpx = context.layers().layer('gpx');
30088         if (gpx && gpx.enabled() && gpx.hasGpx()) {
30089             imageryUsed.push('Local GPX');
30090         }
30091
30092         var mapillary_images = context.layers().layer('mapillary-images');
30093         if (mapillary_images && mapillary_images.enabled()) {
30094             imageryUsed.push('Mapillary Images');
30095         }
30096
30097         var mapillary_signs = context.layers().layer('mapillary-signs');
30098         if (mapillary_signs && mapillary_signs.enabled()) {
30099             imageryUsed.push('Mapillary Signs');
30100         }
30101
30102         context.history().imageryUsed(imageryUsed);
30103     };
30104
30105     background.sources = function(extent) {
30106         return backgroundSources.filter(function(source) {
30107             return source.intersects(extent);
30108         });
30109     };
30110
30111     background.dimensions = function(_) {
30112         baseLayer.dimensions(_);
30113
30114         overlayLayers.forEach(function(layer) {
30115             layer.dimensions(_);
30116         });
30117     };
30118
30119     background.baseLayerSource = function(d) {
30120         if (!arguments.length) return baseLayer.source();
30121         baseLayer.source(d);
30122         dispatch.change();
30123         background.updateImagery();
30124         return background;
30125     };
30126
30127     background.bing = function() {
30128         background.baseLayerSource(findSource('Bing'));
30129     };
30130
30131     background.showsLayer = function(d) {
30132         return d === baseLayer.source() ||
30133             (d.id === 'custom' && baseLayer.source().id === 'custom') ||
30134             overlayLayers.some(function(l) { return l.source() === d; });
30135     };
30136
30137     background.overlayLayerSources = function() {
30138         return overlayLayers.map(function (l) { return l.source(); });
30139     };
30140
30141     background.toggleOverlayLayer = function(d) {
30142         var layer;
30143
30144         for (var i = 0; i < overlayLayers.length; i++) {
30145             layer = overlayLayers[i];
30146             if (layer.source() === d) {
30147                 overlayLayers.splice(i, 1);
30148                 dispatch.change();
30149                 background.updateImagery();
30150                 return;
30151             }
30152         }
30153
30154         layer = iD.TileLayer(context)
30155             .source(d)
30156             .projection(context.projection)
30157             .dimensions(baseLayer.dimensions());
30158
30159         overlayLayers.push(layer);
30160         dispatch.change();
30161         background.updateImagery();
30162     };
30163
30164     background.nudge = function(d, zoom) {
30165         baseLayer.source().nudge(d, zoom);
30166         dispatch.change();
30167         background.updateImagery();
30168         return background;
30169     };
30170
30171     background.offset = function(d) {
30172         if (!arguments.length) return baseLayer.source().offset();
30173         baseLayer.source().offset(d);
30174         dispatch.change();
30175         background.updateImagery();
30176         return background;
30177     };
30178
30179     background.load = function(imagery) {
30180         function parseMap(qmap) {
30181             if (!qmap) return false;
30182             var args = qmap.split('/').map(Number);
30183             if (args.length < 3 || args.some(isNaN)) return false;
30184             return iD.geo.Extent([args[1], args[2]]);
30185         }
30186
30187         var q = iD.util.stringQs(location.hash.substring(1)),
30188             chosen = q.background || q.layer,
30189             extent = parseMap(q.map),
30190             best;
30191
30192         backgroundSources = imagery.map(function(source) {
30193             if (source.type === 'bing') {
30194                 return iD.BackgroundSource.Bing(source, dispatch);
30195             } else {
30196                 return iD.BackgroundSource(source);
30197             }
30198         });
30199
30200         backgroundSources.unshift(iD.BackgroundSource.None());
30201
30202         if (!chosen && extent) {
30203             best = _.find(this.sources(extent), function(s) { return s.best(); });
30204         }
30205
30206         if (chosen && chosen.indexOf('custom:') === 0) {
30207             background.baseLayerSource(iD.BackgroundSource.Custom(chosen.replace(/^custom:/, '')));
30208         } else {
30209             background.baseLayerSource(findSource(chosen) || best || findSource('Bing') || backgroundSources[1] || backgroundSources[0]);
30210         }
30211
30212         var locator = _.find(backgroundSources, function(d) {
30213             return d.overlay && d.default;
30214         });
30215
30216         if (locator) {
30217             background.toggleOverlayLayer(locator);
30218         }
30219
30220         var overlays = (q.overlays || '').split(',');
30221         overlays.forEach(function(overlay) {
30222             overlay = findSource(overlay);
30223             if (overlay) {
30224                 background.toggleOverlayLayer(overlay);
30225             }
30226         });
30227
30228         if (q.gpx) {
30229             var gpx = context.layers().layer('gpx');
30230             if (gpx) {
30231                 gpx.url(q.gpx);
30232             }
30233         }
30234
30235         if (q.offset) {
30236             var offset = q.offset.replace(/;/g, ',').split(',').map(function(n) {
30237                 return !isNaN(n) && n;
30238             });
30239
30240             if (offset.length === 2) {
30241                 background.offset(iD.geo.metersToOffset(offset));
30242             }
30243         }
30244     };
30245
30246     return d3.rebind(background, dispatch, 'on');
30247 };
30248 iD.BackgroundSource = function(data) {
30249     var source = _.clone(data),
30250         offset = [0, 0],
30251         name = source.name,
30252         best = !!source.best;
30253
30254     source.scaleExtent = data.scaleExtent || [0, 20];
30255     source.overzoom = data.overzoom !== false;
30256
30257     source.offset = function(_) {
30258         if (!arguments.length) return offset;
30259         offset = _;
30260         return source;
30261     };
30262
30263     source.nudge = function(_, zoomlevel) {
30264         offset[0] += _[0] / Math.pow(2, zoomlevel);
30265         offset[1] += _[1] / Math.pow(2, zoomlevel);
30266         return source;
30267     };
30268
30269     source.name = function() {
30270         return name;
30271     };
30272
30273     source.best = function() {
30274         return best;
30275     };
30276
30277     source.area = function() {
30278         if (!data.polygon) return Number.MAX_VALUE;  // worldwide
30279         var area = d3.geo.area({ type: 'MultiPolygon', coordinates: [ data.polygon ] });
30280         return isNaN(area) ? 0 : area;
30281     };
30282
30283     source.imageryUsed = function() {
30284         return source.id || name;
30285     };
30286
30287     source.url = function(coord) {
30288         return data.template
30289             .replace('{x}', coord[0])
30290             .replace('{y}', coord[1])
30291             // TMS-flipped y coordinate
30292             .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
30293             .replace(/\{z(oom)?\}/, coord[2])
30294             .replace(/\{switch:([^}]+)\}/, function(s, r) {
30295                 var subdomains = r.split(',');
30296                 return subdomains[(coord[0] + coord[1]) % subdomains.length];
30297             })
30298             .replace('{u}', function() {
30299                 var u = '';
30300                 for (var zoom = coord[2]; zoom > 0; zoom--) {
30301                     var b = 0;
30302                     var mask = 1 << (zoom - 1);
30303                     if ((coord[0] & mask) !== 0) b++;
30304                     if ((coord[1] & mask) !== 0) b += 2;
30305                     u += b.toString();
30306                 }
30307                 return u;
30308             });
30309     };
30310
30311     source.intersects = function(extent) {
30312         extent = extent.polygon();
30313         return !data.polygon || data.polygon.some(function(polygon) {
30314             return iD.geo.polygonIntersectsPolygon(polygon, extent, true);
30315         });
30316     };
30317
30318     source.validZoom = function(z) {
30319         return source.scaleExtent[0] <= z &&
30320             (source.overzoom || source.scaleExtent[1] > z);
30321     };
30322
30323     source.isLocatorOverlay = function() {
30324         return name === 'Locator Overlay';
30325     };
30326
30327     source.copyrightNotices = function() {};
30328
30329     return source;
30330 };
30331
30332 iD.BackgroundSource.Bing = function(data, dispatch) {
30333     // http://msdn.microsoft.com/en-us/library/ff701716.aspx
30334     // http://msdn.microsoft.com/en-us/library/ff701701.aspx
30335
30336     data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
30337
30338     var bing = iD.BackgroundSource(data),
30339         key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU', // Same as P2 and JOSM
30340         url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' +
30341             key + '&jsonp={callback}',
30342         providers = [];
30343
30344     d3.jsonp(url, function(json) {
30345         providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
30346             return {
30347                 attribution: provider.attribution,
30348                 areas: provider.coverageAreas.map(function(area) {
30349                     return {
30350                         zoom: [area.zoomMin, area.zoomMax],
30351                         extent: iD.geo.Extent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
30352                     };
30353                 })
30354             };
30355         });
30356         dispatch.change();
30357     });
30358
30359     bing.copyrightNotices = function(zoom, extent) {
30360         zoom = Math.min(zoom, 21);
30361         return providers.filter(function(provider) {
30362             return _.some(provider.areas, function(area) {
30363                 return extent.intersects(area.extent) &&
30364                     area.zoom[0] <= zoom &&
30365                     area.zoom[1] >= zoom;
30366             });
30367         }).map(function(provider) {
30368             return provider.attribution;
30369         }).join(', ');
30370     };
30371
30372     bing.logo = 'bing_maps.png';
30373     bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
30374
30375     return bing;
30376 };
30377
30378 iD.BackgroundSource.None = function() {
30379     var source = iD.BackgroundSource({id: 'none', template: ''});
30380
30381     source.name = function() {
30382         return t('background.none');
30383     };
30384
30385     source.imageryUsed = function() {
30386         return 'None';
30387     };
30388
30389     source.area = function() {
30390         return -1;
30391     };
30392
30393     return source;
30394 };
30395
30396 iD.BackgroundSource.Custom = function(template) {
30397     var source = iD.BackgroundSource({id: 'custom', template: template});
30398
30399     source.name = function() {
30400         return t('background.custom');
30401     };
30402
30403     source.imageryUsed = function() {
30404         return 'Custom (' + template + ')';
30405     };
30406
30407     source.area = function() {
30408         return -2;
30409     };
30410
30411     return source;
30412 };
30413 iD.Features = function(context) {
30414     var traffic_roads = {
30415         'motorway': true,
30416         'motorway_link': true,
30417         'trunk': true,
30418         'trunk_link': true,
30419         'primary': true,
30420         'primary_link': true,
30421         'secondary': true,
30422         'secondary_link': true,
30423         'tertiary': true,
30424         'tertiary_link': true,
30425         'residential': true,
30426         'unclassified': true,
30427         'living_street': true
30428     };
30429
30430     var service_roads = {
30431         'service': true,
30432         'road': true,
30433         'track': true
30434     };
30435
30436     var paths = {
30437         'path': true,
30438         'footway': true,
30439         'cycleway': true,
30440         'bridleway': true,
30441         'steps': true,
30442         'pedestrian': true,
30443         'corridor': true
30444     };
30445
30446     var past_futures = {
30447         'proposed': true,
30448         'construction': true,
30449         'abandoned': true,
30450         'dismantled': true,
30451         'disused': true,
30452         'razed': true,
30453         'demolished': true,
30454         'obliterated': true
30455     };
30456
30457     var dispatch = d3.dispatch('change', 'redraw'),
30458         _cullFactor = 1,
30459         _cache = {},
30460         _features = {},
30461         _stats = {},
30462         _keys = [],
30463         _hidden = [];
30464
30465     function update() {
30466         _hidden = features.hidden();
30467         dispatch.change();
30468         dispatch.redraw();
30469     }
30470
30471     function defineFeature(k, filter, max) {
30472         _keys.push(k);
30473         _features[k] = {
30474             filter: filter,
30475             enabled: true,   // whether the user wants it enabled..
30476             count: 0,
30477             currentMax: (max || Infinity),
30478             defaultMax: (max || Infinity),
30479             enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
30480             disable: function() { this.enabled = false; this.currentMax = 0; },
30481             hidden: function() { return !context.editable() || this.count > this.currentMax * _cullFactor; },
30482             autoHidden: function() { return this.hidden() && this.currentMax > 0; }
30483         };
30484     }
30485
30486
30487     defineFeature('points', function isPoint(entity, resolver, geometry) {
30488         return geometry === 'point';
30489     }, 200);
30490
30491     defineFeature('traffic_roads', function isTrafficRoad(entity) {
30492         return traffic_roads[entity.tags.highway];
30493     });
30494
30495     defineFeature('service_roads', function isServiceRoad(entity) {
30496         return service_roads[entity.tags.highway];
30497     });
30498
30499     defineFeature('paths', function isPath(entity) {
30500         return paths[entity.tags.highway];
30501     });
30502
30503     defineFeature('buildings', function isBuilding(entity) {
30504         return (
30505             !!entity.tags['building:part'] ||
30506             (!!entity.tags.building && entity.tags.building !== 'no') ||
30507             entity.tags.amenity === 'shelter' ||
30508             entity.tags.parking === 'multi-storey' ||
30509             entity.tags.parking === 'sheds' ||
30510             entity.tags.parking === 'carports' ||
30511             entity.tags.parking === 'garage_boxes'
30512         );
30513     }, 250);
30514
30515     defineFeature('landuse', function isLanduse(entity, resolver, geometry) {
30516         return geometry === 'area' &&
30517             !_features.buildings.filter(entity) &&
30518             !_features.water.filter(entity);
30519     });
30520
30521     defineFeature('boundaries', function isBoundary(entity) {
30522         return !!entity.tags.boundary;
30523     });
30524
30525     defineFeature('water', function isWater(entity) {
30526         return (
30527             !!entity.tags.waterway ||
30528             entity.tags.natural === 'water' ||
30529             entity.tags.natural === 'coastline' ||
30530             entity.tags.natural === 'bay' ||
30531             entity.tags.landuse === 'pond' ||
30532             entity.tags.landuse === 'basin' ||
30533             entity.tags.landuse === 'reservoir' ||
30534             entity.tags.landuse === 'salt_pond'
30535         );
30536     });
30537
30538     defineFeature('rail', function isRail(entity) {
30539         return (
30540             !!entity.tags.railway ||
30541             entity.tags.landuse === 'railway'
30542         ) && !(
30543             traffic_roads[entity.tags.highway] ||
30544             service_roads[entity.tags.highway] ||
30545             paths[entity.tags.highway]
30546         );
30547     });
30548
30549     defineFeature('power', function isPower(entity) {
30550         return !!entity.tags.power;
30551     });
30552
30553     // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
30554     defineFeature('past_future', function isPastFuture(entity) {
30555         if (
30556             traffic_roads[entity.tags.highway] ||
30557             service_roads[entity.tags.highway] ||
30558             paths[entity.tags.highway]
30559         ) { return false; }
30560
30561         var strings = Object.keys(entity.tags);
30562
30563         for (var i = 0; i < strings.length; i++) {
30564             var s = strings[i];
30565             if (past_futures[s] || past_futures[entity.tags[s]]) { return true; }
30566         }
30567         return false;
30568     });
30569
30570     // Lines or areas that don't match another feature filter.
30571     // IMPORTANT: The 'others' feature must be the last one defined,
30572     //   so that code in getMatches can skip this test if `hasMatch = true`
30573     defineFeature('others', function isOther(entity, resolver, geometry) {
30574         return (geometry === 'line' || geometry === 'area');
30575     });
30576
30577
30578     function features() {}
30579
30580     features.features = function() {
30581         return _features;
30582     };
30583
30584     features.keys = function() {
30585         return _keys;
30586     };
30587
30588     features.enabled = function(k) {
30589         if (!arguments.length) {
30590             return _.filter(_keys, function(k) { return _features[k].enabled; });
30591         }
30592         return _features[k] && _features[k].enabled;
30593     };
30594
30595     features.disabled = function(k) {
30596         if (!arguments.length) {
30597             return _.reject(_keys, function(k) { return _features[k].enabled; });
30598         }
30599         return _features[k] && !_features[k].enabled;
30600     };
30601
30602     features.hidden = function(k) {
30603         if (!arguments.length) {
30604             return _.filter(_keys, function(k) { return _features[k].hidden(); });
30605         }
30606         return _features[k] && _features[k].hidden();
30607     };
30608
30609     features.autoHidden = function(k) {
30610         if (!arguments.length) {
30611             return _.filter(_keys, function(k) { return _features[k].autoHidden(); });
30612         }
30613         return _features[k] && _features[k].autoHidden();
30614     };
30615
30616     features.enable = function(k) {
30617         if (_features[k] && !_features[k].enabled) {
30618             _features[k].enable();
30619             update();
30620         }
30621     };
30622
30623     features.disable = function(k) {
30624         if (_features[k] && _features[k].enabled) {
30625             _features[k].disable();
30626             update();
30627         }
30628     };
30629
30630     features.toggle = function(k) {
30631         if (_features[k]) {
30632             (function(f) { return f.enabled ? f.disable() : f.enable(); }(_features[k]));
30633             update();
30634         }
30635     };
30636
30637     features.resetStats = function() {
30638         _.each(_features, function(f) { f.count = 0; });
30639         dispatch.change();
30640     };
30641
30642     features.gatherStats = function(d, resolver, dimensions) {
30643         var needsRedraw = false,
30644             type = _.groupBy(d, function(ent) { return ent.type; }),
30645             entities = [].concat(type.relation || [], type.way || [], type.node || []),
30646             currHidden, geometry, matches;
30647
30648         _.each(_features, function(f) { f.count = 0; });
30649
30650         // adjust the threshold for point/building culling based on viewport size..
30651         // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
30652         _cullFactor = dimensions[0] * dimensions[1] / 1000000;
30653
30654         for (var i = 0; i < entities.length; i++) {
30655             geometry = entities[i].geometry(resolver);
30656             if (!(geometry === 'vertex' || geometry === 'relation')) {
30657                 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
30658                 for (var j = 0; j < matches.length; j++) {
30659                     _features[matches[j]].count++;
30660                 }
30661             }
30662         }
30663
30664         currHidden = features.hidden();
30665         if (currHidden !== _hidden) {
30666             _hidden = currHidden;
30667             needsRedraw = true;
30668             dispatch.change();
30669         }
30670
30671         return needsRedraw;
30672     };
30673
30674     features.stats = function() {
30675         _.each(_keys, function(k) { _stats[k] = _features[k].count; });
30676         return _stats;
30677     };
30678
30679     features.clear = function(d) {
30680         for (var i = 0; i < d.length; i++) {
30681             features.clearEntity(d[i]);
30682         }
30683     };
30684
30685     features.clearEntity = function(entity) {
30686         delete _cache[iD.Entity.key(entity)];
30687     };
30688
30689     features.reset = function() {
30690         _cache = {};
30691     };
30692
30693     features.getMatches = function(entity, resolver, geometry) {
30694         if (geometry === 'vertex' || geometry === 'relation') return {};
30695
30696         var ent = iD.Entity.key(entity);
30697         if (!_cache[ent]) {
30698             _cache[ent] = {};
30699         }
30700
30701         if (!_cache[ent].matches) {
30702             var matches = {},
30703                 hasMatch = false;
30704
30705             for (var i = 0; i < _keys.length; i++) {
30706                 if (_keys[i] === 'others') {
30707                     if (hasMatch) continue;
30708
30709                     // Multipolygon members:
30710                     // If an entity...
30711                     //   1. is a way that hasn't matched other "interesting" feature rules,
30712                     //   2. and it belongs to a single parent multipolygon relation
30713                     // ...then match whatever feature rules the parent multipolygon has matched.
30714                     // see #2548, #2887
30715                     //
30716                     // IMPORTANT:
30717                     // For this to work, getMatches must be called on relations before ways.
30718                     //
30719                     if (entity.type === 'way') {
30720                         var parents = features.getParents(entity, resolver, geometry);
30721                         if (parents.length === 1 && parents[0].isMultipolygon()) {
30722                             var pkey = iD.Entity.key(parents[0]);
30723                             if (_cache[pkey] && _cache[pkey].matches) {
30724                                 matches = _.clone(_cache[pkey].matches);
30725                                 continue;
30726                             }
30727                         }
30728                     }
30729                 }
30730
30731                 if (_features[_keys[i]].filter(entity, resolver, geometry)) {
30732                     matches[_keys[i]] = hasMatch = true;
30733                 }
30734             }
30735             _cache[ent].matches = matches;
30736         }
30737
30738         return _cache[ent].matches;
30739     };
30740
30741     features.getParents = function(entity, resolver, geometry) {
30742         if (geometry === 'point') return [];
30743
30744         var ent = iD.Entity.key(entity);
30745         if (!_cache[ent]) {
30746             _cache[ent] = {};
30747         }
30748
30749         if (!_cache[ent].parents) {
30750             var parents = [];
30751             if (geometry === 'vertex') {
30752                 parents = resolver.parentWays(entity);
30753             } else {   // 'line', 'area', 'relation'
30754                 parents = resolver.parentRelations(entity);
30755             }
30756             _cache[ent].parents = parents;
30757         }
30758         return _cache[ent].parents;
30759     };
30760
30761     features.isHiddenFeature = function(entity, resolver, geometry) {
30762         if (!_hidden.length) return false;
30763         if (!entity.version) return false;
30764
30765         var matches = features.getMatches(entity, resolver, geometry);
30766
30767         for (var i = 0; i < _hidden.length; i++) {
30768             if (matches[_hidden[i]]) return true;
30769         }
30770         return false;
30771     };
30772
30773     features.isHiddenChild = function(entity, resolver, geometry) {
30774         if (!_hidden.length) return false;
30775         if (!entity.version || geometry === 'point') return false;
30776
30777         var parents = features.getParents(entity, resolver, geometry);
30778         if (!parents.length) return false;
30779
30780         for (var i = 0; i < parents.length; i++) {
30781             if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
30782                 return false;
30783             }
30784         }
30785         return true;
30786     };
30787
30788     features.hasHiddenConnections = function(entity, resolver) {
30789         if (!_hidden.length) return false;
30790         var childNodes, connections;
30791
30792         if (entity.type === 'midpoint') {
30793             childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
30794             connections = [];
30795         } else {
30796             childNodes = entity.nodes ? resolver.childNodes(entity) : [];
30797             connections = features.getParents(entity, resolver, entity.geometry(resolver));
30798         }
30799
30800         // gather ways connected to child nodes..
30801         connections = _.reduce(childNodes, function(result, e) {
30802             return resolver.isShared(e) ? _.union(result, resolver.parentWays(e)) : result;
30803         }, connections);
30804
30805         return connections.length ? _.some(connections, function(e) {
30806             return features.isHidden(e, resolver, e.geometry(resolver));
30807         }) : false;
30808     };
30809
30810     features.isHidden = function(entity, resolver, geometry) {
30811         if (!_hidden.length) return false;
30812         if (!entity.version) return false;
30813
30814         var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
30815         return fn(entity, resolver, geometry);
30816     };
30817
30818     features.filter = function(d, resolver) {
30819         if (!_hidden.length) return d;
30820
30821         var result = [];
30822         for (var i = 0; i < d.length; i++) {
30823             var entity = d[i];
30824             if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
30825                 result.push(entity);
30826             }
30827         }
30828         return result;
30829     };
30830
30831     return d3.rebind(features, dispatch, 'on');
30832 };
30833 iD.Map = function(context) {
30834     var dimensions = [1, 1],
30835         dispatch = d3.dispatch('move', 'drawn'),
30836         projection = context.projection,
30837         zoom = d3.behavior.zoom()
30838             .translate(projection.translate())
30839             .scale(projection.scale() * 2 * Math.PI)
30840             .scaleExtent([1024, 256 * Math.pow(2, 24)])
30841             .on('zoom', zoomPan),
30842         dblclickEnabled = true,
30843         redrawEnabled = true,
30844         transformStart,
30845         transformed = false,
30846         easing = false,
30847         minzoom = 0,
30848         drawLayers = iD.svg.Layers(projection, context),
30849         drawPoints = iD.svg.Points(projection, context),
30850         drawVertices = iD.svg.Vertices(projection, context),
30851         drawLines = iD.svg.Lines(projection),
30852         drawAreas = iD.svg.Areas(projection),
30853         drawMidpoints = iD.svg.Midpoints(projection, context),
30854         drawLabels = iD.svg.Labels(projection, context),
30855         supersurface,
30856         wrapper,
30857         surface,
30858         mouse,
30859         mousemove;
30860
30861     function map(selection) {
30862         context
30863             .on('change.map', redraw);
30864         context.history()
30865             .on('change.map', redraw);
30866         context.background()
30867             .on('change.map', redraw);
30868         context.features()
30869             .on('redraw.map', redraw);
30870         drawLayers
30871             .on('change.map', function() {
30872                 context.background().updateImagery();
30873                 redraw();
30874             });
30875
30876         selection
30877             .on('dblclick.map', dblClick)
30878             .call(zoom);
30879
30880         supersurface = selection.append('div')
30881             .attr('id', 'supersurface')
30882             .call(iD.util.setTransform, 0, 0);
30883
30884         // Need a wrapper div because Opera can't cope with an absolutely positioned
30885         // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
30886         wrapper = supersurface
30887             .append('div')
30888             .attr('class', 'layer layer-data');
30889
30890         map.surface = surface = wrapper
30891             .call(drawLayers)
30892             .selectAll('.surface')
30893             .attr('id', 'surface');
30894
30895         surface
30896             .on('mousedown.zoom', function() {
30897                 if (d3.event.button === 2) {
30898                     d3.event.stopPropagation();
30899                 }
30900             }, true)
30901             .on('mouseup.zoom', function() {
30902                 if (resetTransform()) redraw();
30903             })
30904             .on('mousemove.map', function() {
30905                 mousemove = d3.event;
30906             })
30907             .on('mouseover.vertices', function() {
30908                 if (map.editable() && !transformed) {
30909                     var hover = d3.event.target.__data__;
30910                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
30911                     dispatch.drawn({full: false});
30912                 }
30913             })
30914             .on('mouseout.vertices', function() {
30915                 if (map.editable() && !transformed) {
30916                     var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__;
30917                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
30918                     dispatch.drawn({full: false});
30919                 }
30920             });
30921
30922
30923         supersurface
30924             .call(context.background());
30925
30926
30927         context.on('enter.map', function() {
30928             if (map.editable() && !transformed) {
30929                 var all = context.intersects(map.extent()),
30930                     filter = d3.functor(true),
30931                     graph = context.graph();
30932
30933                 all = context.features().filter(all, graph);
30934                 surface
30935                     .call(drawVertices, graph, all, filter, map.extent(), map.zoom())
30936                     .call(drawMidpoints, graph, all, filter, map.trimmedExtent());
30937                 dispatch.drawn({full: false});
30938             }
30939         });
30940
30941         map.dimensions(selection.dimensions());
30942
30943         drawLabels.supersurface(supersurface);
30944     }
30945
30946     function pxCenter() {
30947         return [dimensions[0] / 2, dimensions[1] / 2];
30948     }
30949
30950     function drawVector(difference, extent) {
30951         var graph = context.graph(),
30952             features = context.features(),
30953             all = context.intersects(map.extent()),
30954             data, filter;
30955
30956         if (difference) {
30957             var complete = difference.complete(map.extent());
30958             data = _.compact(_.values(complete));
30959             filter = function(d) { return d.id in complete; };
30960             features.clear(data);
30961
30962         } else {
30963             // force a full redraw if gatherStats detects that a feature
30964             // should be auto-hidden (e.g. points or buildings)..
30965             if (features.gatherStats(all, graph, dimensions)) {
30966                 extent = undefined;
30967             }
30968
30969             if (extent) {
30970                 data = context.intersects(map.extent().intersection(extent));
30971                 var set = d3.set(_.map(data, 'id'));
30972                 filter = function(d) { return set.has(d.id); };
30973
30974             } else {
30975                 data = all;
30976                 filter = d3.functor(true);
30977             }
30978         }
30979
30980         data = features.filter(data, graph);
30981
30982         surface
30983             .call(drawVertices, graph, data, filter, map.extent(), map.zoom())
30984             .call(drawLines, graph, data, filter)
30985             .call(drawAreas, graph, data, filter)
30986             .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
30987             .call(drawLabels, graph, data, filter, dimensions, !difference && !extent)
30988             .call(drawPoints, graph, data, filter);
30989
30990         dispatch.drawn({full: true});
30991     }
30992
30993     function editOff() {
30994         context.features().resetStats();
30995         surface.selectAll('.layer-osm *').remove();
30996         dispatch.drawn({full: true});
30997     }
30998
30999     function dblClick() {
31000         if (!dblclickEnabled) {
31001             d3.event.preventDefault();
31002             d3.event.stopImmediatePropagation();
31003         }
31004     }
31005
31006     function zoomPan() {
31007         if (Math.log(d3.event.scale) / Math.LN2 - 8 < minzoom) {
31008             surface.interrupt();
31009             iD.ui.flash(context.container())
31010                 .select('.content')
31011                 .text(t('cannot_zoom'));
31012             setZoom(context.minEditableZoom(), true);
31013             queueRedraw();
31014             dispatch.move(map);
31015             return;
31016         }
31017
31018         projection
31019             .translate(d3.event.translate)
31020             .scale(d3.event.scale / (2 * Math.PI));
31021
31022         var scale = d3.event.scale / transformStart[0],
31023             tX = (d3.event.translate[0] / scale - transformStart[1][0]) * scale,
31024             tY = (d3.event.translate[1] / scale - transformStart[1][1]) * scale;
31025
31026         transformed = true;
31027         iD.util.setTransform(supersurface, tX, tY, scale);
31028         queueRedraw();
31029
31030         dispatch.move(map);
31031     }
31032
31033     function resetTransform() {
31034         if (!transformed) return false;
31035
31036         surface.selectAll('.radial-menu').interrupt().remove();
31037         iD.util.setTransform(supersurface, 0, 0);
31038         transformed = false;
31039         return true;
31040     }
31041
31042     function redraw(difference, extent) {
31043         if (!surface || !redrawEnabled) return;
31044
31045         clearTimeout(timeoutId);
31046
31047         // If we are in the middle of a zoom/pan, we can't do differenced redraws.
31048         // It would result in artifacts where differenced entities are redrawn with
31049         // one transform and unchanged entities with another.
31050         if (resetTransform()) {
31051             difference = extent = undefined;
31052         }
31053
31054         var zoom = String(~~map.zoom());
31055         if (surface.attr('data-zoom') !== zoom) {
31056             surface.attr('data-zoom', zoom)
31057                 .classed('low-zoom', zoom <= 16);
31058         }
31059
31060         if (!difference) {
31061             supersurface.call(context.background());
31062         }
31063
31064         // OSM
31065         if (map.editable()) {
31066             context.loadTiles(projection, dimensions);
31067             drawVector(difference, extent);
31068         } else {
31069             editOff();
31070         }
31071
31072         wrapper
31073             .call(drawLayers);
31074
31075         transformStart = [
31076             projection.scale() * 2 * Math.PI,
31077             projection.translate().slice()];
31078
31079         return map;
31080     }
31081
31082     var timeoutId;
31083     function queueRedraw() {
31084         timeoutId = setTimeout(function() { redraw(); }, 750);
31085     }
31086
31087     function pointLocation(p) {
31088         var translate = projection.translate(),
31089             scale = projection.scale() * 2 * Math.PI;
31090         return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
31091     }
31092
31093     function locationPoint(l) {
31094         var translate = projection.translate(),
31095             scale = projection.scale() * 2 * Math.PI;
31096         return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
31097     }
31098
31099     map.mouse = function() {
31100         var e = mousemove || d3.event, s;
31101         while ((s = e.sourceEvent)) e = s;
31102         return mouse(e);
31103     };
31104
31105     map.mouseCoordinates = function() {
31106         return projection.invert(map.mouse());
31107     };
31108
31109     map.dblclickEnable = function(_) {
31110         if (!arguments.length) return dblclickEnabled;
31111         dblclickEnabled = _;
31112         return map;
31113     };
31114
31115     map.redrawEnable = function(_) {
31116         if (!arguments.length) return redrawEnabled;
31117         redrawEnabled = _;
31118         return map;
31119     };
31120
31121     function interpolateZoom(_) {
31122         var k = projection.scale(),
31123             t = projection.translate();
31124
31125         surface.node().__chart__ = {
31126             x: t[0],
31127             y: t[1],
31128             k: k * 2 * Math.PI
31129         };
31130
31131         setZoom(_);
31132         projection.scale(k).translate(t);  // undo setZoom projection changes
31133
31134         zoom.event(surface.transition());
31135     }
31136
31137     function setZoom(_, force) {
31138         if (_ === map.zoom() && !force)
31139             return false;
31140         var scale = 256 * Math.pow(2, _),
31141             center = pxCenter(),
31142             l = pointLocation(center);
31143         scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
31144         projection.scale(scale / (2 * Math.PI));
31145         zoom.scale(scale);
31146         var t = projection.translate();
31147         l = locationPoint(l);
31148         t[0] += center[0] - l[0];
31149         t[1] += center[1] - l[1];
31150         projection.translate(t);
31151         zoom.translate(projection.translate());
31152         return true;
31153     }
31154
31155     function setCenter(_) {
31156         var c = map.center();
31157         if (_[0] === c[0] && _[1] === c[1])
31158             return false;
31159         var t = projection.translate(),
31160             pxC = pxCenter(),
31161             ll = projection(_);
31162         projection.translate([
31163             t[0] - ll[0] + pxC[0],
31164             t[1] - ll[1] + pxC[1]]);
31165         zoom.translate(projection.translate());
31166         return true;
31167     }
31168
31169     map.pan = function(d) {
31170         var t = projection.translate();
31171         t[0] += d[0];
31172         t[1] += d[1];
31173         projection.translate(t);
31174         zoom.translate(projection.translate());
31175         dispatch.move(map);
31176         return redraw();
31177     };
31178
31179     map.dimensions = function(_) {
31180         if (!arguments.length) return dimensions;
31181         var center = map.center();
31182         dimensions = _;
31183         drawLayers.dimensions(dimensions);
31184         context.background().dimensions(dimensions);
31185         projection.clipExtent([[0, 0], dimensions]);
31186         mouse = iD.util.fastMouse(supersurface.node());
31187         setCenter(center);
31188         return redraw();
31189     };
31190
31191     function zoomIn(integer) {
31192       interpolateZoom(~~map.zoom() + integer);
31193     }
31194
31195     function zoomOut(integer) {
31196       interpolateZoom(~~map.zoom() - integer);
31197     }
31198
31199     map.zoomIn = function() { zoomIn(1); };
31200     map.zoomInFurther = function() { zoomIn(4); };
31201
31202     map.zoomOut = function() { zoomOut(1); };
31203     map.zoomOutFurther = function() { zoomOut(4); };
31204
31205     map.center = function(loc) {
31206         if (!arguments.length) {
31207             return projection.invert(pxCenter());
31208         }
31209
31210         if (setCenter(loc)) {
31211             dispatch.move(map);
31212         }
31213
31214         return redraw();
31215     };
31216
31217     map.zoom = function(z) {
31218         if (!arguments.length) {
31219             return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
31220         }
31221
31222         if (z < minzoom) {
31223             surface.interrupt();
31224             iD.ui.flash(context.container())
31225                 .select('.content')
31226                 .text(t('cannot_zoom'));
31227             z = context.minEditableZoom();
31228         }
31229
31230         if (setZoom(z)) {
31231             dispatch.move(map);
31232         }
31233
31234         return redraw();
31235     };
31236
31237     map.zoomTo = function(entity, zoomLimits) {
31238         var extent = entity.extent(context.graph());
31239         if (!isFinite(extent.area())) return;
31240
31241         var zoom = map.trimmedExtentZoom(extent);
31242         zoomLimits = zoomLimits || [context.minEditableZoom(), 20];
31243         map.centerZoom(extent.center(), Math.min(Math.max(zoom, zoomLimits[0]), zoomLimits[1]));
31244     };
31245
31246     map.centerZoom = function(loc, z) {
31247         var centered = setCenter(loc),
31248             zoomed   = setZoom(z);
31249
31250         if (centered || zoomed) {
31251             dispatch.move(map);
31252         }
31253
31254         return redraw();
31255     };
31256
31257     map.centerEase = function(loc2, duration) {
31258         duration = duration || 250;
31259
31260         surface.one('mousedown.ease', function() {
31261             map.cancelEase();
31262         });
31263
31264         if (easing) {
31265             map.cancelEase();
31266         }
31267
31268         var t1 = Date.now(),
31269             t2 = t1 + duration,
31270             loc1 = map.center(),
31271             ease = d3.ease('cubic-in-out');
31272
31273         easing = true;
31274
31275         d3.timer(function() {
31276             if (!easing) return true;  // cancelled ease
31277
31278             var tNow = Date.now();
31279             if (tNow > t2) {
31280                 tNow = t2;
31281                 easing = false;
31282             }
31283
31284             var locNow = iD.geo.interp(loc1, loc2, ease((tNow - t1) / duration));
31285             setCenter(locNow);
31286
31287             d3.event = {
31288                 scale: zoom.scale(),
31289                 translate: zoom.translate()
31290             };
31291
31292             zoomPan();
31293             return !easing;
31294         });
31295
31296         return map;
31297     };
31298
31299     map.cancelEase = function() {
31300         easing = false;
31301         d3.timer.flush();
31302         return map;
31303     };
31304
31305     map.extent = function(_) {
31306         if (!arguments.length) {
31307             return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
31308                                  projection.invert([dimensions[0], 0]));
31309         } else {
31310             var extent = iD.geo.Extent(_);
31311             map.centerZoom(extent.center(), map.extentZoom(extent));
31312         }
31313     };
31314
31315     map.trimmedExtent = function(_) {
31316         if (!arguments.length) {
31317             var headerY = 60, footerY = 30, pad = 10;
31318             return new iD.geo.Extent(projection.invert([pad, dimensions[1] - footerY - pad]),
31319                     projection.invert([dimensions[0] - pad, headerY + pad]));
31320         } else {
31321             var extent = iD.geo.Extent(_);
31322             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
31323         }
31324     };
31325
31326     function calcZoom(extent, dim) {
31327         var tl = projection([extent[0][0], extent[1][1]]),
31328             br = projection([extent[1][0], extent[0][1]]);
31329
31330         // Calculate maximum zoom that fits extent
31331         var hFactor = (br[0] - tl[0]) / dim[0],
31332             vFactor = (br[1] - tl[1]) / dim[1],
31333             hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
31334             vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
31335             newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
31336
31337         return newZoom;
31338     }
31339
31340     map.extentZoom = function(_) {
31341         return calcZoom(iD.geo.Extent(_), dimensions);
31342     };
31343
31344     map.trimmedExtentZoom = function(_) {
31345         var trimY = 120, trimX = 40,
31346             trimmed = [dimensions[0] - trimX, dimensions[1] - trimY];
31347         return calcZoom(iD.geo.Extent(_), trimmed);
31348     };
31349
31350     map.editable = function() {
31351         return map.zoom() >= context.minEditableZoom();
31352     };
31353
31354     map.minzoom = function(_) {
31355         if (!arguments.length) return minzoom;
31356         minzoom = _;
31357         return map;
31358     };
31359
31360     map.layers = drawLayers;
31361
31362     return d3.rebind(map, dispatch, 'on');
31363 };
31364 iD.TileLayer = function(context) {
31365     var tileSize = 256,
31366         tile = d3.geo.tile(),
31367         projection,
31368         cache = {},
31369         tileOrigin,
31370         z,
31371         transformProp = iD.util.prefixCSSProperty('Transform'),
31372         source = d3.functor('');
31373
31374
31375     // blacklist overlay tiles around Null Island..
31376     function nearNullIsland(x, y, z) {
31377         if (z >= 7) {
31378             var center = Math.pow(2, z - 1),
31379                 width = Math.pow(2, z - 6),
31380                 min = center - (width / 2),
31381                 max = center + (width / 2) - 1;
31382             return x >= min && x <= max && y >= min && y <= max;
31383         }
31384         return false;
31385     }
31386
31387     function tileSizeAtZoom(d, z) {
31388         var epsilon = 0.002;
31389         return ((tileSize * Math.pow(2, z - d[2])) / tileSize) + epsilon;
31390     }
31391
31392     function atZoom(t, distance) {
31393         var power = Math.pow(2, distance);
31394         return [
31395             Math.floor(t[0] * power),
31396             Math.floor(t[1] * power),
31397             t[2] + distance];
31398     }
31399
31400     function lookUp(d) {
31401         for (var up = -1; up > -d[2]; up--) {
31402             var tile = atZoom(d, up);
31403             if (cache[source.url(tile)] !== false) {
31404                 return tile;
31405             }
31406         }
31407     }
31408
31409     function uniqueBy(a, n) {
31410         var o = [], seen = {};
31411         for (var i = 0; i < a.length; i++) {
31412             if (seen[a[i][n]] === undefined) {
31413                 o.push(a[i]);
31414                 seen[a[i][n]] = true;
31415             }
31416         }
31417         return o;
31418     }
31419
31420     function addSource(d) {
31421         d.push(source.url(d));
31422         return d;
31423     }
31424
31425     // Update tiles based on current state of `projection`.
31426     function background(selection) {
31427         tile.scale(projection.scale() * 2 * Math.PI)
31428             .translate(projection.translate());
31429
31430         tileOrigin = [
31431             projection.scale() * Math.PI - projection.translate()[0],
31432             projection.scale() * Math.PI - projection.translate()[1]];
31433
31434         z = Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.log(2) - 8, 0);
31435
31436         render(selection);
31437     }
31438
31439     // Derive the tiles onscreen, remove those offscreen and position them.
31440     // Important that this part not depend on `projection` because it's
31441     // rentered when tiles load/error (see #644).
31442     function render(selection) {
31443         var requests = [];
31444         var showDebug = context.debugTile() && !source.overlay;
31445
31446         if (source.validZoom(z)) {
31447             tile().forEach(function(d) {
31448                 addSource(d);
31449                 if (d[3] === '') return;
31450                 if (typeof d[3] !== 'string') return; // Workaround for chrome crash https://github.com/openstreetmap/iD/issues/2295
31451                 requests.push(d);
31452                 if (cache[d[3]] === false && lookUp(d)) {
31453                     requests.push(addSource(lookUp(d)));
31454                 }
31455             });
31456
31457             requests = uniqueBy(requests, 3).filter(function(r) {
31458                 if (!!source.overlay && nearNullIsland(r[0], r[1], r[2])) {
31459                     return false;
31460                 }
31461                 // don't re-request tiles which have failed in the past
31462                 return cache[r[3]] !== false;
31463             });
31464         }
31465
31466         var pixelOffset = [
31467             source.offset()[0] * Math.pow(2, z),
31468             source.offset()[1] * Math.pow(2, z)
31469         ];
31470
31471         function load(d) {
31472             cache[d[3]] = true;
31473             d3.select(this)
31474                 .on('error', null)
31475                 .on('load', null)
31476                 .classed('tile-loaded', true);
31477             render(selection);
31478         }
31479
31480         function error(d) {
31481             cache[d[3]] = false;
31482             d3.select(this)
31483                 .on('error', null)
31484                 .on('load', null)
31485                 .remove();
31486             render(selection);
31487         }
31488
31489         function imageTransform(d) {
31490             var _ts = tileSize * Math.pow(2, z - d[2]);
31491             var scale = tileSizeAtZoom(d, z);
31492             return 'translate(' +
31493                 ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0]) + 'px,' +
31494                 ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1]) + 'px)' +
31495                 'scale(' + scale + ',' + scale + ')';
31496         }
31497
31498         function debugTransform(d) {
31499             var _ts = tileSize * Math.pow(2, z - d[2]);
31500             var scale = tileSizeAtZoom(d, z);
31501             return 'translate(' +
31502                 ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0] + scale * (tileSize / 4)) + 'px,' +
31503                 ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1] + scale * (tileSize / 2)) + 'px)';
31504         }
31505
31506         var image = selection
31507             .selectAll('img')
31508             .data(requests, function(d) { return d[3]; });
31509
31510         image.exit()
31511             .style(transformProp, imageTransform)
31512             .classed('tile-removing', true)
31513             .each(function() {
31514                 var tile = d3.select(this);
31515                 window.setTimeout(function() {
31516                     if (tile.classed('tile-removing')) {
31517                         tile.remove();
31518                     }
31519                 }, 300);
31520             });
31521
31522         image.enter().append('img')
31523             .attr('class', 'tile')
31524             .attr('src', function(d) { return d[3]; })
31525             .on('error', error)
31526             .on('load', load);
31527
31528         image
31529             .style(transformProp, imageTransform)
31530             .classed('tile-debug', showDebug)
31531             .classed('tile-removing', false);
31532
31533
31534         var debug = selection.selectAll('.tile-label-debug')
31535             .data(showDebug ? requests : [], function(d) { return d[3]; });
31536
31537         debug.exit()
31538             .remove();
31539
31540         debug.enter()
31541             .append('div')
31542             .attr('class', 'tile-label-debug');
31543
31544         debug
31545             .text(function(d) { return d[2] + ' / ' + d[0] + ' / ' + d[1]; })
31546             .style(transformProp, debugTransform);
31547     }
31548
31549     background.projection = function(_) {
31550         if (!arguments.length) return projection;
31551         projection = _;
31552         return background;
31553     };
31554
31555     background.dimensions = function(_) {
31556         if (!arguments.length) return tile.size();
31557         tile.size(_);
31558         return background;
31559     };
31560
31561     background.source = function(_) {
31562         if (!arguments.length) return source;
31563         source = _;
31564         cache = {};
31565         tile.scaleExtent(source.scaleExtent);
31566         return background;
31567     };
31568
31569     return background;
31570 };
31571 iD.svg = {
31572     PointTransform: function(projection) {
31573         return function(entity) {
31574             // http://jsperf.com/short-array-join
31575             var pt = projection(entity.loc);
31576             return 'translate(' + pt[0] + ',' + pt[1] + ')';
31577         };
31578     },
31579
31580     Path: function(projection, graph, polygon) {
31581         var cache = {},
31582             clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
31583             project = projection.stream,
31584             path = d3.geo.path()
31585                 .projection({stream: function(output) { return polygon ? project(output) : project(clip(output)); }});
31586
31587         return function(entity) {
31588             if (entity.id in cache) {
31589                 return cache[entity.id];
31590             } else {
31591                 return cache[entity.id] = path(entity.asGeoJSON(graph));
31592             }
31593         };
31594     },
31595
31596     OneWaySegments: function(projection, graph, dt) {
31597         return function(entity) {
31598             var a,
31599                 b,
31600                 i = 0,
31601                 offset = dt,
31602                 segments = [],
31603                 clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
31604                 coordinates = graph.childNodes(entity).map(function(n) {
31605                     return n.loc;
31606                 });
31607
31608             if (entity.tags.oneway === '-1') coordinates.reverse();
31609
31610             d3.geo.stream({
31611                 type: 'LineString',
31612                 coordinates: coordinates
31613             }, projection.stream(clip({
31614                 lineStart: function() {},
31615                 lineEnd: function() {
31616                     a = null;
31617                 },
31618                 point: function(x, y) {
31619                     b = [x, y];
31620
31621                     if (a) {
31622                         var span = iD.geo.euclideanDistance(a, b) - offset;
31623
31624                         if (span >= 0) {
31625                             var angle = Math.atan2(b[1] - a[1], b[0] - a[0]),
31626                                 dx = dt * Math.cos(angle),
31627                                 dy = dt * Math.sin(angle),
31628                                 p = [a[0] + offset * Math.cos(angle),
31629                                      a[1] + offset * Math.sin(angle)];
31630
31631                             var segment = 'M' + a[0] + ',' + a[1] +
31632                                           'L' + p[0] + ',' + p[1];
31633
31634                             for (span -= dt; span >= 0; span -= dt) {
31635                                 p[0] += dx;
31636                                 p[1] += dy;
31637                                 segment += 'L' + p[0] + ',' + p[1];
31638                             }
31639
31640                             segment += 'L' + b[0] + ',' + b[1];
31641                             segments.push({id: entity.id, index: i, d: segment});
31642                         }
31643
31644                         offset = -span;
31645                         i++;
31646                     }
31647
31648                     a = b;
31649                 }
31650             })));
31651
31652             return segments;
31653         };
31654     },
31655
31656     RelationMemberTags: function(graph) {
31657         return function(entity) {
31658             var tags = entity.tags;
31659             graph.parentRelations(entity).forEach(function(relation) {
31660                 var type = relation.tags.type;
31661                 if (type === 'multipolygon' || type === 'boundary') {
31662                     tags = _.extend({}, relation.tags, tags);
31663                 }
31664             });
31665             return tags;
31666         };
31667     }
31668 };
31669 iD.svg.Areas = function(projection) {
31670     // Patterns only work in Firefox when set directly on element.
31671     // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
31672     var patterns = {
31673         wetland: 'wetland',
31674         beach: 'beach',
31675         scrub: 'scrub',
31676         construction: 'construction',
31677         military: 'construction',
31678         cemetery: 'cemetery',
31679         grave_yard: 'cemetery',
31680         meadow: 'meadow',
31681         farm: 'farmland',
31682         farmland: 'farmland',
31683         orchard: 'orchard'
31684     };
31685
31686     var patternKeys = ['landuse', 'natural', 'amenity'];
31687
31688     function setPattern(d) {
31689         for (var i = 0; i < patternKeys.length; i++) {
31690             if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) {
31691                 this.style.fill = this.style.stroke = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")';
31692                 return;
31693             }
31694         }
31695         this.style.fill = this.style.stroke = '';
31696     }
31697
31698     return function drawAreas(surface, graph, entities, filter) {
31699         var path = iD.svg.Path(projection, graph, true),
31700             areas = {},
31701             multipolygon;
31702
31703         for (var i = 0; i < entities.length; i++) {
31704             var entity = entities[i];
31705             if (entity.geometry(graph) !== 'area') continue;
31706
31707             multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph);
31708             if (multipolygon) {
31709                 areas[multipolygon.id] = {
31710                     entity: multipolygon.mergeTags(entity.tags),
31711                     area: Math.abs(entity.area(graph))
31712                 };
31713             } else if (!areas[entity.id]) {
31714                 areas[entity.id] = {
31715                     entity: entity,
31716                     area: Math.abs(entity.area(graph))
31717                 };
31718             }
31719         }
31720
31721         areas = d3.values(areas).filter(function hasPath(a) { return path(a.entity); });
31722         areas.sort(function areaSort(a, b) { return b.area - a.area; });
31723         areas = _.map(areas, 'entity');
31724
31725         var strokes = areas.filter(function(area) {
31726             return area.type === 'way';
31727         });
31728
31729         var data = {
31730             clip: areas,
31731             shadow: strokes,
31732             stroke: strokes,
31733             fill: areas
31734         };
31735
31736         var clipPaths = surface.selectAll('defs').selectAll('.clipPath')
31737            .filter(filter)
31738            .data(data.clip, iD.Entity.key);
31739
31740         clipPaths.enter()
31741            .append('clipPath')
31742            .attr('class', 'clipPath')
31743            .attr('id', function(entity) { return entity.id + '-clippath'; })
31744            .append('path');
31745
31746         clipPaths.selectAll('path')
31747            .attr('d', path);
31748
31749         clipPaths.exit()
31750            .remove();
31751
31752         var areagroup = surface
31753             .selectAll('.layer-areas')
31754             .selectAll('g.areagroup')
31755             .data(['fill', 'shadow', 'stroke']);
31756
31757         areagroup.enter()
31758             .append('g')
31759             .attr('class', function(d) { return 'layer areagroup area-' + d; });
31760
31761         var paths = areagroup
31762             .selectAll('path')
31763             .filter(filter)
31764             .data(function(layer) { return data[layer]; }, iD.Entity.key);
31765
31766         // Remove exiting areas first, so they aren't included in the `fills`
31767         // array used for sorting below (https://github.com/openstreetmap/iD/issues/1903).
31768         paths.exit()
31769             .remove();
31770
31771         var fills = surface.selectAll('.area-fill path.area')[0];
31772
31773         var bisect = d3.bisector(function(node) {
31774             return -node.__data__.area(graph);
31775         }).left;
31776
31777         function sortedByArea(entity) {
31778             if (this.__data__ === 'fill') {
31779                 return fills[bisect(fills, -entity.area(graph))];
31780             }
31781         }
31782
31783         paths.enter()
31784             .insert('path', sortedByArea)
31785             .each(function(entity) {
31786                 var layer = this.parentNode.__data__;
31787
31788                 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
31789
31790                 if (layer === 'fill') {
31791                     this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)');
31792                     setPattern.apply(this, arguments);
31793                 }
31794             })
31795             .call(iD.svg.TagClasses());
31796
31797         paths
31798             .attr('d', path);
31799     };
31800 };
31801 /*
31802     A standalone SVG element that contains only a `defs` sub-element. To be
31803     used once globally, since defs IDs must be unique within a document.
31804 */
31805 iD.svg.Defs = function(context) {
31806
31807     function SVGSpriteDefinition(id, href) {
31808         return function(defs) {
31809             d3.xml(href, 'image/svg+xml', function(err, svg) {
31810                 if (err) return;
31811                 defs.node().appendChild(
31812                     d3.select(svg.documentElement).attr('id', id).node()
31813                 );
31814             });
31815         };
31816     }
31817
31818     return function drawDefs(selection) {
31819         var defs = selection.append('defs');
31820
31821         // marker
31822         defs.append('marker')
31823             .attr({
31824                 id: 'oneway-marker',
31825                 viewBox: '0 0 10 10',
31826                 refY: 2.5,
31827                 refX: 5,
31828                 markerWidth: 2,
31829                 markerHeight: 2,
31830                 markerUnits: 'strokeWidth',
31831                 orient: 'auto'
31832             })
31833             .append('path')
31834             .attr('class', 'oneway')
31835             .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')
31836             .attr('stroke', 'none')
31837             .attr('fill', '#000')
31838             .attr('opacity', '0.5');
31839
31840         // patterns
31841         var patterns = defs.selectAll('pattern')
31842             .data([
31843                 // pattern name, pattern image name
31844                 ['wetland', 'wetland'],
31845                 ['construction', 'construction'],
31846                 ['cemetery', 'cemetery'],
31847                 ['orchard', 'orchard'],
31848                 ['farmland', 'farmland'],
31849                 ['beach', 'dots'],
31850                 ['scrub', 'dots'],
31851                 ['meadow', 'dots']
31852             ])
31853             .enter()
31854             .append('pattern')
31855             .attr({
31856                 id: function (d) {
31857                     return 'pattern-' + d[0];
31858                 },
31859                 width: 32,
31860                 height: 32,
31861                 patternUnits: 'userSpaceOnUse'
31862             });
31863
31864         patterns.append('rect')
31865             .attr({
31866                 x: 0,
31867                 y: 0,
31868                 width: 32,
31869                 height: 32,
31870                 'class': function (d) {
31871                     return 'pattern-color-' + d[0];
31872                 }
31873             });
31874
31875         patterns.append('image')
31876             .attr({
31877                 x: 0,
31878                 y: 0,
31879                 width: 32,
31880                 height: 32
31881             })
31882             .attr('xlink:href', function (d) {
31883                 return context.imagePath('pattern/' + d[1] + '.png');
31884             });
31885
31886         // clip paths
31887         defs.selectAll()
31888             .data([12, 18, 20, 32, 45])
31889             .enter().append('clipPath')
31890             .attr('id', function (d) {
31891                 return 'clip-square-' + d;
31892             })
31893             .append('rect')
31894             .attr('x', 0)
31895             .attr('y', 0)
31896             .attr('width', function (d) {
31897                 return d;
31898             })
31899             .attr('height', function (d) {
31900                 return d;
31901             });
31902
31903         defs.call(SVGSpriteDefinition(
31904             'iD-sprite',
31905             context.imagePath('iD-sprite.svg')));
31906
31907         defs.call(SVGSpriteDefinition(
31908             'maki-sprite',
31909             context.imagePath('maki-sprite.svg')));
31910     };
31911 };
31912 iD.svg.Gpx = function(projection, context, dispatch) {
31913     var showLabels = true,
31914         layer;
31915
31916     function init() {
31917         if (iD.svg.Gpx.initialized) return;  // run once
31918
31919         iD.svg.Gpx.geojson = {};
31920         iD.svg.Gpx.enabled = true;
31921
31922         function over() {
31923             d3.event.stopPropagation();
31924             d3.event.preventDefault();
31925             d3.event.dataTransfer.dropEffect = 'copy';
31926         }
31927
31928         d3.select('body')
31929             .attr('dropzone', 'copy')
31930             .on('drop.localgpx', function() {
31931                 d3.event.stopPropagation();
31932                 d3.event.preventDefault();
31933                 if (!iD.detect().filedrop) return;
31934                 drawGpx.files(d3.event.dataTransfer.files);
31935             })
31936             .on('dragenter.localgpx', over)
31937             .on('dragexit.localgpx', over)
31938             .on('dragover.localgpx', over);
31939
31940         iD.svg.Gpx.initialized = true;
31941     }
31942
31943
31944     function drawGpx(surface) {
31945         var geojson = iD.svg.Gpx.geojson,
31946             enabled = iD.svg.Gpx.enabled;
31947
31948         layer = surface.selectAll('.layer-gpx')
31949             .data(enabled ? [0] : []);
31950
31951         layer.enter()
31952             .append('g')
31953             .attr('class', 'layer-gpx');
31954
31955         layer.exit()
31956             .remove();
31957
31958
31959         var paths = layer
31960             .selectAll('path')
31961             .data([geojson]);
31962
31963         paths.enter()
31964             .append('path')
31965             .attr('class', 'gpx');
31966
31967         paths.exit()
31968             .remove();
31969
31970         var path = d3.geo.path()
31971             .projection(projection);
31972
31973         paths
31974             .attr('d', path);
31975
31976
31977         var labels = layer.selectAll('text')
31978             .data(showLabels && geojson.features ? geojson.features : []);
31979
31980         labels.enter()
31981             .append('text')
31982             .attr('class', 'gpx');
31983
31984         labels.exit()
31985             .remove();
31986
31987         labels
31988             .text(function(d) {
31989                 return d.properties.desc || d.properties.name;
31990             })
31991             .attr('x', function(d) {
31992                 var centroid = path.centroid(d);
31993                 return centroid[0] + 7;
31994             })
31995             .attr('y', function(d) {
31996                 var centroid = path.centroid(d);
31997                 return centroid[1];
31998             });
31999
32000     }
32001
32002     function toDom(x) {
32003         return (new DOMParser()).parseFromString(x, 'text/xml');
32004     }
32005
32006     drawGpx.showLabels = function(_) {
32007         if (!arguments.length) return showLabels;
32008         showLabels = _;
32009         return this;
32010     };
32011
32012     drawGpx.enabled = function(_) {
32013         if (!arguments.length) return iD.svg.Gpx.enabled;
32014         iD.svg.Gpx.enabled = _;
32015         dispatch.change();
32016         return this;
32017     };
32018
32019     drawGpx.hasGpx = function() {
32020         var geojson = iD.svg.Gpx.geojson;
32021         return (!(_.isEmpty(geojson) || _.isEmpty(geojson.features)));
32022     };
32023
32024     drawGpx.geojson = function(gj) {
32025         if (!arguments.length) return iD.svg.Gpx.geojson;
32026         if (_.isEmpty(gj) || _.isEmpty(gj.features)) return this;
32027         iD.svg.Gpx.geojson = gj;
32028         dispatch.change();
32029         return this;
32030     };
32031
32032     drawGpx.url = function(url) {
32033         d3.text(url, function(err, data) {
32034             if (!err) {
32035                 drawGpx.geojson(toGeoJSON.gpx(toDom(data)));
32036             }
32037         });
32038         return this;
32039     };
32040
32041     drawGpx.files = function(fileList) {
32042         if (!fileList.length) return this;
32043         var f = fileList[0],
32044             reader = new FileReader();
32045
32046         reader.onload = function(e) {
32047             drawGpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom();
32048         };
32049
32050         reader.readAsText(f);
32051         return this;
32052     };
32053
32054     drawGpx.fitZoom = function() {
32055         if (!this.hasGpx()) return this;
32056         var geojson = iD.svg.Gpx.geojson;
32057
32058         var map = context.map(),
32059             viewport = map.trimmedExtent().polygon(),
32060             coords = _.reduce(geojson.features, function(coords, feature) {
32061                 var c = feature.geometry.coordinates;
32062                 return _.union(coords, feature.geometry.type === 'Point' ? [c] : c);
32063             }, []);
32064
32065         if (!iD.geo.polygonIntersectsPolygon(viewport, coords, true)) {
32066             var extent = iD.geo.Extent(d3.geo.bounds(geojson));
32067             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
32068         }
32069
32070         return this;
32071     };
32072
32073     init();
32074     return drawGpx;
32075 };
32076 iD.svg.Icon = function(name, svgklass, useklass) {
32077     return function drawIcon(selection) {
32078         selection.selectAll('svg')
32079             .data([0])
32080             .enter()
32081             .append('svg')
32082             .attr('class', 'icon ' + (svgklass || ''))
32083             .append('use')
32084             .attr('xlink:href', name)
32085             .attr('class', useklass);
32086     };
32087 };
32088 iD.svg.Labels = function(projection, context) {
32089     var path = d3.geo.path().projection(projection);
32090
32091     // Replace with dict and iterate over entities tags instead?
32092     var label_stack = [
32093         ['line', 'aeroway'],
32094         ['line', 'highway'],
32095         ['line', 'railway'],
32096         ['line', 'waterway'],
32097         ['area', 'aeroway'],
32098         ['area', 'amenity'],
32099         ['area', 'building'],
32100         ['area', 'historic'],
32101         ['area', 'leisure'],
32102         ['area', 'man_made'],
32103         ['area', 'natural'],
32104         ['area', 'shop'],
32105         ['area', 'tourism'],
32106         ['point', 'aeroway'],
32107         ['point', 'amenity'],
32108         ['point', 'building'],
32109         ['point', 'historic'],
32110         ['point', 'leisure'],
32111         ['point', 'man_made'],
32112         ['point', 'natural'],
32113         ['point', 'shop'],
32114         ['point', 'tourism'],
32115         ['line', 'name'],
32116         ['area', 'name'],
32117         ['point', 'name']
32118     ];
32119
32120     var default_size = 12;
32121
32122     var font_sizes = label_stack.map(function(d) {
32123         var style = iD.util.getStyle('text.' + d[0] + '.tag-' + d[1]),
32124             m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
32125         if (m) return parseInt(m[1], 10);
32126
32127         style = iD.util.getStyle('text.' + d[0]);
32128         m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
32129         if (m) return parseInt(m[1], 10);
32130
32131         return default_size;
32132     });
32133
32134     var iconSize = 18;
32135
32136     var pointOffsets = [
32137         [15, -11, 'start'], // right
32138         [10, -11, 'start'], // unused right now
32139         [-15, -11, 'end']
32140     ];
32141
32142     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25,
32143         75, 20, 80, 15, 95, 10, 90, 5, 95];
32144
32145
32146     var noIcons = ['building', 'landuse', 'natural'];
32147     function blacklisted(preset) {
32148         return _.some(noIcons, function(s) {
32149             return preset.id.indexOf(s) >= 0;
32150         });
32151     }
32152
32153     function get(array, prop) {
32154         return function(d, i) { return array[i][prop]; };
32155     }
32156
32157     var textWidthCache = {};
32158
32159     function textWidth(text, size, elem) {
32160         var c = textWidthCache[size];
32161         if (!c) c = textWidthCache[size] = {};
32162
32163         if (c[text]) {
32164             return c[text];
32165
32166         } else if (elem) {
32167             c[text] = elem.getComputedTextLength();
32168             return c[text];
32169
32170         } else {
32171             var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
32172             if (str === null) {
32173                 return size / 3 * 2 * text.length;
32174             } else {
32175                 return size / 3 * (2 * text.length + str.length);
32176             }
32177         }
32178     }
32179
32180     function drawLineLabels(group, entities, filter, classes, labels) {
32181         var texts = group.selectAll('text.' + classes)
32182             .filter(filter)
32183             .data(entities, iD.Entity.key);
32184
32185         texts.enter()
32186             .append('text')
32187             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
32188             .append('textPath')
32189             .attr('class', 'textpath');
32190
32191
32192         texts.selectAll('.textpath')
32193             .filter(filter)
32194             .data(entities, iD.Entity.key)
32195             .attr({
32196                 'startOffset': '50%',
32197                 'xlink:href': function(d) { return '#labelpath-' + d.id; }
32198             })
32199             .text(iD.util.displayName);
32200
32201         texts.exit().remove();
32202     }
32203
32204     function drawLinePaths(group, entities, filter, classes, labels) {
32205         var halos = group.selectAll('path')
32206             .filter(filter)
32207             .data(entities, iD.Entity.key);
32208
32209         halos.enter()
32210             .append('path')
32211             .style('stroke-width', get(labels, 'font-size'))
32212             .attr('id', function(d) { return 'labelpath-' + d.id; })
32213             .attr('class', classes);
32214
32215         halos.attr('d', get(labels, 'lineString'));
32216
32217         halos.exit().remove();
32218     }
32219
32220     function drawPointLabels(group, entities, filter, classes, labels) {
32221         var texts = group.selectAll('text.' + classes)
32222             .filter(filter)
32223             .data(entities, iD.Entity.key);
32224
32225         texts.enter()
32226             .append('text')
32227             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; });
32228
32229         texts.attr('x', get(labels, 'x'))
32230             .attr('y', get(labels, 'y'))
32231             .style('text-anchor', get(labels, 'textAnchor'))
32232             .text(iD.util.displayName)
32233             .each(function(d, i) { textWidth(iD.util.displayName(d), labels[i].height, this); });
32234
32235         texts.exit().remove();
32236         return texts;
32237     }
32238
32239     function drawAreaLabels(group, entities, filter, classes, labels) {
32240         entities = entities.filter(hasText);
32241         labels = labels.filter(hasText);
32242         return drawPointLabels(group, entities, filter, classes, labels);
32243
32244         function hasText(d, i) {
32245             return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
32246         }
32247     }
32248
32249     function drawAreaIcons(group, entities, filter, classes, labels) {
32250         var icons = group.selectAll('use')
32251             .filter(filter)
32252             .data(entities, iD.Entity.key);
32253
32254         icons.enter()
32255             .append('use')
32256             .attr('class', 'icon areaicon')
32257             .attr('width', '18px')
32258             .attr('height', '18px');
32259
32260         icons.attr('transform', get(labels, 'transform'))
32261             .attr('xlink:href', function(d) {
32262                 var icon = context.presets().match(d, context.graph()).icon;
32263                 return '#' + icon + (icon === 'hairdresser' ? '-24': '-18');    // workaround: maki hairdresser-18 broken?
32264             });
32265
32266
32267         icons.exit().remove();
32268     }
32269
32270     function reverse(p) {
32271         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
32272         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
32273     }
32274
32275     function lineString(nodes) {
32276         return 'M' + nodes.join('L');
32277     }
32278
32279     function subpath(nodes, from, to) {
32280         function segmentLength(i) {
32281             var dx = nodes[i][0] - nodes[i + 1][0];
32282             var dy = nodes[i][1] - nodes[i + 1][1];
32283             return Math.sqrt(dx * dx + dy * dy);
32284         }
32285
32286         var sofar = 0,
32287             start, end, i0, i1;
32288         for (var i = 0; i < nodes.length - 1; i++) {
32289             var current = segmentLength(i);
32290             var portion;
32291             if (!start && sofar + current >= from) {
32292                 portion = (from - sofar) / current;
32293                 start = [
32294                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
32295                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
32296                 ];
32297                 i0 = i + 1;
32298             }
32299             if (!end && sofar + current >= to) {
32300                 portion = (to - sofar) / current;
32301                 end = [
32302                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
32303                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
32304                 ];
32305                 i1 = i + 1;
32306             }
32307             sofar += current;
32308
32309         }
32310         var ret = nodes.slice(i0, i1);
32311         ret.unshift(start);
32312         ret.push(end);
32313         return ret;
32314
32315     }
32316
32317     function hideOnMouseover() {
32318         var layers = d3.select(this)
32319             .selectAll('.layer-label, .layer-halo');
32320
32321         layers.selectAll('.proximate')
32322             .classed('proximate', false);
32323
32324         var mouse = context.mouse(),
32325             pad = 50,
32326             rect = [mouse[0] - pad, mouse[1] - pad, mouse[0] + pad, mouse[1] + pad],
32327             ids = _.map(rtree.search(rect), 'id');
32328
32329         if (!ids.length) return;
32330         layers.selectAll('.' + ids.join(', .'))
32331             .classed('proximate', true);
32332     }
32333
32334     var rtree = rbush(),
32335         rectangles = {};
32336
32337     function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
32338         var hidePoints = !surface.selectAll('.node.point').node();
32339
32340         var labelable = [], i, k, entity;
32341         for (i = 0; i < label_stack.length; i++) labelable.push([]);
32342
32343         if (fullRedraw) {
32344             rtree.clear();
32345             rectangles = {};
32346         } else {
32347             for (i = 0; i < entities.length; i++) {
32348                 rtree.remove(rectangles[entities[i].id]);
32349             }
32350         }
32351
32352         // Split entities into groups specified by label_stack
32353         for (i = 0; i < entities.length; i++) {
32354             entity = entities[i];
32355             var geometry = entity.geometry(graph);
32356
32357             if (geometry === 'vertex')
32358                 continue;
32359             if (hidePoints && geometry === 'point')
32360                 continue;
32361
32362             var preset = geometry === 'area' && context.presets().match(entity, graph),
32363                 icon = preset && !blacklisted(preset) && preset.icon;
32364
32365             if (!icon && !iD.util.displayName(entity))
32366                 continue;
32367
32368             for (k = 0; k < label_stack.length; k++) {
32369                 if (geometry === label_stack[k][0] && entity.tags[label_stack[k][1]]) {
32370                     labelable[k].push(entity);
32371                     break;
32372                 }
32373             }
32374         }
32375
32376         var positions = {
32377             point: [],
32378             line: [],
32379             area: []
32380         };
32381
32382         var labelled = {
32383             point: [],
32384             line: [],
32385             area: []
32386         };
32387
32388         // Try and find a valid label for labellable entities
32389         for (k = 0; k < labelable.length; k++) {
32390             var font_size = font_sizes[k];
32391             for (i = 0; i < labelable[k].length; i++) {
32392                 entity = labelable[k][i];
32393                 var name = iD.util.displayName(entity),
32394                     width = name && textWidth(name, font_size),
32395                     p;
32396                 if (entity.geometry(graph) === 'point') {
32397                     p = getPointLabel(entity, width, font_size);
32398                 } else if (entity.geometry(graph) === 'line') {
32399                     p = getLineLabel(entity, width, font_size);
32400                 } else if (entity.geometry(graph) === 'area') {
32401                     p = getAreaLabel(entity, width, font_size);
32402                 }
32403                 if (p) {
32404                     p.classes = entity.geometry(graph) + ' tag-' + label_stack[k][1];
32405                     positions[entity.geometry(graph)].push(p);
32406                     labelled[entity.geometry(graph)].push(entity);
32407                 }
32408             }
32409         }
32410
32411         function getPointLabel(entity, width, height) {
32412             var coord = projection(entity.loc),
32413                 m = 5,  // margin
32414                 offset = pointOffsets[0],
32415                 p = {
32416                     height: height,
32417                     width: width,
32418                     x: coord[0] + offset[0],
32419                     y: coord[1] + offset[1],
32420                     textAnchor: offset[2]
32421                 };
32422             var rect = [p.x - m, p.y - m, p.x + width + m, p.y + height + m];
32423             if (tryInsert(rect, entity.id)) return p;
32424         }
32425
32426
32427         function getLineLabel(entity, width, height) {
32428             var nodes = _.map(graph.childNodes(entity), 'loc').map(projection),
32429                 length = iD.geo.pathLength(nodes);
32430             if (length < width + 20) return;
32431
32432             for (var i = 0; i < lineOffsets.length; i++) {
32433                 var offset = lineOffsets[i],
32434                     middle = offset / 100 * length,
32435                     start = middle - width/2;
32436                 if (start < 0 || start + width > length) continue;
32437                 var sub = subpath(nodes, start, start + width),
32438                     rev = reverse(sub),
32439                     rect = [
32440                         Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
32441                         Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
32442                         Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
32443                         Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
32444                     ];
32445                 if (rev) sub = sub.reverse();
32446                 if (tryInsert(rect, entity.id)) return {
32447                     'font-size': height + 2,
32448                     lineString: lineString(sub),
32449                     startOffset: offset + '%'
32450                 };
32451             }
32452         }
32453
32454         function getAreaLabel(entity, width, height) {
32455             var centroid = path.centroid(entity.asGeoJSON(graph, true)),
32456                 extent = entity.extent(graph),
32457                 entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
32458                 rect;
32459
32460             if (isNaN(centroid[0]) || entitywidth < 20) return;
32461
32462             var iconX = centroid[0] - (iconSize/2),
32463                 iconY = centroid[1] - (iconSize/2),
32464                 textOffset = iconSize + 5;
32465
32466             var p = {
32467                 transform: 'translate(' + iconX + ',' + iconY + ')'
32468             };
32469
32470             if (width && entitywidth >= width + 20) {
32471                 p.x = centroid[0];
32472                 p.y = centroid[1] + textOffset;
32473                 p.textAnchor = 'middle';
32474                 p.height = height;
32475                 rect = [p.x - width/2, p.y, p.x + width/2, p.y + height + textOffset];
32476             } else {
32477                 rect = [iconX, iconY, iconX + iconSize, iconY + iconSize];
32478             }
32479
32480             if (tryInsert(rect, entity.id)) return p;
32481
32482         }
32483
32484         function tryInsert(rect, id) {
32485             // Check that label is visible
32486             if (rect[0] < 0 || rect[1] < 0 || rect[2] > dimensions[0] ||
32487                 rect[3] > dimensions[1]) return false;
32488             var v = rtree.search(rect).length === 0;
32489             if (v) {
32490                 rect.id = id;
32491                 rtree.insert(rect);
32492                 rectangles[id] = rect;
32493             }
32494             return v;
32495         }
32496
32497         var label = surface.selectAll('.layer-label'),
32498             halo = surface.selectAll('.layer-halo');
32499
32500         // points
32501         drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
32502         drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
32503
32504         // lines
32505         drawLinePaths(halo, labelled.line, filter, '', positions.line);
32506         drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
32507         drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
32508
32509         // areas
32510         drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
32511         drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
32512         drawAreaIcons(label, labelled.area, filter, 'arealabel-icon', positions.area);
32513
32514         // debug
32515         var showDebug = context.debugCollision();
32516         var debug = label.selectAll('.layer-label-debug')
32517             .data(showDebug ? [true] : []);
32518
32519         debug.enter()
32520             .append('g')
32521             .attr('class', 'layer-label-debug');
32522
32523         debug.exit()
32524             .remove();
32525
32526         if (showDebug) {
32527             var gj = rtree.all().map(function(d) {
32528                 return { type: 'Polygon', coordinates: [[
32529                     [d[0], d[1]],
32530                     [d[2], d[1]],
32531                     [d[2], d[3]],
32532                     [d[0], d[3]],
32533                     [d[0], d[1]]
32534                 ]]};
32535             });
32536
32537             var debugboxes = debug.selectAll('.bbox').data(gj);
32538
32539             debugboxes.enter()
32540                 .append('path')
32541                 .attr('class', 'bbox');
32542
32543             debugboxes.exit()
32544                 .remove();
32545
32546             debugboxes
32547                 .attr('d', d3.geo.path().projection(null));
32548         }
32549     }
32550
32551     drawLabels.supersurface = function(supersurface) {
32552         supersurface
32553             .on('mousemove.hidelabels', hideOnMouseover)
32554             .on('mousedown.hidelabels', function () {
32555                 supersurface.on('mousemove.hidelabels', null);
32556             })
32557             .on('mouseup.hidelabels', function () {
32558                 supersurface.on('mousemove.hidelabels', hideOnMouseover);
32559             });
32560     };
32561
32562     return drawLabels;
32563 };
32564 iD.svg.Layers = function(projection, context) {
32565     var dispatch = d3.dispatch('change'),
32566         svg = d3.select(null),
32567         layers = [
32568             { id: 'osm', layer: iD.svg.Osm(projection, context, dispatch) },
32569             { id: 'gpx', layer: iD.svg.Gpx(projection, context, dispatch) },
32570             { id: 'mapillary-images', layer: iD.svg.MapillaryImages(projection, context, dispatch) },
32571             { id: 'mapillary-signs',  layer: iD.svg.MapillarySigns(projection, context, dispatch) }
32572         ];
32573
32574
32575     function drawLayers(selection) {
32576         svg = selection.selectAll('.surface')
32577             .data([0]);
32578
32579         svg.enter()
32580             .append('svg')
32581             .attr('class', 'surface')
32582             .append('defs');
32583
32584         var groups = svg.selectAll('.data-layer')
32585             .data(layers);
32586
32587         groups.enter()
32588             .append('g')
32589             .attr('class', function(d) { return 'data-layer data-layer-' + d.id; });
32590
32591         groups
32592             .each(function(d) { d3.select(this).call(d.layer); });
32593
32594         groups.exit()
32595             .remove();
32596     }
32597
32598     drawLayers.all = function() {
32599         return layers;
32600     };
32601
32602     drawLayers.layer = function(id) {
32603         var obj = _.find(layers, function(o) {return o.id === id;});
32604         return obj && obj.layer;
32605     };
32606
32607     drawLayers.only = function(what) {
32608         var arr = [].concat(what);
32609         drawLayers.remove(_.difference(_.map(layers, 'id'), arr));
32610         return this;
32611     };
32612
32613     drawLayers.remove = function(what) {
32614         var arr = [].concat(what);
32615         arr.forEach(function(id) {
32616             layers = _.reject(layers, function(o) {return o.id === id;});
32617         });
32618         dispatch.change();
32619         return this;
32620     };
32621
32622     drawLayers.add = function(what) {
32623         var arr = [].concat(what);
32624         arr.forEach(function(obj) {
32625             if ('id' in obj && 'layer' in obj) {
32626                 layers.push(obj);
32627             }
32628         });
32629         dispatch.change();
32630         return this;
32631     };
32632
32633     drawLayers.dimensions = function(_) {
32634         if (!arguments.length) return svg.dimensions();
32635         svg.dimensions(_);
32636         layers.forEach(function(obj) {
32637             if (obj.layer.dimensions) {
32638                 obj.layer.dimensions(_);
32639             }
32640         });
32641         return this;
32642     };
32643
32644
32645     return d3.rebind(drawLayers, dispatch, 'on');
32646 };
32647 iD.svg.Lines = function(projection) {
32648
32649     var highway_stack = {
32650         motorway: 0,
32651         motorway_link: 1,
32652         trunk: 2,
32653         trunk_link: 3,
32654         primary: 4,
32655         primary_link: 5,
32656         secondary: 6,
32657         tertiary: 7,
32658         unclassified: 8,
32659         residential: 9,
32660         service: 10,
32661         footway: 11
32662     };
32663
32664     function waystack(a, b) {
32665         var as = 0, bs = 0;
32666
32667         if (a.tags.highway) { as -= highway_stack[a.tags.highway]; }
32668         if (b.tags.highway) { bs -= highway_stack[b.tags.highway]; }
32669         return as - bs;
32670     }
32671
32672     return function drawLines(surface, graph, entities, filter) {
32673         var ways = [], pathdata = {}, onewaydata = {},
32674             getPath = iD.svg.Path(projection, graph);
32675
32676         for (var i = 0; i < entities.length; i++) {
32677             var entity = entities[i],
32678                 outer = iD.geo.simpleMultipolygonOuterMember(entity, graph);
32679             if (outer) {
32680                 ways.push(entity.mergeTags(outer.tags));
32681             } else if (entity.geometry(graph) === 'line') {
32682                 ways.push(entity);
32683             }
32684         }
32685
32686         ways = ways.filter(getPath);
32687
32688         pathdata = _.groupBy(ways, function(way) { return way.layer(); });
32689
32690         _.forOwn(pathdata, function(v, k) {
32691             onewaydata[k] = _(v)
32692                 .filter(function(d) { return d.isOneWay(); })
32693                 .map(iD.svg.OneWaySegments(projection, graph, 35))
32694                 .flatten()
32695                 .valueOf();
32696         });
32697
32698         var layergroup = surface
32699             .selectAll('.layer-lines')
32700             .selectAll('g.layergroup')
32701             .data(d3.range(-10, 11));
32702
32703         layergroup.enter()
32704             .append('g')
32705             .attr('class', function(d) { return 'layer layergroup layer' + String(d); });
32706
32707
32708         var linegroup = layergroup
32709             .selectAll('g.linegroup')
32710             .data(['shadow', 'casing', 'stroke']);
32711
32712         linegroup.enter()
32713             .append('g')
32714             .attr('class', function(d) { return 'layer linegroup line-' + d; });
32715
32716
32717         var lines = linegroup
32718             .selectAll('path')
32719             .filter(filter)
32720             .data(
32721                 function() { return pathdata[this.parentNode.parentNode.__data__] || []; },
32722                 iD.Entity.key
32723             );
32724
32725         // Optimization: call simple TagClasses only on enter selection. This
32726         // works because iD.Entity.key is defined to include the entity v attribute.
32727         lines.enter()
32728             .append('path')
32729             .attr('class', function(d) { return 'way line ' + this.parentNode.__data__ + ' ' + d.id; })
32730             .call(iD.svg.TagClasses());
32731
32732         lines
32733             .sort(waystack)
32734             .attr('d', getPath)
32735             .call(iD.svg.TagClasses().tags(iD.svg.RelationMemberTags(graph)));
32736
32737         lines.exit()
32738             .remove();
32739
32740
32741         var onewaygroup = layergroup
32742             .selectAll('g.onewaygroup')
32743             .data(['oneway']);
32744
32745         onewaygroup.enter()
32746             .append('g')
32747             .attr('class', 'layer onewaygroup');
32748
32749
32750         var oneways = onewaygroup
32751             .selectAll('path')
32752             .filter(filter)
32753             .data(
32754                 function() { return onewaydata[this.parentNode.parentNode.__data__] || []; },
32755                 function(d) { return [d.id, d.index]; }
32756             );
32757
32758         oneways.enter()
32759             .append('path')
32760             .attr('class', 'oneway')
32761             .attr('marker-mid', 'url(#oneway-marker)');
32762
32763         oneways
32764             .attr('d', function(d) { return d.d; });
32765
32766         if (iD.detect().ie) {
32767             oneways.each(function() { this.parentNode.insertBefore(this, this); });
32768         }
32769
32770         oneways.exit()
32771             .remove();
32772
32773     };
32774 };
32775 iD.svg.MapillaryImages = function(projection, context, dispatch) {
32776     var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
32777         minZoom = 12,
32778         layer = d3.select(null),
32779         _mapillary;
32780
32781
32782     function init() {
32783         if (iD.svg.MapillaryImages.initialized) return;  // run once
32784         iD.svg.MapillaryImages.enabled = false;
32785         iD.svg.MapillaryImages.initialized = true;
32786     }
32787
32788     function getMapillary() {
32789         if (iD.services.mapillary && !_mapillary) {
32790             _mapillary = iD.services.mapillary();
32791             _mapillary.on('loadedImages', debouncedRedraw);
32792         } else if (!iD.services.mapillary && _mapillary) {
32793             _mapillary = null;
32794         }
32795
32796         return _mapillary;
32797     }
32798
32799     function showLayer() {
32800         var mapillary = getMapillary();
32801         if (!mapillary) return;
32802
32803         mapillary.loadViewer();
32804         editOn();
32805
32806         layer
32807             .style('opacity', 0)
32808             .transition()
32809             .duration(500)
32810             .style('opacity', 1)
32811             .each('end', debouncedRedraw);
32812     }
32813
32814     function hideLayer() {
32815         var mapillary = getMapillary();
32816         if (mapillary) {
32817             mapillary.hideViewer();
32818         }
32819
32820         debouncedRedraw.cancel();
32821
32822         layer
32823             .transition()
32824             .duration(500)
32825             .style('opacity', 0)
32826             .each('end', editOff);
32827     }
32828
32829     function editOn() {
32830         layer.style('display', 'block');
32831     }
32832
32833     function editOff() {
32834         layer.selectAll('.viewfield-group').remove();
32835         layer.style('display', 'none');
32836     }
32837
32838     function click(d) {
32839         var mapillary = getMapillary();
32840         if (!mapillary) return;
32841
32842         context.map().centerEase(d.loc);
32843
32844         mapillary
32845             .setSelectedImage(d.key, true)
32846             .updateViewer(d.key, context)
32847             .showViewer();
32848     }
32849
32850     function transform(d) {
32851         var t = iD.svg.PointTransform(projection)(d);
32852         if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
32853         return t;
32854     }
32855
32856     function update() {
32857         var mapillary = getMapillary(),
32858             data = (mapillary ? mapillary.images(projection, layer.dimensions()) : []),
32859             imageKey = mapillary ? mapillary.getSelectedImage() : null;
32860
32861         var markers = layer.selectAll('.viewfield-group')
32862             .data(data, function(d) { return d.key; });
32863
32864         // Enter
32865         var enter = markers.enter()
32866             .append('g')
32867             .attr('class', 'viewfield-group')
32868             .classed('selected', function(d) { return d.key === imageKey; })
32869             .on('click', click);
32870
32871         enter.append('path')
32872             .attr('class', 'viewfield')
32873             .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
32874             .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');
32875
32876         enter.append('circle')
32877             .attr('dx', '0')
32878             .attr('dy', '0')
32879             .attr('r', '6');
32880
32881         // Exit
32882         markers.exit()
32883             .remove();
32884
32885         // Update
32886         markers
32887             .attr('transform', transform);
32888     }
32889
32890     function drawImages(selection) {
32891         var enabled = iD.svg.MapillaryImages.enabled,
32892             mapillary = getMapillary();
32893
32894         layer = selection.selectAll('.layer-mapillary-images')
32895             .data(mapillary ? [0] : []);
32896
32897         layer.enter()
32898             .append('g')
32899             .attr('class', 'layer-mapillary-images')
32900             .style('display', enabled ? 'block' : 'none');
32901
32902         layer.exit()
32903             .remove();
32904
32905         if (enabled) {
32906             if (mapillary && ~~context.map().zoom() >= minZoom) {
32907                 editOn();
32908                 update();
32909                 mapillary.loadImages(projection, layer.dimensions());
32910             } else {
32911                 editOff();
32912             }
32913         }
32914     }
32915
32916     drawImages.enabled = function(_) {
32917         if (!arguments.length) return iD.svg.MapillaryImages.enabled;
32918         iD.svg.MapillaryImages.enabled = _;
32919         if (iD.svg.MapillaryImages.enabled) {
32920             showLayer();
32921         } else {
32922             hideLayer();
32923         }
32924         dispatch.change();
32925         return this;
32926     };
32927
32928     drawImages.supported = function() {
32929         return !!getMapillary();
32930     };
32931
32932     drawImages.dimensions = function(_) {
32933         if (!arguments.length) return layer.dimensions();
32934         layer.dimensions(_);
32935         return this;
32936     };
32937
32938     init();
32939     return drawImages;
32940 };
32941 iD.svg.MapillarySigns = function(projection, context, dispatch) {
32942     var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
32943         minZoom = 12,
32944         layer = d3.select(null),
32945         _mapillary;
32946
32947
32948     function init() {
32949         if (iD.svg.MapillarySigns.initialized) return;  // run once
32950         iD.svg.MapillarySigns.enabled = false;
32951         iD.svg.MapillarySigns.initialized = true;
32952     }
32953
32954     function getMapillary() {
32955         if (iD.services.mapillary && !_mapillary) {
32956             _mapillary = iD.services.mapillary().on('loadedSigns', debouncedRedraw);
32957         } else if (!iD.services.mapillary && _mapillary) {
32958             _mapillary = null;
32959         }
32960         return _mapillary;
32961     }
32962
32963     function showLayer() {
32964         editOn();
32965         debouncedRedraw();
32966     }
32967
32968     function hideLayer() {
32969         debouncedRedraw.cancel();
32970         editOff();
32971     }
32972
32973     function editOn() {
32974         layer.style('display', 'block');
32975     }
32976
32977     function editOff() {
32978         layer.selectAll('.icon-sign').remove();
32979         layer.style('display', 'none');
32980     }
32981
32982     function click(d) {
32983         var mapillary = getMapillary();
32984         if (!mapillary) return;
32985
32986         context.map().centerEase(d.loc);
32987
32988         mapillary
32989             .setSelectedImage(d.key, true)
32990             .updateViewer(d.key, context)
32991             .showViewer();
32992     }
32993
32994     function update() {
32995         var mapillary = getMapillary(),
32996             data = (mapillary ? mapillary.signs(projection, layer.dimensions()) : []),
32997             imageKey = mapillary ? mapillary.getSelectedImage() : null;
32998
32999         var signs = layer.selectAll('.icon-sign')
33000             .data(data, function(d) { return d.key; });
33001
33002         // Enter
33003         var enter = signs.enter()
33004             .append('foreignObject')
33005             .attr('class', 'icon-sign')
33006             .attr('width', '32px')      // for Firefox
33007             .attr('height', '32px')     // for Firefox
33008             .classed('selected', function(d) { return d.key === imageKey; })
33009             .on('click', click);
33010
33011         enter
33012             .append('xhtml:body')
33013             .html(mapillary.signHTML);
33014
33015         // Exit
33016         signs.exit()
33017             .remove();
33018
33019         // Update
33020         signs
33021             .attr('transform', iD.svg.PointTransform(projection));
33022     }
33023
33024     function drawSigns(selection) {
33025         var enabled = iD.svg.MapillarySigns.enabled,
33026             mapillary = getMapillary();
33027
33028         layer = selection.selectAll('.layer-mapillary-signs')
33029             .data(mapillary ? [0] : []);
33030
33031         layer.enter()
33032             .append('g')
33033             .attr('class', 'layer-mapillary-signs')
33034             .style('display', enabled ? 'block' : 'none')
33035             .attr('transform', 'translate(-16, -16)');  // center signs on loc
33036
33037         layer.exit()
33038             .remove();
33039
33040         if (enabled) {
33041             if (mapillary && ~~context.map().zoom() >= minZoom) {
33042                 editOn();
33043                 update();
33044                 mapillary.loadSigns(context, projection, layer.dimensions());
33045             } else {
33046                 editOff();
33047             }
33048         }
33049     }
33050
33051     drawSigns.enabled = function(_) {
33052         if (!arguments.length) return iD.svg.MapillarySigns.enabled;
33053         iD.svg.MapillarySigns.enabled = _;
33054         if (iD.svg.MapillarySigns.enabled) {
33055             showLayer();
33056         } else {
33057             hideLayer();
33058         }
33059         dispatch.change();
33060         return this;
33061     };
33062
33063     drawSigns.supported = function() {
33064         var mapillary = getMapillary();
33065         return (mapillary && mapillary.signsSupported());
33066     };
33067
33068     drawSigns.dimensions = function(_) {
33069         if (!arguments.length) return layer.dimensions();
33070         layer.dimensions(_);
33071         return this;
33072     };
33073
33074     init();
33075     return drawSigns;
33076 };
33077 iD.svg.Midpoints = function(projection, context) {
33078     return function drawMidpoints(surface, graph, entities, filter, extent) {
33079         var poly = extent.polygon(),
33080             midpoints = {};
33081
33082         for (var i = 0; i < entities.length; i++) {
33083             var entity = entities[i];
33084
33085             if (entity.type !== 'way')
33086                 continue;
33087             if (!filter(entity))
33088                 continue;
33089             if (context.selectedIDs().indexOf(entity.id) < 0)
33090                 continue;
33091
33092             var nodes = graph.childNodes(entity);
33093             for (var j = 0; j < nodes.length - 1; j++) {
33094
33095                 var a = nodes[j],
33096                     b = nodes[j + 1],
33097                     id = [a.id, b.id].sort().join('-');
33098
33099                 if (midpoints[id]) {
33100                     midpoints[id].parents.push(entity);
33101                 } else {
33102                     if (iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
33103                         var point = iD.geo.interp(a.loc, b.loc, 0.5),
33104                             loc = null;
33105
33106                         if (extent.intersects(point)) {
33107                             loc = point;
33108                         } else {
33109                             for (var k = 0; k < 4; k++) {
33110                                 point = iD.geo.lineIntersection([a.loc, b.loc], [poly[k], poly[k+1]]);
33111                                 if (point &&
33112                                     iD.geo.euclideanDistance(projection(a.loc), projection(point)) > 20 &&
33113                                     iD.geo.euclideanDistance(projection(b.loc), projection(point)) > 20)
33114                                 {
33115                                     loc = point;
33116                                     break;
33117                                 }
33118                             }
33119                         }
33120
33121                         if (loc) {
33122                             midpoints[id] = {
33123                                 type: 'midpoint',
33124                                 id: id,
33125                                 loc: loc,
33126                                 edge: [a.id, b.id],
33127                                 parents: [entity]
33128                             };
33129                         }
33130                     }
33131                 }
33132             }
33133         }
33134
33135         function midpointFilter(d) {
33136             if (midpoints[d.id])
33137                 return true;
33138
33139             for (var i = 0; i < d.parents.length; i++)
33140                 if (filter(d.parents[i]))
33141                     return true;
33142
33143             return false;
33144         }
33145
33146         var groups = surface.selectAll('.layer-hit').selectAll('g.midpoint')
33147             .filter(midpointFilter)
33148             .data(_.values(midpoints), function(d) { return d.id; });
33149
33150         var enter = groups.enter()
33151             .insert('g', ':first-child')
33152             .attr('class', 'midpoint');
33153
33154         enter.append('polygon')
33155             .attr('points', '-6,8 10,0 -6,-8')
33156             .attr('class', 'shadow');
33157
33158         enter.append('polygon')
33159             .attr('points', '-3,4 5,0 -3,-4')
33160             .attr('class', 'fill');
33161
33162         groups
33163             .attr('transform', function(d) {
33164                 var translate = iD.svg.PointTransform(projection),
33165                     a = context.entity(d.edge[0]),
33166                     b = context.entity(d.edge[1]),
33167                     angle = Math.round(iD.geo.angle(a, b, projection) * (180 / Math.PI));
33168                 return translate(d) + ' rotate(' + angle + ')';
33169             })
33170             .call(iD.svg.TagClasses().tags(
33171                 function(d) { return d.parents[0].tags; }
33172             ));
33173
33174         // Propagate data bindings.
33175         groups.select('polygon.shadow');
33176         groups.select('polygon.fill');
33177
33178         groups.exit()
33179             .remove();
33180     };
33181 };
33182 iD.svg.Osm = function() {
33183     return function drawOsm(selection) {
33184         var layers = selection.selectAll('.layer-osm')
33185             .data(['areas', 'lines', 'hit', 'halo', 'label']);
33186
33187         layers.enter().append('g')
33188             .attr('class', function(d) { return 'layer-osm layer-' + d; });
33189     };
33190 };
33191 iD.svg.Points = function(projection, context) {
33192     function markerPath(selection, klass) {
33193         selection
33194             .attr('class', klass)
33195             .attr('transform', 'translate(-8, -23)')
33196             .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');
33197     }
33198
33199     function sortY(a, b) {
33200         return b.loc[1] - a.loc[1];
33201     }
33202
33203     return function drawPoints(surface, graph, entities, filter) {
33204         var wireframe = surface.classed('fill-wireframe'),
33205             points = wireframe ? [] : _.filter(entities, function(e) {
33206                 return e.geometry(graph) === 'point';
33207             });
33208
33209         points.sort(sortY);
33210
33211         var groups = surface.selectAll('.layer-hit').selectAll('g.point')
33212             .filter(filter)
33213             .data(points, iD.Entity.key);
33214
33215         var group = groups.enter()
33216             .append('g')
33217             .attr('class', function(d) { return 'node point ' + d.id; })
33218             .order();
33219
33220         group.append('path')
33221             .call(markerPath, 'shadow');
33222
33223         group.append('path')
33224             .call(markerPath, 'stroke');
33225
33226         group.append('use')
33227             .attr('transform', 'translate(-6, -20)')
33228             .attr('class', 'icon')
33229             .attr('width', '12px')
33230             .attr('height', '12px');
33231
33232         groups.attr('transform', iD.svg.PointTransform(projection))
33233             .call(iD.svg.TagClasses());
33234
33235         // Selecting the following implicitly
33236         // sets the data (point entity) on the element
33237         groups.select('.shadow');
33238         groups.select('.stroke');
33239         groups.select('.icon')
33240             .attr('xlink:href', function(entity) {
33241                 var preset = context.presets().match(entity, graph);
33242                 return preset.icon ? '#' + preset.icon + '-12' : '';
33243             });
33244
33245         groups.exit()
33246             .remove();
33247     };
33248 };
33249 iD.svg.TagClasses = function() {
33250     var primaries = [
33251             'building', 'highway', 'railway', 'waterway', 'aeroway',
33252             'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse',
33253             'leisure', 'place'
33254         ],
33255         statuses = [
33256             'proposed', 'construction', 'disused', 'abandoned', 'dismantled',
33257             'razed', 'demolished', 'obliterated'
33258         ],
33259         secondaries = [
33260             'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
33261             'surface', 'tracktype', 'crossing'
33262         ],
33263         tagClassRe = /^tag-/,
33264         tags = function(entity) { return entity.tags; };
33265
33266
33267     var tagClasses = function(selection) {
33268         selection.each(function tagClassesEach(entity) {
33269             var value = this.className,
33270                 classes, primary, status;
33271
33272             if (value.baseVal !== undefined) value = value.baseVal;
33273
33274             classes = value.trim().split(/\s+/).filter(function(name) {
33275                 return name.length && !tagClassRe.test(name);
33276             }).join(' ');
33277
33278             var t = tags(entity), i, k, v;
33279
33280             // pick at most one primary classification tag..
33281             for (i = 0; i < primaries.length; i++) {
33282                 k = primaries[i];
33283                 v = t[k];
33284                 if (!v || v === 'no') continue;
33285
33286                 primary = k;
33287                 if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
33288                     status = v;
33289                     classes += ' tag-' + k;
33290                 } else {
33291                     classes += ' tag-' + k + ' tag-' + k + '-' + v;
33292                 }
33293
33294                 break;
33295             }
33296
33297             // add at most one status tag, only if relates to primary tag..
33298             if (!status) {
33299                 for (i = 0; i < statuses.length; i++) {
33300                     k = statuses[i];
33301                     v = t[k];
33302                     if (!v || v === 'no') continue;
33303
33304                     if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
33305                         status = k;
33306                     }
33307                     else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
33308                         status = k;
33309                     } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
33310                         status = k;
33311                         primary = v;
33312                         classes += ' tag-' + v;
33313                     }  // else ignore e.g.  `highway=path + abandoned=railway`
33314
33315                     if (status) break;
33316                 }
33317             }
33318
33319             if (status) {
33320                 classes += ' tag-status tag-status-' + status;
33321             }
33322
33323             // add any secondary (structure) tags
33324             for (i = 0; i < secondaries.length; i++) {
33325                 k = secondaries[i];
33326                 v = t[k];
33327                 if (!v || v === 'no') continue;
33328                 classes += ' tag-' + k + ' tag-' + k + '-' + v;
33329             }
33330
33331             // For highways, look for surface tagging..
33332             if (primary === 'highway') {
33333                 var paved = (t.highway !== 'track');
33334                 for (k in t) {
33335                     v = t[k];
33336                     if (k in iD.pavedTags) {
33337                         paved = !!iD.pavedTags[k][v];
33338                         break;
33339                     }
33340                 }
33341                 if (!paved) {
33342                     classes += ' tag-unpaved';
33343                 }
33344             }
33345
33346             classes = classes.trim();
33347
33348             if (classes !== value) {
33349                 d3.select(this).attr('class', classes);
33350             }
33351         });
33352     };
33353
33354     tagClasses.tags = function(_) {
33355         if (!arguments.length) return tags;
33356         tags = _;
33357         return tagClasses;
33358     };
33359
33360     return tagClasses;
33361 };
33362 iD.svg.Turns = function(projection) {
33363     return function drawTurns(surface, graph, turns) {
33364         function key(turn) {
33365             return [turn.from.node + turn.via.node + turn.to.node].join('-');
33366         }
33367
33368         function icon(turn) {
33369             var u = turn.u ? '-u' : '';
33370             if (!turn.restriction)
33371                 return '#turn-yes' + u;
33372             var restriction = graph.entity(turn.restriction).tags.restriction;
33373             return '#turn-' +
33374                 (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u;
33375         }
33376
33377         var groups = surface.selectAll('.layer-hit').selectAll('g.turn')
33378             .data(turns, key);
33379
33380         // Enter
33381         var enter = groups.enter().append('g')
33382             .attr('class', 'turn');
33383
33384         var nEnter = enter.filter(function (turn) { return !turn.u; });
33385
33386         nEnter.append('rect')
33387             .attr('transform', 'translate(-22, -12)')
33388             .attr('width', '44')
33389             .attr('height', '24');
33390
33391         nEnter.append('use')
33392             .attr('transform', 'translate(-22, -12)')
33393             .attr('width', '44')
33394             .attr('height', '24');
33395
33396
33397         var uEnter = enter.filter(function (turn) { return turn.u; });
33398
33399         uEnter.append('circle')
33400             .attr('r', '16');
33401
33402         uEnter.append('use')
33403             .attr('transform', 'translate(-16, -16)')
33404             .attr('width', '32')
33405             .attr('height', '32');
33406
33407
33408         // Update
33409         groups
33410             .attr('transform', function (turn) {
33411                 var v = graph.entity(turn.via.node),
33412                     t = graph.entity(turn.to.node),
33413                     a = iD.geo.angle(v, t, projection),
33414                     p = projection(v.loc),
33415                     r = turn.u ? 0 : 60;
33416
33417                 return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' +
33418                     'rotate(' + a * 180 / Math.PI + ')';
33419             });
33420
33421         groups.select('use')
33422             .attr('xlink:href', icon);
33423
33424         groups.select('rect');
33425         groups.select('circle');
33426
33427
33428         // Exit
33429         groups.exit()
33430             .remove();
33431
33432         return this;
33433     };
33434 };
33435 iD.svg.Vertices = function(projection, context) {
33436     var radiuses = {
33437         //       z16-, z17, z18+, tagged
33438         shadow: [6,    7.5,   7.5,  11.5],
33439         stroke: [2.5,  3.5,   3.5,  7],
33440         fill:   [1,    1.5,   1.5,  1.5]
33441     };
33442
33443     var hover;
33444
33445     function siblingAndChildVertices(ids, graph, extent) {
33446         var vertices = {};
33447
33448         function addChildVertices(entity) {
33449             if (!context.features().isHiddenFeature(entity, graph, entity.geometry(graph))) {
33450                 var i;
33451                 if (entity.type === 'way') {
33452                     for (i = 0; i < entity.nodes.length; i++) {
33453                         addChildVertices(graph.entity(entity.nodes[i]));
33454                     }
33455                 } else if (entity.type === 'relation') {
33456                     for (i = 0; i < entity.members.length; i++) {
33457                         var member = context.hasEntity(entity.members[i].id);
33458                         if (member) {
33459                             addChildVertices(member);
33460                         }
33461                     }
33462                 } else if (entity.intersects(extent, graph)) {
33463                     vertices[entity.id] = entity;
33464                 }
33465             }
33466         }
33467
33468         ids.forEach(function(id) {
33469             var entity = context.hasEntity(id);
33470             if (entity && entity.type === 'node') {
33471                 vertices[entity.id] = entity;
33472                 context.graph().parentWays(entity).forEach(function(entity) {
33473                     addChildVertices(entity);
33474                 });
33475             } else if (entity) {
33476                 addChildVertices(entity);
33477             }
33478         });
33479
33480         return vertices;
33481     }
33482
33483     function draw(selection, vertices, klass, graph, zoom) {
33484         var icons = {},
33485             z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
33486
33487         var groups = selection
33488             .data(vertices, iD.Entity.key);
33489
33490         function icon(entity) {
33491             if (entity.id in icons) return icons[entity.id];
33492             icons[entity.id] =
33493                 entity.hasInterestingTags() &&
33494                 context.presets().match(entity, graph).icon;
33495             return icons[entity.id];
33496         }
33497
33498         function setClass(klass) {
33499             return function(entity) {
33500                 this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id);
33501             };
33502         }
33503
33504         function setAttributes(selection) {
33505             ['shadow','stroke','fill'].forEach(function(klass) {
33506                 var rads = radiuses[klass];
33507                 selection.selectAll('.' + klass)
33508                     .each(function(entity) {
33509                         var i = z && icon(entity),
33510                             c = i ? 0.5 : 0,
33511                             r = rads[i ? 3 : z];
33512                         this.setAttribute('cx', c);
33513                         this.setAttribute('cy', -c);
33514                         this.setAttribute('r', r);
33515                         if (i && klass === 'fill') {
33516                             this.setAttribute('visibility', 'hidden');
33517                         } else {
33518                             this.removeAttribute('visibility');
33519                         }
33520                     });
33521             });
33522
33523             selection.selectAll('use')
33524                 .each(function() {
33525                     if (z) {
33526                         this.removeAttribute('visibility');
33527                     } else {
33528                         this.setAttribute('visibility', 'hidden');
33529                     }
33530                 });
33531         }
33532
33533         var enter = groups.enter()
33534             .append('g')
33535             .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; });
33536
33537         enter.append('circle')
33538             .each(setClass('shadow'));
33539
33540         enter.append('circle')
33541             .each(setClass('stroke'));
33542
33543         // Vertices with icons get a `use`.
33544         enter.filter(function(d) { return icon(d); })
33545             .append('use')
33546             .attr('transform', 'translate(-6, -6)')
33547             .attr('xlink:href', function(d) { return '#' + icon(d) + '-12'; })
33548             .attr('width', '12px')
33549             .attr('height', '12px')
33550             .each(setClass('icon'));
33551
33552         // Vertices with tags get a fill.
33553         enter.filter(function(d) { return d.hasInterestingTags(); })
33554             .append('circle')
33555             .each(setClass('fill'));
33556
33557         groups
33558             .attr('transform', iD.svg.PointTransform(projection))
33559             .classed('shared', function(entity) { return graph.isShared(entity); })
33560             .call(setAttributes);
33561
33562         groups.exit()
33563             .remove();
33564     }
33565
33566     function drawVertices(surface, graph, entities, filter, extent, zoom) {
33567         var selected = siblingAndChildVertices(context.selectedIDs(), graph, extent),
33568             wireframe = surface.classed('fill-wireframe'),
33569             vertices = [];
33570
33571         for (var i = 0; i < entities.length; i++) {
33572             var entity = entities[i],
33573                 geometry = entity.geometry(graph);
33574
33575             if (wireframe && geometry === 'point') {
33576                 vertices.push(entity);
33577                 continue;
33578             }
33579
33580             if (geometry !== 'vertex')
33581                 continue;
33582
33583             if (entity.id in selected ||
33584                 entity.hasInterestingTags() ||
33585                 entity.isIntersection(graph)) {
33586                 vertices.push(entity);
33587             }
33588         }
33589
33590         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-persistent')
33591             .filter(filter)
33592             .call(draw, vertices, 'vertex-persistent', graph, zoom);
33593
33594         drawHover(surface, graph, extent, zoom);
33595     }
33596
33597     function drawHover(surface, graph, extent, zoom) {
33598         var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent) : {};
33599
33600         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-hover')
33601             .call(draw, d3.values(hovered), 'vertex-hover', graph, zoom);
33602     }
33603
33604     drawVertices.drawHover = function(surface, graph, target, extent, zoom) {
33605         if (target === hover) return;
33606         hover = target;
33607         drawHover(surface, graph, extent, zoom);
33608     };
33609
33610     return drawVertices;
33611 };
33612 iD.ui = function(context) {
33613     function render(container) {
33614         var map = context.map();
33615
33616         if (iD.detect().opera) container.classed('opera', true);
33617
33618         var hash = iD.behavior.Hash(context);
33619
33620         hash();
33621
33622         if (!hash.hadHash) {
33623             map.centerZoom([0, 0], 2);
33624         }
33625
33626         container.append('svg')
33627             .attr('id', 'defs')
33628             .call(iD.svg.Defs(context));
33629
33630         container.append('div')
33631             .attr('id', 'sidebar')
33632             .attr('class', 'col4')
33633             .call(ui.sidebar);
33634
33635         var content = container.append('div')
33636             .attr('id', 'content');
33637
33638         var bar = content.append('div')
33639             .attr('id', 'bar')
33640             .attr('class', 'fillD');
33641
33642         content.append('div')
33643             .attr('id', 'map')
33644             .call(map);
33645
33646         content
33647             .call(iD.ui.MapInMap(context));
33648
33649         content.append('div')
33650             .call(iD.ui.Info(context));
33651
33652         bar.append('div')
33653             .attr('class', 'spacer col4');
33654
33655         var limiter = bar.append('div')
33656             .attr('class', 'limiter');
33657
33658         limiter.append('div')
33659             .attr('class', 'button-wrap joined col3')
33660             .call(iD.ui.Modes(context), limiter);
33661
33662         limiter.append('div')
33663             .attr('class', 'button-wrap joined col1')
33664             .call(iD.ui.UndoRedo(context));
33665
33666         limiter.append('div')
33667             .attr('class', 'button-wrap col1')
33668             .call(iD.ui.Save(context));
33669
33670         bar.append('div')
33671             .attr('class', 'full-screen')
33672             .call(iD.ui.FullScreen(context));
33673
33674         bar.append('div')
33675             .attr('class', 'spinner')
33676             .call(iD.ui.Spinner(context));
33677
33678         var controls = bar.append('div')
33679             .attr('class', 'map-controls');
33680
33681         controls.append('div')
33682             .attr('class', 'map-control zoombuttons')
33683             .call(iD.ui.Zoom(context));
33684
33685         controls.append('div')
33686             .attr('class', 'map-control geolocate-control')
33687             .call(iD.ui.Geolocate(context));
33688
33689         controls.append('div')
33690             .attr('class', 'map-control background-control')
33691             .call(iD.ui.Background(context));
33692
33693         controls.append('div')
33694             .attr('class', 'map-control map-data-control')
33695             .call(iD.ui.MapData(context));
33696
33697         controls.append('div')
33698             .attr('class', 'map-control help-control')
33699             .call(iD.ui.Help(context));
33700
33701         var about = content.append('div')
33702             .attr('id', 'about');
33703
33704         about.append('div')
33705             .attr('id', 'attrib')
33706             .call(iD.ui.Attribution(context));
33707
33708         var footer = about.append('div')
33709             .attr('id', 'footer')
33710             .attr('class', 'fillD');
33711
33712         footer.append('div')
33713             .attr('class', 'api-status')
33714             .call(iD.ui.Status(context));
33715
33716         footer.append('div')
33717             .attr('id', 'scale-block')
33718             .call(iD.ui.Scale(context));
33719
33720         var aboutList = footer.append('div')
33721             .attr('id', 'info-block')
33722             .append('ul')
33723             .attr('id', 'about-list');
33724
33725         if (!context.embed()) {
33726             aboutList.call(iD.ui.Account(context));
33727         }
33728
33729         aboutList.append('li')
33730             .append('a')
33731             .attr('target', '_blank')
33732             .attr('tabindex', -1)
33733             .attr('href', 'https://github.com/openstreetmap/iD')
33734             .text(iD.version);
33735
33736         var issueLinks = aboutList.append('li');
33737
33738         issueLinks.append('a')
33739             .attr('target', '_blank')
33740             .attr('tabindex', -1)
33741             .attr('href', 'https://github.com/openstreetmap/iD/issues')
33742             .call(iD.svg.Icon('#icon-bug', 'light'))
33743             .call(bootstrap.tooltip()
33744                 .title(t('report_a_bug'))
33745                 .placement('top')
33746             );
33747
33748         issueLinks.append('a')
33749             .attr('target', '_blank')
33750             .attr('tabindex', -1)
33751             .attr('href', 'https://github.com/openstreetmap/iD/blob/master/CONTRIBUTING.md#translating')
33752             .call(iD.svg.Icon('#icon-translate', 'light'))
33753             .call(bootstrap.tooltip()
33754                 .title(t('help_translate'))
33755                 .placement('top')
33756             );
33757
33758         aboutList.append('li')
33759             .attr('class', 'feature-warning')
33760             .attr('tabindex', -1)
33761             .call(iD.ui.FeatureInfo(context));
33762
33763         aboutList.append('li')
33764             .attr('class', 'user-list')
33765             .attr('tabindex', -1)
33766             .call(iD.ui.Contributors(context));
33767
33768         window.onbeforeunload = function() {
33769             return context.save();
33770         };
33771
33772         window.onunload = function() {
33773             context.history().unlock();
33774         };
33775
33776         var mapDimensions = map.dimensions();
33777
33778         d3.select(window).on('resize.editor', function() {
33779             mapDimensions = content.dimensions(null);
33780             map.dimensions(mapDimensions);
33781         });
33782
33783         function pan(d) {
33784             return function() {
33785                 d3.event.preventDefault();
33786                 if (!context.inIntro()) context.pan(d);
33787             };
33788         }
33789
33790         // pan amount
33791         var pa = 10;
33792
33793         var keybinding = d3.keybinding('main')
33794             .on('⌫', function() { d3.event.preventDefault(); })
33795             .on('←', pan([pa, 0]))
33796             .on('↑', pan([0, pa]))
33797             .on('→', pan([-pa, 0]))
33798             .on('↓', pan([0, -pa]))
33799             .on('⇧←', pan([mapDimensions[0], 0]))
33800             .on('⇧↑', pan([0, mapDimensions[1]]))
33801             .on('⇧→', pan([-mapDimensions[0], 0]))
33802             .on('⇧↓', pan([0, -mapDimensions[1]]))
33803             .on(iD.ui.cmd('⌘←'), pan([mapDimensions[0], 0]))
33804             .on(iD.ui.cmd('⌘↑'), pan([0, mapDimensions[1]]))
33805             .on(iD.ui.cmd('⌘→'), pan([-mapDimensions[0], 0]))
33806             .on(iD.ui.cmd('⌘↓'), pan([0, -mapDimensions[1]]));
33807
33808         d3.select(document)
33809             .call(keybinding);
33810
33811         context.enter(iD.modes.Browse(context));
33812
33813         context.container()
33814             .call(iD.ui.Splash(context))
33815             .call(iD.ui.Restore(context));
33816
33817         var authenticating = iD.ui.Loading(context)
33818             .message(t('loading_auth'));
33819
33820         context.connection()
33821             .on('authenticating.ui', function() {
33822                 context.container()
33823                     .call(authenticating);
33824             })
33825             .on('authenticated.ui', function() {
33826                 authenticating.close();
33827             });
33828     }
33829
33830     function ui(container) {
33831         context.container(container);
33832         context.loadLocale(function() {
33833             render(container);
33834         });
33835     }
33836
33837     ui.sidebar = iD.ui.Sidebar(context);
33838
33839     return ui;
33840 };
33841
33842 iD.ui.tooltipHtml = function(text, key) {
33843     var s = '<span>' + text + '</span>';
33844     if (key) {
33845         s += '<div class="keyhint-wrap">' +
33846             '<span> ' + (t('tooltip_keyhint')) + ' </span>' +
33847             '<span class="keyhint"> ' + key + '</span></div>';
33848     }
33849     return s;
33850 };
33851 iD.ui.Account = function(context) {
33852     var connection = context.connection();
33853
33854     function update(selection) {
33855         if (!connection.authenticated()) {
33856             selection.selectAll('#userLink, #logoutLink')
33857                 .classed('hide', true);
33858             return;
33859         }
33860
33861         connection.userDetails(function(err, details) {
33862             var userLink = selection.select('#userLink'),
33863                 logoutLink = selection.select('#logoutLink');
33864
33865             userLink.html('');
33866             logoutLink.html('');
33867
33868             if (err) return;
33869
33870             selection.selectAll('#userLink, #logoutLink')
33871                 .classed('hide', false);
33872
33873             // Link
33874             userLink.append('a')
33875                 .attr('href', connection.userURL(details.display_name))
33876                 .attr('target', '_blank');
33877
33878             // Add thumbnail or dont
33879             if (details.image_url) {
33880                 userLink.append('img')
33881                     .attr('class', 'icon pre-text user-icon')
33882                     .attr('src', details.image_url);
33883             } else {
33884                 userLink
33885                     .call(iD.svg.Icon('#icon-avatar', 'pre-text light'));
33886             }
33887
33888             // Add user name
33889             userLink.append('span')
33890                 .attr('class', 'label')
33891                 .text(details.display_name);
33892
33893             logoutLink.append('a')
33894                 .attr('class', 'logout')
33895                 .attr('href', '#')
33896                 .text(t('logout'))
33897                 .on('click.logout', function() {
33898                     d3.event.preventDefault();
33899                     connection.logout();
33900                 });
33901         });
33902     }
33903
33904     return function(selection) {
33905         selection.append('li')
33906             .attr('id', 'logoutLink')
33907             .classed('hide', true);
33908
33909         selection.append('li')
33910             .attr('id', 'userLink')
33911             .classed('hide', true);
33912
33913         connection.on('auth.account', function() { update(selection); });
33914         update(selection);
33915     };
33916 };
33917 iD.ui.Attribution = function(context) {
33918     var selection;
33919
33920     function attribution(data, klass) {
33921         var div = selection.selectAll('.' + klass)
33922             .data([0]);
33923
33924         div.enter()
33925             .append('div')
33926             .attr('class', klass);
33927
33928         var background = div.selectAll('.attribution')
33929             .data(data, function(d) { return d.name(); });
33930
33931         background.enter()
33932             .append('span')
33933             .attr('class', 'attribution')
33934             .each(function(d) {
33935                 if (d.terms_html) {
33936                     d3.select(this)
33937                         .html(d.terms_html);
33938                     return;
33939                 }
33940
33941                 var source = d.terms_text || d.id || d.name();
33942
33943                 if (d.logo) {
33944                     source = '<img class="source-image" src="' + context.imagePath(d.logo) + '">';
33945                 }
33946
33947                 if (d.terms_url) {
33948                     d3.select(this)
33949                         .append('a')
33950                         .attr('href', d.terms_url)
33951                         .attr('target', '_blank')
33952                         .html(source);
33953                 } else {
33954                     d3.select(this)
33955                         .text(source);
33956                 }
33957             });
33958
33959         background.exit()
33960             .remove();
33961
33962         var copyright = background.selectAll('.copyright-notice')
33963             .data(function(d) {
33964                 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
33965                 return notice ? [notice] : [];
33966             });
33967
33968         copyright.enter()
33969             .append('span')
33970             .attr('class', 'copyright-notice');
33971
33972         copyright.text(String);
33973
33974         copyright.exit()
33975             .remove();
33976     }
33977
33978     function update() {
33979         attribution([context.background().baseLayerSource()], 'base-layer-attribution');
33980         attribution(context.background().overlayLayerSources().filter(function (s) {
33981             return s.validZoom(context.map().zoom());
33982         }), 'overlay-layer-attribution');
33983     }
33984
33985     return function(select) {
33986         selection = select;
33987
33988         context.background()
33989             .on('change.attribution', update);
33990
33991         context.map()
33992             .on('move.attribution', _.throttle(update, 400, {leading: false}));
33993
33994         update();
33995     };
33996 };
33997 iD.ui.Background = function(context) {
33998     var key = 'B',
33999         opacities = [1, 0.75, 0.5, 0.25],
34000         directions = [
34001             ['right', [0.5, 0]],
34002             ['top', [0, -0.5]],
34003             ['left', [-0.5, 0]],
34004             ['bottom', [0, 0.5]]],
34005         opacityDefault = (context.storage('background-opacity') !== null) ?
34006             (+context.storage('background-opacity')) : 1.0,
34007         customTemplate = context.storage('background-custom-template') || '',
34008         previous;
34009
34010     // Can be 0 from <1.3.0 use or due to issue #1923.
34011     if (opacityDefault === 0) opacityDefault = 1.0;
34012
34013
34014     function background(selection) {
34015
34016         function sortSources(a, b) {
34017             return a.best() && !b.best() ? -1
34018                 : b.best() && !a.best() ? 1
34019                 : d3.descending(a.area(), b.area()) || d3.ascending(a.name(), b.name()) || 0;
34020         }
34021
34022         function setOpacity(d) {
34023             var bg = context.container().selectAll('.layer-background')
34024                 .transition()
34025                 .style('opacity', d)
34026                 .attr('data-opacity', d);
34027
34028             if (!iD.detect().opera) {
34029                 iD.util.setTransform(bg, 0, 0);
34030             }
34031
34032             opacityList.selectAll('li')
34033                 .classed('active', function(_) { return _ === d; });
34034
34035             context.storage('background-opacity', d);
34036         }
34037
34038         function setTooltips(selection) {
34039             selection.each(function(d) {
34040                 var item = d3.select(this);
34041                 if (d === previous) {
34042                     item.call(bootstrap.tooltip()
34043                         .html(true)
34044                         .title(function() {
34045                             var tip = '<div>' + t('background.switch') + '</div>';
34046                             return iD.ui.tooltipHtml(tip, iD.ui.cmd('⌘B'));
34047                         })
34048                         .placement('top')
34049                     );
34050                 } else if (d.description) {
34051                     item.call(bootstrap.tooltip()
34052                         .title(d.description)
34053                         .placement('top')
34054                     );
34055                 } else {
34056                     item.call(bootstrap.tooltip().destroy);
34057                 }
34058             });
34059         }
34060
34061         function selectLayer() {
34062             function active(d) {
34063                 return context.background().showsLayer(d);
34064             }
34065
34066             content.selectAll('.layer, .custom_layer')
34067                 .classed('active', active)
34068                 .classed('switch', function(d) { return d === previous; })
34069                 .call(setTooltips)
34070                 .selectAll('input')
34071                 .property('checked', active);
34072         }
34073
34074         function clickSetSource(d) {
34075             previous = context.background().baseLayerSource();
34076             d3.event.preventDefault();
34077             context.background().baseLayerSource(d);
34078             selectLayer();
34079             document.activeElement.blur();
34080         }
34081
34082         function editCustom() {
34083             d3.event.preventDefault();
34084             var template = window.prompt(t('background.custom_prompt'), customTemplate);
34085             if (!template ||
34086                 template.indexOf('google.com') !== -1 ||
34087                 template.indexOf('googleapis.com') !== -1 ||
34088                 template.indexOf('google.ru') !== -1) {
34089                 selectLayer();
34090                 return;
34091             }
34092             setCustom(template);
34093         }
34094
34095         function setCustom(template) {
34096             context.background().baseLayerSource(iD.BackgroundSource.Custom(template));
34097             selectLayer();
34098             context.storage('background-custom-template', template);
34099         }
34100
34101         function clickSetOverlay(d) {
34102             d3.event.preventDefault();
34103             context.background().toggleOverlayLayer(d);
34104             selectLayer();
34105             document.activeElement.blur();
34106         }
34107
34108         function drawList(layerList, type, change, filter) {
34109             var sources = context.background()
34110                 .sources(context.map().extent())
34111                 .filter(filter);
34112
34113             var layerLinks = layerList.selectAll('li.layer')
34114                 .data(sources, function(d) { return d.name(); });
34115
34116             var enter = layerLinks.enter()
34117                 .insert('li', '.custom_layer')
34118                 .attr('class', 'layer')
34119                 .classed('best', function(d) { return d.best(); });
34120
34121             enter.filter(function(d) { return d.best(); })
34122                 .append('div')
34123                 .attr('class', 'best')
34124                 .call(bootstrap.tooltip()
34125                     .title(t('background.best_imagery'))
34126                     .placement('left'))
34127                 .append('span')
34128                 .html('&#9733;');
34129
34130             var label = enter.append('label');
34131
34132             label.append('input')
34133                 .attr('type', type)
34134                 .attr('name', 'layers')
34135                 .on('change', change);
34136
34137             label.append('span')
34138                 .text(function(d) { return d.name(); });
34139
34140
34141             layerLinks.exit()
34142                 .remove();
34143
34144             layerList.selectAll('li.layer')
34145                 .sort(sortSources)
34146                 .style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
34147         }
34148
34149         function update() {
34150             backgroundList.call(drawList, 'radio', clickSetSource, function(d) { return !d.overlay; });
34151             overlayList.call(drawList, 'checkbox', clickSetOverlay, function(d) { return d.overlay; });
34152
34153             selectLayer();
34154
34155             var source = context.background().baseLayerSource();
34156             if (source.id === 'custom') {
34157                 customTemplate = source.template;
34158             }
34159
34160             updateOffsetVal();
34161         }
34162
34163         function updateOffsetVal() {
34164             var meters = iD.geo.offsetToMeters(context.background().offset()),
34165                 x = +meters[0].toFixed(2),
34166                 y = +meters[1].toFixed(2);
34167
34168             d3.selectAll('.nudge-inner-rect')
34169                 .select('input')
34170                 .classed('error', false)
34171                 .property('value', x + ', ' + y);
34172
34173             d3.selectAll('.nudge-reset')
34174                 .classed('disabled', function() {
34175                     return (x === 0 && y === 0);
34176                 });
34177         }
34178
34179         function resetOffset() {
34180             context.background().offset([0, 0]);
34181             updateOffsetVal();
34182         }
34183
34184         function nudge(d) {
34185             context.background().nudge(d, context.map().zoom());
34186             updateOffsetVal();
34187         }
34188
34189         function buttonOffset(d) {
34190             var timeout = window.setTimeout(function() {
34191                     interval = window.setInterval(nudge.bind(null, d), 100);
34192                 }, 500),
34193                 interval;
34194
34195             d3.select(window).on('mouseup', function() {
34196                 window.clearInterval(interval);
34197                 window.clearTimeout(timeout);
34198                 d3.select(window).on('mouseup', null);
34199             });
34200
34201             nudge(d);
34202         }
34203
34204         function inputOffset() {
34205             var input = d3.select(this);
34206             var d = input.node().value;
34207
34208             if (d === '') return resetOffset();
34209
34210             d = d.replace(/;/g, ',').split(',').map(function(n) {
34211                 // if n is NaN, it will always get mapped to false.
34212                 return !isNaN(n) && n;
34213             });
34214
34215             if (d.length !== 2 || !d[0] || !d[1]) {
34216                 input.classed('error', true);
34217                 return;
34218             }
34219
34220             context.background().offset(iD.geo.metersToOffset(d));
34221             updateOffsetVal();
34222         }
34223
34224         function dragOffset() {
34225             var origin = [d3.event.clientX, d3.event.clientY];
34226
34227             context.container()
34228                 .append('div')
34229                 .attr('class', 'nudge-surface');
34230
34231             d3.select(window)
34232                 .on('mousemove.offset', function() {
34233                     var latest = [d3.event.clientX, d3.event.clientY];
34234                     var d = [
34235                         -(origin[0] - latest[0]) / 4,
34236                         -(origin[1] - latest[1]) / 4
34237                     ];
34238
34239                     origin = latest;
34240                     nudge(d);
34241                 })
34242                 .on('mouseup.offset', function() {
34243                     d3.selectAll('.nudge-surface')
34244                         .remove();
34245
34246                     d3.select(window)
34247                         .on('mousemove.offset', null)
34248                         .on('mouseup.offset', null);
34249                 });
34250
34251             d3.event.preventDefault();
34252         }
34253
34254         function hide() {
34255             setVisible(false);
34256         }
34257
34258         function toggle() {
34259             if (d3.event) d3.event.preventDefault();
34260             tooltip.hide(button);
34261             setVisible(!button.classed('active'));
34262         }
34263
34264         function quickSwitch() {
34265             if (previous) {
34266                 clickSetSource(previous);
34267             }
34268         }
34269
34270         function setVisible(show) {
34271             if (show !== shown) {
34272                 button.classed('active', show);
34273                 shown = show;
34274
34275                 if (show) {
34276                     selection.on('mousedown.background-inside', function() {
34277                         return d3.event.stopPropagation();
34278                     });
34279                     content.style('display', 'block')
34280                         .style('right', '-300px')
34281                         .transition()
34282                         .duration(200)
34283                         .style('right', '0px');
34284                 } else {
34285                     content.style('display', 'block')
34286                         .style('right', '0px')
34287                         .transition()
34288                         .duration(200)
34289                         .style('right', '-300px')
34290                         .each('end', function() {
34291                             d3.select(this).style('display', 'none');
34292                         });
34293                     selection.on('mousedown.background-inside', null);
34294                 }
34295             }
34296         }
34297
34298
34299         var content = selection.append('div')
34300                 .attr('class', 'fillL map-overlay col3 content hide'),
34301             tooltip = bootstrap.tooltip()
34302                 .placement('left')
34303                 .html(true)
34304                 .title(iD.ui.tooltipHtml(t('background.description'), key)),
34305             button = selection.append('button')
34306                 .attr('tabindex', -1)
34307                 .on('click', toggle)
34308                 .call(iD.svg.Icon('#icon-layers', 'light'))
34309                 .call(tooltip),
34310             shown = false;
34311
34312
34313         /* opacity switcher */
34314
34315         var opa = content.append('div')
34316                 .attr('class', 'opacity-options-wrapper');
34317
34318         opa.append('h4')
34319             .text(t('background.title'));
34320
34321         var opacityList = opa.append('ul')
34322             .attr('class', 'opacity-options');
34323
34324         opacityList.selectAll('div.opacity')
34325             .data(opacities)
34326             .enter()
34327             .append('li')
34328             .attr('data-original-title', function(d) {
34329                 return t('background.percent_brightness', { opacity: (d * 100) });
34330             })
34331             .on('click.set-opacity', setOpacity)
34332             .html('<div class="select-box"></div>')
34333             .call(bootstrap.tooltip()
34334                 .placement('left'))
34335             .append('div')
34336             .attr('class', 'opacity')
34337             .style('opacity', function(d) { return 1.25 - d; });
34338
34339
34340         /* background switcher */
34341
34342         var backgroundList = content.append('ul')
34343             .attr('class', 'layer-list');
34344
34345         var custom = backgroundList.append('li')
34346             .attr('class', 'custom_layer')
34347             .datum(iD.BackgroundSource.Custom());
34348
34349         custom.append('button')
34350             .attr('class', 'layer-browse')
34351             .call(bootstrap.tooltip()
34352                 .title(t('background.custom_button'))
34353                 .placement('left'))
34354             .on('click', editCustom)
34355             .call(iD.svg.Icon('#icon-search'));
34356
34357         var label = custom.append('label');
34358
34359         label.append('input')
34360             .attr('type', 'radio')
34361             .attr('name', 'layers')
34362             .on('change', function () {
34363                 if (customTemplate) {
34364                     setCustom(customTemplate);
34365                 } else {
34366                     editCustom();
34367                 }
34368             });
34369
34370         label.append('span')
34371             .text(t('background.custom'));
34372
34373         content.append('div')
34374             .attr('class', 'imagery-faq')
34375             .append('a')
34376             .attr('target', '_blank')
34377             .attr('tabindex', -1)
34378             .call(iD.svg.Icon('#icon-out-link', 'inline'))
34379             .attr('href', 'https://github.com/openstreetmap/iD/blob/master/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
34380             .append('span')
34381             .text(t('background.imagery_source_faq'));
34382
34383         var overlayList = content.append('ul')
34384             .attr('class', 'layer-list');
34385
34386         var controls = content.append('div')
34387             .attr('class', 'controls-list');
34388
34389
34390         /* minimap toggle */
34391
34392         var minimapLabel = controls
34393             .append('label')
34394             .call(bootstrap.tooltip()
34395                 .html(true)
34396                 .title(iD.ui.tooltipHtml(t('background.minimap.tooltip'), '/'))
34397                 .placement('top')
34398             );
34399
34400         minimapLabel.classed('minimap-toggle', true)
34401             .append('input')
34402             .attr('type', 'checkbox')
34403             .on('change', function() {
34404                 iD.ui.MapInMap.toggle();
34405                 d3.event.preventDefault();
34406             });
34407
34408         minimapLabel.append('span')
34409             .text(t('background.minimap.description'));
34410
34411
34412         /* imagery offset controls */
34413
34414         var adjustments = content.append('div')
34415             .attr('class', 'adjustments');
34416
34417         adjustments.append('a')
34418             .text(t('background.fix_misalignment'))
34419             .attr('href', '#')
34420             .classed('hide-toggle', true)
34421             .classed('expanded', false)
34422             .on('click', function() {
34423                 var exp = d3.select(this).classed('expanded');
34424                 nudgeContainer.style('display', exp ? 'none' : 'block');
34425                 d3.select(this).classed('expanded', !exp);
34426                 d3.event.preventDefault();
34427             });
34428
34429         var nudgeContainer = adjustments.append('div')
34430             .attr('class', 'nudge-container cf')
34431             .style('display', 'none');
34432
34433         nudgeContainer.append('div')
34434             .attr('class', 'nudge-instructions')
34435             .text(t('background.offset'));
34436
34437         var nudgeRect = nudgeContainer.append('div')
34438             .attr('class', 'nudge-outer-rect')
34439             .on('mousedown', dragOffset);
34440
34441         nudgeRect.append('div')
34442             .attr('class', 'nudge-inner-rect')
34443             .append('input')
34444             .on('change', inputOffset)
34445             .on('mousedown', function() {
34446                 d3.event.stopPropagation();
34447             });
34448
34449         nudgeContainer.append('div')
34450             .selectAll('button')
34451             .data(directions).enter()
34452             .append('button')
34453             .attr('class', function(d) { return d[0] + ' nudge'; })
34454             .on('mousedown', function(d) {
34455                 buttonOffset(d[1]);
34456             });
34457
34458         nudgeContainer.append('button')
34459             .attr('title', t('background.reset'))
34460             .attr('class', 'nudge-reset disabled')
34461             .on('click', resetOffset)
34462             .call(iD.svg.Icon('#icon-undo'));
34463
34464         context.map()
34465             .on('move.background-update', _.debounce(update, 1000));
34466
34467         context.background()
34468             .on('change.background-update', update);
34469
34470
34471         update();
34472         setOpacity(opacityDefault);
34473
34474         var keybinding = d3.keybinding('background')
34475             .on(key, toggle)
34476             .on(iD.ui.cmd('⌘B'), quickSwitch)
34477             .on('F', hide)
34478             .on('H', hide);
34479
34480         d3.select(document)
34481             .call(keybinding);
34482
34483         context.surface().on('mousedown.background-outside', hide);
34484         context.container().on('mousedown.background-outside', hide);
34485     }
34486
34487     return background;
34488 };
34489 // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
34490 // For example, ⌘Z -> Ctrl+Z
34491 iD.ui.cmd = function(code) {
34492     if (iD.detect().os === 'mac') {
34493         return code;
34494     }
34495
34496     if (iD.detect().os === 'win') {
34497         if (code === '⌘⇧Z') return 'Ctrl+Y';
34498     }
34499
34500     var result = '',
34501         replacements = {
34502             '⌘': 'Ctrl',
34503             '⇧': 'Shift',
34504             '⌥': 'Alt',
34505             '⌫': 'Backspace',
34506             '⌦': 'Delete'
34507         };
34508
34509     for (var i = 0; i < code.length; i++) {
34510         if (code[i] in replacements) {
34511             result += replacements[code[i]] + '+';
34512         } else {
34513             result += code[i];
34514         }
34515     }
34516
34517     return result;
34518 };
34519 iD.ui.Commit = function(context) {
34520     var dispatch = d3.dispatch('cancel', 'save');
34521
34522     function commit(selection) {
34523         var changes = context.history().changes(),
34524             summary = context.history().difference().summary();
34525
34526         function zoomToEntity(change) {
34527             var entity = change.entity;
34528             if (change.changeType !== 'deleted' &&
34529                 context.graph().entity(entity.id).geometry(context.graph()) !== 'vertex') {
34530                 context.map().zoomTo(entity);
34531                 context.surface().selectAll(
34532                     iD.util.entityOrMemberSelector([entity.id], context.graph()))
34533                     .classed('hover', true);
34534             }
34535         }
34536
34537         var header = selection.append('div')
34538             .attr('class', 'header fillL');
34539
34540         header.append('h3')
34541             .text(t('commit.title'));
34542
34543         var body = selection.append('div')
34544             .attr('class', 'body');
34545
34546
34547         // Comment Section
34548         var commentSection = body.append('div')
34549             .attr('class', 'modal-section form-field commit-form');
34550
34551         commentSection.append('label')
34552             .attr('class', 'form-label')
34553             .text(t('commit.message_label'));
34554
34555         var commentField = commentSection.append('textarea')
34556             .attr('placeholder', t('commit.description_placeholder'))
34557             .attr('maxlength', 255)
34558             .property('value', context.storage('comment') || '')
34559             .on('input.save', checkComment)
34560             .on('change.save', checkComment)
34561             .on('blur.save', function() {
34562                 context.storage('comment', this.value);
34563             });
34564
34565         function checkComment() {
34566             d3.selectAll('.save-section .save-button')
34567                 .attr('disabled', (this.value.length ? null : true));
34568
34569             var googleWarning = clippyArea
34570                .html('')
34571                .selectAll('a')
34572                .data(this.value.match(/google/i) ? [true] : []);
34573
34574             googleWarning.exit().remove();
34575
34576             googleWarning.enter()
34577                .append('a')
34578                .attr('target', '_blank')
34579                .attr('tabindex', -1)
34580                .call(iD.svg.Icon('#icon-alert', 'inline'))
34581                .attr('href', t('commit.google_warning_link'))
34582                .append('span')
34583                .text(t('commit.google_warning'));
34584         }
34585
34586         commentField.node().select();
34587
34588         context.connection().userChangesets(function (err, changesets) {
34589             if (err) return;
34590
34591             var comments = [];
34592
34593             for (var i = 0; i < changesets.length; i++) {
34594                 if (changesets[i].tags.comment) {
34595                     comments.push({
34596                         title: changesets[i].tags.comment,
34597                         value: changesets[i].tags.comment
34598                     });
34599                 }
34600             }
34601
34602             commentField.call(d3.combobox().caseSensitive(true).data(comments));
34603         });
34604
34605         var clippyArea = commentSection.append('div')
34606             .attr('class', 'clippy-area');
34607
34608
34609         var changeSetInfo = commentSection.append('div')
34610             .attr('class', 'changeset-info');
34611
34612         changeSetInfo.append('a')
34613             .attr('target', '_blank')
34614             .attr('tabindex', -1)
34615             .call(iD.svg.Icon('#icon-out-link', 'inline'))
34616             .attr('href', t('commit.about_changeset_comments_link'))
34617             .append('span')
34618             .text(t('commit.about_changeset_comments'));
34619
34620         // Warnings
34621         var warnings = body.selectAll('div.warning-section')
34622             .data([context.history().validate(changes)])
34623             .enter()
34624             .append('div')
34625             .attr('class', 'modal-section warning-section fillL2')
34626             .style('display', function(d) { return _.isEmpty(d) ? 'none' : null; })
34627             .style('background', '#ffb');
34628
34629         warnings.append('h3')
34630             .text(t('commit.warnings'));
34631
34632         var warningLi = warnings.append('ul')
34633             .attr('class', 'changeset-list')
34634             .selectAll('li')
34635             .data(function(d) { return d; })
34636             .enter()
34637             .append('li')
34638             .style()
34639             .on('mouseover', mouseover)
34640             .on('mouseout', mouseout)
34641             .on('click', warningClick);
34642
34643         warningLi
34644             .call(iD.svg.Icon('#icon-alert', 'pre-text'));
34645
34646         warningLi
34647             .append('strong').text(function(d) {
34648                 return d.message;
34649             });
34650
34651         warningLi.filter(function(d) { return d.tooltip; })
34652             .call(bootstrap.tooltip()
34653                 .title(function(d) { return d.tooltip; })
34654                 .placement('top')
34655             );
34656
34657
34658         // Upload Explanation
34659         var saveSection = body.append('div')
34660             .attr('class','modal-section save-section fillL cf');
34661
34662         var prose = saveSection.append('p')
34663             .attr('class', 'commit-info')
34664             .html(t('commit.upload_explanation'));
34665
34666         context.connection().userDetails(function(err, user) {
34667             if (err) return;
34668
34669             var userLink = d3.select(document.createElement('div'));
34670
34671             if (user.image_url) {
34672                 userLink.append('img')
34673                     .attr('src', user.image_url)
34674                     .attr('class', 'icon pre-text user-icon');
34675             }
34676
34677             userLink.append('a')
34678                 .attr('class','user-info')
34679                 .text(user.display_name)
34680                 .attr('href', context.connection().userURL(user.display_name))
34681                 .attr('tabindex', -1)
34682                 .attr('target', '_blank');
34683
34684             prose.html(t('commit.upload_explanation_with_user', {user: userLink.html()}));
34685         });
34686
34687
34688         // Buttons
34689         var buttonSection = saveSection.append('div')
34690             .attr('class','buttons fillL cf');
34691
34692         var cancelButton = buttonSection.append('button')
34693             .attr('class', 'secondary-action col5 button cancel-button')
34694             .on('click.cancel', function() { dispatch.cancel(); });
34695
34696         cancelButton.append('span')
34697             .attr('class', 'label')
34698             .text(t('commit.cancel'));
34699
34700         var saveButton = buttonSection.append('button')
34701             .attr('class', 'action col5 button save-button')
34702             .attr('disabled', function() {
34703                 var n = d3.select('.commit-form textarea').node();
34704                 return (n && n.value.length) ? null : true;
34705             })
34706             .on('click.save', function() {
34707                 dispatch.save({
34708                     comment: commentField.node().value
34709                 });
34710             });
34711
34712         saveButton.append('span')
34713             .attr('class', 'label')
34714             .text(t('commit.save'));
34715
34716
34717         // Changes
34718         var changeSection = body.selectAll('div.commit-section')
34719             .data([0])
34720             .enter()
34721             .append('div')
34722             .attr('class', 'commit-section modal-section fillL2');
34723
34724         changeSection.append('h3')
34725             .text(t('commit.changes', {count: summary.length}));
34726
34727         var li = changeSection.append('ul')
34728             .attr('class', 'changeset-list')
34729             .selectAll('li')
34730             .data(summary)
34731             .enter()
34732             .append('li')
34733             .on('mouseover', mouseover)
34734             .on('mouseout', mouseout)
34735             .on('click', zoomToEntity);
34736
34737         li.each(function(d) {
34738             d3.select(this)
34739                 .call(iD.svg.Icon('#icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
34740         });
34741
34742         li.append('span')
34743             .attr('class', 'change-type')
34744             .text(function(d) {
34745                 return t('commit.' + d.changeType) + ' ';
34746             });
34747
34748         li.append('strong')
34749             .attr('class', 'entity-type')
34750             .text(function(d) {
34751                 return context.presets().match(d.entity, d.graph).name();
34752             });
34753
34754         li.append('span')
34755             .attr('class', 'entity-name')
34756             .text(function(d) {
34757                 var name = iD.util.displayName(d.entity) || '',
34758                     string = '';
34759                 if (name !== '') string += ':';
34760                 return string += ' ' + name;
34761             });
34762
34763         li.style('opacity', 0)
34764             .transition()
34765             .style('opacity', 1);
34766
34767
34768         function mouseover(d) {
34769             if (d.entity) {
34770                 context.surface().selectAll(
34771                     iD.util.entityOrMemberSelector([d.entity.id], context.graph())
34772                 ).classed('hover', true);
34773             }
34774         }
34775
34776         function mouseout() {
34777             context.surface().selectAll('.hover')
34778                 .classed('hover', false);
34779         }
34780
34781         function warningClick(d) {
34782             if (d.entity) {
34783                 context.map().zoomTo(d.entity);
34784                 context.enter(
34785                     iD.modes.Select(context, [d.entity.id])
34786                         .suppressMenu(true));
34787             }
34788         }
34789
34790         // Call checkComment off the bat, in case a changeset
34791         // comment is recovered from localStorage
34792         commentField.trigger('input');
34793     }
34794
34795     return d3.rebind(commit, dispatch, 'on');
34796 };
34797 iD.ui.confirm = function(selection) {
34798     var modal = iD.ui.modal(selection);
34799
34800     modal.select('.modal')
34801         .classed('modal-alert', true);
34802
34803     var section = modal.select('.content');
34804
34805     section.append('div')
34806         .attr('class', 'modal-section header');
34807
34808     section.append('div')
34809         .attr('class', 'modal-section message-text');
34810
34811     var buttons = section.append('div')
34812         .attr('class', 'modal-section buttons cf');
34813
34814     modal.okButton = function() {
34815         buttons
34816             .append('button')
34817             .attr('class', 'action col4')
34818             .on('click.confirm', function() {
34819                 modal.remove();
34820             })
34821             .text(t('confirm.okay'));
34822
34823         return modal;
34824     };
34825
34826     return modal;
34827 };
34828 iD.ui.Conflicts = function(context) {
34829     var dispatch = d3.dispatch('download', 'cancel', 'save'),
34830         list;
34831
34832     function conflicts(selection) {
34833         var header = selection
34834             .append('div')
34835             .attr('class', 'header fillL');
34836
34837         header
34838             .append('button')
34839             .attr('class', 'fr')
34840             .on('click', function() { dispatch.cancel(); })
34841             .call(iD.svg.Icon('#icon-close'));
34842
34843         header
34844             .append('h3')
34845             .text(t('save.conflict.header'));
34846
34847         var body = selection
34848             .append('div')
34849             .attr('class', 'body fillL');
34850
34851         body
34852             .append('div')
34853             .attr('class', 'conflicts-help')
34854             .text(t('save.conflict.help'))
34855             .append('a')
34856             .attr('class', 'conflicts-download')
34857             .text(t('save.conflict.download_changes'))
34858             .on('click.download', function() { dispatch.download(); });
34859
34860         body
34861             .append('div')
34862             .attr('class', 'conflict-container fillL3')
34863             .call(showConflict, 0);
34864
34865         body
34866             .append('div')
34867             .attr('class', 'conflicts-done')
34868             .attr('opacity', 0)
34869             .style('display', 'none')
34870             .text(t('save.conflict.done'));
34871
34872         var buttons = body
34873             .append('div')
34874             .attr('class','buttons col12 joined conflicts-buttons');
34875
34876         buttons
34877             .append('button')
34878             .attr('disabled', list.length > 1)
34879             .attr('class', 'action conflicts-button col6')
34880             .text(t('save.title'))
34881             .on('click.try_again', function() { dispatch.save(); });
34882
34883         buttons
34884             .append('button')
34885             .attr('class', 'secondary-action conflicts-button col6')
34886             .text(t('confirm.cancel'))
34887             .on('click.cancel', function() { dispatch.cancel(); });
34888     }
34889
34890
34891     function showConflict(selection, index) {
34892         if (index < 0 || index >= list.length) return;
34893
34894         var parent = d3.select(selection.node().parentNode);
34895
34896         // enable save button if this is the last conflict being reviewed..
34897         if (index === list.length - 1) {
34898             window.setTimeout(function() {
34899                 parent.select('.conflicts-button')
34900                     .attr('disabled', null);
34901
34902                 parent.select('.conflicts-done')
34903                     .transition()
34904                     .attr('opacity', 1)
34905                     .style('display', 'block');
34906             }, 250);
34907         }
34908
34909         var item = selection
34910             .selectAll('.conflict')
34911             .data([list[index]]);
34912
34913         var enter = item.enter()
34914             .append('div')
34915             .attr('class', 'conflict');
34916
34917         enter
34918             .append('h4')
34919             .attr('class', 'conflict-count')
34920             .text(t('save.conflict.count', { num: index + 1, total: list.length }));
34921
34922         enter
34923             .append('a')
34924             .attr('class', 'conflict-description')
34925             .attr('href', '#')
34926             .text(function(d) { return d.name; })
34927             .on('click', function(d) {
34928                 zoomToEntity(d.id);
34929                 d3.event.preventDefault();
34930             });
34931
34932         var details = enter
34933             .append('div')
34934             .attr('class', 'conflict-detail-container');
34935
34936         details
34937             .append('ul')
34938             .attr('class', 'conflict-detail-list')
34939             .selectAll('li')
34940             .data(function(d) { return d.details || []; })
34941             .enter()
34942             .append('li')
34943             .attr('class', 'conflict-detail-item')
34944             .html(function(d) { return d; });
34945
34946         details
34947             .append('div')
34948             .attr('class', 'conflict-choices')
34949             .call(addChoices);
34950
34951         details
34952             .append('div')
34953             .attr('class', 'conflict-nav-buttons joined cf')
34954             .selectAll('button')
34955             .data(['previous', 'next'])
34956             .enter()
34957             .append('button')
34958             .text(function(d) { return t('save.conflict.' + d); })
34959             .attr('class', 'conflict-nav-button action col6')
34960             .attr('disabled', function(d, i) {
34961                 return (i === 0 && index === 0) ||
34962                     (i === 1 && index === list.length - 1) || null;
34963             })
34964             .on('click', function(d, i) {
34965                 var container = parent.select('.conflict-container'),
34966                 sign = (i === 0 ? -1 : 1);
34967
34968                 container
34969                     .selectAll('.conflict')
34970                     .remove();
34971
34972                 container
34973                     .call(showConflict, index + sign);
34974
34975                 d3.event.preventDefault();
34976             });
34977
34978         item.exit()
34979             .remove();
34980
34981     }
34982
34983     function addChoices(selection) {
34984         var choices = selection
34985             .append('ul')
34986             .attr('class', 'layer-list')
34987             .selectAll('li')
34988             .data(function(d) { return d.choices || []; });
34989
34990         var enter = choices.enter()
34991             .append('li')
34992             .attr('class', 'layer');
34993
34994         var label = enter
34995             .append('label');
34996
34997         label
34998             .append('input')
34999             .attr('type', 'radio')
35000             .attr('name', function(d) { return d.id; })
35001             .on('change', function(d, i) {
35002                 var ul = this.parentNode.parentNode.parentNode;
35003                 ul.__data__.chosen = i;
35004                 choose(ul, d);
35005             });
35006
35007         label
35008             .append('span')
35009             .text(function(d) { return d.text; });
35010
35011         choices
35012             .each(function(d, i) {
35013                 var ul = this.parentNode;
35014                 if (ul.__data__.chosen === i) choose(ul, d);
35015             });
35016     }
35017
35018     function choose(ul, datum) {
35019         if (d3.event) d3.event.preventDefault();
35020
35021         d3.select(ul)
35022             .selectAll('li')
35023             .classed('active', function(d) { return d === datum; })
35024             .selectAll('input')
35025             .property('checked', function(d) { return d === datum; });
35026
35027         var extent = iD.geo.Extent(),
35028             entity;
35029
35030         entity = context.graph().hasEntity(datum.id);
35031         if (entity) extent._extend(entity.extent(context.graph()));
35032
35033         datum.action();
35034
35035         entity = context.graph().hasEntity(datum.id);
35036         if (entity) extent._extend(entity.extent(context.graph()));
35037
35038         zoomToEntity(datum.id, extent);
35039     }
35040
35041     function zoomToEntity(id, extent) {
35042         context.surface().selectAll('.hover')
35043             .classed('hover', false);
35044
35045         var entity = context.graph().hasEntity(id);
35046         if (entity) {
35047             if (extent) {
35048                 context.map().trimmedExtent(extent);
35049             } else {
35050                 context.map().zoomTo(entity);
35051             }
35052             context.surface().selectAll(
35053                 iD.util.entityOrMemberSelector([entity.id], context.graph()))
35054                 .classed('hover', true);
35055         }
35056     }
35057
35058
35059     // The conflict list should be an array of objects like:
35060     // {
35061     //     id: id,
35062     //     name: entityName(local),
35063     //     details: merge.conflicts(),
35064     //     chosen: 1,
35065     //     choices: [
35066     //         choice(id, keepMine, forceLocal),
35067     //         choice(id, keepTheirs, forceRemote)
35068     //     ]
35069     // }
35070     conflicts.list = function(_) {
35071         if (!arguments.length) return list;
35072         list = _;
35073         return conflicts;
35074     };
35075
35076     return d3.rebind(conflicts, dispatch, 'on');
35077 };
35078 iD.ui.Contributors = function(context) {
35079     var debouncedUpdate = _.debounce(function() { update(); }, 1000),
35080         limit = 4,
35081         hidden = false,
35082         wrap = d3.select(null);
35083
35084     function update() {
35085         var users = {},
35086             entities = context.intersects(context.map().extent());
35087
35088         entities.forEach(function(entity) {
35089             if (entity && entity.user) users[entity.user] = true;
35090         });
35091
35092         var u = Object.keys(users),
35093             subset = u.slice(0, u.length > limit ? limit - 1 : limit);
35094
35095         wrap.html('')
35096             .call(iD.svg.Icon('#icon-nearby', 'pre-text light'));
35097
35098         var userList = d3.select(document.createElement('span'));
35099
35100         userList.selectAll()
35101             .data(subset)
35102             .enter()
35103             .append('a')
35104             .attr('class', 'user-link')
35105             .attr('href', function(d) { return context.connection().userURL(d); })
35106             .attr('target', '_blank')
35107             .attr('tabindex', -1)
35108             .text(String);
35109
35110         if (u.length > limit) {
35111             var count = d3.select(document.createElement('span'));
35112
35113             count.append('a')
35114                 .attr('target', '_blank')
35115                 .attr('tabindex', -1)
35116                 .attr('href', function() {
35117                     return context.connection().changesetsURL(context.map().center(), context.map().zoom());
35118                 })
35119                 .text(u.length - limit + 1);
35120
35121             wrap.append('span')
35122                 .html(t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
35123
35124         } else {
35125             wrap.append('span')
35126                 .html(t('contributors.list', { users: userList.html() }));
35127         }
35128
35129         if (!u.length) {
35130             hidden = true;
35131             wrap
35132                 .transition()
35133                 .style('opacity', 0);
35134
35135         } else if (hidden) {
35136             wrap
35137                 .transition()
35138                 .style('opacity', 1);
35139         }
35140     }
35141
35142     return function(selection) {
35143         wrap = selection;
35144         update();
35145
35146         context.connection().on('loaded.contributors', debouncedUpdate);
35147         context.map().on('move.contributors', debouncedUpdate);
35148     };
35149 };
35150 iD.ui.Disclosure = function() {
35151     var dispatch = d3.dispatch('toggled'),
35152         title,
35153         expanded = false,
35154         content = function () {};
35155
35156     var disclosure = function(selection) {
35157         var $link = selection.selectAll('.hide-toggle')
35158             .data([0]);
35159
35160         $link.enter().append('a')
35161             .attr('href', '#')
35162             .attr('class', 'hide-toggle');
35163
35164         $link.text(title)
35165             .on('click', toggle)
35166             .classed('expanded', expanded);
35167
35168         var $body = selection.selectAll('div')
35169             .data([0]);
35170
35171         $body.enter().append('div');
35172
35173         $body.classed('hide', !expanded)
35174             .call(content);
35175
35176         function toggle() {
35177             expanded = !expanded;
35178             $link.classed('expanded', expanded);
35179             $body.call(iD.ui.Toggle(expanded));
35180             dispatch.toggled(expanded);
35181         }
35182     };
35183
35184     disclosure.title = function(_) {
35185         if (!arguments.length) return title;
35186         title = _;
35187         return disclosure;
35188     };
35189
35190     disclosure.expanded = function(_) {
35191         if (!arguments.length) return expanded;
35192         expanded = _;
35193         return disclosure;
35194     };
35195
35196     disclosure.content = function(_) {
35197         if (!arguments.length) return content;
35198         content = _;
35199         return disclosure;
35200     };
35201
35202     return d3.rebind(disclosure, dispatch, 'on');
35203 };
35204 iD.ui.EntityEditor = function(context) {
35205     var dispatch = d3.dispatch('choose'),
35206         state = 'select',
35207         coalesceChanges = false,
35208         modified = false,
35209         base,
35210         id,
35211         preset,
35212         reference;
35213
35214     var presetEditor = iD.ui.preset(context)
35215         .on('change', changeTags);
35216     var rawTagEditor = iD.ui.RawTagEditor(context)
35217         .on('change', changeTags);
35218
35219     function entityEditor(selection) {
35220         var entity = context.entity(id),
35221             tags = _.clone(entity.tags);
35222
35223         var $header = selection.selectAll('.header')
35224             .data([0]);
35225
35226         // Enter
35227         var $enter = $header.enter().append('div')
35228             .attr('class', 'header fillL cf');
35229
35230         $enter.append('button')
35231             .attr('class', 'fl preset-reset preset-choose')
35232             .append('span')
35233             .html('&#9668;');
35234
35235         $enter.append('button')
35236             .attr('class', 'fr preset-close')
35237             .call(iD.svg.Icon(modified ? '#icon-apply' : '#icon-close'));
35238
35239         $enter.append('h3');
35240
35241         // Update
35242         $header.select('h3')
35243             .text(t('inspector.edit'));
35244
35245         $header.select('.preset-close')
35246             .on('click', function() {
35247                 context.enter(iD.modes.Browse(context));
35248             });
35249
35250         var $body = selection.selectAll('.inspector-body')
35251             .data([0]);
35252
35253         // Enter
35254         $enter = $body.enter().append('div')
35255             .attr('class', 'inspector-body');
35256
35257         $enter.append('div')
35258             .attr('class', 'preset-list-item inspector-inner')
35259             .append('div')
35260             .attr('class', 'preset-list-button-wrap')
35261             .append('button')
35262             .attr('class', 'preset-list-button preset-reset')
35263             .call(bootstrap.tooltip()
35264                 .title(t('inspector.back_tooltip'))
35265                 .placement('bottom'))
35266             .append('div')
35267             .attr('class', 'label');
35268
35269         $body.select('.preset-list-button-wrap')
35270             .call(reference.button);
35271
35272         $body.select('.preset-list-item')
35273             .call(reference.body);
35274
35275         $enter.append('div')
35276             .attr('class', 'inspector-border inspector-preset');
35277
35278         $enter.append('div')
35279             .attr('class', 'inspector-border raw-tag-editor inspector-inner');
35280
35281         $enter.append('div')
35282             .attr('class', 'inspector-border raw-member-editor inspector-inner');
35283
35284         $enter.append('div')
35285             .attr('class', 'raw-membership-editor inspector-inner');
35286
35287         selection.selectAll('.preset-reset')
35288             .on('click', function() {
35289                 dispatch.choose(preset);
35290             });
35291
35292         // Update
35293         $body.select('.preset-list-item button')
35294             .call(iD.ui.PresetIcon()
35295                 .geometry(context.geometry(id))
35296                 .preset(preset));
35297
35298         $body.select('.preset-list-item .label')
35299             .text(preset.name());
35300
35301         $body.select('.inspector-preset')
35302             .call(presetEditor
35303                 .preset(preset)
35304                 .entityID(id)
35305                 .tags(tags)
35306                 .state(state));
35307
35308         $body.select('.raw-tag-editor')
35309             .call(rawTagEditor
35310                 .preset(preset)
35311                 .entityID(id)
35312                 .tags(tags)
35313                 .state(state));
35314
35315         if (entity.type === 'relation') {
35316             $body.select('.raw-member-editor')
35317                 .style('display', 'block')
35318                 .call(iD.ui.RawMemberEditor(context)
35319                     .entityID(id));
35320         } else {
35321             $body.select('.raw-member-editor')
35322                 .style('display', 'none');
35323         }
35324
35325         $body.select('.raw-membership-editor')
35326             .call(iD.ui.RawMembershipEditor(context)
35327                 .entityID(id));
35328
35329         function historyChanged() {
35330             if (state === 'hide') return;
35331
35332             var entity = context.hasEntity(id),
35333                 graph = context.graph();
35334             if (!entity) return;
35335
35336             entityEditor.preset(context.presets().match(entity, graph));
35337             entityEditor.modified(base !== graph);
35338             entityEditor(selection);
35339         }
35340
35341         context.history()
35342             .on('change.entity-editor', historyChanged);
35343     }
35344
35345     function clean(o) {
35346
35347         function cleanVal(k, v) {
35348             function keepSpaces(k) {
35349                 var whitelist = ['opening_hours', 'service_times', 'collection_times',
35350                     'operating_times', 'smoking_hours', 'happy_hours'];
35351                 return _.some(whitelist, function(s) { return k.indexOf(s) !== -1; });
35352             }
35353
35354             var blacklist = ['description', 'note', 'fixme'];
35355             if (_.some(blacklist, function(s) { return k.indexOf(s) !== -1; })) return v;
35356
35357             var cleaned = v.split(';')
35358                 .map(function(s) { return s.trim(); })
35359                 .join(keepSpaces(k) ? '; ' : ';');
35360
35361             // The code below is not intended to validate websites and emails.
35362             // It is only intended to prevent obvious copy-paste errors. (#2323)
35363
35364             // clean website- and email-like tags
35365             if (k.indexOf('website') !== -1 ||
35366                 k.indexOf('email') !== -1 ||
35367                 cleaned.indexOf('http') === 0) {
35368                 cleaned = cleaned
35369                     .replace(/[\u200B-\u200F\uFEFF]/g, '');  // strip LRM and other zero width chars
35370
35371             }
35372
35373             return cleaned;
35374         }
35375
35376         var out = {}, k, v;
35377         for (k in o) {
35378             if (k && (v = o[k]) !== undefined) {
35379                 out[k] = cleanVal(k, v);
35380             }
35381         }
35382         return out;
35383     }
35384
35385     // Tag changes that fire on input can all get coalesced into a single
35386     // history operation when the user leaves the field.  #2342
35387     function changeTags(changed, onInput) {
35388         var entity = context.entity(id),
35389             annotation = t('operations.change_tags.annotation'),
35390             tags = _.extend({}, entity.tags, changed);
35391
35392         if (!onInput) {
35393             tags = clean(tags);
35394         }
35395         if (!_.isEqual(entity.tags, tags)) {
35396             if (coalesceChanges) {
35397                 context.overwrite(iD.actions.ChangeTags(id, tags), annotation);
35398             } else {
35399                 context.perform(iD.actions.ChangeTags(id, tags), annotation);
35400                 coalesceChanges = !!onInput;
35401             }
35402         }
35403     }
35404
35405     entityEditor.modified = function(_) {
35406         if (!arguments.length) return modified;
35407         modified = _;
35408         d3.selectAll('button.preset-close use')
35409             .attr('xlink:href', (modified ? '#icon-apply' : '#icon-close'));
35410     };
35411
35412     entityEditor.state = function(_) {
35413         if (!arguments.length) return state;
35414         state = _;
35415         return entityEditor;
35416     };
35417
35418     entityEditor.entityID = function(_) {
35419         if (!arguments.length) return id;
35420         id = _;
35421         base = context.graph();
35422         entityEditor.preset(context.presets().match(context.entity(id), base));
35423         entityEditor.modified(false);
35424         coalesceChanges = false;
35425         return entityEditor;
35426     };
35427
35428     entityEditor.preset = function(_) {
35429         if (!arguments.length) return preset;
35430         if (_ !== preset) {
35431             preset = _;
35432             reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context)
35433                 .showing(false);
35434         }
35435         return entityEditor;
35436     };
35437
35438     return d3.rebind(entityEditor, dispatch, 'on');
35439 };
35440 iD.ui.FeatureInfo = function(context) {
35441     function update(selection) {
35442         var features = context.features(),
35443             stats = features.stats(),
35444             count = 0,
35445             hiddenList = _.compact(_.map(features.hidden(), function(k) {
35446                 if (stats[k]) {
35447                     count += stats[k];
35448                     return String(stats[k]) + ' ' + t('feature.' + k + '.description');
35449                 }
35450             }));
35451
35452         selection.html('');
35453
35454         if (hiddenList.length) {
35455             var tooltip = bootstrap.tooltip()
35456                     .placement('top')
35457                     .html(true)
35458                     .title(function() {
35459                         return iD.ui.tooltipHtml(hiddenList.join('<br/>'));
35460                     });
35461
35462             var warning = selection.append('a')
35463                 .attr('href', '#')
35464                 .attr('tabindex', -1)
35465                 .html(t('feature_info.hidden_warning', { count: count }))
35466                 .call(tooltip)
35467                 .on('click', function() {
35468                     tooltip.hide(warning);
35469                     // open map data panel?
35470                     d3.event.preventDefault();
35471                 });
35472         }
35473
35474         selection
35475             .classed('hide', !hiddenList.length);
35476     }
35477
35478     return function(selection) {
35479         update(selection);
35480
35481         context.features().on('change.feature_info', function() {
35482             update(selection);
35483         });
35484     };
35485 };
35486 iD.ui.FeatureList = function(context) {
35487     var geocodeResults;
35488
35489     function featureList(selection) {
35490         var header = selection.append('div')
35491             .attr('class', 'header fillL cf');
35492
35493         header.append('h3')
35494             .text(t('inspector.feature_list'));
35495
35496         function keypress() {
35497             var q = search.property('value'),
35498                 items = list.selectAll('.feature-list-item');
35499             if (d3.event.keyCode === 13 && q.length && items.size()) {
35500                 click(items.datum());
35501             }
35502         }
35503
35504         function inputevent() {
35505             geocodeResults = undefined;
35506             drawList();
35507         }
35508
35509         var searchWrap = selection.append('div')
35510             .attr('class', 'search-header');
35511
35512         var search = searchWrap.append('input')
35513             .attr('placeholder', t('inspector.search'))
35514             .attr('type', 'search')
35515             .on('keypress', keypress)
35516             .on('input', inputevent);
35517
35518         searchWrap
35519             .call(iD.svg.Icon('#icon-search', 'pre-text'));
35520
35521         var listWrap = selection.append('div')
35522             .attr('class', 'inspector-body');
35523
35524         var list = listWrap.append('div')
35525             .attr('class', 'feature-list cf');
35526
35527         context
35528             .on('exit.feature-list', clearSearch);
35529         context.map()
35530             .on('drawn.feature-list', mapDrawn);
35531
35532         function clearSearch() {
35533             search.property('value', '');
35534             drawList();
35535         }
35536
35537         function mapDrawn(e) {
35538             if (e.full) {
35539                 drawList();
35540             }
35541         }
35542
35543         function features() {
35544             var entities = {},
35545                 result = [],
35546                 graph = context.graph(),
35547                 q = search.property('value').toLowerCase();
35548
35549             if (!q) return result;
35550
35551             var idMatch = q.match(/^([nwr])([0-9]+)$/);
35552
35553             if (idMatch) {
35554                 result.push({
35555                     id: idMatch[0],
35556                     geometry: idMatch[1] === 'n' ? 'point' : idMatch[1] === 'w' ? 'line' : 'relation',
35557                     type: idMatch[1] === 'n' ? t('inspector.node') : idMatch[1] === 'w' ? t('inspector.way') : t('inspector.relation'),
35558                     name: idMatch[2]
35559                 });
35560             }
35561
35562             var locationMatch = sexagesimal.pair(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
35563
35564             if (locationMatch) {
35565                 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
35566                 result.push({
35567                     id: -1,
35568                     geometry: 'point',
35569                     type: t('inspector.location'),
35570                     name: loc[0].toFixed(6) + ', ' + loc[1].toFixed(6),
35571                     location: loc
35572                 });
35573             }
35574
35575             function addEntity(entity) {
35576                 if (entity.id in entities || result.length > 200)
35577                     return;
35578
35579                 entities[entity.id] = true;
35580
35581                 var name = iD.util.displayName(entity) || '';
35582                 if (name.toLowerCase().indexOf(q) >= 0) {
35583                     result.push({
35584                         id: entity.id,
35585                         entity: entity,
35586                         geometry: context.geometry(entity.id),
35587                         type: context.presets().match(entity, graph).name(),
35588                         name: name
35589                     });
35590                 }
35591
35592                 graph.parentRelations(entity).forEach(function(parent) {
35593                     addEntity(parent);
35594                 });
35595             }
35596
35597             var visible = context.surface().selectAll('.point, .line, .area')[0];
35598             for (var i = 0; i < visible.length && result.length <= 200; i++) {
35599                 addEntity(visible[i].__data__);
35600             }
35601
35602             (geocodeResults || []).forEach(function(d) {
35603                 // https://github.com/openstreetmap/iD/issues/1890
35604                 if (d.osm_type && d.osm_id) {
35605                     result.push({
35606                         id: iD.Entity.id.fromOSM(d.osm_type, d.osm_id),
35607                         geometry: d.osm_type === 'relation' ? 'relation' : d.osm_type === 'way' ? 'line' : 'point',
35608                         type: d.type !== 'yes' ? (d.type.charAt(0).toUpperCase() + d.type.slice(1)).replace('_', ' ')
35609                                                : (d.class.charAt(0).toUpperCase() + d.class.slice(1)).replace('_', ' '),
35610                         name: d.display_name,
35611                         extent: new iD.geo.Extent(
35612                             [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
35613                             [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
35614                     });
35615                 }
35616             });
35617
35618             return result;
35619         }
35620
35621         function drawList() {
35622             var value = search.property('value'),
35623                 results = features();
35624
35625             list.classed('filtered', value.length);
35626
35627             var noResultsWorldwide = geocodeResults && geocodeResults.length === 0;
35628
35629             var resultsIndicator = list.selectAll('.no-results-item')
35630                 .data([0])
35631                 .enter().append('button')
35632                 .property('disabled', true)
35633                 .attr('class', 'no-results-item')
35634                 .call(iD.svg.Icon('#icon-alert', 'pre-text'));
35635
35636             resultsIndicator.append('span')
35637                 .attr('class', 'entity-name');
35638
35639             list.selectAll('.no-results-item .entity-name')
35640                 .text(noResultsWorldwide ? t('geocoder.no_results_worldwide') : t('geocoder.no_results_visible'));
35641
35642             list.selectAll('.geocode-item')
35643                 .data([0])
35644                 .enter().append('button')
35645                 .attr('class', 'geocode-item')
35646                 .on('click', geocode)
35647                 .append('div')
35648                 .attr('class', 'label')
35649                 .append('span')
35650                 .attr('class', 'entity-name')
35651                 .text(t('geocoder.search'));
35652
35653             list.selectAll('.no-results-item')
35654                 .style('display', (value.length && !results.length) ? 'block' : 'none');
35655
35656             list.selectAll('.geocode-item')
35657                 .style('display', (value && geocodeResults === undefined) ? 'block' : 'none');
35658
35659             list.selectAll('.feature-list-item')
35660                 .data([-1])
35661                 .remove();
35662
35663             var items = list.selectAll('.feature-list-item')
35664                 .data(results, function(d) { return d.id; });
35665
35666             var enter = items.enter()
35667                 .insert('button', '.geocode-item')
35668                 .attr('class', 'feature-list-item')
35669                 .on('mouseover', mouseover)
35670                 .on('mouseout', mouseout)
35671                 .on('click', click);
35672
35673             var label = enter
35674                 .append('div')
35675                 .attr('class', 'label');
35676
35677             label.each(function(d) {
35678                 d3.select(this)
35679                     .call(iD.svg.Icon('#icon-' + d.geometry, 'pre-text'));
35680             });
35681
35682             label.append('span')
35683                 .attr('class', 'entity-type')
35684                 .text(function(d) { return d.type; });
35685
35686             label.append('span')
35687                 .attr('class', 'entity-name')
35688                 .text(function(d) { return d.name; });
35689
35690             enter.style('opacity', 0)
35691                 .transition()
35692                 .style('opacity', 1);
35693
35694             items.order();
35695
35696             items.exit()
35697                 .remove();
35698         }
35699
35700         function mouseover(d) {
35701             if (d.id === -1) return;
35702
35703             context.surface().selectAll(iD.util.entityOrMemberSelector([d.id], context.graph()))
35704                 .classed('hover', true);
35705         }
35706
35707         function mouseout() {
35708             context.surface().selectAll('.hover')
35709                 .classed('hover', false);
35710         }
35711
35712         function click(d) {
35713             d3.event.preventDefault();
35714             if (d.location) {
35715                 context.map().centerZoom([d.location[1], d.location[0]], 20);
35716             }
35717             else if (d.entity) {
35718                 if (d.entity.type === 'node') {
35719                     context.map().center(d.entity.loc);
35720                 } else if (d.entity.type === 'way') {
35721                     var center = context.projection(context.map().center()),
35722                         edge = iD.geo.chooseEdge(context.childNodes(d.entity), center, context.projection);
35723                     context.map().center(edge.loc);
35724                 }
35725                 context.enter(iD.modes.Select(context, [d.entity.id]).suppressMenu(true));
35726             } else {
35727                 context.zoomToEntity(d.id);
35728             }
35729         }
35730
35731         function geocode() {
35732             var searchVal = encodeURIComponent(search.property('value'));
35733             d3.json('https://nominatim.openstreetmap.org/search/' + searchVal + '?limit=10&format=json', function(err, resp) {
35734                 geocodeResults = resp || [];
35735                 drawList();
35736             });
35737         }
35738     }
35739
35740     return featureList;
35741 };
35742 iD.ui.flash = function(selection) {
35743     var modal = iD.ui.modal(selection);
35744
35745     modal.select('.modal').classed('modal-flash', true);
35746
35747     modal.select('.content')
35748         .classed('modal-section', true)
35749         .append('div')
35750         .attr('class', 'description');
35751
35752     modal.on('click.flash', function() { modal.remove(); });
35753
35754     setTimeout(function() {
35755         modal.remove();
35756         return true;
35757     }, 1500);
35758
35759     return modal;
35760 };
35761 iD.ui.FullScreen = function(context) {
35762     var element = context.container().node(),
35763         keybinding = d3.keybinding('full-screen');
35764         // button;
35765
35766     function getFullScreenFn() {
35767         if (element.requestFullscreen) {
35768             return element.requestFullscreen;
35769         } else if (element.msRequestFullscreen) {
35770             return  element.msRequestFullscreen;
35771         } else if (element.mozRequestFullScreen) {
35772             return  element.mozRequestFullScreen;
35773         } else if (element.webkitRequestFullscreen) {
35774             return element.webkitRequestFullscreen;
35775         }
35776     }
35777
35778     function getExitFullScreenFn() {
35779         if (document.exitFullscreen) {
35780             return document.exitFullscreen;
35781         } else if (document.msExitFullscreen) {
35782             return  document.msExitFullscreen;
35783         } else if (document.mozCancelFullScreen) {
35784             return  document.mozCancelFullScreen;
35785         } else if (document.webkitExitFullscreen) {
35786             return document.webkitExitFullscreen;
35787         }
35788     }
35789
35790     function isFullScreen() {
35791         return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement ||
35792             document.msFullscreenElement;
35793     }
35794
35795     function isSupported() {
35796         return !!getFullScreenFn();
35797     }
35798
35799     function fullScreen() {
35800         d3.event.preventDefault();
35801         if (!isFullScreen()) {
35802             // button.classed('active', true);
35803             getFullScreenFn().apply(element);
35804         } else {
35805             // button.classed('active', false);
35806             getExitFullScreenFn().apply(document);
35807         }
35808     }
35809
35810     return function() { // selection) {
35811         if (!isSupported())
35812             return;
35813
35814         // var tooltip = bootstrap.tooltip()
35815         //     .placement('left');
35816
35817         // button = selection.append('button')
35818         //     .attr('title', t('full_screen'))
35819         //     .attr('tabindex', -1)
35820         //     .on('click', fullScreen)
35821         //     .call(tooltip);
35822
35823         // button.append('span')
35824         //     .attr('class', 'icon full-screen');
35825
35826         keybinding
35827             .on('f11', fullScreen)
35828             .on(iD.ui.cmd('⌘⇧F'), fullScreen);
35829
35830         d3.select(document)
35831             .call(keybinding);
35832     };
35833 };
35834 iD.ui.Geolocate = function(context) {
35835     var geoOptions = { enableHighAccuracy: false, timeout: 6000 /* 6sec */ },
35836         locating = iD.ui.Loading(context).message(t('geolocate.locating')).blocking(true),
35837         timeoutId;
35838
35839     function click() {
35840         context.enter(iD.modes.Browse(context));
35841         context.container().call(locating);
35842         navigator.geolocation.getCurrentPosition(success, error, geoOptions);
35843
35844         // This timeout ensures that we still call finish() even if
35845         // the user declines to share their location in Firefox
35846         timeoutId = setTimeout(finish, 10000 /* 10sec */ );
35847     }
35848
35849     function success(position) {
35850         var map = context.map(),
35851             extent = iD.geo.Extent([position.coords.longitude, position.coords.latitude])
35852                 .padByMeters(position.coords.accuracy);
35853
35854         map.centerZoom(extent.center(), Math.min(20, map.extentZoom(extent)));
35855         finish();
35856     }
35857
35858     function error() {
35859         finish();
35860     }
35861
35862     function finish() {
35863         locating.close();  // unblock ui
35864         if (timeoutId) { clearTimeout(timeoutId); }
35865         timeoutId = undefined;
35866     }
35867
35868     return function(selection) {
35869         if (!navigator.geolocation) return;
35870
35871         selection.append('button')
35872             .attr('tabindex', -1)
35873             .attr('title', t('geolocate.title'))
35874             .on('click', click)
35875             .call(iD.svg.Icon('#icon-geolocate', 'light'))
35876             .call(bootstrap.tooltip()
35877                 .placement('left'));
35878     };
35879 };
35880 iD.ui.Help = function(context) {
35881     var key = 'H';
35882
35883     var docKeys = [
35884         'help.help',
35885         'help.editing_saving',
35886         'help.roads',
35887         'help.gps',
35888         'help.imagery',
35889         'help.addresses',
35890         'help.inspector',
35891         'help.buildings',
35892         'help.relations'];
35893
35894     var docs = docKeys.map(function(key) {
35895         var text = t(key);
35896         return {
35897             title: text.split('\n')[0].replace('#', '').trim(),
35898             html: marked(text.split('\n').slice(1).join('\n'))
35899         };
35900     });
35901
35902     function help(selection) {
35903
35904         function hide() {
35905             setVisible(false);
35906         }
35907
35908         function toggle() {
35909             if (d3.event) d3.event.preventDefault();
35910             tooltip.hide(button);
35911             setVisible(!button.classed('active'));
35912         }
35913
35914         function setVisible(show) {
35915             if (show !== shown) {
35916                 button.classed('active', show);
35917                 shown = show;
35918
35919                 if (show) {
35920                     selection.on('mousedown.help-inside', function() {
35921                         return d3.event.stopPropagation();
35922                     });
35923                     pane.style('display', 'block')
35924                         .style('right', '-500px')
35925                         .transition()
35926                         .duration(200)
35927                         .style('right', '0px');
35928                 } else {
35929                     pane.style('right', '0px')
35930                         .transition()
35931                         .duration(200)
35932                         .style('right', '-500px')
35933                         .each('end', function() {
35934                             d3.select(this).style('display', 'none');
35935                         });
35936                     selection.on('mousedown.help-inside', null);
35937                 }
35938             }
35939         }
35940
35941         function clickHelp(d, i) {
35942             pane.property('scrollTop', 0);
35943             doctitle.html(d.title);
35944             body.html(d.html);
35945             body.selectAll('a')
35946                 .attr('target', '_blank');
35947             menuItems.classed('selected', function(m) {
35948                 return m.title === d.title;
35949             });
35950
35951             nav.html('');
35952
35953             if (i > 0) {
35954                 var prevLink = nav.append('a')
35955                     .attr('class', 'previous')
35956                     .on('click', function() {
35957                         clickHelp(docs[i - 1], i - 1);
35958                     });
35959                 prevLink.append('span').html('&#9668; ' + docs[i - 1].title);
35960             }
35961             if (i < docs.length - 1) {
35962                 var nextLink = nav.append('a')
35963                     .attr('class', 'next')
35964                     .on('click', function() {
35965                         clickHelp(docs[i + 1], i + 1);
35966                     });
35967                 nextLink.append('span').html(docs[i + 1].title + ' &#9658;');
35968             }
35969         }
35970
35971         function clickWalkthrough() {
35972             d3.select(document.body).call(iD.ui.intro(context));
35973             setVisible(false);
35974         }
35975
35976
35977         var pane = selection.append('div')
35978                 .attr('class', 'help-wrap map-overlay fillL col5 content hide'),
35979             tooltip = bootstrap.tooltip()
35980                 .placement('left')
35981                 .html(true)
35982                 .title(iD.ui.tooltipHtml(t('help.title'), key)),
35983             button = selection.append('button')
35984                 .attr('tabindex', -1)
35985                 .on('click', toggle)
35986                 .call(iD.svg.Icon('#icon-help', 'light'))
35987                 .call(tooltip),
35988             shown = false;
35989
35990
35991         var toc = pane.append('ul')
35992             .attr('class', 'toc');
35993
35994         var menuItems = toc.selectAll('li')
35995             .data(docs)
35996             .enter()
35997             .append('li')
35998             .append('a')
35999             .html(function(d) { return d.title; })
36000             .on('click', clickHelp);
36001
36002         toc.append('li')
36003             .attr('class','walkthrough')
36004             .append('a')
36005             .text(t('splash.walkthrough'))
36006             .on('click', clickWalkthrough);
36007
36008         var content = pane.append('div')
36009             .attr('class', 'left-content');
36010
36011         var doctitle = content.append('h2')
36012             .text(t('help.title'));
36013
36014         var body = content.append('div')
36015             .attr('class', 'body');
36016
36017         var nav = content.append('div')
36018             .attr('class', 'nav');
36019
36020         clickHelp(docs[0], 0);
36021
36022         var keybinding = d3.keybinding('help')
36023             .on(key, toggle)
36024             .on('B', hide)
36025             .on('F', hide);
36026
36027         d3.select(document)
36028             .call(keybinding);
36029
36030         context.surface().on('mousedown.help-outside', hide);
36031         context.container().on('mousedown.help-outside', hide);
36032     }
36033
36034     return help;
36035 };
36036 iD.ui.Info = function(context) {
36037     var key = iD.ui.cmd('⌘I'),
36038         imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
36039         hidden = true;
36040
36041     function info(selection) {
36042         function radiansToMeters(r) {
36043             // using WGS84 authalic radius (6371007.1809 m)
36044             return r * 6371007.1809;
36045         }
36046
36047         function steradiansToSqmeters(r) {
36048             // http://gis.stackexchange.com/a/124857/40446
36049             return r / 12.56637 * 510065621724000;
36050         }
36051
36052         function toLineString(feature) {
36053             if (feature.type === 'LineString') return feature;
36054
36055             var result = { type: 'LineString', coordinates: [] };
36056             if (feature.type === 'Polygon') {
36057                 result.coordinates = feature.coordinates[0];
36058             } else if (feature.type === 'MultiPolygon') {
36059                 result.coordinates = feature.coordinates[0][0];
36060             }
36061
36062             return result;
36063         }
36064
36065         function displayLength(m) {
36066             var d = m * (imperial ? 3.28084 : 1),
36067                 p, unit;
36068
36069             if (imperial) {
36070                 if (d >= 5280) {
36071                     d /= 5280;
36072                     unit = 'mi';
36073                 } else {
36074                     unit = 'ft';
36075                 }
36076             } else {
36077                 if (d >= 1000) {
36078                     d /= 1000;
36079                     unit = 'km';
36080                 } else {
36081                     unit = 'm';
36082                 }
36083             }
36084
36085             // drop unnecessary precision
36086             p = d > 1000 ? 0 : d > 100 ? 1 : 2;
36087
36088             return String(d.toFixed(p)) + ' ' + unit;
36089         }
36090
36091         function displayArea(m2) {
36092             var d = m2 * (imperial ? 10.7639111056 : 1),
36093                 d1, d2, p1, p2, unit1, unit2;
36094
36095             if (imperial) {
36096                 if (d >= 6969600) {     // > 0.25mi² show mi²
36097                     d1 = d / 27878400;
36098                     unit1 = 'mi²';
36099                 } else {
36100                     d1 = d;
36101                     unit1 = 'ft²';
36102                 }
36103
36104                 if (d > 4356 && d < 43560000) {   // 0.1 - 1000 acres
36105                     d2 = d / 43560;
36106                     unit2 = 'ac';
36107                 }
36108
36109             } else {
36110                 if (d >= 250000) {    // > 0.25km² show km²
36111                     d1 = d / 1000000;
36112                     unit1 = 'km²';
36113                 } else {
36114                     d1 = d;
36115                     unit1 = 'm²';
36116                 }
36117
36118                 if (d > 1000 && d < 10000000) {   // 0.1 - 1000 hectares
36119                     d2 = d / 10000;
36120                     unit2 = 'ha';
36121                 }
36122             }
36123
36124             // drop unnecessary precision
36125             p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2;
36126             p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2;
36127
36128             return String(d1.toFixed(p1)) + ' ' + unit1 +
36129                 (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : '');
36130         }
36131
36132
36133         function redraw() {
36134             if (hidden) return;
36135
36136             var resolver = context.graph(),
36137                 selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }),
36138                 singular = selected.length === 1 ? selected[0] : null,
36139                 extent = iD.geo.Extent(),
36140                 entity;
36141
36142             wrap.html('');
36143             wrap.append('h4')
36144                 .attr('class', 'infobox-heading fillD')
36145                 .text(singular || t('infobox.selected', { n: selected.length }));
36146
36147             if (!selected.length) return;
36148
36149             var center;
36150             for (var i = 0; i < selected.length; i++) {
36151                 entity = context.entity(selected[i]);
36152                 extent._extend(entity.extent(resolver));
36153             }
36154             center = extent.center();
36155
36156
36157             var list = wrap.append('ul');
36158
36159             // multiple wrap, just display extent center..
36160             if (!singular) {
36161                 list.append('li')
36162                     .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
36163                 return;
36164             }
36165
36166             // single wrap, display details..
36167             if (!entity) return;
36168             var geometry = entity.geometry(resolver);
36169
36170             if (geometry === 'line' || geometry === 'area') {
36171                 var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()),
36172                     feature = entity.asGeoJSON(resolver),
36173                     length = radiansToMeters(d3.geo.length(toLineString(feature))),
36174                     lengthLabel = t('infobox.' + (closed ? 'perimeter' : 'length')),
36175                     centroid = d3.geo.centroid(feature);
36176
36177                 list.append('li')
36178                     .text(t('infobox.geometry') + ': ' +
36179                         (closed ? t('infobox.closed') + ' ' : '') + t('geometry.' + geometry) );
36180
36181                 if (closed) {
36182                     var area = steradiansToSqmeters(entity.area(resolver));
36183                     list.append('li')
36184                         .text(t('infobox.area') + ': ' + displayArea(area));
36185                 }
36186
36187                 list.append('li')
36188                     .text(lengthLabel + ': ' + displayLength(length));
36189
36190                 list.append('li')
36191                     .text(t('infobox.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5));
36192
36193
36194                 var toggle  = imperial ? 'imperial' : 'metric';
36195                 wrap.append('a')
36196                     .text(t('infobox.' + toggle))
36197                     .attr('href', '#')
36198                     .attr('class', 'button')
36199                     .on('click', function() {
36200                         d3.event.preventDefault();
36201                         imperial = !imperial;
36202                         redraw();
36203                     });
36204
36205             } else {
36206                 var centerLabel = t('infobox.' + (entity.type === 'node' ? 'location' : 'center'));
36207
36208                 list.append('li')
36209                     .text(t('infobox.geometry') + ': ' + t('geometry.' + geometry));
36210
36211                 list.append('li')
36212                     .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
36213             }
36214         }
36215
36216
36217         function toggle() {
36218             if (d3.event) d3.event.preventDefault();
36219
36220             hidden = !hidden;
36221
36222             if (hidden) {
36223                 wrap
36224                     .style('display', 'block')
36225                     .style('opacity', 1)
36226                     .transition()
36227                     .duration(200)
36228                     .style('opacity', 0)
36229                     .each('end', function() {
36230                         d3.select(this).style('display', 'none');
36231                     });
36232             } else {
36233                 wrap
36234                     .style('display', 'block')
36235                     .style('opacity', 0)
36236                     .transition()
36237                     .duration(200)
36238                     .style('opacity', 1);
36239
36240                 redraw();
36241             }
36242         }
36243
36244
36245         var wrap = selection.selectAll('.infobox')
36246             .data([0]);
36247
36248         wrap.enter()
36249             .append('div')
36250             .attr('class', 'infobox fillD2')
36251             .style('display', (hidden ? 'none' : 'block'));
36252
36253         context.map()
36254             .on('drawn.info', redraw);
36255
36256         redraw();
36257
36258         var keybinding = d3.keybinding('info')
36259             .on(key, toggle);
36260
36261         d3.select(document)
36262             .call(keybinding);
36263     }
36264
36265     return info;
36266 };
36267 iD.ui.Inspector = function(context) {
36268     var presetList = iD.ui.PresetList(context),
36269         entityEditor = iD.ui.EntityEditor(context),
36270         state = 'select',
36271         entityID,
36272         newFeature = false;
36273
36274     function inspector(selection) {
36275         presetList
36276             .entityID(entityID)
36277             .autofocus(newFeature)
36278             .on('choose', setPreset);
36279
36280         entityEditor
36281             .state(state)
36282             .entityID(entityID)
36283             .on('choose', showList);
36284
36285         var $wrap = selection.selectAll('.panewrap')
36286             .data([0]);
36287
36288         var $enter = $wrap.enter().append('div')
36289             .attr('class', 'panewrap');
36290
36291         $enter.append('div')
36292             .attr('class', 'preset-list-pane pane');
36293
36294         $enter.append('div')
36295             .attr('class', 'entity-editor-pane pane');
36296
36297         var $presetPane = $wrap.select('.preset-list-pane');
36298         var $editorPane = $wrap.select('.entity-editor-pane');
36299
36300         var graph = context.graph(),
36301             entity = context.entity(entityID),
36302             showEditor = state === 'hover' ||
36303                 entity.isUsed(graph) ||
36304                 entity.isHighwayIntersection(graph);
36305
36306         if (showEditor) {
36307             $wrap.style('right', '0%');
36308             $editorPane.call(entityEditor);
36309         } else {
36310             $wrap.style('right', '-100%');
36311             $presetPane.call(presetList);
36312         }
36313
36314         var $footer = selection.selectAll('.footer')
36315             .data([0]);
36316
36317         $footer.enter().append('div')
36318             .attr('class', 'footer');
36319
36320         selection.select('.footer')
36321             .call(iD.ui.ViewOnOSM(context)
36322                 .entityID(entityID));
36323
36324         function showList(preset) {
36325             $wrap.transition()
36326                 .styleTween('right', function() { return d3.interpolate('0%', '-100%'); });
36327
36328             $presetPane.call(presetList
36329                 .preset(preset)
36330                 .autofocus(true));
36331         }
36332
36333         function setPreset(preset) {
36334             $wrap.transition()
36335                 .styleTween('right', function() { return d3.interpolate('-100%', '0%'); });
36336
36337             $editorPane.call(entityEditor
36338                 .preset(preset));
36339         }
36340     }
36341
36342     inspector.state = function(_) {
36343         if (!arguments.length) return state;
36344         state = _;
36345         entityEditor.state(state);
36346         return inspector;
36347     };
36348
36349     inspector.entityID = function(_) {
36350         if (!arguments.length) return entityID;
36351         entityID = _;
36352         return inspector;
36353     };
36354
36355     inspector.newFeature = function(_) {
36356         if (!arguments.length) return newFeature;
36357         newFeature = _;
36358         return inspector;
36359     };
36360
36361     return inspector;
36362 };
36363 iD.ui.intro = function(context) {
36364     var step;
36365
36366     function intro(selection) {
36367
36368         function localizedName(id) {
36369             var features = {
36370                 n2140018997: 'city_hall',
36371                 n367813436: 'fire_department',
36372                 w203988286: 'memory_isle_park',
36373                 w203972937: 'riverwalk_trail',
36374                 w203972938: 'riverwalk_trail',
36375                 w203972940: 'riverwalk_trail',
36376                 w41785752: 'w_michigan_ave',
36377                 w134150789: 'w_michigan_ave',
36378                 w134150795: 'w_michigan_ave',
36379                 w134150800: 'w_michigan_ave',
36380                 w134150811: 'w_michigan_ave',
36381                 w134150802: 'e_michigan_ave',
36382                 w134150836: 'e_michigan_ave',
36383                 w41074896: 'e_michigan_ave',
36384                 w17965834: 'spring_st',
36385                 w203986457: 'scidmore_park',
36386                 w203049587: 'petting_zoo',
36387                 w17967397: 'n_andrews_st',
36388                 w17967315: 's_andrews_st',
36389                 w17967326: 'n_constantine_st',
36390                 w17966400: 's_constantine_st',
36391                 w170848823: 'rocky_river',
36392                 w170848824: 'rocky_river',
36393                 w170848331: 'rocky_river',
36394                 w17967752: 'railroad_dr',
36395                 w17965998: 'conrail_rr',
36396                 w134150845: 'conrail_rr',
36397                 w170989131: 'st_joseph_river',
36398                 w143497377: 'n_main_st',
36399                 w134150801: 's_main_st',
36400                 w134150830: 's_main_st',
36401                 w17966462: 's_main_st',
36402                 w17967734: 'water_st',
36403                 w17964996: 'foster_st',
36404                 w170848330: 'portage_river',
36405                 w17965351: 'flower_st',
36406                 w17965502: 'elm_st',
36407                 w17965402: 'walnut_st',
36408                 w17964793: 'morris_ave',
36409                 w17967444: 'east_st',
36410                 w17966984: 'portage_ave'
36411             };
36412             return features[id] && t('intro.graph.' + features[id]);
36413         }
36414
36415         context.enter(iD.modes.Browse(context));
36416
36417         // Save current map state
36418         var history = context.history().toJSON(),
36419             hash = window.location.hash,
36420             center = context.map().center(),
36421             zoom = context.map().zoom(),
36422             background = context.background().baseLayerSource(),
36423             opacity = d3.selectAll('#map .layer-background').style('opacity'),
36424             loadedTiles = context.connection().loadedTiles(),
36425             baseEntities = context.history().graph().base().entities,
36426             introGraph, name;
36427
36428         // Block saving
36429         context.inIntro(true);
36430
36431         // Load semi-real data used in intro
36432         context.connection().toggle(false).flush();
36433         context.history().reset();
36434
36435         introGraph = JSON.parse(iD.introGraph);
36436         for (var key in introGraph) {
36437             introGraph[key] = iD.Entity(introGraph[key]);
36438             name = localizedName(key);
36439             if (name) {
36440                 introGraph[key].tags.name = name;
36441             }
36442         }
36443         context.history().merge(d3.values(iD.Graph().load(introGraph).entities));
36444         context.background().bing();
36445
36446         d3.selectAll('#map .layer-background').style('opacity', 1);
36447
36448         var curtain = d3.curtain();
36449         selection.call(curtain);
36450
36451         function reveal(box, text, options) {
36452             options = options || {};
36453             if (text) curtain.reveal(box, text, options.tooltipClass, options.duration);
36454             else curtain.reveal(box, '', '', options.duration);
36455         }
36456
36457         var steps = ['navigation', 'point', 'area', 'line', 'startEditing'].map(function(step, i) {
36458             var s = iD.ui.intro[step](context, reveal)
36459                 .on('done', function() {
36460                     entered.filter(function(d) {
36461                         return d.title === s.title;
36462                     }).classed('finished', true);
36463                     enter(steps[i + 1]);
36464                 });
36465             return s;
36466         });
36467
36468         steps[steps.length - 1].on('startEditing', function() {
36469             curtain.remove();
36470             navwrap.remove();
36471             d3.selectAll('#map .layer-background').style('opacity', opacity);
36472             context.connection().toggle(true).flush().loadedTiles(loadedTiles);
36473             context.history().reset().merge(d3.values(baseEntities));
36474             context.background().baseLayerSource(background);
36475             if (history) context.history().fromJSON(history, false);
36476             context.map().centerZoom(center, zoom);
36477             window.location.replace(hash);
36478             context.inIntro(false);
36479         });
36480
36481         var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
36482
36483         var buttonwrap = navwrap.append('div')
36484             .attr('class', 'joined')
36485             .selectAll('button.step');
36486
36487         var entered = buttonwrap
36488             .data(steps)
36489             .enter()
36490             .append('button')
36491             .attr('class', 'step')
36492             .on('click', enter);
36493
36494         entered
36495             .call(iD.svg.Icon('#icon-apply', 'pre-text'));
36496
36497         entered
36498             .append('label')
36499             .text(function(d) { return t(d.title); });
36500
36501         enter(steps[0]);
36502
36503         function enter (newStep) {
36504             if (step) { step.exit(); }
36505
36506             context.enter(iD.modes.Browse(context));
36507
36508             step = newStep;
36509             step.enter();
36510
36511             entered.classed('active', function(d) {
36512                 return d.title === step.title;
36513             });
36514         }
36515
36516     }
36517     return intro;
36518 };
36519
36520 iD.ui.intro.pointBox = function(point, context) {
36521     var rect = context.surfaceRect();
36522     point = context.projection(point);
36523     return {
36524         left: point[0] + rect.left - 30,
36525         top: point[1] + rect.top - 50,
36526         width: 60,
36527         height: 70
36528     };
36529 };
36530
36531 iD.ui.intro.pad = function(box, padding, context) {
36532     if (box instanceof Array) {
36533         var rect = context.surfaceRect();
36534         box = context.projection(box);
36535         box = {
36536             left: box[0] + rect.left,
36537             top: box[1] + rect.top
36538         };
36539     }
36540     return {
36541         left: box.left - padding,
36542         top: box.top - padding,
36543         width: (box.width || 0) + 2 * padding,
36544         height: (box.width || 0) + 2 * padding
36545     };
36546 };
36547
36548 iD.ui.intro.icon = function(name, svgklass) {
36549     return '<svg class="icon ' + (svgklass || '') + '">' +
36550         '<use xlink:href="' + name + '"></use></svg>';
36551 };
36552 iD.ui.Lasso = function(context) {
36553     var group, polygon;
36554
36555     lasso.coordinates = [];
36556
36557     function lasso(selection) {
36558
36559         context.container().classed('lasso', true);
36560
36561         group = selection.append('g')
36562             .attr('class', 'lasso hide');
36563
36564         polygon = group.append('path')
36565             .attr('class', 'lasso-path');
36566
36567         group.call(iD.ui.Toggle(true));
36568
36569     }
36570
36571     function draw() {
36572         if (polygon) {
36573             polygon.data([lasso.coordinates])
36574                 .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; });
36575         }
36576     }
36577
36578     lasso.extent = function () {
36579         return lasso.coordinates.reduce(function(extent, point) {
36580             return extent.extend(iD.geo.Extent(point));
36581         }, iD.geo.Extent());
36582     };
36583
36584     lasso.p = function(_) {
36585         if (!arguments.length) return lasso;
36586         lasso.coordinates.push(_);
36587         draw();
36588         return lasso;
36589     };
36590
36591     lasso.close = function() {
36592         if (group) {
36593             group.call(iD.ui.Toggle(false, function() {
36594                 d3.select(this).remove();
36595             }));
36596         }
36597         context.container().classed('lasso', false);
36598     };
36599
36600     return lasso;
36601 };
36602 iD.ui.Loading = function(context) {
36603     var message = '',
36604         blocking = false,
36605         modal;
36606
36607     var loading = function(selection) {
36608         modal = iD.ui.modal(selection, blocking);
36609
36610         var loadertext = modal.select('.content')
36611             .classed('loading-modal', true)
36612             .append('div')
36613             .attr('class', 'modal-section fillL');
36614
36615         loadertext.append('img')
36616             .attr('class', 'loader')
36617             .attr('src', context.imagePath('loader-white.gif'));
36618
36619         loadertext.append('h3')
36620             .text(message);
36621
36622         modal.select('button.close')
36623             .attr('class', 'hide');
36624
36625         return loading;
36626     };
36627
36628     loading.message = function(_) {
36629         if (!arguments.length) return message;
36630         message = _;
36631         return loading;
36632     };
36633
36634     loading.blocking = function(_) {
36635         if (!arguments.length) return blocking;
36636         blocking = _;
36637         return loading;
36638     };
36639
36640     loading.close = function() {
36641         modal.remove();
36642     };
36643
36644     return loading;
36645 };
36646 iD.ui.MapData = function(context) {
36647     var key = 'F',
36648         features = context.features().keys(),
36649         layers = context.layers(),
36650         fills = ['wireframe', 'partial', 'full'],
36651         fillDefault = context.storage('area-fill') || 'partial',
36652         fillSelected = fillDefault;
36653
36654
36655     function map_data(selection) {
36656
36657         function showsFeature(d) {
36658             return context.features().enabled(d);
36659         }
36660
36661         function autoHiddenFeature(d) {
36662             return context.features().autoHidden(d);
36663         }
36664
36665         function clickFeature(d) {
36666             context.features().toggle(d);
36667             update();
36668         }
36669
36670         function showsFill(d) {
36671             return fillSelected === d;
36672         }
36673
36674         function setFill(d) {
36675             _.each(fills, function(opt) {
36676                 context.surface().classed('fill-' + opt, Boolean(opt === d));
36677             });
36678
36679             fillSelected = d;
36680             if (d !== 'wireframe') {
36681                 fillDefault = d;
36682                 context.storage('area-fill', d);
36683             }
36684             update();
36685         }
36686
36687         function showsLayer(which) {
36688             var layer = layers.layer(which);
36689             if (layer) {
36690                 return layer.enabled();
36691             }
36692             return false;
36693         }
36694
36695         function setLayer(which, enabled) {
36696             var layer = layers.layer(which);
36697             if (layer) {
36698                 layer.enabled(enabled);
36699                 update();
36700             }
36701         }
36702
36703         function toggleLayer(which) {
36704             setLayer(which, !showsLayer(which));
36705         }
36706
36707         function clickGpx() {
36708             toggleLayer('gpx');
36709         }
36710
36711         function clickMapillaryImages() {
36712             toggleLayer('mapillary-images');
36713             if (!showsLayer('mapillary-images')) {
36714                 setLayer('mapillary-signs', false);
36715             }
36716         }
36717
36718         function clickMapillarySigns() {
36719             toggleLayer('mapillary-signs');
36720         }
36721
36722
36723         function drawMapillaryItems(selection) {
36724             var mapillaryImages = layers.layer('mapillary-images'),
36725                 mapillarySigns = layers.layer('mapillary-signs'),
36726                 supportsMapillaryImages = mapillaryImages && mapillaryImages.supported(),
36727                 supportsMapillarySigns = mapillarySigns && mapillarySigns.supported(),
36728                 showsMapillaryImages = supportsMapillaryImages && mapillaryImages.enabled(),
36729                 showsMapillarySigns = supportsMapillarySigns && mapillarySigns.enabled();
36730
36731             var mapillaryList = selection
36732                 .selectAll('.layer-list-mapillary')
36733                 .data([0]);
36734
36735             // Enter
36736             mapillaryList
36737                 .enter()
36738                 .append('ul')
36739                 .attr('class', 'layer-list layer-list-mapillary');
36740
36741             var mapillaryImageLayerItem = mapillaryList
36742                 .selectAll('.list-item-mapillary-images')
36743                 .data(supportsMapillaryImages ? [0] : []);
36744
36745             var enterImages = mapillaryImageLayerItem.enter()
36746                 .append('li')
36747                 .attr('class', 'list-item-mapillary-images');
36748
36749             var labelImages = enterImages.append('label')
36750                 .call(bootstrap.tooltip()
36751                     .title(t('mapillary_images.tooltip'))
36752                     .placement('top'));
36753
36754             labelImages.append('input')
36755                 .attr('type', 'checkbox')
36756                 .on('change', clickMapillaryImages);
36757
36758             labelImages.append('span')
36759                 .text(t('mapillary_images.title'));
36760
36761
36762             var mapillarySignLayerItem = mapillaryList
36763                 .selectAll('.list-item-mapillary-signs')
36764                 .data(supportsMapillarySigns ? [0] : []);
36765
36766             var enterSigns = mapillarySignLayerItem.enter()
36767                 .append('li')
36768                 .attr('class', 'list-item-mapillary-signs');
36769
36770             var labelSigns = enterSigns.append('label')
36771                 .call(bootstrap.tooltip()
36772                     .title(t('mapillary_signs.tooltip'))
36773                     .placement('top'));
36774
36775             labelSigns.append('input')
36776                 .attr('type', 'checkbox')
36777                 .on('change', clickMapillarySigns);
36778
36779             labelSigns.append('span')
36780                 .text(t('mapillary_signs.title'));
36781
36782             // Update
36783             mapillaryImageLayerItem
36784                 .classed('active', showsMapillaryImages)
36785                 .selectAll('input')
36786                 .property('checked', showsMapillaryImages);
36787
36788             mapillarySignLayerItem
36789                 .classed('active', showsMapillarySigns)
36790                 .selectAll('input')
36791                 .property('disabled', !showsMapillaryImages)
36792                 .property('checked', showsMapillarySigns);
36793
36794             mapillarySignLayerItem
36795                 .selectAll('label')
36796                 .classed('deemphasize', !showsMapillaryImages);
36797
36798             // Exit
36799             mapillaryImageLayerItem.exit()
36800                 .remove();
36801             mapillarySignLayerItem.exit()
36802                 .remove();
36803         }
36804
36805
36806         function drawGpxItem(selection) {
36807             var gpx = layers.layer('gpx'),
36808                 hasGpx = gpx && gpx.hasGpx(),
36809                 showsGpx = hasGpx && gpx.enabled();
36810
36811             var gpxLayerItem = selection
36812                 .selectAll('.layer-list-gpx')
36813                 .data(gpx ? [0] : []);
36814
36815             // Enter
36816             var enter = gpxLayerItem.enter()
36817                 .append('ul')
36818                 .attr('class', 'layer-list layer-list-gpx')
36819                 .append('li')
36820                 .classed('list-item-gpx', true);
36821
36822             enter.append('button')
36823                 .attr('class', 'list-item-gpx-extent')
36824                 .call(bootstrap.tooltip()
36825                     .title(t('gpx.zoom'))
36826                     .placement('left'))
36827                 .on('click', function() {
36828                     d3.event.preventDefault();
36829                     d3.event.stopPropagation();
36830                     gpx.fitZoom();
36831                 })
36832                 .call(iD.svg.Icon('#icon-search'));
36833
36834             enter.append('button')
36835                 .attr('class', 'list-item-gpx-browse')
36836                 .call(bootstrap.tooltip()
36837                     .title(t('gpx.browse'))
36838                     .placement('left'))
36839                 .on('click', function() {
36840                     d3.select(document.createElement('input'))
36841                         .attr('type', 'file')
36842                         .on('change', function() {
36843                             gpx.files(d3.event.target.files);
36844                         })
36845                         .node().click();
36846                 })
36847                 .call(iD.svg.Icon('#icon-geolocate'));
36848
36849             var labelGpx = enter.append('label')
36850                 .call(bootstrap.tooltip()
36851                     .title(t('gpx.drag_drop'))
36852                     .placement('top'));
36853
36854             labelGpx.append('input')
36855                 .attr('type', 'checkbox')
36856                 .on('change', clickGpx);
36857
36858             labelGpx.append('span')
36859                 .text(t('gpx.local_layer'));
36860
36861             // Update
36862             gpxLayerItem
36863                 .classed('active', showsGpx)
36864                 .selectAll('input')
36865                 .property('disabled', !hasGpx)
36866                 .property('checked', showsGpx);
36867
36868             gpxLayerItem
36869                 .selectAll('label')
36870                 .classed('deemphasize', !hasGpx);
36871
36872             // Exit
36873             gpxLayerItem.exit()
36874                 .remove();
36875         }
36876
36877
36878         function drawList(selection, data, type, name, change, active) {
36879             var items = selection.selectAll('li')
36880                 .data(data);
36881
36882             // Enter
36883             var enter = items.enter()
36884                 .append('li')
36885                 .attr('class', 'layer')
36886                 .call(bootstrap.tooltip()
36887                     .html(true)
36888                     .title(function(d) {
36889                         var tip = t(name + '.' + d + '.tooltip'),
36890                             key = (d === 'wireframe' ? 'W' : null);
36891
36892                         if (name === 'feature' && autoHiddenFeature(d)) {
36893                             tip += '<div>' + t('map_data.autohidden') + '</div>';
36894                         }
36895                         return iD.ui.tooltipHtml(tip, key);
36896                     })
36897                     .placement('top')
36898                 );
36899
36900             var label = enter.append('label');
36901
36902             label.append('input')
36903                 .attr('type', type)
36904                 .attr('name', name)
36905                 .on('change', change);
36906
36907             label.append('span')
36908                 .text(function(d) { return t(name + '.' + d + '.description'); });
36909
36910             // Update
36911             items
36912                 .classed('active', active)
36913                 .selectAll('input')
36914                 .property('checked', active)
36915                 .property('indeterminate', function(d) {
36916                     return (name === 'feature' && autoHiddenFeature(d));
36917                 });
36918
36919             // Exit
36920             items.exit()
36921                 .remove();
36922         }
36923
36924
36925         function update() {
36926             dataLayerContainer.call(drawMapillaryItems);
36927             dataLayerContainer.call(drawGpxItem);
36928
36929             fillList.call(drawList, fills, 'radio', 'area_fill', setFill, showsFill);
36930
36931             featureList.call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature);
36932         }
36933
36934         function hidePanel() {
36935             setVisible(false);
36936         }
36937
36938         function togglePanel() {
36939             if (d3.event) d3.event.preventDefault();
36940             tooltip.hide(button);
36941             setVisible(!button.classed('active'));
36942         }
36943
36944         function toggleWireframe() {
36945             if (d3.event) {
36946                 d3.event.preventDefault();
36947                 d3.event.stopPropagation();
36948             }
36949             setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe'));
36950             context.map().pan([0,0]);  // trigger a redraw
36951         }
36952
36953         function setVisible(show) {
36954             if (show !== shown) {
36955                 button.classed('active', show);
36956                 shown = show;
36957
36958                 if (show) {
36959                     update();
36960                     selection.on('mousedown.map_data-inside', function() {
36961                         return d3.event.stopPropagation();
36962                     });
36963                     content.style('display', 'block')
36964                         .style('right', '-300px')
36965                         .transition()
36966                         .duration(200)
36967                         .style('right', '0px');
36968                 } else {
36969                     content.style('display', 'block')
36970                         .style('right', '0px')
36971                         .transition()
36972                         .duration(200)
36973                         .style('right', '-300px')
36974                         .each('end', function() {
36975                             d3.select(this).style('display', 'none');
36976                         });
36977                     selection.on('mousedown.map_data-inside', null);
36978                 }
36979             }
36980         }
36981
36982
36983         var content = selection.append('div')
36984                 .attr('class', 'fillL map-overlay col3 content hide'),
36985             tooltip = bootstrap.tooltip()
36986                 .placement('left')
36987                 .html(true)
36988                 .title(iD.ui.tooltipHtml(t('map_data.description'), key)),
36989             button = selection.append('button')
36990                 .attr('tabindex', -1)
36991                 .on('click', togglePanel)
36992                 .call(iD.svg.Icon('#icon-data', 'light'))
36993                 .call(tooltip),
36994             shown = false;
36995
36996         content.append('h4')
36997             .text(t('map_data.title'));
36998
36999
37000         // data layers
37001         content.append('a')
37002             .text(t('map_data.data_layers'))
37003             .attr('href', '#')
37004             .classed('hide-toggle', true)
37005             .classed('expanded', true)
37006             .on('click', function() {
37007                 var exp = d3.select(this).classed('expanded');
37008                 dataLayerContainer.style('display', exp ? 'none' : 'block');
37009                 d3.select(this).classed('expanded', !exp);
37010                 d3.event.preventDefault();
37011             });
37012
37013         var dataLayerContainer = content.append('div')
37014             .attr('class', 'data-data-layers')
37015             .style('display', 'block');
37016
37017
37018         // area fills
37019         content.append('a')
37020             .text(t('map_data.fill_area'))
37021             .attr('href', '#')
37022             .classed('hide-toggle', true)
37023             .classed('expanded', false)
37024             .on('click', function() {
37025                 var exp = d3.select(this).classed('expanded');
37026                 fillContainer.style('display', exp ? 'none' : 'block');
37027                 d3.select(this).classed('expanded', !exp);
37028                 d3.event.preventDefault();
37029             });
37030
37031         var fillContainer = content.append('div')
37032             .attr('class', 'data-area-fills')
37033             .style('display', 'none');
37034
37035         var fillList = fillContainer.append('ul')
37036             .attr('class', 'layer-list layer-fill-list');
37037
37038
37039         // feature filters
37040         content.append('a')
37041             .text(t('map_data.map_features'))
37042             .attr('href', '#')
37043             .classed('hide-toggle', true)
37044             .classed('expanded', false)
37045             .on('click', function() {
37046                 var exp = d3.select(this).classed('expanded');
37047                 featureContainer.style('display', exp ? 'none' : 'block');
37048                 d3.select(this).classed('expanded', !exp);
37049                 d3.event.preventDefault();
37050             });
37051
37052         var featureContainer = content.append('div')
37053             .attr('class', 'data-feature-filters')
37054             .style('display', 'none');
37055
37056         var featureList = featureContainer.append('ul')
37057             .attr('class', 'layer-list layer-feature-list');
37058
37059
37060         context.features()
37061             .on('change.map_data-update', update);
37062
37063         setFill(fillDefault);
37064
37065         var keybinding = d3.keybinding('features')
37066             .on(key, togglePanel)
37067             .on('W', toggleWireframe)
37068             .on('B', hidePanel)
37069             .on('H', hidePanel);
37070
37071         d3.select(document)
37072             .call(keybinding);
37073
37074         context.surface().on('mousedown.map_data-outside', hidePanel);
37075         context.container().on('mousedown.map_data-outside', hidePanel);
37076     }
37077
37078     return map_data;
37079 };
37080 iD.ui.MapInMap = function(context) {
37081     var key = '/';
37082
37083     function map_in_map(selection) {
37084         var backgroundLayer = iD.TileLayer(context),
37085             overlayLayers = {},
37086             projection = iD.geo.RawMercator(),
37087             gpxLayer = iD.svg.Gpx(projection, context).showLabels(false),
37088             zoom = d3.behavior.zoom()
37089                 .scaleExtent([ztok(0.5), ztok(24)])
37090                 .on('zoom', zoomPan),
37091             transformed = false,
37092             panning = false,
37093             hidden = true,
37094             zDiff = 6,    // by default, minimap renders at (main zoom - 6)
37095             tStart, tLast, tCurr, kLast, kCurr, tiles, svg, timeoutId;
37096
37097         function ztok(z) { return 256 * Math.pow(2, z); }
37098         function ktoz(k) { return Math.log(k) / Math.LN2 - 8; }
37099
37100
37101         function startMouse() {
37102             context.surface().on('mouseup.map-in-map-outside', endMouse);
37103             context.container().on('mouseup.map-in-map-outside', endMouse);
37104
37105             tStart = tLast = tCurr = projection.translate();
37106             panning = true;
37107         }
37108
37109
37110         function zoomPan() {
37111             var e = d3.event.sourceEvent,
37112                 t = d3.event.translate,
37113                 k = d3.event.scale,
37114                 zMain = ktoz(context.projection.scale() * 2 * Math.PI),
37115                 zMini = ktoz(k);
37116
37117             // restrict minimap zoom to < (main zoom - 3)
37118             if (zMini > zMain - 3) {
37119                 zMini = zMain - 3;
37120                 zoom.scale(kCurr).translate(tCurr);  // restore last good values
37121                 return;
37122             }
37123
37124             tCurr = t;
37125             kCurr = k;
37126             zDiff = zMain - zMini;
37127
37128             var scale = kCurr / kLast,
37129                 tX = (tCurr[0] / scale - tLast[0]) * scale,
37130                 tY = (tCurr[1] / scale - tLast[1]) * scale;
37131
37132             iD.util.setTransform(tiles, tX, tY, scale);
37133             iD.util.setTransform(svg, 0, 0, scale);
37134             transformed = true;
37135
37136             queueRedraw();
37137
37138             e.preventDefault();
37139             e.stopPropagation();
37140         }
37141
37142
37143         function endMouse() {
37144             context.surface().on('mouseup.map-in-map-outside', null);
37145             context.container().on('mouseup.map-in-map-outside', null);
37146
37147             updateProjection();
37148             panning = false;
37149
37150             if (tCurr[0] !== tStart[0] && tCurr[1] !== tStart[1]) {
37151                 var dMini = wrap.dimensions(),
37152                     cMini = [ dMini[0] / 2, dMini[1] / 2 ];
37153
37154                 context.map().center(projection.invert(cMini));
37155             }
37156         }
37157
37158
37159         function updateProjection() {
37160             var loc = context.map().center(),
37161                 dMini = wrap.dimensions(),
37162                 cMini = [ dMini[0] / 2, dMini[1] / 2 ],
37163                 tMain = context.projection.translate(),
37164                 kMain = context.projection.scale(),
37165                 zMain = ktoz(kMain * 2 * Math.PI),
37166                 zMini = Math.max(zMain - zDiff, 0.5),
37167                 kMini = ztok(zMini);
37168
37169             projection
37170                 .translate(tMain)
37171                 .scale(kMini / (2 * Math.PI));
37172
37173             var s = projection(loc),
37174                 mouse = panning ? [ tCurr[0] - tStart[0], tCurr[1] - tStart[1] ] : [0, 0],
37175                 tMini = [
37176                     cMini[0] - s[0] + tMain[0] + mouse[0],
37177                     cMini[1] - s[1] + tMain[1] + mouse[1]
37178                 ];
37179
37180             projection
37181                 .translate(tMini)
37182                 .clipExtent([[0, 0], dMini]);
37183
37184             zoom
37185                 .center(cMini)
37186                 .translate(tMini)
37187                 .scale(kMini);
37188
37189             tLast = tCurr = tMini;
37190             kLast = kCurr = kMini;
37191
37192             if (transformed) {
37193                 iD.util.setTransform(tiles, 0, 0);
37194                 iD.util.setTransform(svg, 0, 0);
37195                 transformed = false;
37196             }
37197         }
37198
37199
37200         function redraw() {
37201             if (hidden) return;
37202
37203             updateProjection();
37204
37205             var dMini = wrap.dimensions(),
37206                 zMini = ktoz(projection.scale() * 2 * Math.PI);
37207
37208             // setup tile container
37209             tiles = wrap
37210                 .selectAll('.map-in-map-tiles')
37211                 .data([0]);
37212
37213             tiles
37214                 .enter()
37215                 .append('div')
37216                 .attr('class', 'map-in-map-tiles');
37217
37218             // redraw background
37219             backgroundLayer
37220                 .source(context.background().baseLayerSource())
37221                 .projection(projection)
37222                 .dimensions(dMini);
37223
37224             var background = tiles
37225                 .selectAll('.map-in-map-background')
37226                 .data([0]);
37227
37228             background.enter()
37229                 .append('div')
37230                 .attr('class', 'map-in-map-background');
37231
37232             background
37233                 .call(backgroundLayer);
37234
37235
37236             // redraw overlay
37237             var overlaySources = context.background().overlayLayerSources();
37238             var activeOverlayLayers = [];
37239             for (var i = 0; i < overlaySources.length; i++) {
37240                 if (overlaySources[i].validZoom(zMini)) {
37241                     if (!overlayLayers[i]) overlayLayers[i] = iD.TileLayer(context);
37242                     activeOverlayLayers.push(overlayLayers[i]
37243                         .source(overlaySources[i])
37244                         .projection(projection)
37245                         .dimensions(dMini));
37246                 }
37247             }
37248
37249             var overlay = tiles
37250                 .selectAll('.map-in-map-overlay')
37251                 .data([0]);
37252
37253             overlay.enter()
37254                 .append('div')
37255                 .attr('class', 'map-in-map-overlay');
37256
37257             var overlays = overlay
37258                 .selectAll('div')
37259                 .data(activeOverlayLayers, function(d) { return d.source().name(); });
37260
37261             overlays.enter().append('div');
37262             overlays.each(function(layer) {
37263                 d3.select(this).call(layer);
37264             });
37265
37266             overlays.exit()
37267                 .remove();
37268
37269
37270             var gpx = tiles
37271                 .selectAll('.map-in-map-gpx')
37272                 .data(gpxLayer.enabled() ? [0] : []);
37273
37274             gpx.enter()
37275                 .append('svg')
37276                 .attr('class', 'map-in-map-gpx');
37277
37278             gpx.exit()
37279                 .remove();
37280
37281             gpx.call(gpxLayer);
37282
37283
37284             // redraw bounding box
37285             if (!panning) {
37286                 var getPath = d3.geo.path().projection(projection),
37287                     bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
37288
37289                 svg = wrap.selectAll('.map-in-map-svg')
37290                     .data([0]);
37291
37292                 svg.enter()
37293                     .append('svg')
37294                     .attr('class', 'map-in-map-svg');
37295
37296                 var path = svg.selectAll('.map-in-map-bbox')
37297                     .data([bbox]);
37298
37299                 path.enter()
37300                     .append('path')
37301                     .attr('class', 'map-in-map-bbox');
37302
37303                 path
37304                     .attr('d', getPath)
37305                     .classed('thick', function(d) { return getPath.area(d) < 30; });
37306             }
37307         }
37308
37309
37310         function queueRedraw() {
37311             clearTimeout(timeoutId);
37312             timeoutId = setTimeout(function() { redraw(); }, 300);
37313         }
37314
37315
37316         function toggle() {
37317             if (d3.event) d3.event.preventDefault();
37318
37319             hidden = !hidden;
37320
37321             var label = d3.select('.minimap-toggle');
37322             label.classed('active', !hidden)
37323                 .select('input').property('checked', !hidden);
37324
37325             if (hidden) {
37326                 wrap
37327                     .style('display', 'block')
37328                     .style('opacity', 1)
37329                     .transition()
37330                     .duration(200)
37331                     .style('opacity', 0)
37332                     .each('end', function() {
37333                         d3.select(this).style('display', 'none');
37334                     });
37335             } else {
37336                 wrap
37337                     .style('display', 'block')
37338                     .style('opacity', 0)
37339                     .transition()
37340                     .duration(200)
37341                     .style('opacity', 1);
37342
37343                 redraw();
37344             }
37345         }
37346
37347         iD.ui.MapInMap.toggle = toggle;
37348
37349         var wrap = selection.selectAll('.map-in-map')
37350             .data([0]);
37351
37352         wrap.enter()
37353             .append('div')
37354             .attr('class', 'map-in-map')
37355             .style('display', (hidden ? 'none' : 'block'))
37356             .on('mousedown.map-in-map', startMouse)
37357             .on('mouseup.map-in-map', endMouse)
37358             .call(zoom)
37359             .on('dblclick.zoom', null);
37360
37361         context.map()
37362             .on('drawn.map-in-map', function(drawn) {
37363                 if (drawn.full === true) redraw();
37364             });
37365
37366         redraw();
37367
37368         var keybinding = d3.keybinding('map-in-map')
37369             .on(key, toggle);
37370
37371         d3.select(document)
37372             .call(keybinding);
37373     }
37374
37375     return map_in_map;
37376 };
37377 iD.ui.modal = function(selection, blocking) {
37378     var keybinding = d3.keybinding('modal');
37379     var previous = selection.select('div.modal');
37380     var animate = previous.empty();
37381
37382     previous.transition()
37383         .duration(200)
37384         .style('opacity', 0)
37385         .remove();
37386
37387     var shaded = selection
37388         .append('div')
37389         .attr('class', 'shaded')
37390         .style('opacity', 0);
37391
37392     shaded.close = function() {
37393         shaded
37394             .transition()
37395             .duration(200)
37396             .style('opacity',0)
37397             .remove();
37398         modal
37399             .transition()
37400             .duration(200)
37401             .style('top','0px');
37402
37403         keybinding.off();
37404     };
37405
37406
37407     var modal = shaded.append('div')
37408         .attr('class', 'modal fillL col6');
37409
37410     if (!blocking) {
37411         shaded.on('click.remove-modal', function() {
37412             if (d3.event.target === this) {
37413                 shaded.close();
37414             }
37415         });
37416
37417         modal.append('button')
37418             .attr('class', 'close')
37419             .on('click', shaded.close)
37420             .call(iD.svg.Icon('#icon-close'));
37421
37422         keybinding
37423             .on('⌫', shaded.close)
37424             .on('⎋', shaded.close);
37425
37426         d3.select(document).call(keybinding);
37427     }
37428
37429     modal.append('div')
37430         .attr('class', 'content');
37431
37432     if (animate) {
37433         shaded.transition().style('opacity', 1);
37434     } else {
37435         shaded.style('opacity', 1);
37436     }
37437
37438     return shaded;
37439 };
37440 iD.ui.Modes = function(context) {
37441     var modes = [
37442         iD.modes.AddPoint(context),
37443         iD.modes.AddLine(context),
37444         iD.modes.AddArea(context)];
37445
37446     function editable() {
37447         return context.editable() && context.mode().id !== 'save';
37448     }
37449
37450     return function(selection) {
37451         var buttons = selection.selectAll('button.add-button')
37452             .data(modes);
37453
37454        buttons.enter().append('button')
37455            .attr('tabindex', -1)
37456            .attr('class', function(mode) { return mode.id + ' add-button col4'; })
37457            .on('click.mode-buttons', function(mode) {
37458                if (mode.id === context.mode().id) {
37459                    context.enter(iD.modes.Browse(context));
37460                } else {
37461                    context.enter(mode);
37462                }
37463            })
37464            .call(bootstrap.tooltip()
37465                .placement('bottom')
37466                .html(true)
37467                .title(function(mode) {
37468                    return iD.ui.tooltipHtml(mode.description, mode.key);
37469                }));
37470
37471         context.map()
37472             .on('move.modes', _.debounce(update, 500));
37473
37474         context
37475             .on('enter.modes', update);
37476
37477         buttons.each(function(d) {
37478             d3.select(this)
37479                 .call(iD.svg.Icon('#icon-' + d.button, 'pre-text'));
37480         });
37481
37482         buttons.append('span')
37483             .attr('class', 'label')
37484             .text(function(mode) { return mode.title; });
37485
37486         context.on('enter.editor', function(entered) {
37487             buttons.classed('active', function(mode) { return entered.button === mode.button; });
37488             context.container()
37489                 .classed('mode-' + entered.id, true);
37490         });
37491
37492         context.on('exit.editor', function(exited) {
37493             context.container()
37494                 .classed('mode-' + exited.id, false);
37495         });
37496
37497         var keybinding = d3.keybinding('mode-buttons');
37498
37499         modes.forEach(function(m) {
37500             keybinding.on(m.key, function() { if (editable()) context.enter(m); });
37501         });
37502
37503         d3.select(document)
37504             .call(keybinding);
37505
37506         function update() {
37507             buttons.property('disabled', !editable());
37508         }
37509     };
37510 };
37511 iD.ui.Notice = function(context) {
37512     return function(selection) {
37513         var div = selection.append('div')
37514             .attr('class', 'notice');
37515
37516         var button = div.append('button')
37517             .attr('class', 'zoom-to notice')
37518             .on('click', function() { context.map().zoom(context.minEditableZoom()); });
37519
37520         button
37521             .call(iD.svg.Icon('#icon-plus', 'pre-text'))
37522             .append('span')
37523             .attr('class', 'label')
37524             .text(t('zoom_in_edit'));
37525
37526         function disableTooHigh() {
37527             div.style('display', context.editable() ? 'none' : 'block');
37528         }
37529
37530         context.map()
37531             .on('move.notice', _.debounce(disableTooHigh, 500));
37532
37533         disableTooHigh();
37534     };
37535 };
37536 iD.ui.PresetIcon = function() {
37537     var preset, geometry;
37538
37539     function presetIcon(selection) {
37540         selection.each(render);
37541     }
37542
37543     function render() {
37544         var selection = d3.select(this),
37545             p = preset.apply(this, arguments),
37546             geom = geometry.apply(this, arguments),
37547             icon = p.icon || (geom === 'line' ? 'other-line' : 'marker-stroked'),
37548             maki = iD.data.featureIcons.hasOwnProperty(icon + '-24');
37549
37550         if (icon === 'dentist') maki = true;  // workaround for dentist icon missing in `maki-sprite.json`
37551
37552         function tag_classes(p) {
37553             var s = '';
37554             for (var i in p.tags) {
37555                 s += ' tag-' + i;
37556                 if (p.tags[i] !== '*') {
37557                     s += ' tag-' + i + '-' + p.tags[i];
37558                 }
37559             }
37560             return s;
37561         }
37562
37563         var $fill = selection.selectAll('.preset-icon-fill')
37564             .data([0]);
37565
37566         $fill.enter().append('div');
37567
37568         $fill.attr('class', function() {
37569             return 'preset-icon-fill preset-icon-fill-' + geom + tag_classes(p);
37570         });
37571
37572         var $frame = selection.selectAll('.preset-icon-frame')
37573             .data([0]);
37574
37575         $frame.enter()
37576             .append('div')
37577             .call(iD.svg.Icon('#preset-icon-frame'));
37578
37579         $frame.attr('class', function() {
37580             return 'preset-icon-frame ' + (geom === 'area' ? '' : 'hide');
37581         });
37582
37583
37584         var $icon = selection.selectAll('.preset-icon')
37585             .data([0]);
37586
37587         $icon.enter()
37588             .append('div')
37589             .attr('class', 'preset-icon')
37590             .call(iD.svg.Icon(''));
37591
37592         $icon
37593             .attr('class', 'preset-icon preset-icon-' + (maki ? '32' : (geom === 'area' ? '44' : '60')));
37594
37595         $icon.selectAll('svg')
37596             .attr('class', function() {
37597                 return 'icon ' + icon + tag_classes(p);
37598             });
37599
37600         $icon.selectAll('use')       // workaround: maki parking-24 broken?
37601             .attr('href', '#' + icon + (maki ? ( icon === 'parking' ? '-18' : '-24') : ''));
37602     }
37603
37604     presetIcon.preset = function(_) {
37605         if (!arguments.length) return preset;
37606         preset = d3.functor(_);
37607         return presetIcon;
37608     };
37609
37610     presetIcon.geometry = function(_) {
37611         if (!arguments.length) return geometry;
37612         geometry = d3.functor(_);
37613         return presetIcon;
37614     };
37615
37616     return presetIcon;
37617 };
37618 iD.ui.preset = function(context) {
37619     var event = d3.dispatch('change'),
37620         state,
37621         fields,
37622         preset,
37623         tags,
37624         id;
37625
37626     function UIField(field, entity, show) {
37627         field = _.clone(field);
37628
37629         field.input = iD.ui.preset[field.type](field, context)
37630             .on('change', event.change);
37631
37632         if (field.input.entity) field.input.entity(entity);
37633
37634         field.keys = field.keys || [field.key];
37635
37636         field.show = show;
37637
37638         field.shown = function() {
37639             return field.id === 'name' || field.show || _.some(field.keys, function(key) { return !!tags[key]; });
37640         };
37641
37642         field.modified = function() {
37643             var original = context.graph().base().entities[entity.id];
37644             return _.some(field.keys, function(key) {
37645                 return original ? tags[key] !== original.tags[key] : tags[key];
37646             });
37647         };
37648
37649         field.revert = function() {
37650             var original = context.graph().base().entities[entity.id],
37651                 t = {};
37652             field.keys.forEach(function(key) {
37653                 t[key] = original ? original.tags[key] : undefined;
37654             });
37655             return t;
37656         };
37657
37658         field.present = function() {
37659             return _.some(field.keys, function(key) {
37660                 return tags[key];
37661             });
37662         };
37663
37664         field.remove = function() {
37665             var t = {};
37666             field.keys.forEach(function(key) {
37667                 t[key] = undefined;
37668             });
37669             return t;
37670         };
37671
37672         return field;
37673     }
37674
37675     function fieldKey(field) {
37676         return field.id;
37677     }
37678
37679     function presets(selection) {
37680         selection.call(iD.ui.Disclosure()
37681             .title(t('inspector.all_fields'))
37682             .expanded(context.storage('preset_fields.expanded') !== 'false')
37683             .on('toggled', toggled)
37684             .content(content));
37685
37686         function toggled(expanded) {
37687             context.storage('preset_fields.expanded', expanded);
37688         }
37689     }
37690
37691     function content(selection) {
37692         if (!fields) {
37693             var entity = context.entity(id),
37694                 geometry = context.geometry(id);
37695
37696             fields = [UIField(context.presets().field('name'), entity)];
37697
37698             preset.fields.forEach(function(field) {
37699                 if (field.matchGeometry(geometry)) {
37700                     fields.push(UIField(field, entity, true));
37701                 }
37702             });
37703
37704             if (entity.isHighwayIntersection(context.graph())) {
37705                 fields.push(UIField(context.presets().field('restrictions'), entity, true));
37706             }
37707
37708             context.presets().universal().forEach(function(field) {
37709                 if (preset.fields.indexOf(field) < 0) {
37710                     fields.push(UIField(field, entity));
37711                 }
37712             });
37713         }
37714
37715         var shown = fields.filter(function(field) { return field.shown(); }),
37716             notShown = fields.filter(function(field) { return !field.shown(); });
37717
37718         var $form = selection.selectAll('.preset-form')
37719             .data([0]);
37720
37721         $form.enter().append('div')
37722             .attr('class', 'preset-form inspector-inner fillL3');
37723
37724         var $fields = $form.selectAll('.form-field')
37725             .data(shown, fieldKey);
37726
37727         // Enter
37728
37729         var $enter = $fields.enter()
37730             .append('div')
37731             .attr('class', function(field) {
37732                 return 'form-field form-field-' + field.id;
37733             });
37734
37735         var $label = $enter.append('label')
37736             .attr('class', 'form-label')
37737             .attr('for', function(field) { return 'preset-input-' + field.id; })
37738             .text(function(field) { return field.label(); });
37739
37740         var wrap = $label.append('div')
37741             .attr('class', 'form-label-button-wrap');
37742
37743         wrap.append('button')
37744             .attr('class', 'remove-icon')
37745             .attr('tabindex', -1)
37746             .call(iD.svg.Icon('#operation-delete'));
37747
37748         wrap.append('button')
37749             .attr('class', 'modified-icon')
37750             .attr('tabindex', -1)
37751             .call(iD.svg.Icon('#icon-undo'));
37752
37753         // Update
37754
37755         $fields.select('.form-label-button-wrap .remove-icon')
37756             .on('click', remove);
37757
37758         $fields.select('.modified-icon')
37759             .on('click', revert);
37760
37761         $fields
37762             .order()
37763             .classed('modified', function(field) {
37764                 return field.modified();
37765             })
37766             .classed('present', function(field) {
37767                 return field.present();
37768             })
37769             .each(function(field) {
37770                 var reference = iD.ui.TagReference(field.reference || {key: field.key}, context);
37771
37772                 if (state === 'hover') {
37773                     reference.showing(false);
37774                 }
37775
37776                 d3.select(this)
37777                     .call(field.input)
37778                     .selectAll('input')
37779                     .on('keydown', function() {
37780                         // if user presses enter, and combobox is not active, accept edits..
37781                         if (d3.event.keyCode === 13 && d3.select('.combobox').empty()) {
37782                             context.enter(iD.modes.Browse(context));
37783                         }
37784                     })
37785                     .call(reference.body)
37786                     .select('.form-label-button-wrap')
37787                     .call(reference.button);
37788
37789                 field.input.tags(tags);
37790             });
37791
37792         $fields.exit()
37793             .remove();
37794
37795         notShown = notShown.map(function(field) {
37796             return {
37797                 title: field.label(),
37798                 value: field.label(),
37799                 field: field
37800             };
37801         });
37802
37803         var $more = selection.selectAll('.more-fields')
37804             .data((notShown.length > 0) ? [0] : []);
37805
37806         $more.enter().append('div')
37807             .attr('class', 'more-fields')
37808             .append('label')
37809                 .text(t('inspector.add_fields'));
37810
37811         var $input = $more.selectAll('.value')
37812             .data([0]);
37813
37814         $input.enter().append('input')
37815             .attr('class', 'value')
37816             .attr('type', 'text');
37817
37818         $input.value('')
37819             .attr('placeholder', function() {
37820                 var placeholder = [];
37821                 for (var field in notShown) {
37822                     placeholder.push(notShown[field].title);
37823                 }
37824                 return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : '');
37825             })
37826             .call(d3.combobox().data(notShown)
37827                 .minItems(1)
37828                 .on('accept', show));
37829
37830         $more.exit()
37831             .remove();
37832
37833         $input.exit()
37834             .remove();
37835
37836         function show(field) {
37837             field = field.field;
37838             field.show = true;
37839             content(selection);
37840             field.input.focus();
37841         }
37842
37843         function revert(field) {
37844             d3.event.stopPropagation();
37845             d3.event.preventDefault();
37846             event.change(field.revert());
37847         }
37848
37849         function remove(field) {
37850             d3.event.stopPropagation();
37851             d3.event.preventDefault();
37852             event.change(field.remove());
37853         }
37854     }
37855
37856     presets.preset = function(_) {
37857         if (!arguments.length) return preset;
37858         if (preset && preset.id === _.id) return presets;
37859         preset = _;
37860         fields = null;
37861         return presets;
37862     };
37863
37864     presets.state = function(_) {
37865         if (!arguments.length) return state;
37866         state = _;
37867         return presets;
37868     };
37869
37870     presets.tags = function(_) {
37871         if (!arguments.length) return tags;
37872         tags = _;
37873         // Don't reset fields here.
37874         return presets;
37875     };
37876
37877     presets.entityID = function(_) {
37878         if (!arguments.length) return id;
37879         if (id === _) return presets;
37880         id = _;
37881         fields = null;
37882         return presets;
37883     };
37884
37885     return d3.rebind(presets, event, 'on');
37886 };
37887 iD.ui.PresetList = function(context) {
37888     var event = d3.dispatch('choose'),
37889         id,
37890         currentPreset,
37891         autofocus = false;
37892
37893     function presetList(selection) {
37894         var geometry = context.geometry(id),
37895             presets = context.presets().matchGeometry(geometry);
37896
37897         selection.html('');
37898
37899         var messagewrap = selection.append('div')
37900             .attr('class', 'header fillL cf');
37901
37902         var message = messagewrap.append('h3')
37903             .text(t('inspector.choose'));
37904
37905         if (context.entity(id).isUsed(context.graph())) {
37906             messagewrap.append('button')
37907                 .attr('class', 'preset-choose')
37908                 .on('click', function() { event.choose(currentPreset); })
37909                 .append('span')
37910                 .html('&#9658;');
37911         } else {
37912             messagewrap.append('button')
37913                 .attr('class', 'close')
37914                 .on('click', function() {
37915                     context.enter(iD.modes.Browse(context));
37916                 })
37917                 .call(iD.svg.Icon('#icon-close'));
37918         }
37919
37920         function keydown() {
37921             // hack to let delete shortcut work when search is autofocused
37922             if (search.property('value').length === 0 &&
37923                 (d3.event.keyCode === d3.keybinding.keyCodes['⌫'] ||
37924                  d3.event.keyCode === d3.keybinding.keyCodes['⌦'])) {
37925                 d3.event.preventDefault();
37926                 d3.event.stopPropagation();
37927                 iD.operations.Delete([id], context)();
37928             } else if (search.property('value').length === 0 &&
37929                 (d3.event.ctrlKey || d3.event.metaKey) &&
37930                 d3.event.keyCode === d3.keybinding.keyCodes.z) {
37931                 d3.event.preventDefault();
37932                 d3.event.stopPropagation();
37933                 context.undo();
37934             } else if (!d3.event.ctrlKey && !d3.event.metaKey) {
37935                 d3.select(this).on('keydown', null);
37936             }
37937         }
37938
37939         function keypress() {
37940             // enter
37941             var value = search.property('value');
37942             if (d3.event.keyCode === 13 && value.length) {
37943                 list.selectAll('.preset-list-item:first-child').datum().choose();
37944             }
37945         }
37946
37947         function inputevent() {
37948             var value = search.property('value');
37949             list.classed('filtered', value.length);
37950             if (value.length) {
37951                 var results = presets.search(value, geometry);
37952                 message.text(t('inspector.results', {
37953                     n: results.collection.length,
37954                     search: value
37955                 }));
37956                 list.call(drawList, results);
37957             } else {
37958                 list.call(drawList, context.presets().defaults(geometry, 36));
37959                 message.text(t('inspector.choose'));
37960             }
37961         }
37962
37963         var searchWrap = selection.append('div')
37964             .attr('class', 'search-header');
37965
37966         var search = searchWrap.append('input')
37967             .attr('class', 'preset-search-input')
37968             .attr('placeholder', t('inspector.search'))
37969             .attr('type', 'search')
37970             .on('keydown', keydown)
37971             .on('keypress', keypress)
37972             .on('input', inputevent);
37973
37974         searchWrap
37975             .call(iD.svg.Icon('#icon-search', 'pre-text'));
37976
37977         if (autofocus) {
37978             search.node().focus();
37979         }
37980
37981         var listWrap = selection.append('div')
37982             .attr('class', 'inspector-body');
37983
37984         var list = listWrap.append('div')
37985             .attr('class', 'preset-list fillL cf')
37986             .call(drawList, context.presets().defaults(geometry, 36));
37987     }
37988
37989     function drawList(list, presets) {
37990         var collection = presets.collection.map(function(preset) {
37991             return preset.members ? CategoryItem(preset) : PresetItem(preset);
37992         });
37993
37994         var items = list.selectAll('.preset-list-item')
37995             .data(collection, function(d) { return d.preset.id; });
37996
37997         items.enter().append('div')
37998             .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
37999             .classed('current', function(item) { return item.preset === currentPreset; })
38000             .each(function(item) {
38001                 d3.select(this).call(item);
38002             })
38003             .style('opacity', 0)
38004             .transition()
38005             .style('opacity', 1);
38006
38007         items.order();
38008
38009         items.exit()
38010             .remove();
38011     }
38012
38013     function CategoryItem(preset) {
38014         var box, sublist, shown = false;
38015
38016         function item(selection) {
38017             var wrap = selection.append('div')
38018                 .attr('class', 'preset-list-button-wrap category col12');
38019
38020             wrap.append('button')
38021                 .attr('class', 'preset-list-button')
38022                 .classed('expanded', false)
38023                 .call(iD.ui.PresetIcon()
38024                     .geometry(context.geometry(id))
38025                     .preset(preset))
38026                 .on('click', function() {
38027                     var isExpanded = d3.select(this).classed('expanded');
38028                     var triangle = isExpanded ? '▶ ' :  '▼ ';
38029                     d3.select(this).classed('expanded', !isExpanded);
38030                     d3.select(this).selectAll('.label').text(triangle + preset.name());
38031                     item.choose();
38032                 })
38033                 .append('div')
38034                 .attr('class', 'label')
38035                 .text(function() {
38036                   return '▶ ' + preset.name();
38037                 });
38038
38039             box = selection.append('div')
38040                 .attr('class', 'subgrid col12')
38041                 .style('max-height', '0px')
38042                 .style('opacity', 0);
38043
38044             box.append('div')
38045                 .attr('class', 'arrow');
38046
38047             sublist = box.append('div')
38048                 .attr('class', 'preset-list fillL3 cf fl');
38049         }
38050
38051         item.choose = function() {
38052             if (!box || !sublist) return;
38053
38054             if (shown) {
38055                 shown = false;
38056                 box.transition()
38057                     .duration(200)
38058                     .style('opacity', '0')
38059                     .style('max-height', '0px')
38060                     .style('padding-bottom', '0px');
38061             } else {
38062                 shown = true;
38063                 sublist.call(drawList, preset.members);
38064                 box.transition()
38065                     .duration(200)
38066                     .style('opacity', '1')
38067                     .style('max-height', 200 + preset.members.collection.length * 80 + 'px')
38068                     .style('padding-bottom', '20px');
38069             }
38070         };
38071
38072         item.preset = preset;
38073
38074         return item;
38075     }
38076
38077     function PresetItem(preset) {
38078         function item(selection) {
38079             var wrap = selection.append('div')
38080                 .attr('class', 'preset-list-button-wrap col12');
38081
38082             wrap.append('button')
38083                 .attr('class', 'preset-list-button')
38084                 .call(iD.ui.PresetIcon()
38085                     .geometry(context.geometry(id))
38086                     .preset(preset))
38087                 .on('click', item.choose)
38088                 .append('div')
38089                 .attr('class', 'label')
38090                 .text(preset.name());
38091
38092             wrap.call(item.reference.button);
38093             selection.call(item.reference.body);
38094         }
38095
38096         item.choose = function() {
38097             context.presets().choose(preset);
38098
38099             context.perform(
38100                 iD.actions.ChangePreset(id, currentPreset, preset),
38101                 t('operations.change_tags.annotation'));
38102
38103             event.choose(preset);
38104         };
38105
38106         item.help = function() {
38107             d3.event.stopPropagation();
38108             item.reference.toggle();
38109         };
38110
38111         item.preset = preset;
38112         item.reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context);
38113
38114         return item;
38115     }
38116
38117     presetList.autofocus = function(_) {
38118         if (!arguments.length) return autofocus;
38119         autofocus = _;
38120         return presetList;
38121     };
38122
38123     presetList.entityID = function(_) {
38124         if (!arguments.length) return id;
38125         id = _;
38126         presetList.preset(context.presets().match(context.entity(id), context.graph()));
38127         return presetList;
38128     };
38129
38130     presetList.preset = function(_) {
38131         if (!arguments.length) return currentPreset;
38132         currentPreset = _;
38133         return presetList;
38134     };
38135
38136     return d3.rebind(presetList, event, 'on');
38137 };
38138 iD.ui.RadialMenu = function(context, operations) {
38139     var menu,
38140         center = [0, 0],
38141         tooltip;
38142
38143     var radialMenu = function(selection) {
38144         if (!operations.length)
38145             return;
38146
38147         selection.node().parentNode.focus();
38148
38149         function click(operation) {
38150             d3.event.stopPropagation();
38151             if (operation.disabled())
38152                 return;
38153             operation();
38154             radialMenu.close();
38155         }
38156
38157         menu = selection.append('g')
38158             .attr('class', 'radial-menu')
38159             .attr('transform', 'translate(' + center + ')')
38160             .attr('opacity', 0);
38161
38162         menu.transition()
38163             .attr('opacity', 1);
38164
38165         var r = 50,
38166             a = Math.PI / 4,
38167             a0 = -Math.PI / 4,
38168             a1 = a0 + (operations.length - 1) * a;
38169
38170         menu.append('path')
38171             .attr('class', 'radial-menu-background')
38172             .attr('d', 'M' + r * Math.sin(a0) + ',' +
38173                              r * Math.cos(a0) +
38174                       ' A' + r + ',' + r + ' 0 ' + (operations.length > 5 ? '1' : '0') + ',0 ' +
38175                              (r * Math.sin(a1) + 1e-3) + ',' +
38176                              (r * Math.cos(a1) + 1e-3)) // Force positive-length path (#1305)
38177             .attr('stroke-width', 50)
38178             .attr('stroke-linecap', 'round');
38179
38180         var button = menu.selectAll()
38181             .data(operations)
38182             .enter()
38183             .append('g')
38184             .attr('class', function(d) { return 'radial-menu-item radial-menu-item-' + d.id; })
38185             .classed('disabled', function(d) { return d.disabled(); })
38186             .attr('transform', function(d, i) {
38187                 return 'translate(' + iD.geo.roundCoords([
38188                         r * Math.sin(a0 + i * a),
38189                         r * Math.cos(a0 + i * a)]).join(',') + ')';
38190             });
38191
38192         button.append('circle')
38193             .attr('r', 15)
38194             .on('click', click)
38195             .on('mousedown', mousedown)
38196             .on('mouseover', mouseover)
38197             .on('mouseout', mouseout);
38198
38199         button.append('use')
38200             .attr('transform', 'translate(-10,-10)')
38201             .attr('width', '20')
38202             .attr('height', '20')
38203             .attr('xlink:href', function(d) { return '#operation-' + d.id; });
38204
38205         tooltip = d3.select(document.body)
38206             .append('div')
38207             .attr('class', 'tooltip-inner radial-menu-tooltip');
38208
38209         function mousedown() {
38210             d3.event.stopPropagation(); // https://github.com/openstreetmap/iD/issues/1869
38211         }
38212
38213         function mouseover(d, i) {
38214             var rect = context.surfaceRect(),
38215                 angle = a0 + i * a,
38216                 top = rect.top + (r + 25) * Math.cos(angle) + center[1] + 'px',
38217                 left = rect.left + (r + 25) * Math.sin(angle) + center[0] + 'px',
38218                 bottom = rect.height - (r + 25) * Math.cos(angle) - center[1] + 'px',
38219                 right = rect.width - (r + 25) * Math.sin(angle) - center[0] + 'px';
38220
38221             tooltip
38222                 .style('top', null)
38223                 .style('left', null)
38224                 .style('bottom', null)
38225                 .style('right', null)
38226                 .style('display', 'block')
38227                 .html(iD.ui.tooltipHtml(d.tooltip(), d.keys[0]));
38228
38229             if (i === 0) {
38230                 tooltip
38231                     .style('right', right)
38232                     .style('top', top);
38233             } else if (i >= 4) {
38234                 tooltip
38235                     .style('left', left)
38236                     .style('bottom', bottom);
38237             } else {
38238                 tooltip
38239                     .style('left', left)
38240                     .style('top', top);
38241             }
38242         }
38243
38244         function mouseout() {
38245             tooltip.style('display', 'none');
38246         }
38247     };
38248
38249     radialMenu.close = function() {
38250         if (menu) {
38251             menu
38252                 .style('pointer-events', 'none')
38253                 .transition()
38254                 .attr('opacity', 0)
38255                 .remove();
38256         }
38257
38258         if (tooltip) {
38259             tooltip.remove();
38260         }
38261     };
38262
38263     radialMenu.center = function(_) {
38264         if (!arguments.length) return center;
38265         center = _;
38266         return radialMenu;
38267     };
38268
38269     return radialMenu;
38270 };
38271 iD.ui.RawMemberEditor = function(context) {
38272     var id;
38273
38274     function selectMember(d) {
38275         d3.event.preventDefault();
38276         context.enter(iD.modes.Select(context, [d.id]));
38277     }
38278
38279     function changeRole(d) {
38280         var role = d3.select(this).property('value');
38281         var member = {id: d.id, type: d.type, role: role};
38282         context.perform(
38283             iD.actions.ChangeMember(d.relation.id, member, d.index),
38284             t('operations.change_role.annotation'));
38285     }
38286
38287     function deleteMember(d) {
38288         context.perform(
38289             iD.actions.DeleteMember(d.relation.id, d.index),
38290             t('operations.delete_member.annotation'));
38291
38292         if (!context.hasEntity(d.relation.id)) {
38293             context.enter(iD.modes.Browse(context));
38294         }
38295     }
38296
38297     function rawMemberEditor(selection) {
38298         var entity = context.entity(id),
38299             memberships = [];
38300
38301         entity.members.forEach(function(member, index) {
38302             memberships.push({
38303                 index: index,
38304                 id: member.id,
38305                 type: member.type,
38306                 role: member.role,
38307                 relation: entity,
38308                 member: context.hasEntity(member.id)
38309             });
38310         });
38311
38312         selection.call(iD.ui.Disclosure()
38313             .title(t('inspector.all_members') + ' (' + memberships.length + ')')
38314             .expanded(true)
38315             .on('toggled', toggled)
38316             .content(content));
38317
38318         function toggled(expanded) {
38319             if (expanded) {
38320                 selection.node().parentNode.scrollTop += 200;
38321             }
38322         }
38323
38324         function content($wrap) {
38325             var $list = $wrap.selectAll('.member-list')
38326                 .data([0]);
38327
38328             $list.enter().append('ul')
38329                 .attr('class', 'member-list');
38330
38331             var $items = $list.selectAll('li')
38332                 .data(memberships, function(d) {
38333                     return iD.Entity.key(d.relation) + ',' + d.index + ',' +
38334                         (d.member ? iD.Entity.key(d.member) : 'incomplete');
38335                 });
38336
38337             var $enter = $items.enter().append('li')
38338                 .attr('class', 'member-row form-field')
38339                 .classed('member-incomplete', function(d) { return !d.member; });
38340
38341             $enter.each(function(d) {
38342                 if (d.member) {
38343                     var $label = d3.select(this).append('label')
38344                         .attr('class', 'form-label')
38345                         .append('a')
38346                         .attr('href', '#')
38347                         .on('click', selectMember);
38348
38349                     $label.append('span')
38350                         .attr('class', 'member-entity-type')
38351                         .text(function(d) { return context.presets().match(d.member, context.graph()).name(); });
38352
38353                     $label.append('span')
38354                         .attr('class', 'member-entity-name')
38355                         .text(function(d) { return iD.util.displayName(d.member); });
38356
38357                 } else {
38358                     d3.select(this).append('label')
38359                         .attr('class', 'form-label')
38360                         .text(t('inspector.incomplete'));
38361                 }
38362             });
38363
38364             $enter.append('input')
38365                 .attr('class', 'member-role')
38366                 .property('type', 'text')
38367                 .attr('maxlength', 255)
38368                 .attr('placeholder', t('inspector.role'))
38369                 .property('value', function(d) { return d.role; })
38370                 .on('change', changeRole);
38371
38372             $enter.append('button')
38373                 .attr('tabindex', -1)
38374                 .attr('class', 'remove button-input-action member-delete minor')
38375                 .on('click', deleteMember)
38376                 .call(iD.svg.Icon('#operation-delete'));
38377
38378             $items.exit()
38379                 .remove();
38380         }
38381     }
38382
38383     rawMemberEditor.entityID = function(_) {
38384         if (!arguments.length) return id;
38385         id = _;
38386         return rawMemberEditor;
38387     };
38388
38389     return rawMemberEditor;
38390 };
38391 iD.ui.RawMembershipEditor = function(context) {
38392     var id, showBlank;
38393
38394     function selectRelation(d) {
38395         d3.event.preventDefault();
38396         context.enter(iD.modes.Select(context, [d.relation.id]));
38397     }
38398
38399     function changeRole(d) {
38400         var role = d3.select(this).property('value');
38401         context.perform(
38402             iD.actions.ChangeMember(d.relation.id, _.extend({}, d.member, {role: role}), d.index),
38403             t('operations.change_role.annotation'));
38404     }
38405
38406     function addMembership(d, role) {
38407         showBlank = false;
38408
38409         if (d.relation) {
38410             context.perform(
38411                 iD.actions.AddMember(d.relation.id, {id: id, type: context.entity(id).type, role: role}),
38412                 t('operations.add_member.annotation'));
38413
38414         } else {
38415             var relation = iD.Relation();
38416
38417             context.perform(
38418                 iD.actions.AddEntity(relation),
38419                 iD.actions.AddMember(relation.id, {id: id, type: context.entity(id).type, role: role}),
38420                 t('operations.add.annotation.relation'));
38421
38422             context.enter(iD.modes.Select(context, [relation.id]));
38423         }
38424     }
38425
38426     function deleteMembership(d) {
38427         context.perform(
38428             iD.actions.DeleteMember(d.relation.id, d.index),
38429             t('operations.delete_member.annotation'));
38430     }
38431
38432     function relations(q) {
38433         var newRelation = {
38434                 relation: null,
38435                 value: t('inspector.new_relation')
38436             },
38437             result = [],
38438             graph = context.graph();
38439
38440         context.intersects(context.extent()).forEach(function(entity) {
38441             if (entity.type !== 'relation' || entity.id === id)
38442                 return;
38443
38444             var presetName = context.presets().match(entity, graph).name(),
38445                 entityName = iD.util.displayName(entity) || '';
38446
38447             var value = presetName + ' ' + entityName;
38448             if (q && value.toLowerCase().indexOf(q.toLowerCase()) === -1)
38449                 return;
38450
38451             result.push({
38452                 relation: entity,
38453                 value: value
38454             });
38455         });
38456
38457         result.sort(function(a, b) {
38458             return iD.Relation.creationOrder(a.relation, b.relation);
38459         });
38460
38461         // Dedupe identical names by appending relation id - see #2891
38462         var dupeGroups = _(result)
38463             .groupBy('value')
38464             .filter(function(v) { return v.length > 1; })
38465             .value();
38466
38467         dupeGroups.forEach(function(group) {
38468             group.forEach(function(obj) {
38469                 obj.value += ' ' + obj.relation.id;
38470             });
38471         });
38472
38473         result.unshift(newRelation);
38474
38475         return result;
38476     }
38477
38478     function rawMembershipEditor(selection) {
38479         var entity = context.entity(id),
38480             memberships = [];
38481
38482         context.graph().parentRelations(entity).forEach(function(relation) {
38483             relation.members.forEach(function(member, index) {
38484                 if (member.id === entity.id) {
38485                     memberships.push({relation: relation, member: member, index: index});
38486                 }
38487             });
38488         });
38489
38490         selection.call(iD.ui.Disclosure()
38491             .title(t('inspector.all_relations') + ' (' + memberships.length + ')')
38492             .expanded(true)
38493             .on('toggled', toggled)
38494             .content(content));
38495
38496         function toggled(expanded) {
38497             if (expanded) {
38498                 selection.node().parentNode.scrollTop += 200;
38499             }
38500         }
38501
38502         function content($wrap) {
38503             var $list = $wrap.selectAll('.member-list')
38504                 .data([0]);
38505
38506             $list.enter().append('ul')
38507                 .attr('class', 'member-list');
38508
38509             var $items = $list.selectAll('li.member-row-normal')
38510                 .data(memberships, function(d) { return iD.Entity.key(d.relation) + ',' + d.index; });
38511
38512             var $enter = $items.enter().append('li')
38513                 .attr('class', 'member-row member-row-normal form-field');
38514
38515             var $label = $enter.append('label')
38516                 .attr('class', 'form-label')
38517                 .append('a')
38518                 .attr('href', '#')
38519                 .on('click', selectRelation);
38520
38521             $label.append('span')
38522                 .attr('class', 'member-entity-type')
38523                 .text(function(d) { return context.presets().match(d.relation, context.graph()).name(); });
38524
38525             $label.append('span')
38526                 .attr('class', 'member-entity-name')
38527                 .text(function(d) { return iD.util.displayName(d.relation); });
38528
38529             $enter.append('input')
38530                 .attr('class', 'member-role')
38531                 .property('type', 'text')
38532                 .attr('maxlength', 255)
38533                 .attr('placeholder', t('inspector.role'))
38534                 .property('value', function(d) { return d.member.role; })
38535                 .on('change', changeRole);
38536
38537             $enter.append('button')
38538                 .attr('tabindex', -1)
38539                 .attr('class', 'remove button-input-action member-delete minor')
38540                 .on('click', deleteMembership)
38541                 .call(iD.svg.Icon('#operation-delete'));
38542
38543             $items.exit()
38544                 .remove();
38545
38546             if (showBlank) {
38547                 var $new = $list.selectAll('.member-row-new')
38548                     .data([0]);
38549
38550                 $enter = $new.enter().append('li')
38551                     .attr('class', 'member-row member-row-new form-field');
38552
38553                 $enter.append('input')
38554                     .attr('type', 'text')
38555                     .attr('class', 'member-entity-input')
38556                     .call(d3.combobox()
38557                         .minItems(1)
38558                         .fetcher(function(value, callback) {
38559                             callback(relations(value));
38560                         })
38561                         .on('accept', function(d) {
38562                             addMembership(d, $new.select('.member-role').property('value'));
38563                         }));
38564
38565                 $enter.append('input')
38566                     .attr('class', 'member-role')
38567                     .property('type', 'text')
38568                     .attr('maxlength', 255)
38569                     .attr('placeholder', t('inspector.role'))
38570                     .on('change', changeRole);
38571
38572                 $enter.append('button')
38573                     .attr('tabindex', -1)
38574                     .attr('class', 'remove button-input-action member-delete minor')
38575                     .on('click', deleteMembership)
38576                     .call(iD.svg.Icon('#operation-delete'));
38577
38578             } else {
38579                 $list.selectAll('.member-row-new')
38580                     .remove();
38581             }
38582
38583             var $add = $wrap.selectAll('.add-relation')
38584                 .data([0]);
38585
38586             $add.enter()
38587                 .append('button')
38588                 .attr('class', 'add-relation')
38589                 .call(iD.svg.Icon('#icon-plus', 'light'));
38590
38591             $wrap.selectAll('.add-relation')
38592                 .on('click', function() {
38593                     showBlank = true;
38594                     content($wrap);
38595                     $list.selectAll('.member-entity-input').node().focus();
38596                 });
38597         }
38598     }
38599
38600     rawMembershipEditor.entityID = function(_) {
38601         if (!arguments.length) return id;
38602         id = _;
38603         return rawMembershipEditor;
38604     };
38605
38606     return rawMembershipEditor;
38607 };
38608 iD.ui.RawTagEditor = function(context) {
38609     var event = d3.dispatch('change'),
38610         showBlank = false,
38611         state,
38612         preset,
38613         tags,
38614         id;
38615
38616     function rawTagEditor(selection) {
38617         var count = Object.keys(tags).filter(function(d) { return d; }).length;
38618
38619         selection.call(iD.ui.Disclosure()
38620             .title(t('inspector.all_tags') + ' (' + count + ')')
38621             .expanded(context.storage('raw_tag_editor.expanded') === 'true' || preset.isFallback())
38622             .on('toggled', toggled)
38623             .content(content));
38624
38625         function toggled(expanded) {
38626             context.storage('raw_tag_editor.expanded', expanded);
38627             if (expanded) {
38628                 selection.node().parentNode.scrollTop += 200;
38629             }
38630         }
38631     }
38632
38633     function content($wrap) {
38634         var entries = d3.entries(tags);
38635
38636         if (!entries.length || showBlank) {
38637             showBlank = false;
38638             entries.push({key: '', value: ''});
38639         }
38640
38641         var $list = $wrap.selectAll('.tag-list')
38642             .data([0]);
38643
38644         $list.enter().append('ul')
38645             .attr('class', 'tag-list');
38646
38647         var $newTag = $wrap.selectAll('.add-tag')
38648             .data([0]);
38649
38650         $newTag.enter()
38651             .append('button')
38652             .attr('class', 'add-tag')
38653             .call(iD.svg.Icon('#icon-plus', 'light'));
38654
38655         $newTag.on('click', addTag);
38656
38657         var $items = $list.selectAll('li')
38658             .data(entries, function(d) { return d.key; });
38659
38660         // Enter
38661
38662         var $enter = $items.enter().append('li')
38663             .attr('class', 'tag-row cf');
38664
38665         $enter.append('div')
38666             .attr('class', 'key-wrap')
38667             .append('input')
38668             .property('type', 'text')
38669             .attr('class', 'key')
38670             .attr('maxlength', 255);
38671
38672         $enter.append('div')
38673             .attr('class', 'input-wrap-position')
38674             .append('input')
38675             .property('type', 'text')
38676             .attr('class', 'value')
38677             .attr('maxlength', 255);
38678
38679         $enter.append('button')
38680             .attr('tabindex', -1)
38681             .attr('class', 'remove minor')
38682             .call(iD.svg.Icon('#operation-delete'));
38683
38684         if (context.taginfo()) {
38685             $enter.each(bindTypeahead);
38686         }
38687
38688         // Update
38689
38690         $items.order();
38691
38692         $items.each(function(tag) {
38693             var isRelation = (context.entity(id).type === 'relation'),
38694                 reference;
38695             if (isRelation && tag.key === 'type')
38696                 reference = iD.ui.TagReference({rtype: tag.value}, context);
38697             else
38698                 reference = iD.ui.TagReference({key: tag.key, value: tag.value}, context);
38699
38700             if (state === 'hover') {
38701                 reference.showing(false);
38702             }
38703
38704             d3.select(this)
38705                 .call(reference.button)
38706                 .call(reference.body);
38707         });
38708
38709         $items.select('input.key')
38710             .attr('title', function(d) { return d.key; })
38711             .value(function(d) { return d.key; })
38712             .on('blur', keyChange)
38713             .on('change', keyChange);
38714
38715         $items.select('input.value')
38716             .attr('title', function(d) { return d.value; })
38717             .value(function(d) { return d.value; })
38718             .on('blur', valueChange)
38719             .on('change', valueChange)
38720             .on('keydown.push-more', pushMore);
38721
38722         $items.select('button.remove')
38723             .on('click', removeTag);
38724
38725         $items.exit()
38726             .each(unbind)
38727             .remove();
38728
38729         function pushMore() {
38730             if (d3.event.keyCode === 9 && !d3.event.shiftKey &&
38731                 $list.selectAll('li:last-child input.value').node() === this) {
38732                 addTag();
38733             }
38734         }
38735
38736         function bindTypeahead() {
38737             var row = d3.select(this),
38738                 key = row.selectAll('input.key'),
38739                 value = row.selectAll('input.value');
38740
38741             function sort(value, data) {
38742                 var sameletter = [],
38743                     other = [];
38744                 for (var i = 0; i < data.length; i++) {
38745                     if (data[i].value.substring(0, value.length) === value) {
38746                         sameletter.push(data[i]);
38747                     } else {
38748                         other.push(data[i]);
38749                     }
38750                 }
38751                 return sameletter.concat(other);
38752             }
38753
38754             key.call(d3.combobox()
38755                 .fetcher(function(value, callback) {
38756                     context.taginfo().keys({
38757                         debounce: true,
38758                         geometry: context.geometry(id),
38759                         query: value
38760                     }, function(err, data) {
38761                         if (!err) callback(sort(value, data));
38762                     });
38763                 }));
38764
38765             value.call(d3.combobox()
38766                 .fetcher(function(value, callback) {
38767                     context.taginfo().values({
38768                         debounce: true,
38769                         key: key.value(),
38770                         geometry: context.geometry(id),
38771                         query: value
38772                     }, function(err, data) {
38773                         if (!err) callback(sort(value, data));
38774                     });
38775                 }));
38776         }
38777
38778         function unbind() {
38779             var row = d3.select(this);
38780
38781             row.selectAll('input.key')
38782                 .call(d3.combobox.off);
38783
38784             row.selectAll('input.value')
38785                 .call(d3.combobox.off);
38786         }
38787
38788         function keyChange(d) {
38789             var kOld = d.key,
38790                 kNew = this.value.trim(),
38791                 tag = {};
38792
38793             if (kNew && kNew !== kOld) {
38794                 var match = kNew.match(/^(.*?)(?:_(\d+))?$/),
38795                     base = match[1],
38796                     suffix = +(match[2] || 1);
38797                 while (tags[kNew]) {  // rename key if already in use
38798                     kNew = base + '_' + suffix++;
38799                 }
38800             }
38801             tag[kOld] = undefined;
38802             tag[kNew] = d.value;
38803             d.key = kNew; // Maintain DOM identity through the subsequent update.
38804             this.value = kNew;
38805             event.change(tag);
38806         }
38807
38808         function valueChange(d) {
38809             var tag = {};
38810             tag[d.key] = this.value;
38811             event.change(tag);
38812         }
38813
38814         function removeTag(d) {
38815             var tag = {};
38816             tag[d.key] = undefined;
38817             event.change(tag);
38818             d3.select(this.parentNode).remove();
38819         }
38820
38821         function addTag() {
38822             // Wrapped in a setTimeout in case it's being called from a blur
38823             // handler. Without the setTimeout, the call to `content` would
38824             // wipe out the pending value change.
38825             setTimeout(function() {
38826                 showBlank = true;
38827                 content($wrap);
38828                 $list.selectAll('li:last-child input.key').node().focus();
38829             }, 0);
38830         }
38831     }
38832
38833     rawTagEditor.state = function(_) {
38834         if (!arguments.length) return state;
38835         state = _;
38836         return rawTagEditor;
38837     };
38838
38839     rawTagEditor.preset = function(_) {
38840         if (!arguments.length) return preset;
38841         preset = _;
38842         return rawTagEditor;
38843     };
38844
38845     rawTagEditor.tags = function(_) {
38846         if (!arguments.length) return tags;
38847         tags = _;
38848         return rawTagEditor;
38849     };
38850
38851     rawTagEditor.entityID = function(_) {
38852         if (!arguments.length) return id;
38853         id = _;
38854         return rawTagEditor;
38855     };
38856
38857     return d3.rebind(rawTagEditor, event, 'on');
38858 };
38859 iD.ui.Restore = function(context) {
38860     return function(selection) {
38861         if (!context.history().lock() || !context.history().restorableChanges())
38862             return;
38863
38864         var modal = iD.ui.modal(selection, true);
38865
38866         modal.select('.modal')
38867             .attr('class', 'modal fillL col6');
38868
38869         var introModal = modal.select('.content');
38870
38871         introModal.attr('class','cf');
38872
38873         introModal.append('div')
38874             .attr('class', 'modal-section')
38875             .append('h3')
38876             .text(t('restore.heading'));
38877
38878         introModal.append('div')
38879             .attr('class','modal-section')
38880             .append('p')
38881             .text(t('restore.description'));
38882
38883         var buttonWrap = introModal.append('div')
38884             .attr('class', 'modal-actions cf');
38885
38886         var restore = buttonWrap.append('button')
38887             .attr('class', 'restore col6')
38888             .text(t('restore.restore'))
38889             .on('click', function() {
38890                 context.history().restore();
38891                 modal.remove();
38892             });
38893
38894         buttonWrap.append('button')
38895             .attr('class', 'reset col6')
38896             .text(t('restore.reset'))
38897             .on('click', function() {
38898                 context.history().clearSaved();
38899                 modal.remove();
38900             });
38901
38902         restore.node().focus();
38903     };
38904 };
38905 iD.ui.Save = function(context) {
38906     var history = context.history(),
38907         key = iD.ui.cmd('⌘S');
38908
38909
38910     function saving() {
38911         return context.mode().id === 'save';
38912     }
38913
38914     function save() {
38915         d3.event.preventDefault();
38916         if (!context.inIntro() && !saving() && history.hasChanges()) {
38917             context.enter(iD.modes.Save(context));
38918         }
38919     }
38920
38921     function getBackground(numChanges) {
38922         var step;
38923         if (numChanges === 0) {
38924             return null;
38925         } else if (numChanges <= 50) {
38926             step = numChanges / 50;
38927             return d3.interpolateRgb('#fff', '#ff8')(step);  // white -> yellow
38928         } else {
38929             step = Math.min((numChanges - 50) / 50, 1.0);
38930             return d3.interpolateRgb('#ff8', '#f88')(step);  // yellow -> red
38931         }
38932     }
38933
38934     return function(selection) {
38935         var tooltip = bootstrap.tooltip()
38936             .placement('bottom')
38937             .html(true)
38938             .title(iD.ui.tooltipHtml(t('save.no_changes'), key));
38939
38940         var button = selection.append('button')
38941             .attr('class', 'save col12 disabled')
38942             .attr('tabindex', -1)
38943             .on('click', save)
38944             .call(tooltip);
38945
38946         button.append('span')
38947             .attr('class', 'label')
38948             .text(t('save.title'));
38949
38950         button.append('span')
38951             .attr('class', 'count')
38952             .text('0');
38953
38954         var keybinding = d3.keybinding('undo-redo')
38955             .on(key, save, true);
38956
38957         d3.select(document)
38958             .call(keybinding);
38959
38960         var numChanges = 0;
38961
38962         context.history().on('change.save', function() {
38963             var _ = history.difference().summary().length;
38964             if (_ === numChanges)
38965                 return;
38966             numChanges = _;
38967
38968             tooltip.title(iD.ui.tooltipHtml(t(numChanges > 0 ?
38969                     'save.help' : 'save.no_changes'), key));
38970
38971             var background = getBackground(numChanges);
38972
38973             button
38974                 .classed('disabled', numChanges === 0)
38975                 .classed('has-count', numChanges > 0)
38976                 .style('background', background);
38977
38978             button.select('span.count')
38979                 .text(numChanges)
38980                 .style('background', background)
38981                 .style('border-color', background);
38982         });
38983
38984         context.on('enter.save', function() {
38985             button.property('disabled', saving());
38986             if (saving()) button.call(tooltip.hide);
38987         });
38988     };
38989 };
38990 iD.ui.Scale = function(context) {
38991     var projection = context.projection,
38992         imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
38993         maxLength = 180,
38994         tickHeight = 8;
38995
38996     function scaleDefs(loc1, loc2) {
38997         var lat = (loc2[1] + loc1[1]) / 2,
38998             conversion = (imperial ? 3.28084 : 1),
38999             dist = iD.geo.lonToMeters(loc2[0] - loc1[0], lat) * conversion,
39000             scale = { dist: 0, px: 0, text: '' },
39001             buckets, i, val, dLon;
39002
39003         if (imperial) {
39004             buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
39005         } else {
39006             buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
39007         }
39008
39009         // determine a user-friendly endpoint for the scale
39010         for (i = 0; i < buckets.length; i++) {
39011             val = buckets[i];
39012             if (dist >= val) {
39013                 scale.dist = Math.floor(dist / val) * val;
39014                 break;
39015             }
39016         }
39017
39018         dLon = iD.geo.metersToLon(scale.dist / conversion, lat);
39019         scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
39020
39021         if (imperial) {
39022             if (scale.dist >= 5280) {
39023                 scale.dist /= 5280;
39024                 scale.text = String(scale.dist) + ' mi';
39025             } else {
39026                 scale.text = String(scale.dist) + ' ft';
39027             }
39028         } else {
39029             if (scale.dist >= 1000) {
39030                 scale.dist /= 1000;
39031                 scale.text = String(scale.dist) + ' km';
39032             } else {
39033                 scale.text = String(scale.dist) + ' m';
39034             }
39035         }
39036
39037         return scale;
39038     }
39039
39040     function update(selection) {
39041         // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
39042         var dims = context.map().dimensions(),
39043             loc1 = projection.invert([0, dims[1]]),
39044             loc2 = projection.invert([maxLength, dims[1]]),
39045             scale = scaleDefs(loc1, loc2);
39046
39047         selection.select('#scalepath')
39048             .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
39049
39050         selection.select('#scaletext')
39051             .attr('x', scale.px + 8)
39052             .attr('y', tickHeight)
39053             .text(scale.text);
39054     }
39055
39056
39057     return function(selection) {
39058         function switchUnits() {
39059             imperial = !imperial;
39060             selection.call(update);
39061         }
39062
39063         var g = selection.append('svg')
39064             .attr('id', 'scale')
39065             .on('click', switchUnits)
39066             .append('g')
39067             .attr('transform', 'translate(10,11)');
39068
39069         g.append('path').attr('id', 'scalepath');
39070         g.append('text').attr('id', 'scaletext');
39071
39072         selection.call(update);
39073
39074         context.map().on('move.scale', function() {
39075             update(selection);
39076         });
39077     };
39078 };
39079 iD.ui.SelectionList = function(context, selectedIDs) {
39080
39081     function selectEntity(entity) {
39082         context.enter(iD.modes.Select(context, [entity.id]).suppressMenu(true));
39083     }
39084
39085
39086     function selectionList(selection) {
39087         selection.classed('selection-list-pane', true);
39088
39089         var header = selection.append('div')
39090             .attr('class', 'header fillL cf');
39091
39092         header.append('h3')
39093             .text(t('inspector.multiselect'));
39094
39095         var listWrap = selection.append('div')
39096             .attr('class', 'inspector-body');
39097
39098         var list = listWrap.append('div')
39099             .attr('class', 'feature-list cf');
39100
39101         context.history().on('change.selection-list', drawList);
39102         drawList();
39103
39104         function drawList() {
39105             var entities = selectedIDs
39106                 .map(function(id) { return context.hasEntity(id); })
39107                 .filter(function(entity) { return entity; });
39108
39109             var items = list.selectAll('.feature-list-item')
39110                 .data(entities, iD.Entity.key);
39111
39112             var enter = items.enter().append('button')
39113                 .attr('class', 'feature-list-item')
39114                 .on('click', selectEntity);
39115
39116             // Enter
39117             var label = enter.append('div')
39118                 .attr('class', 'label')
39119                 .call(iD.svg.Icon('', 'pre-text'));
39120
39121             label.append('span')
39122                 .attr('class', 'entity-type');
39123
39124             label.append('span')
39125                 .attr('class', 'entity-name');
39126
39127             // Update
39128             items.selectAll('use')
39129                 .attr('href', function() {
39130                     var entity = this.parentNode.parentNode.__data__;
39131                     return '#icon-' + context.geometry(entity.id);
39132                 });
39133
39134             items.selectAll('.entity-type')
39135                 .text(function(entity) { return context.presets().match(entity, context.graph()).name(); });
39136
39137             items.selectAll('.entity-name')
39138                 .text(function(entity) { return iD.util.displayName(entity); });
39139
39140             // Exit
39141             items.exit()
39142                 .remove();
39143         }
39144     }
39145
39146     return selectionList;
39147
39148 };
39149 iD.ui.Sidebar = function(context) {
39150     var inspector = iD.ui.Inspector(context),
39151         current;
39152
39153     function sidebar(selection) {
39154         var featureListWrap = selection.append('div')
39155             .attr('class', 'feature-list-pane')
39156             .call(iD.ui.FeatureList(context));
39157
39158         selection.call(iD.ui.Notice(context));
39159
39160         var inspectorWrap = selection.append('div')
39161             .attr('class', 'inspector-hidden inspector-wrap fr');
39162
39163         function hover(id) {
39164             if (!current && context.hasEntity(id)) {
39165                 featureListWrap.classed('inspector-hidden', true);
39166                 inspectorWrap.classed('inspector-hidden', false)
39167                     .classed('inspector-hover', true);
39168
39169                 if (inspector.entityID() !== id || inspector.state() !== 'hover') {
39170                     inspector
39171                         .state('hover')
39172                         .entityID(id);
39173
39174                     inspectorWrap.call(inspector);
39175                 }
39176             } else if (!current) {
39177                 featureListWrap.classed('inspector-hidden', false);
39178                 inspectorWrap.classed('inspector-hidden', true);
39179                 inspector.state('hide');
39180             }
39181         }
39182
39183         sidebar.hover = _.throttle(hover, 200);
39184
39185         sidebar.select = function(id, newFeature) {
39186             if (!current && id) {
39187                 featureListWrap.classed('inspector-hidden', true);
39188                 inspectorWrap.classed('inspector-hidden', false)
39189                     .classed('inspector-hover', false);
39190
39191                 if (inspector.entityID() !== id || inspector.state() !== 'select') {
39192                     inspector
39193                         .state('select')
39194                         .entityID(id)
39195                         .newFeature(newFeature);
39196
39197                     inspectorWrap.call(inspector);
39198                 }
39199             } else if (!current) {
39200                 featureListWrap.classed('inspector-hidden', false);
39201                 inspectorWrap.classed('inspector-hidden', true);
39202                 inspector.state('hide');
39203             }
39204         };
39205
39206         sidebar.show = function(component) {
39207             featureListWrap.classed('inspector-hidden', true);
39208             inspectorWrap.classed('inspector-hidden', true);
39209             if (current) current.remove();
39210             current = selection.append('div')
39211                 .attr('class', 'sidebar-component')
39212                 .call(component);
39213         };
39214
39215         sidebar.hide = function() {
39216             featureListWrap.classed('inspector-hidden', false);
39217             inspectorWrap.classed('inspector-hidden', true);
39218             if (current) current.remove();
39219             current = null;
39220         };
39221     }
39222
39223     sidebar.hover = function() {};
39224     sidebar.hover.cancel = function() {};
39225     sidebar.select = function() {};
39226     sidebar.show = function() {};
39227     sidebar.hide = function() {};
39228
39229     return sidebar;
39230 };
39231 iD.ui.SourceSwitch = function(context) {
39232     var keys;
39233
39234     function click() {
39235         d3.event.preventDefault();
39236
39237         if (context.history().hasChanges() &&
39238             !window.confirm(t('source_switch.lose_changes'))) return;
39239
39240         var live = d3.select(this)
39241             .classed('live');
39242
39243         context.connection()
39244             .switch(live ? keys[1] : keys[0]);
39245
39246         context.enter(iD.modes.Browse(context));
39247         context.flush();
39248
39249         d3.select(this)
39250             .text(live ? t('source_switch.dev') : t('source_switch.live'))
39251             .classed('live', !live);
39252     }
39253
39254     var sourceSwitch = function(selection) {
39255         selection.append('a')
39256             .attr('href', '#')
39257             .text(t('source_switch.live'))
39258             .classed('live', true)
39259             .attr('tabindex', -1)
39260             .on('click', click);
39261     };
39262
39263     sourceSwitch.keys = function(_) {
39264         if (!arguments.length) return keys;
39265         keys = _;
39266         return sourceSwitch;
39267     };
39268
39269     return sourceSwitch;
39270 };
39271 iD.ui.Spinner = function(context) {
39272     var connection = context.connection();
39273
39274     return function(selection) {
39275         var img = selection.append('img')
39276             .attr('src', context.imagePath('loader-black.gif'))
39277             .style('opacity', 0);
39278
39279         connection.on('loading.spinner', function() {
39280             img.transition()
39281                 .style('opacity', 1);
39282         });
39283
39284         connection.on('loaded.spinner', function() {
39285             img.transition()
39286                 .style('opacity', 0);
39287         });
39288     };
39289 };
39290 iD.ui.Splash = function(context) {
39291     return function(selection) {
39292         if (context.storage('sawSplash'))
39293              return;
39294
39295         context.storage('sawSplash', true);
39296
39297         var modal = iD.ui.modal(selection);
39298
39299         modal.select('.modal')
39300             .attr('class', 'modal-splash modal col6');
39301
39302         var introModal = modal.select('.content')
39303             .append('div')
39304             .attr('class', 'fillL');
39305
39306         introModal.append('div')
39307             .attr('class','modal-section cf')
39308             .append('h3').text(t('splash.welcome'));
39309
39310         introModal.append('div')
39311             .attr('class','modal-section')
39312             .append('p')
39313             .html(t('splash.text', {
39314                 version: iD.version,
39315                 website: '<a href="http://ideditor.com/">ideditor.com</a>',
39316                 github: '<a href="https://github.com/openstreetmap/iD">github.com</a>'
39317             }));
39318
39319         var buttons = introModal.append('div').attr('class', 'modal-actions cf');
39320
39321         buttons.append('button')
39322             .attr('class', 'col6 walkthrough')
39323             .text(t('splash.walkthrough'))
39324             .on('click', function() {
39325                 d3.select(document.body).call(iD.ui.intro(context));
39326                 modal.close();
39327             });
39328
39329         buttons.append('button')
39330             .attr('class', 'col6 start')
39331             .text(t('splash.start'))
39332             .on('click', modal.close);
39333
39334         modal.select('button.close').attr('class','hide');
39335
39336     };
39337 };
39338 iD.ui.Status = function(context) {
39339     var connection = context.connection(),
39340         errCount = 0;
39341
39342     return function(selection) {
39343
39344         function update() {
39345
39346             connection.status(function(err, apiStatus) {
39347
39348                 selection.html('');
39349
39350                 if (err && errCount++ < 2) return;
39351
39352                 if (err) {
39353                     selection.text(t('status.error'));
39354
39355                 } else if (apiStatus === 'readonly') {
39356                     selection.text(t('status.readonly'));
39357
39358                 } else if (apiStatus === 'offline') {
39359                     selection.text(t('status.offline'));
39360                 }
39361
39362                 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
39363                 if (!err) errCount = 0;
39364
39365             });
39366         }
39367
39368         connection.on('auth', function() { update(selection); });
39369         window.setInterval(update, 90000);
39370         update(selection);
39371     };
39372 };
39373 iD.ui.Success = function(context) {
39374     var dispatch = d3.dispatch('cancel'),
39375         changeset;
39376
39377     function success(selection) {
39378         var message = (changeset.comment || t('success.edited_osm')).substring(0, 130) +
39379             ' ' + context.connection().changesetURL(changeset.id);
39380
39381         var header = selection.append('div')
39382             .attr('class', 'header fillL');
39383
39384         header.append('button')
39385             .attr('class', 'fr')
39386             .on('click', function() { dispatch.cancel(); })
39387             .call(iD.svg.Icon('#icon-close'));
39388
39389         header.append('h3')
39390             .text(t('success.just_edited'));
39391
39392         var body = selection.append('div')
39393             .attr('class', 'body save-success fillL');
39394
39395         body.append('p')
39396             .html(t('success.help_html'));
39397
39398         body.append('a')
39399             .attr('class', 'details')
39400             .attr('target', '_blank')
39401             .attr('tabindex', -1)
39402             .call(iD.svg.Icon('#icon-out-link', 'inline'))
39403             .attr('href', t('success.help_link_url'))
39404             .append('span')
39405             .text(t('success.help_link_text'));
39406
39407         var changesetURL = context.connection().changesetURL(changeset.id);
39408
39409         body.append('a')
39410             .attr('class', 'button col12 osm')
39411             .attr('target', '_blank')
39412             .attr('href', changesetURL)
39413             .text(t('success.view_on_osm'));
39414
39415         var sharing = {
39416             facebook: 'https://facebook.com/sharer/sharer.php?u=' + encodeURIComponent(changesetURL),
39417             twitter: 'https://twitter.com/intent/tweet?source=webclient&text=' + encodeURIComponent(message),
39418             google: 'https://plus.google.com/share?url=' + encodeURIComponent(changesetURL)
39419         };
39420
39421         body.selectAll('.button.social')
39422             .data(d3.entries(sharing))
39423             .enter()
39424             .append('a')
39425             .attr('class', 'button social col4')
39426             .attr('target', '_blank')
39427             .attr('href', function(d) { return d.value; })
39428             .call(bootstrap.tooltip()
39429                 .title(function(d) { return t('success.' + d.key); })
39430                 .placement('bottom'))
39431             .each(function(d) { d3.select(this).call(iD.svg.Icon('#logo-' + d.key, 'social')); });
39432     }
39433
39434     success.changeset = function(_) {
39435         if (!arguments.length) return changeset;
39436         changeset = _;
39437         return success;
39438     };
39439
39440     return d3.rebind(success, dispatch, 'on');
39441 };
39442 iD.ui.TagReference = function(tag, context) {
39443     var tagReference = {},
39444         button,
39445         body,
39446         loaded,
39447         showing;
39448
39449     function findLocal(data) {
39450         var locale = iD.detect().locale.toLowerCase(),
39451             localized;
39452
39453         localized = _.find(data, function(d) {
39454             return d.lang.toLowerCase() === locale;
39455         });
39456         if (localized) return localized;
39457
39458         // try the non-regional version of a language, like
39459         // 'en' if the language is 'en-US'
39460         if (locale.indexOf('-') !== -1) {
39461             var first = locale.split('-')[0];
39462             localized = _.find(data, function(d) {
39463                 return d.lang.toLowerCase() === first;
39464             });
39465             if (localized) return localized;
39466         }
39467
39468         // finally fall back to english
39469         return _.find(data, function(d) {
39470             return d.lang.toLowerCase() === 'en';
39471         });
39472     }
39473
39474     function load(param) {
39475         button.classed('tag-reference-loading', true);
39476
39477         context.taginfo().docs(param, function show(err, data) {
39478             var docs;
39479             if (!err && data) {
39480                 docs = findLocal(data);
39481             }
39482
39483             body.html('');
39484
39485             if (!docs || !docs.description) {
39486                 if (param.hasOwnProperty('value')) {
39487                     load(_.omit(param, 'value'));   // retry with key only
39488                 } else {
39489                     body.append('p').text(t('inspector.no_documentation_key'));
39490                     done();
39491                 }
39492                 return;
39493             }
39494
39495             if (docs.image && docs.image.thumb_url_prefix) {
39496                 body
39497                     .append('img')
39498                     .attr('class', 'wiki-image')
39499                     .attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix)
39500                     .on('load', function() { done(); })
39501                     .on('error', function() { d3.select(this).remove(); done(); });
39502             } else {
39503                 done();
39504             }
39505
39506             body
39507                 .append('p')
39508                 .text(docs.description);
39509
39510             body
39511                 .append('a')
39512                 .attr('target', '_blank')
39513                 .attr('tabindex', -1)
39514                 .attr('href', 'https://wiki.openstreetmap.org/wiki/' + docs.title)
39515                 .call(iD.svg.Icon('#icon-out-link', 'inline'))
39516                 .append('span')
39517                 .text(t('inspector.reference'));
39518         });
39519     }
39520
39521     function done() {
39522         loaded = true;
39523
39524         button.classed('tag-reference-loading', false);
39525
39526         body.transition()
39527             .duration(200)
39528             .style('max-height', '200px')
39529             .style('opacity', '1');
39530
39531         showing = true;
39532     }
39533
39534     function hide(selection) {
39535         selection = selection || body.transition().duration(200);
39536
39537         selection
39538             .style('max-height', '0px')
39539             .style('opacity', '0');
39540
39541         showing = false;
39542     }
39543
39544     tagReference.button = function(selection) {
39545         button = selection.selectAll('.tag-reference-button')
39546             .data([0]);
39547
39548         button.enter()
39549             .append('button')
39550             .attr('class', 'tag-reference-button')
39551             .attr('tabindex', -1)
39552             .call(iD.svg.Icon('#icon-inspect'));
39553
39554         button.on('click', function () {
39555             d3.event.stopPropagation();
39556             d3.event.preventDefault();
39557             if (showing) {
39558                 hide();
39559             } else if (loaded) {
39560                 done();
39561             } else {
39562                 if (context.taginfo()) {
39563                     load(tag);
39564                 }
39565             }
39566         });
39567     };
39568
39569     tagReference.body = function(selection) {
39570         body = selection.selectAll('.tag-reference-body')
39571             .data([0]);
39572
39573         body.enter().append('div')
39574             .attr('class', 'tag-reference-body cf')
39575             .style('max-height', '0')
39576             .style('opacity', '0');
39577
39578         if (showing === false) {
39579             hide(body);
39580         }
39581     };
39582
39583     tagReference.showing = function(_) {
39584         if (!arguments.length) return showing;
39585         showing = _;
39586         return tagReference;
39587     };
39588
39589     return tagReference;
39590 };
39591 // toggles the visibility of ui elements, using a combination of the
39592 // hide class, which sets display=none, and a d3 transition for opacity.
39593 // this will cause blinking when called repeatedly, so check that the
39594 // value actually changes between calls.
39595 iD.ui.Toggle = function(show, callback) {
39596     return function(selection) {
39597         selection
39598             .style('opacity', show ? 0 : 1)
39599             .classed('hide', false)
39600             .transition()
39601             .style('opacity', show ? 1 : 0)
39602             .each('end', function() {
39603                 d3.select(this)
39604                     .classed('hide', !show)
39605                     .style('opacity', null);
39606                 if (callback) callback.apply(this);
39607             });
39608     };
39609 };
39610 iD.ui.UndoRedo = function(context) {
39611     var commands = [{
39612         id: 'undo',
39613         cmd: iD.ui.cmd('⌘Z'),
39614         action: function() { if (!(context.inIntro() || saving())) context.undo(); },
39615         annotation: function() { return context.history().undoAnnotation(); }
39616     }, {
39617         id: 'redo',
39618         cmd: iD.ui.cmd('⌘⇧Z'),
39619         action: function() {if (!(context.inIntro() || saving())) context.redo(); },
39620         annotation: function() { return context.history().redoAnnotation(); }
39621     }];
39622
39623     function saving() {
39624         return context.mode().id === 'save';
39625     }
39626
39627     return function(selection) {
39628         var tooltip = bootstrap.tooltip()
39629             .placement('bottom')
39630             .html(true)
39631             .title(function (d) {
39632                 return iD.ui.tooltipHtml(d.annotation() ?
39633                     t(d.id + '.tooltip', {action: d.annotation()}) :
39634                     t(d.id + '.nothing'), d.cmd);
39635             });
39636
39637         var buttons = selection.selectAll('button')
39638             .data(commands)
39639             .enter().append('button')
39640             .attr('class', 'col6 disabled')
39641             .on('click', function(d) { return d.action(); })
39642             .call(tooltip);
39643
39644         buttons.each(function(d) {
39645             d3.select(this)
39646                 .call(iD.svg.Icon('#icon-' + d.id));
39647         });
39648
39649         var keybinding = d3.keybinding('undo')
39650             .on(commands[0].cmd, function() { d3.event.preventDefault(); commands[0].action(); })
39651             .on(commands[1].cmd, function() { d3.event.preventDefault(); commands[1].action(); });
39652
39653         d3.select(document)
39654             .call(keybinding);
39655
39656         context.history()
39657             .on('change.undo_redo', update);
39658
39659         context
39660             .on('enter.undo_redo', update);
39661
39662         function update() {
39663             buttons
39664                 .property('disabled', saving())
39665                 .classed('disabled', function(d) { return !d.annotation(); })
39666                 .each(function() {
39667                     var selection = d3.select(this);
39668                     if (selection.property('tooltipVisible')) {
39669                         selection.call(tooltip.show);
39670                     }
39671                 });
39672         }
39673     };
39674 };
39675 iD.ui.ViewOnOSM = function(context) {
39676     var id;
39677
39678     function viewOnOSM(selection) {
39679         var entity = context.entity(id);
39680
39681         selection.style('display', entity.isNew() ? 'none' : null);
39682
39683         var $link = selection.selectAll('.view-on-osm')
39684             .data([0]);
39685
39686         $link.enter()
39687             .append('a')
39688             .attr('class', 'view-on-osm')
39689             .attr('target', '_blank')
39690             .call(iD.svg.Icon('#icon-out-link', 'inline'))
39691             .append('span')
39692             .text(t('inspector.view_on_osm'));
39693
39694         $link
39695             .attr('href', context.connection().entityURL(entity));
39696     }
39697
39698     viewOnOSM.entityID = function(_) {
39699         if (!arguments.length) return id;
39700         id = _;
39701         return viewOnOSM;
39702     };
39703
39704     return viewOnOSM;
39705 };
39706 iD.ui.Zoom = function(context) {
39707     var zooms = [{
39708         id: 'zoom-in',
39709         icon: 'plus',
39710         title: t('zoom.in'),
39711         action: context.zoomIn,
39712         key: '+'
39713     }, {
39714         id: 'zoom-out',
39715         icon: 'minus',
39716         title: t('zoom.out'),
39717         action: context.zoomOut,
39718         key: '-'
39719     }];
39720
39721     function zoomIn() {
39722         d3.event.preventDefault();
39723         if (!context.inIntro()) context.zoomIn();
39724     }
39725
39726     function zoomOut() {
39727         d3.event.preventDefault();
39728         if (!context.inIntro()) context.zoomOut();
39729     }
39730
39731     function zoomInFurther() {
39732         d3.event.preventDefault();
39733         if (!context.inIntro()) context.zoomInFurther();
39734     }
39735
39736     function zoomOutFurther() {
39737         d3.event.preventDefault();
39738         if (!context.inIntro()) context.zoomOutFurther();
39739     }
39740
39741
39742     return function(selection) {
39743         var button = selection.selectAll('button')
39744             .data(zooms)
39745             .enter().append('button')
39746             .attr('tabindex', -1)
39747             .attr('class', function(d) { return d.id; })
39748             .on('click.editor', function(d) { d.action(); })
39749             .call(bootstrap.tooltip()
39750                 .placement('left')
39751                 .html(true)
39752                 .title(function(d) {
39753                     return iD.ui.tooltipHtml(d.title, d.key);
39754                 }));
39755
39756         button.each(function(d) {
39757             d3.select(this)
39758                 .call(iD.svg.Icon('#icon-' + d.icon, 'light'));
39759         });
39760
39761         var keybinding = d3.keybinding('zoom');
39762
39763         _.each(['=','ffequals','plus','ffplus'], function(key) {
39764             keybinding.on(key, zoomIn);
39765             keybinding.on('⇧' + key, zoomIn);
39766             keybinding.on(iD.ui.cmd('⌘' + key), zoomInFurther);
39767             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomInFurther);
39768         });
39769         _.each(['-','ffminus','_','dash'], function(key) {
39770             keybinding.on(key, zoomOut);
39771             keybinding.on('⇧' + key, zoomOut);
39772             keybinding.on(iD.ui.cmd('⌘' + key), zoomOutFurther);
39773             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomOutFurther);
39774         });
39775
39776         d3.select(document)
39777             .call(keybinding);
39778     };
39779 };
39780 iD.ui.preset.access = function(field) {
39781     var dispatch = d3.dispatch('change'),
39782         items;
39783
39784     function access(selection) {
39785         var wrap = selection.selectAll('.preset-input-wrap')
39786             .data([0]);
39787
39788         wrap.enter().append('div')
39789             .attr('class', 'cf preset-input-wrap')
39790             .append('ul');
39791
39792         items = wrap.select('ul').selectAll('li')
39793             .data(field.keys);
39794
39795         // Enter
39796
39797         var enter = items.enter().append('li')
39798             .attr('class', function(d) { return 'cf preset-access-' + d; });
39799
39800         enter.append('span')
39801             .attr('class', 'col6 label preset-label-access')
39802             .attr('for', function(d) { return 'preset-input-access-' + d; })
39803             .text(function(d) { return field.t('types.' + d); });
39804
39805         enter.append('div')
39806             .attr('class', 'col6 preset-input-access-wrap')
39807             .append('input')
39808             .attr('type', 'text')
39809             .attr('class', 'preset-input-access')
39810             .attr('id', function(d) { return 'preset-input-access-' + d; })
39811             .each(function(d) {
39812                 d3.select(this)
39813                     .call(d3.combobox()
39814                         .data(access.options(d)));
39815             });
39816
39817         // Update
39818
39819         wrap.selectAll('.preset-input-access')
39820             .on('change', change)
39821             .on('blur', change);
39822     }
39823
39824     function change(d) {
39825         var tag = {};
39826         tag[d] = d3.select(this).value() || undefined;
39827         dispatch.change(tag);
39828     }
39829
39830     access.options = function(type) {
39831         var options = ['no', 'permissive', 'private', 'destination'];
39832
39833         if (type !== 'access') {
39834             options.unshift('yes');
39835             options.push('designated');
39836
39837             if (type === 'bicycle') {
39838                 options.push('dismount');
39839             }
39840         }
39841
39842         return options.map(function(option) {
39843             return {
39844                 title: field.t('options.' + option + '.description'),
39845                 value: option
39846             };
39847         });
39848     };
39849
39850     var placeholders = {
39851         footway: {
39852             foot: 'designated',
39853             motor_vehicle: 'no'
39854         },
39855         steps: {
39856             foot: 'yes',
39857             motor_vehicle: 'no',
39858             bicycle: 'no',
39859             horse: 'no'
39860         },
39861         pedestrian: {
39862             foot: 'yes',
39863             motor_vehicle: 'no'
39864         },
39865         cycleway: {
39866             motor_vehicle: 'no',
39867             bicycle: 'designated'
39868         },
39869         bridleway: {
39870             motor_vehicle: 'no',
39871             horse: 'designated'
39872         },
39873         path: {
39874             foot: 'yes',
39875             motor_vehicle: 'no',
39876             bicycle: 'yes',
39877             horse: 'yes'
39878         },
39879         motorway: {
39880             foot: 'no',
39881             motor_vehicle: 'yes',
39882             bicycle: 'no',
39883             horse: 'no'
39884         },
39885         trunk: {
39886             motor_vehicle: 'yes'
39887         },
39888         primary: {
39889             foot: 'yes',
39890             motor_vehicle: 'yes',
39891             bicycle: 'yes',
39892             horse: 'yes'
39893         },
39894         secondary: {
39895             foot: 'yes',
39896             motor_vehicle: 'yes',
39897             bicycle: 'yes',
39898             horse: 'yes'
39899         },
39900         tertiary: {
39901             foot: 'yes',
39902             motor_vehicle: 'yes',
39903             bicycle: 'yes',
39904             horse: 'yes'
39905         },
39906         residential: {
39907             foot: 'yes',
39908             motor_vehicle: 'yes',
39909             bicycle: 'yes',
39910             horse: 'yes'
39911         },
39912         unclassified: {
39913             foot: 'yes',
39914             motor_vehicle: 'yes',
39915             bicycle: 'yes',
39916             horse: 'yes'
39917         },
39918         service: {
39919             foot: 'yes',
39920             motor_vehicle: 'yes',
39921             bicycle: 'yes',
39922             horse: 'yes'
39923         },
39924         motorway_link: {
39925             foot: 'no',
39926             motor_vehicle: 'yes',
39927             bicycle: 'no',
39928             horse: 'no'
39929         },
39930         trunk_link: {
39931             motor_vehicle: 'yes'
39932         },
39933         primary_link: {
39934             foot: 'yes',
39935             motor_vehicle: 'yes',
39936             bicycle: 'yes',
39937             horse: 'yes'
39938         },
39939         secondary_link: {
39940             foot: 'yes',
39941             motor_vehicle: 'yes',
39942             bicycle: 'yes',
39943             horse: 'yes'
39944         },
39945         tertiary_link: {
39946             foot: 'yes',
39947             motor_vehicle: 'yes',
39948             bicycle: 'yes',
39949             horse: 'yes'
39950         }
39951     };
39952
39953     access.tags = function(tags) {
39954         items.selectAll('.preset-input-access')
39955             .value(function(d) { return tags[d] || ''; })
39956             .attr('placeholder', function() {
39957                 return tags.access ? tags.access : field.placeholder();
39958             });
39959
39960         // items.selectAll('#preset-input-access-access')
39961         //     .attr('placeholder', 'yes');
39962
39963         _.forEach(placeholders[tags.highway], function(v, k) {
39964             items.selectAll('#preset-input-access-' + k)
39965                 .attr('placeholder', function() { return (tags.access || v); });
39966         });
39967     };
39968
39969     access.focus = function() {
39970         items.selectAll('.preset-input-access')
39971             .node().focus();
39972     };
39973
39974     return d3.rebind(access, dispatch, 'on');
39975 };
39976 iD.ui.preset.address = function(field, context) {
39977     var dispatch = d3.dispatch('init', 'change'),
39978         wrap,
39979         entity,
39980         isInitialized;
39981
39982     var widths = {
39983         housenumber: 1/3,
39984         street: 2/3,
39985         city: 2/3,
39986         state: 1/4,
39987         postcode: 1/3
39988     };
39989
39990     function getStreets() {
39991         var extent = entity.extent(context.graph()),
39992             l = extent.center(),
39993             box = iD.geo.Extent(l).padByMeters(200);
39994
39995         return context.intersects(box)
39996             .filter(isAddressable)
39997             .map(function(d) {
39998                 var loc = context.projection([
39999                     (extent[0][0] + extent[1][0]) / 2,
40000                     (extent[0][1] + extent[1][1]) / 2]),
40001                     choice = iD.geo.chooseEdge(context.childNodes(d), loc, context.projection);
40002                 return {
40003                     title: d.tags.name,
40004                     value: d.tags.name,
40005                     dist: choice.distance
40006                 };
40007             }).sort(function(a, b) {
40008                 return a.dist - b.dist;
40009             });
40010
40011         function isAddressable(d) {
40012             return d.tags.highway && d.tags.name && d.type === 'way';
40013         }
40014     }
40015
40016     function getCities() {
40017         var extent = entity.extent(context.graph()),
40018             l = extent.center(),
40019             box = iD.geo.Extent(l).padByMeters(200);
40020
40021         return context.intersects(box)
40022             .filter(isAddressable)
40023             .map(function(d) {
40024                 return {
40025                     title: d.tags['addr:city'] || d.tags.name,
40026                     value: d.tags['addr:city'] || d.tags.name,
40027                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
40028                 };
40029             }).sort(function(a, b) {
40030                 return a.dist - b.dist;
40031             });
40032
40033         function isAddressable(d) {
40034             if (d.tags.name &&
40035                 (d.tags.admin_level === '8' || d.tags.border_type === 'city'))
40036                 return true;
40037
40038             if (d.tags.place && d.tags.name && (
40039                     d.tags.place === 'city' ||
40040                     d.tags.place === 'town' ||
40041                     d.tags.place === 'village'))
40042                 return true;
40043
40044             if (d.tags['addr:city']) return true;
40045
40046             return false;
40047         }
40048     }
40049
40050     function getPostCodes() {
40051         var extent = entity.extent(context.graph()),
40052             l = extent.center(),
40053             box = iD.geo.Extent(l).padByMeters(200);
40054
40055         return context.intersects(box)
40056             .filter(isAddressable)
40057             .map(function(d) {
40058                 return {
40059                     title: d.tags['addr:postcode'],
40060                     value: d.tags['addr:postcode'],
40061                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
40062                 };
40063             }).sort(function(a, b) {
40064                 return a.dist - b.dist;
40065             });
40066
40067         function isAddressable(d) {
40068             return d.tags['addr:postcode'];
40069         }
40070     }
40071
40072     function address(selection) {
40073         isInitialized = false;
40074
40075         wrap = selection.selectAll('.preset-input-wrap')
40076             .data([0]);
40077
40078         // Enter
40079
40080         wrap.enter()
40081             .append('div')
40082             .attr('class', 'preset-input-wrap');
40083
40084         var center = entity.extent(context.graph()).center(),
40085             addressFormat;
40086
40087         iD.services.nominatim().countryCode(center, function (err, countryCode) {
40088             addressFormat = _.find(iD.data.addressFormats, function (a) {
40089                 return a && a.countryCodes && _.includes(a.countryCodes, countryCode);
40090             }) || _.first(iD.data.addressFormats);
40091
40092             function row(r) {
40093                 // Normalize widths.
40094                 var total = _.reduce(r, function(sum, field) {
40095                     return sum + (widths[field] || 0.5);
40096                 }, 0);
40097
40098                 return r.map(function (field) {
40099                     return {
40100                         id: field,
40101                         width: (widths[field] || 0.5) / total
40102                     };
40103                 });
40104             }
40105
40106             wrap.selectAll('div')
40107                 .data(addressFormat.format)
40108                 .enter()
40109                 .append('div')
40110                 .attr('class', 'addr-row')
40111                 .selectAll('input')
40112                 .data(row)
40113                 .enter()
40114                 .append('input')
40115                 .property('type', 'text')
40116                 .attr('placeholder', function (d) { return field.t('placeholders.' + d.id); })
40117                 .attr('class', function (d) { return 'addr-' + d.id; })
40118                 .style('width', function (d) { return d.width * 100 + '%'; });
40119
40120             // Update
40121
40122             wrap.selectAll('.addr-street')
40123                 .call(d3.combobox()
40124                     .fetcher(function(value, callback) {
40125                         callback(getStreets());
40126                     }));
40127
40128             wrap.selectAll('.addr-city')
40129                 .call(d3.combobox()
40130                     .fetcher(function(value, callback) {
40131                         callback(getCities());
40132                     }));
40133
40134             wrap.selectAll('.addr-postcode')
40135                 .call(d3.combobox()
40136                     .fetcher(function(value, callback) {
40137                         callback(getPostCodes());
40138                     }));
40139
40140             wrap.selectAll('input')
40141                 .on('blur', change())
40142                 .on('change', change());
40143
40144             wrap.selectAll('input:not(.combobox-input)')
40145                 .on('input', change(true));
40146
40147             dispatch.init();
40148             isInitialized = true;
40149         });
40150     }
40151
40152     function change(onInput) {
40153         return function() {
40154             var tags = {};
40155
40156             wrap.selectAll('input')
40157                 .each(function (field) {
40158                     tags['addr:' + field.id] = this.value || undefined;
40159                 });
40160
40161             dispatch.change(tags, onInput);
40162         };
40163     }
40164
40165     function updateTags(tags) {
40166         wrap.selectAll('input')
40167             .value(function (field) {
40168                 return tags['addr:' + field.id] || '';
40169             });
40170     }
40171
40172     address.entity = function(_) {
40173         if (!arguments.length) return entity;
40174         entity = _;
40175         return address;
40176     };
40177
40178     address.tags = function(tags) {
40179         if (isInitialized) {
40180             updateTags(tags);
40181         } else {
40182             dispatch.on('init', function () {
40183                 updateTags(tags);
40184             });
40185         }
40186     };
40187
40188     address.focus = function() {
40189         var node = wrap.selectAll('input').node();
40190         if (node) node.focus();
40191     };
40192
40193     return d3.rebind(address, dispatch, 'on');
40194 };
40195 iD.ui.preset.check =
40196 iD.ui.preset.defaultcheck = function(field) {
40197     var dispatch = d3.dispatch('change'),
40198         options = field.strings && field.strings.options,
40199         values = [],
40200         texts = [],
40201         entity, value, box, text, label;
40202
40203     if (options) {
40204         for (var k in options) {
40205             values.push(k === 'undefined' ? undefined : k);
40206             texts.push(field.t('options.' + k, { 'default': options[k] }));
40207         }
40208     } else {
40209         values = [undefined, 'yes'];
40210         texts = [t('inspector.unknown'), t('inspector.check.yes')];
40211         if (field.type === 'check') {
40212             values.push('no');
40213             texts.push(t('inspector.check.no'));
40214         }
40215     }
40216
40217     var check = function(selection) {
40218         // hack: pretend oneway field is a oneway_yes field
40219         // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
40220         if (field.id === 'oneway') {
40221             for (var key in entity.tags) {
40222                 if (key in iD.oneWayTags && (entity.tags[key] in iD.oneWayTags[key])) {
40223                     texts[0] = t('presets.fields.oneway_yes.options.undefined');
40224                     break;
40225                 }
40226             }
40227         }
40228
40229         selection.classed('checkselect', 'true');
40230
40231         label = selection.selectAll('.preset-input-wrap')
40232             .data([0]);
40233
40234         var enter = label.enter().append('label')
40235             .attr('class', 'preset-input-wrap');
40236
40237         enter.append('input')
40238             .property('indeterminate', field.type === 'check')
40239             .attr('type', 'checkbox')
40240             .attr('id', 'preset-input-' + field.id);
40241
40242         enter.append('span')
40243             .text(texts[0])
40244             .attr('class', 'value');
40245
40246         box = label.select('input')
40247             .on('click', function() {
40248                 var t = {};
40249                 t[field.key] = values[(values.indexOf(value) + 1) % values.length];
40250                 dispatch.change(t);
40251                 d3.event.stopPropagation();
40252             });
40253
40254         text = label.select('span.value');
40255     };
40256
40257     check.entity = function(_) {
40258         if (!arguments.length) return entity;
40259         entity = _;
40260         return check;
40261     };
40262
40263     check.tags = function(tags) {
40264         value = tags[field.key];
40265         box.property('indeterminate', field.type === 'check' && !value);
40266         box.property('checked', value === 'yes');
40267         text.text(texts[values.indexOf(value)]);
40268         label.classed('set', !!value);
40269     };
40270
40271     check.focus = function() {
40272         box.node().focus();
40273     };
40274
40275     return d3.rebind(check, dispatch, 'on');
40276 };
40277 iD.ui.preset.combo =
40278 iD.ui.preset.typeCombo =
40279 iD.ui.preset.multiCombo = function(field, context) {
40280     var dispatch = d3.dispatch('change'),
40281         isMulti = (field.type === 'multiCombo'),
40282         optstrings = field.strings && field.strings.options,
40283         optarray = field.options,
40284         snake_case = (field.snake_case || (field.snake_case === undefined)),
40285         combobox = d3.combobox().minItems(isMulti ? 1 : 2),
40286         comboData = [],
40287         multiData = [],
40288         container,
40289         input,
40290         entity;
40291
40292     // ensure multiCombo field.key ends with a ':'
40293     if (isMulti && field.key.match(/:$/) === null) {
40294         field.key += ':';
40295     }
40296
40297
40298     function snake(s) {
40299         return s.replace(/\s+/g, '_');
40300     }
40301
40302     function unsnake(s) {
40303         return s.replace(/_+/g, ' ');
40304     }
40305
40306     function clean(s) {
40307         return s.split(';')
40308             .map(function(s) { return s.trim(); })
40309             .join(';');
40310     }
40311
40312
40313     // returns the tag value for a display value
40314     // (for multiCombo, dval should be the key suffix, not the entire key)
40315     function tagValue(dval) {
40316         dval = clean(dval || '');
40317
40318         if (optstrings) {
40319             var match = _.find(comboData, function(o) {
40320                 return o.key && clean(o.value) === dval;
40321             });
40322             if (match) {
40323                 return match.key;
40324             }
40325         }
40326
40327         if (field.type === 'typeCombo' && !dval) {
40328             return 'yes';
40329         }
40330
40331         return (snake_case ? snake(dval) : dval) || undefined;
40332     }
40333
40334
40335     // returns the display value for a tag value
40336     // (for multiCombo, tval should be the key suffix, not the entire key)
40337     function displayValue(tval) {
40338         tval = tval || '';
40339
40340         if (optstrings) {
40341             var match = _.find(comboData, function(o) { return o.key === tval && o.value; });
40342             if (match) {
40343                 return match.value;
40344             }
40345         }
40346
40347         if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
40348             return '';
40349         }
40350
40351         return snake_case ? unsnake(tval) : tval;
40352     }
40353
40354
40355     function objectDifference(a, b) {
40356         return _.reject(a, function(d1) {
40357             return _.some(b, function(d2) { return d1.value === d2.value; });
40358         });
40359     }
40360
40361
40362     function initCombo(selection, attachTo) {
40363         if (optstrings) {
40364             selection.attr('readonly', 'readonly');
40365             selection.call(combobox, attachTo);
40366             setStaticValues(setPlaceholder);
40367
40368         } else if (optarray) {
40369             selection.call(combobox, attachTo);
40370             setStaticValues(setPlaceholder);
40371
40372         } else if (context.taginfo()) {
40373             selection.call(combobox.fetcher(setTaginfoValues), attachTo);
40374             setTaginfoValues('', setPlaceholder);
40375         }
40376     }
40377
40378
40379     function setStaticValues(callback) {
40380         if (!(optstrings || optarray)) return;
40381
40382         if (optstrings) {
40383             comboData = Object.keys(optstrings).map(function(k) {
40384                 var v = field.t('options.' + k, { 'default': optstrings[k] });
40385                 return {
40386                     key: k,
40387                     value: v,
40388                     title: v
40389                 };
40390             });
40391
40392         } else if (optarray) {
40393             comboData = optarray.map(function(k) {
40394                 var v = snake_case ? unsnake(k) : k;
40395                 return {
40396                     key: k,
40397                     value: v,
40398                     title: v
40399                 };
40400             });
40401         }
40402
40403         combobox.data(objectDifference(comboData, multiData));
40404         if (callback) callback(comboData);
40405     }
40406
40407
40408     function setTaginfoValues(q, callback) {
40409         var fn = isMulti ? 'multikeys' : 'values';
40410         context.taginfo()[fn]({
40411             debounce: true,
40412             key: field.key,
40413             geometry: context.geometry(entity.id),
40414             query: (isMulti ? field.key : '') + q
40415         }, function(err, data) {
40416             if (err) return;
40417             comboData = _.map(data, 'value').map(function(k) {
40418                 if (isMulti) k = k.replace(field.key, '');
40419                 var v = snake_case ? unsnake(k) : k;
40420                 return {
40421                     key: k,
40422                     value: v,
40423                     title: v
40424                 };
40425             });
40426             comboData = objectDifference(comboData, multiData);
40427             if (callback) callback(comboData);
40428         });
40429     }
40430
40431
40432     function setPlaceholder(d) {
40433         var ph;
40434         if (isMulti) {
40435             ph = field.placeholder() || t('inspector.add');
40436         } else {
40437             var vals = _.map(d, 'value').filter(function(s) { return s.length < 20; }),
40438                 placeholders = vals.length > 1 ? vals : _.map(d, 'key');
40439             ph = field.placeholder() || placeholders.slice(0, 3).join(', ');
40440         }
40441
40442         input.attr('placeholder', ph + '…');
40443     }
40444
40445
40446     function change() {
40447         var val = tagValue(input.value()),
40448             t = {};
40449
40450         if (isMulti) {
40451             if (!val) return;
40452             container.classed('active', false);
40453             input.value('');
40454             field.keys.push(field.key + val);
40455             t[field.key + val] = 'yes';
40456             window.setTimeout(function() { input.node().focus(); }, 10);
40457
40458         } else {
40459             t[field.key] = val;
40460         }
40461
40462         dispatch.change(t);
40463     }
40464
40465
40466     function removeMultikey(d) {
40467         d3.event.stopPropagation();
40468         var t = {};
40469         t[d.key] = undefined;
40470         dispatch.change(t);
40471     }
40472
40473
40474     function combo(selection) {
40475         if (isMulti) {
40476             container = selection.selectAll('ul').data([0]);
40477
40478             container.enter()
40479                 .append('ul')
40480                 .attr('class', 'form-field-multicombo')
40481                 .on('click', function() {
40482                     window.setTimeout(function() { input.node().focus(); }, 10);
40483                 });
40484
40485         } else {
40486             container = selection;
40487         }
40488
40489         input = container.selectAll('input')
40490             .data([0]);
40491
40492         input.enter()
40493             .append('input')
40494             .attr('type', 'text')
40495             .attr('id', 'preset-input-' + field.id)
40496             .call(initCombo, selection);
40497
40498         input
40499             .on('change', change)
40500             .on('blur', change);
40501
40502         if (isMulti) {
40503             combobox
40504                 .on('accept', function() {
40505                     input.node().blur();
40506                     input.node().focus();
40507                 });
40508
40509             input
40510                 .on('focus', function() { container.classed('active', true); });
40511         }
40512     }
40513
40514
40515     combo.tags = function(tags) {
40516         if (isMulti) {
40517             multiData = [];
40518
40519             // Build multiData array containing keys already set..
40520             Object.keys(tags).forEach(function(key) {
40521                 if (key.indexOf(field.key) !== 0 || tags[key].toLowerCase() !== 'yes') return;
40522
40523                 var suffix = key.substring(field.key.length);
40524                 multiData.push({
40525                     key: key,
40526                     value: displayValue(suffix)
40527                 });
40528             });
40529
40530             // Set keys for form-field modified (needed for undo and reset buttons)..
40531             field.keys = _.map(multiData, 'key');
40532
40533             // Exclude existing multikeys from combo options..
40534             var available = objectDifference(comboData, multiData);
40535             combobox.data(available);
40536
40537             // Hide "Add" button if this field uses fixed set of
40538             // translateable optstrings and they're all currently used..
40539             container.selectAll('.combobox-input, .combobox-caret')
40540                 .classed('hide', optstrings && !available.length);
40541
40542
40543             // Render chips
40544             var chips = container.selectAll('.chips').data(multiData);
40545
40546             var enter = chips.enter()
40547                 .insert('li', 'input')
40548                 .attr('class', 'chips');
40549
40550             enter.append('span');
40551             enter.append('a');
40552
40553             chips.select('span')
40554                 .text(function(d) { return d.value; });
40555
40556             chips.select('a')
40557                 .on('click', removeMultikey)
40558                 .attr('class', 'remove')
40559                 .text('×');
40560
40561             chips.exit()
40562                 .remove();
40563
40564         } else {
40565             input.value(displayValue(tags[field.key]));
40566         }
40567     };
40568
40569
40570     combo.focus = function() {
40571         input.node().focus();
40572     };
40573
40574
40575     combo.entity = function(_) {
40576         if (!arguments.length) return entity;
40577         entity = _;
40578         return combo;
40579     };
40580
40581
40582     return d3.rebind(combo, dispatch, 'on');
40583 };
40584 iD.ui.preset.cycleway = function(field) {
40585     var dispatch = d3.dispatch('change'),
40586         items;
40587
40588     function cycleway(selection) {
40589         var wrap = selection.selectAll('.preset-input-wrap')
40590             .data([0]);
40591
40592         wrap.enter().append('div')
40593             .attr('class', 'cf preset-input-wrap')
40594             .append('ul');
40595
40596         items = wrap.select('ul').selectAll('li')
40597             .data(field.keys);
40598
40599         // Enter
40600
40601         var enter = items.enter().append('li')
40602             .attr('class', function(d) { return 'cf preset-cycleway-' + d; });
40603
40604         enter.append('span')
40605             .attr('class', 'col6 label preset-label-cycleway')
40606             .attr('for', function(d) { return 'preset-input-cycleway-' + d; })
40607             .text(function(d) { return field.t('types.' + d); });
40608
40609         enter.append('div')
40610             .attr('class', 'col6 preset-input-cycleway-wrap')
40611             .append('input')
40612             .attr('type', 'text')
40613             .attr('class', 'preset-input-cycleway')
40614             .attr('id', function(d) { return 'preset-input-cycleway-' + d; })
40615             .each(function(d) {
40616                 d3.select(this)
40617                     .call(d3.combobox()
40618                         .data(cycleway.options(d)));
40619             });
40620
40621         // Update
40622
40623         wrap.selectAll('.preset-input-cycleway')
40624             .on('change', change)
40625             .on('blur', change);
40626     }
40627
40628     function change() {
40629         var inputs = d3.selectAll('.preset-input-cycleway')[0],
40630             left = d3.select(inputs[0]).value(),
40631             right = d3.select(inputs[1]).value(),
40632             tag = {};
40633         if (left === 'none' || left === '') { left = undefined; }
40634         if (right === 'none' || right === '') { right = undefined; }
40635
40636         // Always set both left and right as changing one can affect the other
40637         tag = {
40638             cycleway: undefined,
40639             'cycleway:left': left,
40640             'cycleway:right': right
40641         };
40642
40643         // If the left and right tags match, use the cycleway tag to tag both
40644         // sides the same way
40645         if (left === right) {
40646             tag = {
40647                 cycleway: left,
40648                 'cycleway:left': undefined,
40649                 'cycleway:right': undefined
40650             };
40651         }
40652
40653         dispatch.change(tag);
40654     }
40655
40656     cycleway.options = function() {
40657         return d3.keys(field.strings.options).map(function(option) {
40658             return {
40659                 title: field.t('options.' + option + '.description'),
40660                 value: option
40661             };
40662         });
40663     };
40664
40665     cycleway.tags = function(tags) {
40666         items.selectAll('.preset-input-cycleway')
40667             .value(function(d) {
40668                 // If cycleway is set, always return that
40669                 if (tags.cycleway) {
40670                     return tags.cycleway;
40671                 }
40672                 return tags[d] || '';
40673             })
40674             .attr('placeholder', field.placeholder());
40675     };
40676
40677     cycleway.focus = function() {
40678         items.selectAll('.preset-input-cycleway')
40679             .node().focus();
40680     };
40681
40682     return d3.rebind(cycleway, dispatch, 'on');
40683 };
40684 iD.ui.preset.text =
40685 iD.ui.preset.number =
40686 iD.ui.preset.tel =
40687 iD.ui.preset.email =
40688 iD.ui.preset.url = function(field, context) {
40689
40690     var dispatch = d3.dispatch('change'),
40691         input,
40692         entity;
40693
40694     function i(selection) {
40695         var fieldId = 'preset-input-' + field.id;
40696
40697         input = selection.selectAll('input')
40698             .data([0]);
40699
40700         input.enter().append('input')
40701             .attr('type', field.type)
40702             .attr('id', fieldId)
40703             .attr('placeholder', field.placeholder() || t('inspector.unknown'));
40704
40705         input
40706             .on('input', change(true))
40707             .on('blur', change())
40708             .on('change', change());
40709
40710         if (field.type === 'tel') {
40711             var center = entity.extent(context.graph()).center();
40712             iD.services.nominatim().countryCode(center, function (err, countryCode) {
40713                 if (err || !iD.data.phoneFormats[countryCode]) return;
40714                 selection.selectAll('#' + fieldId)
40715                     .attr('placeholder', iD.data.phoneFormats[countryCode]);
40716             });
40717
40718         } else if (field.type === 'number') {
40719             input.attr('type', 'text');
40720
40721             var spinControl = selection.selectAll('.spin-control')
40722                 .data([0]);
40723
40724             var enter = spinControl.enter().append('div')
40725                 .attr('class', 'spin-control');
40726
40727             enter.append('button')
40728                 .datum(1)
40729                 .attr('class', 'increment')
40730                 .attr('tabindex', -1);
40731
40732             enter.append('button')
40733                 .datum(-1)
40734                 .attr('class', 'decrement')
40735                 .attr('tabindex', -1);
40736
40737             spinControl.selectAll('button')
40738                 .on('click', function(d) {
40739                     d3.event.preventDefault();
40740                     var num = parseInt(input.node().value || 0, 10);
40741                     if (!isNaN(num)) input.node().value = num + d;
40742                     change()();
40743                 });
40744         }
40745     }
40746
40747     function change(onInput) {
40748         return function() {
40749             var t = {};
40750             t[field.key] = input.value() || undefined;
40751             dispatch.change(t, onInput);
40752         };
40753     }
40754
40755     i.entity = function(_) {
40756         if (!arguments.length) return entity;
40757         entity = _;
40758         return i;
40759     };
40760
40761     i.tags = function(tags) {
40762         input.value(tags[field.key] || '');
40763     };
40764
40765     i.focus = function() {
40766         var node = input.node();
40767         if (node) node.focus();
40768     };
40769
40770     return d3.rebind(i, dispatch, 'on');
40771 };
40772 iD.ui.preset.localized = function(field, context) {
40773     var dispatch = d3.dispatch('change', 'input'),
40774         wikipedia = iD.services.wikipedia(),
40775         input, localizedInputs, wikiTitles,
40776         entity;
40777
40778     function localized(selection) {
40779         input = selection.selectAll('.localized-main')
40780             .data([0]);
40781
40782         input.enter().append('input')
40783             .attr('type', 'text')
40784             .attr('id', 'preset-input-' + field.id)
40785             .attr('class', 'localized-main')
40786             .attr('placeholder', field.placeholder());
40787
40788         if (field.id === 'name') {
40789             var preset = context.presets().match(entity, context.graph());
40790             input.call(d3.combobox().fetcher(
40791                 iD.util.SuggestNames(preset, iD.data.suggestions)
40792             ));
40793         }
40794
40795         input
40796             .on('input', change(true))
40797             .on('blur', change())
40798             .on('change', change());
40799
40800         var translateButton = selection.selectAll('.localized-add')
40801             .data([0]);
40802
40803         translateButton.enter()
40804             .append('button')
40805             .attr('class', 'button-input-action localized-add minor')
40806             .attr('tabindex', -1)
40807             .call(iD.svg.Icon('#icon-plus'))
40808             .call(bootstrap.tooltip()
40809                 .title(t('translate.translate'))
40810                 .placement('left'));
40811
40812         translateButton
40813             .on('click', addNew);
40814
40815         localizedInputs = selection.selectAll('.localized-wrap')
40816             .data([0]);
40817
40818         localizedInputs.enter().append('div')
40819             .attr('class', 'localized-wrap');
40820     }
40821
40822     function addNew() {
40823         d3.event.preventDefault();
40824         var data = localizedInputs.selectAll('div.entry').data();
40825         var defaultLang = iD.detect().locale.toLowerCase().split('-')[0];
40826         var langExists = _.find(data, function(datum) { return datum.lang === defaultLang;});
40827         var isLangEn = defaultLang.indexOf('en') > -1;
40828         if (isLangEn || langExists) {
40829           defaultLang = '';
40830         }
40831         data.push({ lang: defaultLang, value: '' });
40832         localizedInputs.call(render, data);
40833     }
40834
40835     function change(onInput) {
40836         return function() {
40837             var t = {};
40838             t[field.key] = d3.select(this).value() || undefined;
40839             dispatch.change(t, onInput);
40840         };
40841     }
40842
40843     function key(lang) { return field.key + ':' + lang; }
40844
40845     function changeLang(d) {
40846         var lang = d3.select(this).value(),
40847             t = {},
40848             language = _.find(iD.data.wikipedia, function(d) {
40849                 return d[0].toLowerCase() === lang.toLowerCase() ||
40850                     d[1].toLowerCase() === lang.toLowerCase();
40851             });
40852
40853         if (language) lang = language[2];
40854
40855         if (d.lang && d.lang !== lang) {
40856             t[key(d.lang)] = undefined;
40857         }
40858
40859         var value = d3.select(this.parentNode)
40860             .selectAll('.localized-value')
40861             .value();
40862
40863         if (lang && value) {
40864             t[key(lang)] = value;
40865         } else if (lang && wikiTitles && wikiTitles[d.lang]) {
40866             t[key(lang)] = wikiTitles[d.lang];
40867         }
40868
40869         d.lang = lang;
40870         dispatch.change(t);
40871     }
40872
40873     function changeValue(d) {
40874         if (!d.lang) return;
40875         var t = {};
40876         t[key(d.lang)] = d3.select(this).value() || undefined;
40877         dispatch.change(t);
40878     }
40879
40880     function fetcher(value, cb) {
40881         var v = value.toLowerCase();
40882
40883         cb(iD.data.wikipedia.filter(function(d) {
40884             return d[0].toLowerCase().indexOf(v) >= 0 ||
40885             d[1].toLowerCase().indexOf(v) >= 0 ||
40886             d[2].toLowerCase().indexOf(v) >= 0;
40887         }).map(function(d) {
40888             return { value: d[1] };
40889         }));
40890     }
40891
40892     function render(selection, data) {
40893         var wraps = selection.selectAll('div.entry').
40894             data(data, function(d) { return d.lang; });
40895
40896         var innerWrap = wraps.enter()
40897             .insert('div', ':first-child');
40898
40899         innerWrap.attr('class', 'entry')
40900             .each(function() {
40901                 var wrap = d3.select(this);
40902                 var langcombo = d3.combobox().fetcher(fetcher).minItems(0);
40903
40904                 var label = wrap.append('label')
40905                     .attr('class','form-label')
40906                     .text(t('translate.localized_translation_label'))
40907                     .attr('for','localized-lang');
40908
40909                 label.append('button')
40910                     .attr('class', 'minor remove')
40911                     .on('click', function(d){
40912                         d3.event.preventDefault();
40913                         var t = {};
40914                         t[key(d.lang)] = undefined;
40915                         dispatch.change(t);
40916                         d3.select(this.parentNode.parentNode)
40917                             .style('top','0')
40918                             .style('max-height','240px')
40919                             .transition()
40920                             .style('opacity', '0')
40921                             .style('max-height','0px')
40922                             .remove();
40923                     })
40924                     .call(iD.svg.Icon('#operation-delete'));
40925
40926                 wrap.append('input')
40927                     .attr('class', 'localized-lang')
40928                     .attr('type', 'text')
40929                     .attr('placeholder',t('translate.localized_translation_language'))
40930                     .on('blur', changeLang)
40931                     .on('change', changeLang)
40932                     .call(langcombo);
40933
40934                 wrap.append('input')
40935                     .on('blur', changeValue)
40936                     .on('change', changeValue)
40937                     .attr('type', 'text')
40938                     .attr('placeholder', t('translate.localized_translation_name'))
40939                     .attr('class', 'localized-value');
40940             });
40941
40942         innerWrap
40943             .style('margin-top', '0px')
40944             .style('max-height', '0px')
40945             .style('opacity', '0')
40946             .transition()
40947             .duration(200)
40948             .style('margin-top', '10px')
40949             .style('max-height', '240px')
40950             .style('opacity', '1')
40951             .each('end', function() {
40952                 d3.select(this)
40953                     .style('max-height', '')
40954                     .style('overflow', 'visible');
40955             });
40956
40957         wraps.exit()
40958             .transition()
40959             .duration(200)
40960             .style('max-height','0px')
40961             .style('opacity', '0')
40962             .style('top','-10px')
40963             .remove();
40964
40965         var entry = selection.selectAll('.entry');
40966
40967         entry.select('.localized-lang')
40968             .value(function(d) {
40969                 var lang = _.find(iD.data.wikipedia, function(lang) { return lang[2] === d.lang; });
40970                 return lang ? lang[1] : d.lang;
40971             });
40972
40973         entry.select('.localized-value')
40974             .value(function(d) { return d.value; });
40975     }
40976
40977     localized.tags = function(tags) {
40978         // Fetch translations from wikipedia
40979         if (tags.wikipedia && !wikiTitles) {
40980             wikiTitles = {};
40981             var wm = tags.wikipedia.match(/([^:]+):(.+)/);
40982             if (wm && wm[0] && wm[1]) {
40983                 wikipedia.translations(wm[1], wm[2], function(d) {
40984                     wikiTitles = d;
40985                 });
40986             }
40987         }
40988
40989         input.value(tags[field.key] || '');
40990
40991         var postfixed = [], k, m;
40992         for (k in tags) {
40993             m = k.match(/^(.*):([a-zA-Z_-]+)$/);
40994             if (m && m[1] === field.key && m[2]) {
40995                 postfixed.push({ lang: m[2], value: tags[k] });
40996             }
40997         }
40998
40999         localizedInputs.call(render, postfixed.reverse());
41000     };
41001
41002     localized.focus = function() {
41003         input.node().focus();
41004     };
41005
41006     localized.entity = function(_) {
41007         if (!arguments.length) return entity;
41008         entity = _;
41009         return localized;
41010     };
41011
41012     return d3.rebind(localized, dispatch, 'on');
41013 };
41014 iD.ui.preset.maxspeed = function(field, context) {
41015     var dispatch = d3.dispatch('change'),
41016         entity,
41017         imperial,
41018         unitInput,
41019         combobox,
41020         input;
41021
41022     var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
41023         imperialValues = [20, 25, 30, 35, 40, 45, 50, 55, 65, 70];
41024
41025     function maxspeed(selection) {
41026         combobox = d3.combobox();
41027         var unitCombobox = d3.combobox().data(['km/h', 'mph'].map(comboValues));
41028
41029         input = selection.selectAll('#preset-input-' + field.id)
41030             .data([0]);
41031
41032         input.enter().append('input')
41033             .attr('type', 'text')
41034             .attr('id', 'preset-input-' + field.id)
41035             .attr('placeholder', field.placeholder());
41036
41037         input
41038             .call(combobox)
41039             .on('change', change)
41040             .on('blur', change);
41041
41042         var childNodes = context.graph().childNodes(context.entity(entity.id)),
41043             loc = childNodes[~~(childNodes.length/2)].loc;
41044
41045         imperial = _.some(iD.data.imperial.features, function(f) {
41046             return _.some(f.geometry.coordinates, function(d) {
41047                 return iD.geo.pointInPolygon(loc, d[0]);
41048             });
41049         });
41050
41051         unitInput = selection.selectAll('input.maxspeed-unit')
41052             .data([0]);
41053
41054         unitInput.enter().append('input')
41055             .attr('type', 'text')
41056             .attr('class', 'maxspeed-unit');
41057
41058         unitInput
41059             .on('blur', changeUnits)
41060             .on('change', changeUnits)
41061             .call(unitCombobox);
41062
41063         function changeUnits() {
41064             imperial = unitInput.value() === 'mph';
41065             unitInput.value(imperial ? 'mph' : 'km/h');
41066             setSuggestions();
41067             change();
41068         }
41069
41070     }
41071
41072     function setSuggestions() {
41073         combobox.data((imperial ? imperialValues : metricValues).map(comboValues));
41074         unitInput.value(imperial ? 'mph' : 'km/h');
41075     }
41076
41077     function comboValues(d) {
41078         return {
41079             value: d.toString(),
41080             title: d.toString()
41081         };
41082     }
41083
41084     function change() {
41085         var tag = {},
41086             value = input.value();
41087
41088         if (!value) {
41089             tag[field.key] = undefined;
41090         } else if (isNaN(value) || !imperial) {
41091             tag[field.key] = value;
41092         } else {
41093             tag[field.key] = value + ' mph';
41094         }
41095
41096         dispatch.change(tag);
41097     }
41098
41099     maxspeed.tags = function(tags) {
41100         var value = tags[field.key];
41101
41102         if (value && value.indexOf('mph') >= 0) {
41103             value = parseInt(value, 10);
41104             imperial = true;
41105         } else if (value) {
41106             imperial = false;
41107         }
41108
41109         setSuggestions();
41110
41111         input.value(value || '');
41112     };
41113
41114     maxspeed.focus = function() {
41115         input.node().focus();
41116     };
41117
41118     maxspeed.entity = function(_) {
41119         entity = _;
41120     };
41121
41122     return d3.rebind(maxspeed, dispatch, 'on');
41123 };
41124 iD.ui.preset.radio = function(field) {
41125     var dispatch = d3.dispatch('change'),
41126         labels, radios, placeholder;
41127
41128     function radio(selection) {
41129         selection.classed('preset-radio', true);
41130
41131         var wrap = selection.selectAll('.preset-input-wrap')
41132             .data([0]);
41133
41134         var buttonWrap = wrap.enter().append('div')
41135             .attr('class', 'preset-input-wrap toggle-list');
41136
41137         buttonWrap.append('span')
41138             .attr('class', 'placeholder');
41139
41140         placeholder = selection.selectAll('.placeholder');
41141
41142         labels = wrap.selectAll('label')
41143             .data(field.options || field.keys);
41144
41145         var enter = labels.enter().append('label');
41146
41147         enter.append('input')
41148             .attr('type', 'radio')
41149             .attr('name', field.id)
41150             .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
41151             .attr('checked', false);
41152
41153         enter.append('span')
41154             .text(function(d) { return field.t('options.' + d, { 'default': d }); });
41155
41156         radios = labels.selectAll('input')
41157             .on('change', change);
41158     }
41159
41160     function change() {
41161         var t = {};
41162         if (field.key) t[field.key] = undefined;
41163         radios.each(function(d) {
41164             var active = d3.select(this).property('checked');
41165             if (field.key) {
41166                 if (active) t[field.key] = d;
41167             } else {
41168                 t[d] = active ? 'yes' : undefined;
41169             }
41170         });
41171         dispatch.change(t);
41172     }
41173
41174     radio.tags = function(tags) {
41175         function checked(d) {
41176             if (field.key) {
41177                 return tags[field.key] === d;
41178             } else {
41179                 return !!(tags[d] && tags[d] !== 'no');
41180             }
41181         }
41182
41183         labels.classed('active', checked);
41184         radios.property('checked', checked);
41185         var selection = radios.filter(function() { return this.checked; });
41186         if (selection.empty()) {
41187             placeholder.text(t('inspector.none'));
41188         } else {
41189             placeholder.text(selection.attr('value'));
41190         }
41191     };
41192
41193     radio.focus = function() {
41194         radios.node().focus();
41195     };
41196
41197     return d3.rebind(radio, dispatch, 'on');
41198 };
41199 iD.ui.preset.restrictions = function(field, context) {
41200     var dispatch = d3.dispatch('change'),
41201         hover = iD.behavior.Hover(context),
41202         vertexID,
41203         fromNodeID;
41204
41205
41206     function restrictions(selection) {
41207         // if form field is hidden or has detached from dom, clean up.
41208         if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
41209             selection.call(restrictions.off);
41210             return;
41211         }
41212
41213         var wrap = selection.selectAll('.preset-input-wrap')
41214             .data([0]);
41215
41216         var enter = wrap.enter()
41217             .append('div')
41218             .attr('class', 'preset-input-wrap');
41219
41220         enter
41221             .append('div')
41222             .attr('class', 'restriction-help');
41223
41224
41225         var intersection = iD.geo.Intersection(context.graph(), vertexID),
41226             graph = intersection.graph,
41227             vertex = graph.entity(vertexID),
41228             filter = d3.functor(true),
41229             extent = iD.geo.Extent(),
41230             projection = iD.geo.RawMercator();
41231
41232         var d = wrap.dimensions(),
41233             c = [d[0] / 2, d[1] / 2],
41234             z = 24;
41235
41236         projection
41237             .scale(256 * Math.pow(2, z) / (2 * Math.PI));
41238
41239         var s = projection(vertex.loc);
41240
41241         projection
41242             .translate([c[0] - s[0], c[1] - s[1]])
41243             .clipExtent([[0, 0], d]);
41244
41245         var drawLayers = iD.svg.Layers(projection, context).only('osm').dimensions(d),
41246             drawVertices = iD.svg.Vertices(projection, context),
41247             drawLines = iD.svg.Lines(projection, context),
41248             drawTurns = iD.svg.Turns(projection, context);
41249
41250         enter
41251             .call(drawLayers)
41252             .selectAll('.surface')
41253             .call(hover);
41254
41255
41256         var surface = wrap.selectAll('.surface');
41257
41258         surface
41259             .dimensions(d)
41260             .call(drawVertices, graph, [vertex], filter, extent, z)
41261             .call(drawLines, graph, intersection.ways, filter)
41262             .call(drawTurns, graph, intersection.turns(fromNodeID));
41263
41264         surface
41265             .on('click.restrictions', click)
41266             .on('mouseover.restrictions', mouseover)
41267             .on('mouseout.restrictions', mouseout);
41268
41269         surface
41270             .selectAll('.selected')
41271             .classed('selected', false);
41272
41273         if (fromNodeID) {
41274             surface
41275                 .selectAll('.' + intersection.highways[fromNodeID].id)
41276                 .classed('selected', true);
41277         }
41278
41279         mouseout();
41280
41281         context.history()
41282             .on('change.restrictions', render);
41283
41284         d3.select(window)
41285             .on('resize.restrictions', function() {
41286                 wrap.dimensions(null);
41287                 render();
41288             });
41289
41290         function click() {
41291             var datum = d3.event.target.__data__;
41292             if (datum instanceof iD.Entity) {
41293                 fromNodeID = intersection.adjacentNodeId(datum.id);
41294                 render();
41295             } else if (datum instanceof iD.geo.Turn) {
41296                 if (datum.restriction) {
41297                     context.perform(
41298                         iD.actions.UnrestrictTurn(datum, projection),
41299                         t('operations.restriction.annotation.delete'));
41300                 } else {
41301                     context.perform(
41302                         iD.actions.RestrictTurn(datum, projection),
41303                         t('operations.restriction.annotation.create'));
41304                 }
41305             }
41306         }
41307
41308         function mouseover() {
41309             var datum = d3.event.target.__data__;
41310             if (datum instanceof iD.geo.Turn) {
41311                 var graph = context.graph(),
41312                     presets = context.presets(),
41313                     preset;
41314
41315                 if (datum.restriction) {
41316                     preset = presets.match(graph.entity(datum.restriction), graph);
41317                 } else {
41318                     preset = presets.item('type/restriction/' +
41319                         iD.geo.inferRestriction(
41320                             graph,
41321                             datum.from,
41322                             datum.via,
41323                             datum.to,
41324                             projection));
41325                 }
41326
41327                 wrap.selectAll('.restriction-help')
41328                     .text(t('operations.restriction.help.' +
41329                         (datum.restriction ? 'toggle_off' : 'toggle_on'),
41330                         {restriction: preset.name()}));
41331             }
41332         }
41333
41334         function mouseout() {
41335             wrap.selectAll('.restriction-help')
41336                 .text(t('operations.restriction.help.' +
41337                     (fromNodeID ? 'toggle' : 'select')));
41338         }
41339
41340         function render() {
41341             if (context.hasEntity(vertexID)) {
41342                 restrictions(selection);
41343             }
41344         }
41345     }
41346
41347     restrictions.entity = function(_) {
41348         if (!vertexID || vertexID !== _.id) {
41349             fromNodeID = null;
41350             vertexID = _.id;
41351         }
41352     };
41353
41354     restrictions.tags = function() {};
41355     restrictions.focus = function() {};
41356
41357     restrictions.off = function(selection) {
41358         selection.selectAll('.surface')
41359             .call(hover.off)
41360             .on('click.restrictions', null)
41361             .on('mouseover.restrictions', null)
41362             .on('mouseout.restrictions', null);
41363
41364         context.history()
41365             .on('change.restrictions', null);
41366
41367         d3.select(window)
41368             .on('resize.restrictions', null);
41369     };
41370
41371     return d3.rebind(restrictions, dispatch, 'on');
41372 };
41373 iD.ui.preset.textarea = function(field) {
41374     var dispatch = d3.dispatch('change'),
41375         input;
41376
41377     function textarea(selection) {
41378         input = selection.selectAll('textarea')
41379             .data([0]);
41380
41381         input.enter().append('textarea')
41382             .attr('id', 'preset-input-' + field.id)
41383             .attr('placeholder', field.placeholder() || t('inspector.unknown'))
41384             .attr('maxlength', 255);
41385
41386         input
41387             .on('input', change(true))
41388             .on('blur', change())
41389             .on('change', change());
41390     }
41391
41392     function change(onInput) {
41393         return function() {
41394             var t = {};
41395             t[field.key] = input.value() || undefined;
41396             dispatch.change(t, onInput);
41397         };
41398     }
41399
41400     textarea.tags = function(tags) {
41401         input.value(tags[field.key] || '');
41402     };
41403
41404     textarea.focus = function() {
41405         input.node().focus();
41406     };
41407
41408     return d3.rebind(textarea, dispatch, 'on');
41409 };
41410 iD.ui.preset.wikipedia = function(field, context) {
41411     var dispatch = d3.dispatch('change'),
41412         wikipedia = iD.services.wikipedia(),
41413         wikidata = iD.services.wikidata(),
41414         link, entity, lang, title;
41415
41416     function wiki(selection) {
41417         var langcombo = d3.combobox()
41418             .fetcher(function(value, cb) {
41419                 var v = value.toLowerCase();
41420
41421                 cb(iD.data.wikipedia.filter(function(d) {
41422                     return d[0].toLowerCase().indexOf(v) >= 0 ||
41423                         d[1].toLowerCase().indexOf(v) >= 0 ||
41424                         d[2].toLowerCase().indexOf(v) >= 0;
41425                 }).map(function(d) {
41426                     return { value: d[1] };
41427                 }));
41428             });
41429
41430         var titlecombo = d3.combobox()
41431             .fetcher(function(value, cb) {
41432
41433                 if (!value) value = context.entity(entity.id).tags.name || '';
41434                 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
41435
41436                 searchfn(language()[2], value, function(query, data) {
41437                     cb(data.map(function(d) {
41438                         return { value: d };
41439                     }));
41440                 });
41441             });
41442
41443         lang = selection.selectAll('input.wiki-lang')
41444             .data([0]);
41445
41446         lang.enter().append('input')
41447             .attr('type', 'text')
41448             .attr('class', 'wiki-lang')
41449             .attr('placeholder', t('translate.localized_translation_language'))
41450             .value('English');
41451
41452         lang
41453             .call(langcombo)
41454             .on('blur', changeLang)
41455             .on('change', changeLang);
41456
41457         title = selection.selectAll('input.wiki-title')
41458             .data([0]);
41459
41460         title.enter().append('input')
41461             .attr('type', 'text')
41462             .attr('class', 'wiki-title')
41463             .attr('id', 'preset-input-' + field.id);
41464
41465         title
41466             .call(titlecombo)
41467             .on('blur', blur)
41468             .on('change', change);
41469
41470         link = selection.selectAll('a.wiki-link')
41471             .data([0]);
41472
41473         link.enter().append('a')
41474             .attr('class', 'wiki-link button-input-action minor')
41475             .attr('tabindex', -1)
41476             .attr('target', '_blank')
41477             .call(iD.svg.Icon('#icon-out-link', 'inline'));
41478     }
41479
41480     function language() {
41481         var value = lang.value().toLowerCase();
41482         var locale = iD.detect().locale.toLowerCase();
41483         var localeLanguage;
41484         return _.find(iD.data.wikipedia, function(d) {
41485             if (d[2] === locale) localeLanguage = d;
41486             return d[0].toLowerCase() === value ||
41487                 d[1].toLowerCase() === value ||
41488                 d[2] === value;
41489         }) || localeLanguage || ['English', 'English', 'en'];
41490     }
41491
41492     function changeLang() {
41493         lang.value(language()[1]);
41494         change(true);
41495     }
41496
41497     function blur() {
41498         change(true);
41499     }
41500
41501     function change(skipWikidata) {
41502         var value = title.value(),
41503             m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/),
41504             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
41505             anchor,
41506             syncTags = {};
41507
41508         if (l) {
41509             // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
41510             value = decodeURIComponent(m[2]).replace(/_/g, ' ');
41511             if (m[3]) {
41512                 try {
41513                     // Best-effort `anchordecode:` implementation
41514                     anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
41515                 } catch (e) {
41516                     anchor = decodeURIComponent(m[3]);
41517                 }
41518                 value += '#' + anchor.replace(/_/g, ' ');
41519             }
41520             value = value.slice(0, 1).toUpperCase() + value.slice(1);
41521             lang.value(l[1]);
41522             title.value(value);
41523         }
41524
41525         syncTags.wikipedia = value ? language()[2] + ':' + value : undefined;
41526         if (!skipWikidata) {
41527             syncTags.wikidata = undefined;
41528         }
41529
41530         dispatch.change(syncTags);
41531
41532
41533         if (skipWikidata || !value || !language()[2]) return;
41534
41535         // attempt asynchronous update of wikidata tag..
41536         var initEntityId = entity.id,
41537             initWikipedia = context.entity(initEntityId).tags.wikipedia;
41538
41539         wikidata.itemsByTitle(language()[2], value, function (title, data) {
41540             // 1. most recent change was a tag change
41541             var annotation = t('operations.change_tags.annotation'),
41542                 currAnnotation = context.history().undoAnnotation();
41543             if (currAnnotation !== annotation) return;
41544
41545             // 2. same entity exists and still selected
41546             var selectedIds = context.selectedIDs(),
41547                 currEntityId = selectedIds.length > 0 && selectedIds[0];
41548             if (currEntityId !== initEntityId) return;
41549
41550             // 3. wikipedia value has not changed
41551             var currTags = _.clone(context.entity(currEntityId).tags),
41552                 qids = data && Object.keys(data);
41553             if (initWikipedia !== currTags.wikipedia) return;
41554
41555             // ok to coalesce the update of wikidata tag into the previous tag change
41556             currTags.wikidata = qids && _.find(qids, function (id) {
41557                 return id.match(/^Q\d+$/);
41558             });
41559
41560             context.overwrite(iD.actions.ChangeTags(currEntityId, currTags), annotation);
41561             dispatch.change(currTags);
41562         });
41563     }
41564
41565     wiki.tags = function(tags) {
41566         var value = tags[field.key] || '',
41567             m = value.match(/([^:]+):([^#]+)(?:#(.+))?/),
41568             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
41569             anchor = m && m[3];
41570
41571         // value in correct format
41572         if (l) {
41573             lang.value(l[1]);
41574             title.value(m[2] + (anchor ? ('#' + anchor) : ''));
41575             if (anchor) {
41576                 try {
41577                     // Best-effort `anchorencode:` implementation
41578                     anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
41579                 } catch (e) {
41580                     anchor = anchor.replace(/ /g, '_');
41581                 }
41582             }
41583             link.attr('href', 'https://' + m[1] + '.wikipedia.org/wiki/' +
41584                 m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : ''));
41585
41586         // unrecognized value format
41587         } else {
41588             title.value(value);
41589             if (value && value !== '') {
41590                 lang.value('');
41591             }
41592             link.attr('href', 'https://en.wikipedia.org/wiki/Special:Search?search=' + value);
41593         }
41594     };
41595
41596     wiki.entity = function(_) {
41597         if (!arguments.length) return entity;
41598         entity = _;
41599         return wiki;
41600     };
41601
41602     wiki.focus = function() {
41603         title.node().focus();
41604     };
41605
41606     return d3.rebind(wiki, dispatch, 'on');
41607 };
41608 iD.ui.intro.area = function(context, reveal) {
41609     var event = d3.dispatch('done'),
41610         timeout;
41611
41612     var step = {
41613         title: 'intro.areas.title'
41614     };
41615
41616     step.enter = function() {
41617         var playground = [-85.63552, 41.94159],
41618             corner = [-85.63565411045074, 41.9417715536927];
41619         context.map().centerZoom(playground, 19);
41620         reveal('button.add-area',
41621             t('intro.areas.add', { button: iD.ui.intro.icon('#icon-area', 'pre-text') }),
41622             { tooltipClass: 'intro-areas-add' });
41623
41624         context.on('enter.intro', addArea);
41625
41626         function addArea(mode) {
41627             if (mode.id !== 'add-area') return;
41628             context.on('enter.intro', drawArea);
41629
41630             var padding = 120 * Math.pow(2, context.map().zoom() - 19);
41631             var pointBox = iD.ui.intro.pad(corner, padding, context);
41632             reveal(pointBox, t('intro.areas.corner'));
41633
41634             context.map().on('move.intro', function() {
41635                 padding = 120 * Math.pow(2, context.map().zoom() - 19);
41636                 pointBox = iD.ui.intro.pad(corner, padding, context);
41637                 reveal(pointBox, t('intro.areas.corner'), {duration: 0});
41638             });
41639         }
41640
41641         function drawArea(mode) {
41642             if (mode.id !== 'draw-area') return;
41643             context.on('enter.intro', enterSelect);
41644
41645             var padding = 150 * Math.pow(2, context.map().zoom() - 19);
41646             var pointBox = iD.ui.intro.pad(playground, padding, context);
41647             reveal(pointBox, t('intro.areas.place'));
41648
41649             context.map().on('move.intro', function() {
41650                 padding = 150 * Math.pow(2, context.map().zoom() - 19);
41651                 pointBox = iD.ui.intro.pad(playground, padding, context);
41652                 reveal(pointBox, t('intro.areas.place'), {duration: 0});
41653             });
41654         }
41655
41656         function enterSelect(mode) {
41657             if (mode.id !== 'select') return;
41658             context.map().on('move.intro', null);
41659             context.on('enter.intro', null);
41660
41661             timeout = setTimeout(function() {
41662                 reveal('.preset-search-input',
41663                     t('intro.areas.search',
41664                     { name: context.presets().item('leisure/playground').name() }));
41665                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
41666             }, 500);
41667         }
41668
41669         function keySearch() {
41670             var first = d3.select('.preset-list-item:first-child');
41671             if (first.classed('preset-leisure-playground')) {
41672                 reveal(first.select('.preset-list-button').node(), t('intro.areas.choose'));
41673                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
41674                 d3.select('.preset-search-input').on('keyup.intro', null);
41675             }
41676         }
41677
41678         function selectedPreset() {
41679             reveal('.pane',
41680                 t('intro.areas.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
41681             context.on('exit.intro', event.done);
41682         }
41683     };
41684
41685     step.exit = function() {
41686         window.clearTimeout(timeout);
41687         context.on('enter.intro', null);
41688         context.on('exit.intro', null);
41689         context.history().on('change.intro', null);
41690         context.map().on('move.intro', null);
41691         d3.select('.preset-search-input').on('keyup.intro', null);
41692     };
41693
41694     return d3.rebind(step, event, 'on');
41695 };
41696 iD.ui.intro.line = function(context, reveal) {
41697     var event = d3.dispatch('done'),
41698         timeouts = [];
41699
41700     var step = {
41701         title: 'intro.lines.title'
41702     };
41703
41704     function timeout(f, t) {
41705         timeouts.push(window.setTimeout(f, t));
41706     }
41707
41708     function eventCancel() {
41709         d3.event.stopPropagation();
41710         d3.event.preventDefault();
41711     }
41712
41713     step.enter = function() {
41714         var centroid = [-85.62830, 41.95699];
41715         var midpoint = [-85.62975395449628, 41.95787501510204];
41716         var start = [-85.6297754121684, 41.95805253325314];
41717         var intersection = [-85.62974496187628, 41.95742515554585];
41718
41719         context.map().centerZoom(start, 18);
41720         reveal('button.add-line',
41721             t('intro.lines.add', { button: iD.ui.intro.icon('#icon-line', 'pre-text') }),
41722             { tooltipClass: 'intro-lines-add' });
41723
41724         context.on('enter.intro', addLine);
41725
41726         function addLine(mode) {
41727             if (mode.id !== 'add-line') return;
41728             context.on('enter.intro', drawLine);
41729
41730             var padding = 150 * Math.pow(2, context.map().zoom() - 18);
41731             var pointBox = iD.ui.intro.pad(start, padding, context);
41732             reveal(pointBox, t('intro.lines.start'));
41733
41734             context.map().on('move.intro', function() {
41735                 padding = 150 * Math.pow(2, context.map().zoom() - 18);
41736                 pointBox = iD.ui.intro.pad(start, padding, context);
41737                 reveal(pointBox, t('intro.lines.start'), {duration: 0});
41738             });
41739         }
41740
41741         function drawLine(mode) {
41742             if (mode.id !== 'draw-line') return;
41743             context.history().on('change.intro', addIntersection);
41744             context.on('enter.intro', retry);
41745
41746             var padding = 300 * Math.pow(2, context.map().zoom() - 19);
41747             var pointBox = iD.ui.intro.pad(midpoint, padding, context);
41748             reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}));
41749
41750             context.map().on('move.intro', function() {
41751                 padding = 300 * Math.pow(2, context.map().zoom() - 19);
41752                 pointBox = iD.ui.intro.pad(midpoint, padding, context);
41753                 reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}), {duration: 0});
41754             });
41755         }
41756
41757         // ended line before creating intersection
41758         function retry(mode) {
41759             if (mode.id !== 'select') return;
41760             var pointBox = iD.ui.intro.pad(intersection, 30, context),
41761                 ids = mode.selectedIDs();
41762             reveal(pointBox, t('intro.lines.restart', {name: t('intro.graph.flower_st')}));
41763             d3.select(window).on('mousedown.intro', eventCancel, true);
41764
41765             timeout(function() {
41766                 context.replace(iD.actions.DeleteMultiple(ids));
41767                 step.exit();
41768                 step.enter();
41769             }, 3000);
41770         }
41771
41772         function addIntersection(changes) {
41773             if ( _.some(changes.created(), function(d) {
41774                 return d.type === 'node' && context.graph().parentWays(d).length > 1;
41775             })) {
41776                 context.history().on('change.intro', null);
41777                 context.on('enter.intro', enterSelect);
41778
41779                 var padding = 900 * Math.pow(2, context.map().zoom() - 19);
41780                 var pointBox = iD.ui.intro.pad(centroid, padding, context);
41781                 reveal(pointBox, t('intro.lines.finish'));
41782
41783                 context.map().on('move.intro', function() {
41784                     padding = 900 * Math.pow(2, context.map().zoom() - 19);
41785                     pointBox = iD.ui.intro.pad(centroid, padding, context);
41786                     reveal(pointBox, t('intro.lines.finish'), {duration: 0});
41787                 });
41788             }
41789         }
41790
41791         function enterSelect(mode) {
41792             if (mode.id !== 'select') return;
41793             context.map().on('move.intro', null);
41794             context.on('enter.intro', null);
41795             d3.select('#curtain').style('pointer-events', 'all');
41796
41797             presetCategory();
41798         }
41799
41800         function presetCategory() {
41801             timeout(function() {
41802                 d3.select('#curtain').style('pointer-events', 'none');
41803                 var road = d3.select('.preset-category-road .preset-list-button');
41804                 reveal(road.node(), t('intro.lines.road'));
41805                 road.one('click.intro', roadCategory);
41806             }, 500);
41807         }
41808
41809         function roadCategory() {
41810             timeout(function() {
41811                 var grid = d3.select('.subgrid');
41812                 reveal(grid.node(), t('intro.lines.residential'));
41813                 grid.selectAll(':not(.preset-highway-residential) .preset-list-button')
41814                     .one('click.intro', retryPreset);
41815                 grid.selectAll('.preset-highway-residential .preset-list-button')
41816                     .one('click.intro', roadDetails);
41817             }, 500);
41818         }
41819
41820         // selected wrong road type
41821         function retryPreset() {
41822             timeout(function() {
41823                 var preset = d3.select('.entity-editor-pane .preset-list-button');
41824                 reveal(preset.node(), t('intro.lines.wrong_preset'));
41825                 preset.one('click.intro', presetCategory);
41826             }, 500);
41827         }
41828
41829         function roadDetails() {
41830             reveal('.pane',
41831                 t('intro.lines.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
41832             context.on('exit.intro', event.done);
41833         }
41834
41835     };
41836
41837     step.exit = function() {
41838         d3.select(window).on('mousedown.intro', null, true);
41839         d3.select('#curtain').style('pointer-events', 'none');
41840         timeouts.forEach(window.clearTimeout);
41841         context.on('enter.intro', null);
41842         context.on('exit.intro', null);
41843         context.map().on('move.intro', null);
41844         context.history().on('change.intro', null);
41845     };
41846
41847     return d3.rebind(step, event, 'on');
41848 };
41849 iD.ui.intro.navigation = function(context, reveal) {
41850     var event = d3.dispatch('done'),
41851         timeouts = [];
41852
41853     var step = {
41854         title: 'intro.navigation.title'
41855     };
41856
41857     function set(f, t) {
41858         timeouts.push(window.setTimeout(f, t));
41859     }
41860
41861     function eventCancel() {
41862         d3.event.stopPropagation();
41863         d3.event.preventDefault();
41864     }
41865
41866     step.enter = function() {
41867         var rect = context.surfaceRect(),
41868             map = {
41869                 left: rect.left + 10,
41870                 top: rect.top + 70,
41871                 width: rect.width - 70,
41872                 height: rect.height - 170
41873             };
41874
41875         context.map().centerZoom([-85.63591, 41.94285], 19);
41876
41877         reveal(map, t('intro.navigation.drag'));
41878
41879         context.map().on('move.intro', _.debounce(function() {
41880             context.map().on('move.intro', null);
41881             townhall();
41882             context.on('enter.intro', inspectTownHall);
41883         }, 400));
41884
41885         function townhall() {
41886             var hall = [-85.63645945147184, 41.942986488012565];
41887
41888             var point = context.projection(hall);
41889             if (point[0] < 0 || point[0] > rect.width ||
41890                 point[1] < 0 || point[1] > rect.height) {
41891                 context.map().center(hall);
41892             }
41893
41894             var box = iD.ui.intro.pointBox(hall, context);
41895             reveal(box, t('intro.navigation.select'));
41896
41897             context.map().on('move.intro', function() {
41898                 var box = iD.ui.intro.pointBox(hall, context);
41899                 reveal(box, t('intro.navigation.select'), {duration: 0});
41900             });
41901         }
41902
41903         function inspectTownHall(mode) {
41904             if (mode.id !== 'select') return;
41905             context.on('enter.intro', null);
41906             context.map().on('move.intro', null);
41907             set(function() {
41908                 reveal('.entity-editor-pane',
41909                     t('intro.navigation.pane', { button: iD.ui.intro.icon('#icon-close', 'pre-text') }));
41910                 context.on('exit.intro', streetSearch);
41911             }, 700);
41912         }
41913
41914         function streetSearch() {
41915             context.on('exit.intro', null);
41916             reveal('.search-header input',
41917                 t('intro.navigation.search', { name: t('intro.graph.spring_st') }));
41918             d3.select('.search-header input').on('keyup.intro', searchResult);
41919         }
41920
41921         function searchResult() {
41922             var first = d3.select('.feature-list-item:nth-child(0n+2)'),  // skip No Results item
41923                 firstName = first.select('.entity-name'),
41924                 name = t('intro.graph.spring_st');
41925
41926             if (!firstName.empty() && firstName.text() === name) {
41927                 reveal(first.node(), t('intro.navigation.choose', { name: name }));
41928                 context.on('exit.intro', selectedStreet);
41929                 d3.select('.search-header input')
41930                     .on('keydown.intro', eventCancel, true)
41931                     .on('keyup.intro', null);
41932             }
41933         }
41934
41935         function selectedStreet() {
41936             var springSt = [-85.63585099140167, 41.942506848938926];
41937             context.map().center(springSt);
41938             context.on('exit.intro', event.done);
41939             set(function() {
41940                 reveal('.entity-editor-pane',
41941                     t('intro.navigation.chosen', {
41942                         name: t('intro.graph.spring_st'),
41943                         button: iD.ui.intro.icon('#icon-close', 'pre-text')
41944                     }));
41945             }, 400);
41946         }
41947     };
41948
41949     step.exit = function() {
41950         timeouts.forEach(window.clearTimeout);
41951         context.map().on('move.intro', null);
41952         context.on('enter.intro', null);
41953         context.on('exit.intro', null);
41954         d3.select('.search-header input')
41955             .on('keydown.intro', null)
41956             .on('keyup.intro', null);
41957     };
41958
41959     return d3.rebind(step, event, 'on');
41960 };
41961 iD.ui.intro.point = function(context, reveal) {
41962     var event = d3.dispatch('done'),
41963         timeouts = [];
41964
41965     var step = {
41966         title: 'intro.points.title'
41967     };
41968
41969     function setTimeout(f, t) {
41970         timeouts.push(window.setTimeout(f, t));
41971     }
41972
41973     function eventCancel() {
41974         d3.event.stopPropagation();
41975         d3.event.preventDefault();
41976     }
41977
41978     step.enter = function() {
41979         context.map().centerZoom([-85.63279, 41.94394], 19);
41980         reveal('button.add-point',
41981             t('intro.points.add', { button: iD.ui.intro.icon('#icon-point', 'pre-text') }),
41982             { tooltipClass: 'intro-points-add' });
41983
41984         var corner = [-85.632481,41.944094];
41985
41986         context.on('enter.intro', addPoint);
41987
41988         function addPoint(mode) {
41989             if (mode.id !== 'add-point') return;
41990             context.on('enter.intro', enterSelect);
41991
41992             var pointBox = iD.ui.intro.pad(corner, 150, context);
41993             reveal(pointBox, t('intro.points.place'));
41994
41995             context.map().on('move.intro', function() {
41996                 pointBox = iD.ui.intro.pad(corner, 150, context);
41997                 reveal(pointBox, t('intro.points.place'), {duration: 0});
41998             });
41999         }
42000
42001         function enterSelect(mode) {
42002             if (mode.id !== 'select') return;
42003             context.map().on('move.intro', null);
42004             context.on('enter.intro', null);
42005
42006             setTimeout(function() {
42007                 reveal('.preset-search-input',
42008                     t('intro.points.search', {name: context.presets().item('amenity/cafe').name()}));
42009                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
42010             }, 500);
42011         }
42012
42013         function keySearch() {
42014             var first = d3.select('.preset-list-item:first-child');
42015             if (first.classed('preset-amenity-cafe')) {
42016                 reveal(first.select('.preset-list-button').node(), t('intro.points.choose'));
42017                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
42018                 d3.select('.preset-search-input')
42019                     .on('keydown.intro', eventCancel, true)
42020                     .on('keyup.intro', null);
42021             }
42022         }
42023
42024         function selectedPreset() {
42025             setTimeout(function() {
42026                 reveal('.entity-editor-pane', t('intro.points.describe'), {tooltipClass: 'intro-points-describe'});
42027                 context.history().on('change.intro', closeEditor);
42028                 context.on('exit.intro', selectPoint);
42029             }, 400);
42030         }
42031
42032         function closeEditor() {
42033             d3.select('.preset-search-input').on('keydown.intro', null);
42034             context.history().on('change.intro', null);
42035             reveal('.entity-editor-pane',
42036                 t('intro.points.close', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
42037         }
42038
42039         function selectPoint() {
42040             context.on('exit.intro', null);
42041             context.history().on('change.intro', null);
42042             context.on('enter.intro', enterReselect);
42043
42044             var pointBox = iD.ui.intro.pad(corner, 150, context);
42045             reveal(pointBox, t('intro.points.reselect'));
42046
42047             context.map().on('move.intro', function() {
42048                 pointBox = iD.ui.intro.pad(corner, 150, context);
42049                 reveal(pointBox, t('intro.points.reselect'), {duration: 0});
42050             });
42051         }
42052
42053         function enterReselect(mode) {
42054             if (mode.id !== 'select') return;
42055             context.map().on('move.intro', null);
42056             context.on('enter.intro', null);
42057
42058             setTimeout(function() {
42059                 reveal('.entity-editor-pane',
42060                     t('intro.points.fixname', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
42061                 context.on('exit.intro', deletePoint);
42062             }, 500);
42063         }
42064
42065         function deletePoint() {
42066             context.on('exit.intro', null);
42067             context.on('enter.intro', enterDelete);
42068
42069             var pointBox = iD.ui.intro.pad(corner, 150, context);
42070             reveal(pointBox, t('intro.points.reselect_delete'));
42071
42072             context.map().on('move.intro', function() {
42073                 pointBox = iD.ui.intro.pad(corner, 150, context);
42074                 reveal(pointBox, t('intro.points.reselect_delete'), {duration: 0});
42075             });
42076         }
42077
42078         function enterDelete(mode) {
42079             if (mode.id !== 'select') return;
42080             context.map().on('move.intro', null);
42081             context.on('enter.intro', null);
42082             context.on('exit.intro', deletePoint);
42083             context.map().on('move.intro', deletePoint);
42084             context.history().on('change.intro', deleted);
42085
42086             setTimeout(function() {
42087                 var node = d3.select('.radial-menu-item-delete').node();
42088                 var pointBox = iD.ui.intro.pad(node.getBoundingClientRect(), 50, context);
42089                 reveal(pointBox,
42090                     t('intro.points.delete', { button: iD.ui.intro.icon('#operation-delete', 'pre-text') }));
42091             }, 300);
42092         }
42093
42094         function deleted(changed) {
42095             if (changed.deleted().length) event.done();
42096         }
42097
42098     };
42099
42100     step.exit = function() {
42101         timeouts.forEach(window.clearTimeout);
42102         context.on('exit.intro', null);
42103         context.on('enter.intro', null);
42104         context.map().on('move.intro', null);
42105         context.history().on('change.intro', null);
42106         d3.select('.preset-search-input')
42107             .on('keyup.intro', null)
42108             .on('keydown.intro', null);
42109     };
42110
42111     return d3.rebind(step, event, 'on');
42112 };
42113 iD.ui.intro.startEditing = function(context, reveal) {
42114     var event = d3.dispatch('done', 'startEditing'),
42115         modal,
42116         timeouts = [];
42117
42118     var step = {
42119         title: 'intro.startediting.title'
42120     };
42121
42122     function timeout(f, t) {
42123         timeouts.push(window.setTimeout(f, t));
42124     }
42125
42126     step.enter = function() {
42127         reveal('.map-control.help-control',
42128             t('intro.startediting.help', { button: iD.ui.intro.icon('#icon-help', 'pre-text') }));
42129
42130         timeout(function() {
42131             reveal('#bar button.save', t('intro.startediting.save'));
42132         }, 5000);
42133
42134         timeout(function() {
42135             reveal('#surface');
42136         }, 10000);
42137
42138         timeout(function() {
42139             modal = iD.ui.modal(context.container());
42140
42141             modal.select('.modal')
42142                 .attr('class', 'modal-splash modal col6');
42143
42144             modal.selectAll('.close').remove();
42145
42146             var startbutton = modal.select('.content')
42147                 .attr('class', 'fillL')
42148                     .append('button')
42149                         .attr('class', 'modal-section huge-modal-button')
42150                         .on('click', function() {
42151                             modal.remove();
42152                         });
42153
42154                 startbutton.append('div')
42155                     .attr('class','illustration');
42156                 startbutton.append('h2')
42157                     .text(t('intro.startediting.start'));
42158
42159             event.startEditing();
42160         }, 10500);
42161     };
42162
42163     step.exit = function() {
42164         if (modal) modal.remove();
42165         timeouts.forEach(window.clearTimeout);
42166     };
42167
42168     return d3.rebind(step, event, 'on');
42169 };
42170 iD.presets = function() {
42171
42172     // an iD.presets.Collection with methods for
42173     // loading new data and returning defaults
42174
42175     var all = iD.presets.Collection([]),
42176         defaults = { area: all, line: all, point: all, vertex: all, relation: all },
42177         fields = {},
42178         universal = [],
42179         recent = iD.presets.Collection([]);
42180
42181     // Index of presets by (geometry, tag key).
42182     var index = {
42183         point: {},
42184         vertex: {},
42185         line: {},
42186         area: {},
42187         relation: {}
42188     };
42189
42190     all.match = function(entity, resolver) {
42191         var geometry = entity.geometry(resolver),
42192             geometryMatches = index[geometry],
42193             best = -1,
42194             match;
42195
42196         for (var k in entity.tags) {
42197             var keyMatches = geometryMatches[k];
42198             if (!keyMatches) continue;
42199
42200             for (var i = 0; i < keyMatches.length; i++) {
42201                 var score = keyMatches[i].matchScore(entity);
42202                 if (score > best) {
42203                     best = score;
42204                     match = keyMatches[i];
42205                 }
42206             }
42207         }
42208
42209         return match || all.item(geometry);
42210     };
42211
42212     // Because of the open nature of tagging, iD will never have a complete
42213     // list of tags used in OSM, so we want it to have logic like "assume
42214     // that a closed way with an amenity tag is an area, unless the amenity
42215     // is one of these specific types". This function computes a structure
42216     // that allows testing of such conditions, based on the presets designated
42217     // as as supporting (or not supporting) the area geometry.
42218     //
42219     // The returned object L is a whitelist/blacklist of tags. A closed way
42220     // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
42221     // (see `iD.Way#isArea()`). In other words, the keys of L form the whitelist,
42222     // and the subkeys form the blacklist.
42223     all.areaKeys = function() {
42224         var areaKeys = {},
42225             ignore = ['barrier', 'highway', 'footway', 'railway', 'type'],
42226             presets = _.reject(all.collection, 'suggestion');
42227
42228         // whitelist
42229         presets.forEach(function(d) {
42230             for (var key in d.tags) break;
42231             if (!key) return;
42232             if (ignore.indexOf(key) !== -1) return;
42233
42234             if (d.geometry.indexOf('area') !== -1) {
42235                 areaKeys[key] = areaKeys[key] || {};
42236             }
42237         });
42238
42239         // blacklist
42240         presets.forEach(function(d) {
42241             for (var key in d.tags) break;
42242             if (!key) return;
42243             if (ignore.indexOf(key) !== -1) return;
42244
42245             var value = d.tags[key];
42246             if (d.geometry.indexOf('area') === -1 &&
42247                 d.geometry.indexOf('line') !== -1 &&
42248                 key in areaKeys && value !== '*') {
42249                 areaKeys[key][value] = true;
42250             }
42251         });
42252
42253         return areaKeys;
42254     };
42255
42256     all.load = function(d) {
42257
42258         if (d.fields) {
42259             _.forEach(d.fields, function(d, id) {
42260                 fields[id] = iD.presets.Field(id, d);
42261                 if (d.universal) universal.push(fields[id]);
42262             });
42263         }
42264
42265         if (d.presets) {
42266             _.forEach(d.presets, function(d, id) {
42267                 all.collection.push(iD.presets.Preset(id, d, fields));
42268             });
42269         }
42270
42271         if (d.categories) {
42272             _.forEach(d.categories, function(d, id) {
42273                 all.collection.push(iD.presets.Category(id, d, all));
42274             });
42275         }
42276
42277         if (d.defaults) {
42278             var getItem = _.bind(all.item, all);
42279             defaults = {
42280                 area: iD.presets.Collection(d.defaults.area.map(getItem)),
42281                 line: iD.presets.Collection(d.defaults.line.map(getItem)),
42282                 point: iD.presets.Collection(d.defaults.point.map(getItem)),
42283                 vertex: iD.presets.Collection(d.defaults.vertex.map(getItem)),
42284                 relation: iD.presets.Collection(d.defaults.relation.map(getItem))
42285             };
42286         }
42287
42288         for (var i = 0; i < all.collection.length; i++) {
42289             var preset = all.collection[i],
42290                 geometry = preset.geometry;
42291
42292             for (var j = 0; j < geometry.length; j++) {
42293                 var g = index[geometry[j]];
42294                 for (var k in preset.tags) {
42295                     (g[k] = g[k] || []).push(preset);
42296                 }
42297             }
42298         }
42299
42300         return all;
42301     };
42302
42303     all.field = function(id) {
42304         return fields[id];
42305     };
42306
42307     all.universal = function() {
42308         return universal;
42309     };
42310
42311     all.defaults = function(geometry, n) {
42312         var rec = recent.matchGeometry(geometry).collection.slice(0, 4),
42313             def = _.uniq(rec.concat(defaults[geometry].collection)).slice(0, n - 1);
42314         return iD.presets.Collection(_.uniq(rec.concat(def).concat(all.item(geometry))));
42315     };
42316
42317     all.choose = function(preset) {
42318         if (!preset.isFallback()) {
42319             recent = iD.presets.Collection(_.uniq([preset].concat(recent.collection)));
42320         }
42321         return all;
42322     };
42323
42324     return all;
42325 };
42326 iD.presets.Category = function(id, category, all) {
42327     category = _.clone(category);
42328
42329     category.id = id;
42330
42331     category.members = iD.presets.Collection(category.members.map(function(id) {
42332         return all.item(id);
42333     }));
42334
42335     category.matchGeometry = function(geometry) {
42336         return category.geometry.indexOf(geometry) >= 0;
42337     };
42338
42339     category.matchScore = function() { return -1; };
42340
42341     category.name = function() {
42342         return t('presets.categories.' + id + '.name', {'default': id});
42343     };
42344
42345     category.terms = function() {
42346         return [];
42347     };
42348
42349     return category;
42350 };
42351 iD.presets.Collection = function(collection) {
42352
42353     var maxSearchResults = 50,
42354         maxSuggestionResults = 10;
42355
42356     var presets = {
42357
42358         collection: collection,
42359
42360         item: function(id) {
42361             return _.find(collection, function(d) {
42362                 return d.id === id;
42363             });
42364         },
42365
42366         matchGeometry: function(geometry) {
42367             return iD.presets.Collection(collection.filter(function(d) {
42368                 return d.matchGeometry(geometry);
42369             }));
42370         },
42371
42372         search: function(value, geometry) {
42373             if (!value) return this;
42374
42375             value = value.toLowerCase();
42376
42377             var searchable = _.filter(collection, function(a) {
42378                     return a.searchable !== false && a.suggestion !== true;
42379                 }),
42380                 suggestions = _.filter(collection, function(a) {
42381                     return a.suggestion === true;
42382                 });
42383
42384             function leading(a) {
42385                 var index = a.indexOf(value);
42386                 return index === 0 || a[index - 1] === ' ';
42387             }
42388
42389             // matches value to preset.name
42390             var leading_name = _.filter(searchable, function(a) {
42391                     return leading(a.name().toLowerCase());
42392                 }).sort(function(a, b) {
42393                     var i = a.name().toLowerCase().indexOf(value) - b.name().toLowerCase().indexOf(value);
42394                     if (i === 0) return a.name().length - b.name().length;
42395                     else return i;
42396                 });
42397
42398             // matches value to preset.terms values
42399             var leading_terms = _.filter(searchable, function(a) {
42400                     return _.some(a.terms() || [], leading);
42401                 });
42402
42403             // matches value to preset.tags values
42404             var leading_tag_values = _.filter(searchable, function(a) {
42405                     return _.some(_.without(_.values(a.tags || {}), '*'), leading);
42406                 });
42407
42408
42409             // finds close matches to value in preset.name
42410             var levenstein_name = searchable.map(function(a) {
42411                     return {
42412                         preset: a,
42413                         dist: iD.util.editDistance(value, a.name().toLowerCase())
42414                     };
42415                 }).filter(function(a) {
42416                     return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
42417                 }).sort(function(a, b) {
42418                     return a.dist - b.dist;
42419                 }).map(function(a) {
42420                     return a.preset;
42421                 });
42422
42423             // finds close matches to value in preset.terms
42424             var leventstein_terms = _.filter(searchable, function(a) {
42425                     return _.some(a.terms() || [], function(b) {
42426                         return iD.util.editDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
42427                     });
42428                 });
42429
42430             function suggestionName(name) {
42431                 var nameArray = name.split(' - ');
42432                 if (nameArray.length > 1) {
42433                     name = nameArray.slice(0, nameArray.length-1).join(' - ');
42434                 }
42435                 return name.toLowerCase();
42436             }
42437
42438             var leading_suggestions = _.filter(suggestions, function(a) {
42439                     return leading(suggestionName(a.name()));
42440                 }).sort(function(a, b) {
42441                     a = suggestionName(a.name());
42442                     b = suggestionName(b.name());
42443                     var i = a.indexOf(value) - b.indexOf(value);
42444                     if (i === 0) return a.length - b.length;
42445                     else return i;
42446                 });
42447
42448             var leven_suggestions = suggestions.map(function(a) {
42449                     return {
42450                         preset: a,
42451                         dist: iD.util.editDistance(value, suggestionName(a.name()))
42452                     };
42453                 }).filter(function(a) {
42454                     return a.dist + Math.min(value.length - suggestionName(a.preset.name()).length, 0) < 1;
42455                 }).sort(function(a, b) {
42456                     return a.dist - b.dist;
42457                 }).map(function(a) {
42458                     return a.preset;
42459                 });
42460
42461             var other = presets.item(geometry);
42462
42463             var results = leading_name.concat(
42464                             leading_terms,
42465                             leading_tag_values,
42466                             leading_suggestions.slice(0, maxSuggestionResults+5),
42467                             levenstein_name,
42468                             leventstein_terms,
42469                             leven_suggestions.slice(0, maxSuggestionResults)
42470                         ).slice(0, maxSearchResults-1);
42471
42472             return iD.presets.Collection(_.uniq(
42473                     results.concat(other)
42474                 ));
42475         }
42476     };
42477
42478     return presets;
42479 };
42480 iD.presets.Field = function(id, field) {
42481     field = _.clone(field);
42482
42483     field.id = id;
42484
42485     field.matchGeometry = function(geometry) {
42486         return !field.geometry || field.geometry === geometry;
42487     };
42488
42489     field.t = function(scope, options) {
42490         return t('presets.fields.' + id + '.' + scope, options);
42491     };
42492
42493     field.label = function() {
42494         return field.t('label', {'default': id});
42495     };
42496
42497     var placeholder = field.placeholder;
42498     field.placeholder = function() {
42499         return field.t('placeholder', {'default': placeholder});
42500     };
42501
42502     return field;
42503 };
42504 iD.presets.Preset = function(id, preset, fields) {
42505     preset = _.clone(preset);
42506
42507     preset.id = id;
42508     preset.fields = (preset.fields || []).map(getFields);
42509     preset.geometry = (preset.geometry || []);
42510
42511     function getFields(f) {
42512         return fields[f];
42513     }
42514
42515     preset.matchGeometry = function(geometry) {
42516         return preset.geometry.indexOf(geometry) >= 0;
42517     };
42518
42519     var matchScore = preset.matchScore || 1;
42520     preset.matchScore = function(entity) {
42521         var tags = preset.tags,
42522             score = 0;
42523
42524         for (var t in tags) {
42525             if (entity.tags[t] === tags[t]) {
42526                 score += matchScore;
42527             } else if (tags[t] === '*' && t in entity.tags) {
42528                 score += matchScore / 2;
42529             } else {
42530                 return -1;
42531             }
42532         }
42533
42534         return score;
42535     };
42536
42537     preset.t = function(scope, options) {
42538         return t('presets.presets.' + id + '.' + scope, options);
42539     };
42540
42541     var name = preset.name;
42542     preset.name = function() {
42543         if (preset.suggestion) {
42544             id = id.split('/');
42545             id = id[0] + '/' + id[1];
42546             return name + ' - ' + t('presets.presets.' + id + '.name');
42547         }
42548         return preset.t('name', {'default': name});
42549     };
42550
42551     preset.terms = function() {
42552         return preset.t('terms', {'default': ''}).toLowerCase().trim().split(/\s*,+\s*/);
42553     };
42554
42555     preset.isFallback = function() {
42556         var tagCount = Object.keys(preset.tags).length;
42557         return tagCount === 0 || (tagCount === 1 && preset.tags.hasOwnProperty('area'));
42558     };
42559
42560     preset.reference = function(geometry) {
42561         var key = Object.keys(preset.tags)[0],
42562             value = preset.tags[key];
42563
42564         if (geometry === 'relation' && key === 'type') {
42565             return { rtype: value };
42566         } else if (value === '*') {
42567             return { key: key };
42568         } else {
42569             return { key: key, value: value };
42570         }
42571     };
42572
42573     var removeTags = preset.removeTags || preset.tags;
42574     preset.removeTags = function(tags, geometry) {
42575         tags = _.omit(tags, _.keys(removeTags));
42576
42577         for (var f in preset.fields) {
42578             var field = preset.fields[f];
42579             if (field.matchGeometry(geometry) && field.default === tags[field.key]) {
42580                 delete tags[field.key];
42581             }
42582         }
42583
42584         delete tags.area;
42585         return tags;
42586     };
42587
42588     var applyTags = preset.addTags || preset.tags;
42589     preset.applyTags = function(tags, geometry) {
42590         var k;
42591
42592         tags = _.clone(tags);
42593
42594         for (k in applyTags) {
42595             if (applyTags[k] === '*') {
42596                 tags[k] = 'yes';
42597             } else {
42598                 tags[k] = applyTags[k];
42599             }
42600         }
42601
42602         // Add area=yes if necessary.
42603         // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
42604         // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
42605         // 2. chosen preset doesn't have a key in areaKeys (`railway=station`)
42606         if (geometry === 'area') {
42607             var needsAreaTag = true;
42608             if (preset.geometry.indexOf('line') === -1) {
42609                 for (k in applyTags) {
42610                     if (k in iD.areaKeys) {
42611                         needsAreaTag = false;
42612                         break;
42613                     }
42614                 }
42615             }
42616             if (needsAreaTag) {
42617                 tags.area = 'yes';
42618             }
42619         }
42620
42621         for (var f in preset.fields) {
42622             var field = preset.fields[f];
42623             if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
42624                 tags[field.key] = field.default;
42625             }
42626         }
42627
42628         return tags;
42629     };
42630
42631     return preset;
42632 };
42633 iD.validations = {};
42634 iD.validations.DeprecatedTag = function() {
42635
42636     var validation = function(changes) {
42637         var warnings = [];
42638         for (var i = 0; i < changes.created.length; i++) {
42639             var change = changes.created[i],
42640                 deprecatedTags = change.deprecatedTags();
42641
42642             if (!_.isEmpty(deprecatedTags)) {
42643                 var tags = iD.util.tagText({ tags: deprecatedTags });
42644                 warnings.push({
42645                     id: 'deprecated_tags',
42646                     message: t('validations.deprecated_tags', { tags: tags }),
42647                     entity: change
42648                 });
42649             }
42650         }
42651         return warnings;
42652     };
42653
42654     return validation;
42655 };
42656 iD.validations.ManyDeletions = function() {
42657     var threshold = 100;
42658
42659     var validation = function(changes) {
42660         var warnings = [];
42661         if (changes.deleted.length > threshold) {
42662             warnings.push({
42663                 id: 'many_deletions',
42664                 message: t('validations.many_deletions', { n: changes.deleted.length })
42665             });
42666         }
42667         return warnings;
42668     };
42669
42670     return validation;
42671 };
42672 iD.validations.MissingTag = function() {
42673
42674     // Slightly stricter check than Entity#isUsed (#3091)
42675     function hasTags(entity, graph) {
42676         return _.without(Object.keys(entity.tags), 'area', 'name').length > 0 ||
42677             graph.parentRelations(entity).length > 0;
42678     }
42679
42680     var validation = function(changes, graph) {
42681         var warnings = [];
42682         for (var i = 0; i < changes.created.length; i++) {
42683             var change = changes.created[i],
42684                 geometry = change.geometry(graph);
42685
42686             if ((geometry === 'point' || geometry === 'line' || geometry === 'area') && !hasTags(change, graph)) {
42687                 warnings.push({
42688                     id: 'missing_tag',
42689                     message: t('validations.untagged_' + geometry),
42690                     tooltip: t('validations.untagged_' + geometry + '_tooltip'),
42691                     entity: change
42692                 });
42693             }
42694         }
42695         return warnings;
42696     };
42697
42698     return validation;
42699 };
42700 iD.validations.TagSuggestsArea = function() {
42701
42702     // https://github.com/openstreetmap/josm/blob/mirror/src/org/
42703     // openstreetmap/josm/data/validation/tests/UnclosedWays.java#L80
42704     function tagSuggestsArea(tags) {
42705         if (_.isEmpty(tags)) return false;
42706
42707         var presence = ['landuse', 'amenities', 'tourism', 'shop'];
42708         for (var i = 0; i < presence.length; i++) {
42709             if (tags[presence[i]] !== undefined) {
42710                 return presence[i] + '=' + tags[presence[i]];
42711             }
42712         }
42713
42714         if (tags.building && tags.building === 'yes') return 'building=yes';
42715     }
42716
42717     var validation = function(changes, graph) {
42718         var warnings = [];
42719         for (var i = 0; i < changes.created.length; i++) {
42720             var change = changes.created[i],
42721                 geometry = change.geometry(graph),
42722                 suggestion = (geometry === 'line' ? tagSuggestsArea(change.tags) : undefined);
42723
42724             if (suggestion) {
42725                 warnings.push({
42726                     id: 'tag_suggests_area',
42727                     message: t('validations.tag_suggests_area', { tag: suggestion }),
42728                     entity: change
42729                 });
42730             }
42731         }
42732         return warnings;
42733     };
42734
42735     return validation;
42736 };
42737 })();
42738 window.locale = { _current: 'en' };
42739
42740 locale.current = function(_) {
42741     if (!arguments.length) return locale._current;
42742     if (locale[_] !== undefined) locale._current = _;
42743     else if (locale[_.split('-')[0]]) locale._current = _.split('-')[0];
42744     return locale;
42745 };
42746
42747 function t(s, o, loc) {
42748     loc = loc || locale._current;
42749
42750     var path = s.split(".").reverse(),
42751         rep = locale[loc];
42752
42753     while (rep !== undefined && path.length) rep = rep[path.pop()];
42754
42755     if (rep !== undefined) {
42756         if (o) for (var k in o) rep = rep.replace('{' + k + '}', o[k]);
42757         return rep;
42758     }
42759
42760     if (loc !== 'en') {
42761         return t(s, o, 'en');
42762     }
42763
42764     if (o && 'default' in o) {
42765         return o['default'];
42766     }
42767
42768     var missing = 'Missing ' + loc + ' translation: ' + s;
42769     if (typeof console !== "undefined") console.error(missing);
42770
42771     return missing;
42772 }
42773 iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]},"n185964961":{"id":"n185964961","loc":[-85.6406588,41.942601]},"n185964962":{"id":"n185964962","loc":[-85.6394548,41.94261]},"n185970607":{"id":"n185970607","loc":[-85.641094,41.94006]},"n185970614":{"id":"n185970614","loc":[-85.641825,41.941316]},"n185970616":{"id":"n185970616","loc":[-85.641838,41.941556]},"n185973650":{"id":"n185973650","loc":[-85.639918,41.940064]},"n185973660":{"id":"n185973660","loc":[-85.640645,41.941339]},"n185973659":{"id":"n185973659","loc":[-85.6406115,41.9400658]},"n185974479":{"id":"n185974479","loc":[-85.639402,41.941344]},"n185974481":{"id":"n185974481","loc":[-85.643071,41.941288]},"n185976259":{"id":"n185976259","loc":[-85.642213,41.940043]},"n185976261":{"id":"n185976261","loc":[-85.643056,41.94001]},"n185964959":{"id":"n185964959","loc":[-85.6431031,41.9425754]},"n185964960":{"id":"n185964960","loc":[-85.6418749,41.9425864]},"n185981481":{"id":"n185981481","loc":[-85.6386827,41.9400828]},"n185981482":{"id":"n185981482","loc":[-85.6393664,41.9400854]},"n2138493844":{"id":"n2138493844","loc":[-85.6427969,41.940522]},"n2138493845":{"id":"n2138493845","loc":[-85.6425891,41.9405228]},"n2138493846":{"id":"n2138493846","loc":[-85.6425868,41.9402875]},"n2138493847":{"id":"n2138493847","loc":[-85.6427969,41.9402858]},"n2138493848":{"id":"n2138493848","loc":[-85.6425708,41.9405234]},"n2138493849":{"id":"n2138493849","loc":[-85.642568,41.9402855]},"n2138493850":{"id":"n2138493850","loc":[-85.6423157,41.9402886]},"n2138493851":{"id":"n2138493851","loc":[-85.6423212,41.9404362]},"n2138493852":{"id":"n2138493852","loc":[-85.6422923,41.9404578]},"n2138493853":{"id":"n2138493853","loc":[-85.6422868,41.9404834]},"n2138493854":{"id":"n2138493854","loc":[-85.6423226,41.9405091]},"n2138493855":{"id":"n2138493855","loc":[-85.6423847,41.9405111]},"n2138493856":{"id":"n2138493856","loc":[-85.6424081,41.9405265]},"n2140155811":{"id":"n2140155811","loc":[-85.6419547,41.9410956]},"n2140155814":{"id":"n2140155814","loc":[-85.6427577,41.9410884]},"n2140155816":{"id":"n2140155816","loc":[-85.6427545,41.9410052]},"n2140155818":{"id":"n2140155818","loc":[-85.6428057,41.9410028]},"n2140155821":{"id":"n2140155821","loc":[-85.6427993,41.9407339]},"n2140155823":{"id":"n2140155823","loc":[-85.6427385,41.9407339]},"n2140155825":{"id":"n2140155825","loc":[-85.6427417,41.9406435]},"n2140155827":{"id":"n2140155827","loc":[-85.6419515,41.9406482]},"n2140155828":{"id":"n2140155828","loc":[-85.6429368,41.9412407]},"n2140155829":{"id":"n2140155829","loc":[-85.6417756,41.9412526]},"n2140155830":{"id":"n2140155830","loc":[-85.641766,41.9405983]},"n2140155831":{"id":"n2140155831","loc":[-85.6419803,41.9405983]},"n2140155832":{"id":"n2140155832","loc":[-85.6419611,41.9401366]},"n2140155833":{"id":"n2140155833","loc":[-85.6429336,41.94012]},"n2140155834":{"id":"n2140155834","loc":[-85.6430697,41.9411732]},"n2140155835":{"id":"n2140155835","loc":[-85.6428411,41.9409974]},"n2140155837":{"id":"n2140155837","loc":[-85.6428388,41.9407211]},"n2140155839":{"id":"n2140155839","loc":[-85.6430624,41.9405521]},"n2140155840":{"id":"n2140155840","loc":[-85.6427323,41.9412396]},"n2140155842":{"id":"n2140155842","loc":[-85.6418147,41.9412457]},"n2140155844":{"id":"n2140155844","loc":[-85.641813,41.9411319]},"n2140155845":{"id":"n2140155845","loc":[-85.6418394,41.9411111]},"n2140155847":{"id":"n2140155847","loc":[-85.6418838,41.9410977]},"n2140155849":{"id":"n2140155849","loc":[-85.6427324,41.9410921]},"n2140155851":{"id":"n2140155851","loc":[-85.6427798,41.9412945]},"n2140155852":{"id":"n2140155852","loc":[-85.6427701,41.9411777]},"n2140155854":{"id":"n2140155854","loc":[-85.6427323,41.9411572]},"n2140155856":{"id":"n2140155856","loc":[-85.6418478,41.9411666]},"n2165942818":{"id":"n2165942818","loc":[-85.6437533,41.9415029]},"n2165942819":{"id":"n2165942819","loc":[-85.6437623,41.9421195]},"n2168510551":{"id":"n2168510551","loc":[-85.6423795,41.9422615]},"n2168510552":{"id":"n2168510552","loc":[-85.6423744,41.9419439]},"n2168510553":{"id":"n2168510553","loc":[-85.642518,41.9419427]},"n2168510554":{"id":"n2168510554","loc":[-85.6425186,41.9419801]},"n2168510555":{"id":"n2168510555","loc":[-85.6428314,41.9419773]},"n2168510556":{"id":"n2168510556","loc":[-85.6428368,41.9423116]},"n2168510557":{"id":"n2168510557","loc":[-85.6424947,41.9423146]},"n2168510558":{"id":"n2168510558","loc":[-85.6424938,41.9422605]},"n2189046007":{"id":"n2189046007","loc":[-85.6410866,41.9424327]},"n2189046009":{"id":"n2189046009","loc":[-85.6410805,41.9420061]},"n2189046011":{"id":"n2189046011","loc":[-85.6412443,41.9420048]},"n2189046012":{"id":"n2189046012","loc":[-85.6412505,41.9424314]},"n2189046014":{"id":"n2189046014","loc":[-85.6413311,41.942968]},"n2189046016":{"id":"n2189046016","loc":[-85.6413281,41.942713]},"n2189046018":{"id":"n2189046018","loc":[-85.641521,41.9427117]},"n2189046021":{"id":"n2189046021","loc":[-85.6415234,41.9429236]},"n2189046022":{"id":"n2189046022","loc":[-85.6415045,41.9429238]},"n2189046025":{"id":"n2189046025","loc":[-85.641505,41.9429668]},"n2189046053":{"id":"n2189046053","loc":[-85.6385988,41.942412]},"n2189046054":{"id":"n2189046054","loc":[-85.6385985,41.9423311]},"n2189046055":{"id":"n2189046055","loc":[-85.6387617,41.9423308]},"n2189046056":{"id":"n2189046056","loc":[-85.6387616,41.9423026]},"n2189046058":{"id":"n2189046058","loc":[-85.6388215,41.9423025]},"n2189046059":{"id":"n2189046059","loc":[-85.6388219,41.9424115]},"n2189046060":{"id":"n2189046060","loc":[-85.6391096,41.9424486]},"n2189046061":{"id":"n2189046061","loc":[-85.6391105,41.9423673]},"n2189046063":{"id":"n2189046063","loc":[-85.6392911,41.9423684]},"n2189046065":{"id":"n2189046065","loc":[-85.6392903,41.9424497]},"n2189046067":{"id":"n2189046067","loc":[-85.6397927,41.9423876]},"n2189046069":{"id":"n2189046069","loc":[-85.6397897,41.9422981]},"n2189046070":{"id":"n2189046070","loc":[-85.6399702,41.9422947]},"n2189046072":{"id":"n2189046072","loc":[-85.6399732,41.9423843]},"n2189046074":{"id":"n2189046074","loc":[-85.6396331,41.9430227]},"n2189046075":{"id":"n2189046075","loc":[-85.6398673,41.9430189]},"n2189046077":{"id":"n2189046077","loc":[-85.6398656,41.9429637]},"n2189046079":{"id":"n2189046079","loc":[-85.6398885,41.9429633]},"n2189046082":{"id":"n2189046082","loc":[-85.6398832,41.942779]},"n2189046083":{"id":"n2189046083","loc":[-85.6398513,41.9427796]},"n2189046085":{"id":"n2189046085","loc":[-85.6398502,41.9427401]},"n2189046087":{"id":"n2189046087","loc":[-85.6397889,41.9427411]},"n2189046089":{"id":"n2189046089","loc":[-85.6397892,41.942753]},"n2189046090":{"id":"n2189046090","loc":[-85.6396983,41.9427544]},"n2189046092":{"id":"n2189046092","loc":[-85.6396993,41.9427882]},"n2189046094":{"id":"n2189046094","loc":[-85.6396746,41.9427886]},"n2189046096":{"id":"n2189046096","loc":[-85.6396758,41.9428296]},"n2189046097":{"id":"n2189046097","loc":[-85.6397007,41.9428292]},"n2189046099":{"id":"n2189046099","loc":[-85.6397018,41.9428686]},"n2189046103":{"id":"n2189046103","loc":[-85.6396289,41.9428697]},"n2189046112":{"id":"n2189046112","loc":[-85.6435683,41.9429457]},"n2189046113":{"id":"n2189046113","loc":[-85.643568,41.9427766]},"n2189046115":{"id":"n2189046115","loc":[-85.6434011,41.9427767]},"n2189046116":{"id":"n2189046116","loc":[-85.6434012,41.9428631]},"n2189046117":{"id":"n2189046117","loc":[-85.643448,41.9428631]},"n2189046118":{"id":"n2189046118","loc":[-85.6434481,41.9429457]},"n2189046119":{"id":"n2189046119","loc":[-85.6428363,41.9429809]},"n2189046120":{"id":"n2189046120","loc":[-85.6429171,41.9429791]},"n2189046121":{"id":"n2189046121","loc":[-85.642914,41.9429041]},"n2189046122":{"id":"n2189046122","loc":[-85.6429385,41.9429035]},"n2189046123":{"id":"n2189046123","loc":[-85.6429348,41.9428126]},"n2189046124":{"id":"n2189046124","loc":[-85.6427746,41.9428163]},"n2189046125":{"id":"n2189046125","loc":[-85.6427783,41.942906]},"n2189046126":{"id":"n2189046126","loc":[-85.6428332,41.9429047]},"n2189046127":{"id":"n2189046127","loc":[-85.6423018,41.9428859]},"n2189046128":{"id":"n2189046128","loc":[-85.6422987,41.9427208]},"n2189046130":{"id":"n2189046130","loc":[-85.6424218,41.9427195]},"n2189046131":{"id":"n2189046131","loc":[-85.6424246,41.9428684]},"n2189046132":{"id":"n2189046132","loc":[-85.6423845,41.9428689]},"n2189046133":{"id":"n2189046133","loc":[-85.6423848,41.942885]},"n2189046134":{"id":"n2189046134","loc":[-85.641533,41.9429392]},"n2189046135":{"id":"n2189046135","loc":[-85.6416096,41.9428768]},"n2189046137":{"id":"n2189046137","loc":[-85.6416763,41.9429221]},"n2189046138":{"id":"n2189046138","loc":[-85.6415997,41.9429845]},"n2189046139":{"id":"n2189046139","loc":[-85.6420598,41.9428016]},"n2189046140":{"id":"n2189046140","loc":[-85.6420593,41.9427415]},"n2189046141":{"id":"n2189046141","loc":[-85.6421957,41.9427409]},"n2189046142":{"id":"n2189046142","loc":[-85.6421963,41.9428182]},"n2189046143":{"id":"n2189046143","loc":[-85.6421281,41.9428185]},"n2189046144":{"id":"n2189046144","loc":[-85.6421279,41.9428013]},"n2189046145":{"id":"n2189046145","loc":[-85.6409429,41.9429345]},"n2189046146":{"id":"n2189046146","loc":[-85.6410354,41.9429334]},"n2189046147":{"id":"n2189046147","loc":[-85.6410325,41.9427972]},"n2189046148":{"id":"n2189046148","loc":[-85.640997,41.9427976]},"n2189046149":{"id":"n2189046149","loc":[-85.6409963,41.9427643]},"n2189046150":{"id":"n2189046150","loc":[-85.6408605,41.9427659]},"n2189046152":{"id":"n2189046152","loc":[-85.6408623,41.9428482]},"n2189046153":{"id":"n2189046153","loc":[-85.640941,41.9428473]},"n2189152992":{"id":"n2189152992","loc":[-85.6437661,41.9422257]},"n2189152993":{"id":"n2189152993","loc":[-85.643768,41.9424067]},"n2189152994":{"id":"n2189152994","loc":[-85.6432176,41.9417705]},"n2189152995":{"id":"n2189152995","loc":[-85.6432097,41.941327]},"n2189152996":{"id":"n2189152996","loc":[-85.6436493,41.9413226]},"n2189152997":{"id":"n2189152997","loc":[-85.6436563,41.9417164]},"n2189152998":{"id":"n2189152998","loc":[-85.6435796,41.9417171]},"n2189152999":{"id":"n2189152999","loc":[-85.6435805,41.9417669]},"n2189153000":{"id":"n2189153000","loc":[-85.6438202,41.9414953]},"n2189153001":{"id":"n2189153001","loc":[-85.6438173,41.9413175]},"n2189153004":{"id":"n2189153004","loc":[-85.6432535,41.9418466]},"n2189153005":{"id":"n2189153005","loc":[-85.6433935,41.9418599]},"n2189153006":{"id":"n2189153006","loc":[-85.6434831,41.9418986]},"n2189153007":{"id":"n2189153007","loc":[-85.6435678,41.9419774]},"n2189153008":{"id":"n2189153008","loc":[-85.6435987,41.9420282]},"n2189153009":{"id":"n2189153009","loc":[-85.643438,41.9419573]},"n2189153010":{"id":"n2189153010","loc":[-85.6435284,41.9424676]},"n2189153011":{"id":"n2189153011","loc":[-85.6436207,41.9423631]},"n2189153012":{"id":"n2189153012","loc":[-85.6434957,41.9422973]},"n2189153013":{"id":"n2189153013","loc":[-85.6434457,41.9422458]},"n2189153014":{"id":"n2189153014","loc":[-85.6433976,41.9421772]},"n2189153015":{"id":"n2189153015","loc":[-85.6433861,41.9420785]},"n2189153016":{"id":"n2189153016","loc":[-85.6433765,41.9420313]},"n2189153017":{"id":"n2189153017","loc":[-85.6432207,41.9420284]},"n2189153018":{"id":"n2189153018","loc":[-85.6432245,41.9422759]},"n2189153019":{"id":"n2189153019","loc":[-85.6432649,41.9423474]},"n2189153020":{"id":"n2189153020","loc":[-85.6433226,41.9424132]},"n2189153021":{"id":"n2189153021","loc":[-85.6434111,41.9424704]},"n2189153022":{"id":"n2189153022","loc":[-85.6434591,41.9424347]},"n2189153025":{"id":"n2189153025","loc":[-85.6437669,41.9423073]},"n2189153026":{"id":"n2189153026","loc":[-85.6436611,41.942293]},"n2189153027":{"id":"n2189153027","loc":[-85.6435784,41.9422473]},"n2189153028":{"id":"n2189153028","loc":[-85.6435245,41.9421443]},"n2189153029":{"id":"n2189153029","loc":[-85.6435149,41.9420613]},"n2189153030":{"id":"n2189153030","loc":[-85.6433528,41.9419269]},"n2189153031":{"id":"n2189153031","loc":[-85.6432535,41.9419191]},"n2189153032":{"id":"n2189153032","loc":[-85.6430868,41.9419198]},"n2189153033":{"id":"n2189153033","loc":[-85.6434894,41.9420033]},"n2189153034":{"id":"n2189153034","loc":[-85.6432974,41.9419225]},"n2189153035":{"id":"n2189153035","loc":[-85.6433055,41.9421632]},"n2189153036":{"id":"n2189153036","loc":[-85.6433538,41.9422849]},"n2189153037":{"id":"n2189153037","loc":[-85.6434718,41.9423887]},"n2189153038":{"id":"n2189153038","loc":[-85.6436134,41.9422667]},"n2189153040":{"id":"n2189153040","loc":[-85.6438759,41.9414017]},"n2189153041":{"id":"n2189153041","loc":[-85.6438181,41.9413687]},"n2189153042":{"id":"n2189153042","loc":[-85.6436821,41.9413044]},"n2189153043":{"id":"n2189153043","loc":[-85.6435899,41.9412862]},"n2189153044":{"id":"n2189153044","loc":[-85.6433169,41.9417268]},"n2189153045":{"id":"n2189153045","loc":[-85.643301,41.9412859]},"n2189153046":{"id":"n2189153046","loc":[-85.6435531,41.9416981]},"n2189153047":{"id":"n2189153047","loc":[-85.6435427,41.9412863]},"n185948706":{"id":"n185948706","loc":[-85.6369439,41.940122]},"n185949348":{"id":"n185949348","loc":[-85.640039,41.931135]},"n185949870":{"id":"n185949870","loc":[-85.643195,41.949261]},"n185954680":{"id":"n185954680","loc":[-85.6337802,41.9401143]},"n185954784":{"id":"n185954784","loc":[-85.6487485,41.942527]},"n185958670":{"id":"n185958670","loc":[-85.637255,41.940104]},"n185958672":{"id":"n185958672","loc":[-85.636996,41.941355]},"n185960207":{"id":"n185960207","loc":[-85.634992,41.940118]},"n185963163":{"id":"n185963163","loc":[-85.638831,41.93398]},"n185963165":{"id":"n185963165","loc":[-85.640073,41.933968]},"n185963167":{"id":"n185963167","loc":[-85.641225,41.933972]},"n185963168":{"id":"n185963168","loc":[-85.642386,41.933952]},"n185964695":{"id":"n185964695","loc":[-85.6443608,41.9425645]},"n185964697":{"id":"n185964697","loc":[-85.644384,41.939941]},"n185964963":{"id":"n185964963","loc":[-85.6382347,41.9426146]},"n185964965":{"id":"n185964965","loc":[-85.637022,41.942622]},"n185964967":{"id":"n185964967","loc":[-85.6363706,41.9426606]},"n185964968":{"id":"n185964968","loc":[-85.6357988,41.9427748]},"n185964969":{"id":"n185964969","loc":[-85.6355409,41.9428465]},"n185964970":{"id":"n185964970","loc":[-85.6348729,41.9430443]},"n185966958":{"id":"n185966958","loc":[-85.641946,41.946413]},"n185966960":{"id":"n185966960","loc":[-85.643148,41.946389]},"n185967774":{"id":"n185967774","loc":[-85.641889,41.943852]},"n185967775":{"id":"n185967775","loc":[-85.641922,41.945121]},"n185967776":{"id":"n185967776","loc":[-85.641927,41.947544]},"n185967777":{"id":"n185967777","loc":[-85.641982,41.947622]},"n185969289":{"id":"n185969289","loc":[-85.63928,41.929221]},"n185969704":{"id":"n185969704","loc":[-85.6388186,41.9350099]},"n185969706":{"id":"n185969706","loc":[-85.6400709,41.9349957]},"n185969708":{"id":"n185969708","loc":[-85.6412214,41.9349827]},"n185969710":{"id":"n185969710","loc":[-85.6423509,41.934974]},"n185970602":{"id":"n185970602","loc":[-85.641293,41.931817]},"n185970604":{"id":"n185970604","loc":[-85.641258,41.932705]},"n185970605":{"id":"n185970605","loc":[-85.641148,41.936984]},"n185970606":{"id":"n185970606","loc":[-85.641112,41.938169]},"n185970906":{"id":"n185970906","loc":[-85.639454,41.943871]},"n185970908":{"id":"n185970908","loc":[-85.6394635,41.9450504]},"n185970909":{"id":"n185970909","loc":[-85.6394914,41.9451911]},"n185971368":{"id":"n185971368","loc":[-85.635769,41.940122]},"n185971978":{"id":"n185971978","loc":[-85.640003,41.936988]},"n185971980":{"id":"n185971980","loc":[-85.642299,41.936988]},"n185973633":{"id":"n185973633","loc":[-85.639023,41.92861]},"n185973635":{"id":"n185973635","loc":[-85.639153,41.928969]},"n185973637":{"id":"n185973637","loc":[-85.639213,41.929088]},"n185973639":{"id":"n185973639","loc":[-85.63935,41.929396]},"n185973641":{"id":"n185973641","loc":[-85.640143,41.931462]},"n185973644":{"id":"n185973644","loc":[-85.64019,41.931788]},"n185973646":{"id":"n185973646","loc":[-85.6401365,41.9327199]},"n185973648":{"id":"n185973648","loc":[-85.639983,41.938174]},"n185974477":{"id":"n185974477","loc":[-85.638206,41.941331]},"n185975928":{"id":"n185975928","loc":[-85.640683,41.94513]},"n185975930":{"id":"n185975930","loc":[-85.643102,41.945103]},"n185976255":{"id":"n185976255","loc":[-85.642424,41.931817]},"n185976257":{"id":"n185976257","loc":[-85.64242,41.932699]},"n185976258":{"id":"n185976258","loc":[-85.6422621,41.9381489]},"n185977452":{"id":"n185977452","loc":[-85.6457497,41.9398834]},"n185978772":{"id":"n185978772","loc":[-85.646656,41.939869]},"n185981472":{"id":"n185981472","loc":[-85.6388962,41.9321266]},"n185981474":{"id":"n185981474","loc":[-85.6388769,41.9327334]},"n185981476":{"id":"n185981476","loc":[-85.638829,41.934116]},"n185981478":{"id":"n185981478","loc":[-85.63876,41.937002]},"n185981480":{"id":"n185981480","loc":[-85.638682,41.93819]},"n185981999":{"id":"n185981999","loc":[-85.638194,41.9400866]},"n185982001":{"id":"n185982001","loc":[-85.646302,41.93988]},"n185982877":{"id":"n185982877","loc":[-85.640676,41.943867]},"n185982879":{"id":"n185982879","loc":[-85.640734,41.945887]},"n185985823":{"id":"n185985823","loc":[-85.643106,41.943841]},"n185985824":{"id":"n185985824","loc":[-85.643145,41.947641]},"n185985825":{"id":"n185985825","loc":[-85.643219,41.950829]},"n1475301385":{"id":"n1475301385","loc":[-85.6360612,41.9427042]},"n1475301397":{"id":"n1475301397","loc":[-85.6366651,41.9426328]},"n2139795811":{"id":"n2139795811","loc":[-85.6469154,41.9425427]},"n2139795830":{"id":"n2139795830","loc":[-85.6443194,41.9399444]},"n2139795834":{"id":"n2139795834","loc":[-85.6453506,41.9399002]},"n2139795837":{"id":"n2139795837","loc":[-85.645806,41.9398831]},"n2139858932":{"id":"n2139858932","loc":[-85.6351721,41.9429557]},"n2140019000":{"id":"n2140019000","loc":[-85.6359935,41.9427224]},"n2165942817":{"id":"n2165942817","loc":[-85.6442017,41.9414993]},"n2165942820":{"id":"n2165942820","loc":[-85.6442107,41.9421159]},"n2189152990":{"id":"n2189152990","loc":[-85.6442328,41.942404]},"n2189152991":{"id":"n2189152991","loc":[-85.6442309,41.9422229]},"n2189153002":{"id":"n2189153002","loc":[-85.6441329,41.9413147]},"n2189153003":{"id":"n2189153003","loc":[-85.6441357,41.9414925]},"n2189153023":{"id":"n2189153023","loc":[-85.6443453,41.9423074]},"n2189153024":{"id":"n2189153024","loc":[-85.6442318,41.9423045]},"n2189153039":{"id":"n2189153039","loc":[-85.6441343,41.9414025]},"w208643102":{"id":"w208643102","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153034","n2189153035","n2189153036","n2189153037","n2189153038"]},"w17966942":{"id":"w17966942","tags":{"highway":"residential","name":"Millard St"},"nodes":["n185954680","n185960207","n185971368","n185948706","n185958670","n185981999","n185981481","n185981482","n185973650","n185973659","n185970607","n185976259","n185976261","n2139795830","n185964697","n2139795834","n185977452","n2139795837","n185982001","n185978772"]},"w208643105":{"id":"w208643105","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153046","n2189153047"]},"w208631637":{"id":"w208631637","tags":{"area":"yes","building":"yes"},"nodes":["n2189046014","n2189046016","n2189046018","n2189046021","n2189046022","n2189046025","n2189046014"]},"w208643096":{"id":"w208643096","tags":{"amenity":"parking","area":"yes","fee":"no"},"nodes":["n2189152990","n2189153024","n2189152991","n2189152992","n2189153025","n2189152993","n2189152990"]},"w208631656":{"id":"w208631656","tags":{"area":"yes","building":"yes"},"nodes":["n2189046134","n2189046135","n2189046137","n2189046138","n2189046134"]},"w204003417":{"id":"w204003417","tags":{"area":"yes","building":"school"},"nodes":["n2140155811","n2140155814","n2140155816","n2140155818","n2140155821","n2140155823","n2140155825","n2140155827","n2140155811"]},"w208631654":{"id":"w208631654","tags":{"area":"yes","building":"yes"},"nodes":["n2189046127","n2189046128","n2189046130","n2189046131","n2189046132","n2189046133","n2189046127"]},"w17966327":{"id":"w17966327","tags":{"highway":"residential","name":"S Douglas Ave"},"nodes":["n185976261","n2140155839","n2140155834","n185974481","n2189153032","n185964959"]},"w41785752":{"id":"w41785752","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60","access":"yes"},"nodes":["n185954784","n2139795811","n185964695","n185964959","n185964960","n185964961","n185964962","n185964963","n185964965","n1475301397","n185964967","n1475301385","n2140019000","n185964968","n185964969","n2139858932","n185964970"]},"w203841842":{"id":"w203841842","tags":{"area":"yes","leisure":"playground"},"nodes":["n2138493848","n2138493849","n2138493850","n2138493851","n2138493852","n2138493853","n2138493854","n2138493855","n2138493856","n2138493848"]},"w208643103":{"id":"w208643103","tags":{"highway":"service"},"nodes":["n2189153039","n2189153040","n2189153041","n2189153042","n2189153043","n2189153047","n2189153045","n185974481"]},"w208643098":{"id":"w208643098","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153000","n2189153041","n2189153001","n2189153002","n2189153039","n2189153003","n2189153000"]},"w208631646":{"id":"w208631646","tags":{"area":"yes","building":"yes"},"nodes":["n2189046067","n2189046069","n2189046070","n2189046072","n2189046067"]},"w208631653":{"id":"w208631653","tags":{"area":"yes","building":"yes"},"nodes":["n2189046119","n2189046120","n2189046121","n2189046122","n2189046123","n2189046124","n2189046125","n2189046126","n2189046119"]},"w17966041":{"id":"w17966041","tags":{"highway":"residential","name":"S Lincoln Ave"},"nodes":["n185973659","n185973660","n185964961"]},"w208631645":{"id":"w208631645","tags":{"area":"yes","building":"yes"},"nodes":["n2189046060","n2189046061","n2189046063","n2189046065","n2189046060"]},"w206803397":{"id":"w206803397","tags":{"area":"yes","building":"yes"},"nodes":["n2168510551","n2168510552","n2168510553","n2168510554","n2168510555","n2168510556","n2168510557","n2168510558","n2168510551"]},"w17965792":{"id":"w17965792","tags":{"highway":"residential","name":"N Hooker Ave"},"nodes":["n185964962","n185970906","n185970908","n185970909"]},"w208631651":{"id":"w208631651","tags":{"area":"yes","building":"yes"},"nodes":["n2189046112","n2189046113","n2189046115","n2189046116","n2189046117","n2189046118","n2189046112"]},"w208631643":{"id":"w208631643","tags":{"area":"yes","building":"yes"},"nodes":["n2189046053","n2189046054","n2189046055","n2189046056","n2189046058","n2189046059","n2189046053"]},"w17966878":{"id":"w17966878","tags":{"highway":"residential","name":"S Hooker Ave"},"nodes":["n185981472","n185981474","n185963163","n185981476","n185969704","n185981478","n185981480","n185981481"]},"w17966102":{"id":"w17966102","tags":{"highway":"residential","name":"South St"},"nodes":["n185958672","n185974477","n185974479","n185973660","n185970614"]},"w208631660":{"id":"w208631660","tags":{"area":"yes","building":"yes"},"nodes":["n2189046145","n2189046146","n2189046147","n2189046148","n2189046149","n2189046150","n2189046152","n2189046153","n2189046145"]},"w208643101":{"id":"w208643101","tags":{"highway":"service"},"nodes":["n2189153023","n2189153024","n2189153025","n2189153026","n2189153038","n2189153027","n2189153028","n2189153029","n2189153033","n2189153009","n2189153030","n2189153034","n2189153031","n2189153032"]},"w204000205":{"id":"w204000205","tags":{"highway":"residential","name":"South St","oneway":"yes"},"nodes":["n185974481","n2140155851","n185970614"]},"w203841841":{"id":"w203841841","tags":{"area":"yes","leisure":"pitch","pitch":"basketball"},"nodes":["n2138493844","n2138493845","n2138493846","n2138493847","n2138493844"]},"w17965444":{"id":"w17965444","tags":{"highway":"residential","name":"N Grant Ave"},"nodes":["n185964960","n185967774","n185967775","n185966958","n185967776","n185967777"]},"w208631648":{"id":"w208631648","tags":{"area":"yes","building":"yes"},"nodes":["n2189046074","n2189046075","n2189046077","n2189046079","n2189046082","n2189046083","n2189046085","n2189046087","n2189046089","n2189046090","n2189046092","n2189046094","n2189046096","n2189046097","n2189046099","n2189046103","n2189046074"]},"w208643100":{"id":"w208643100","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153010","n2189153011","n2189153012","n2189153013","n2189153014","n2189153015","n2189153016","n2189153017","n2189153018","n2189153019","n2189153020","n2189153021","n2189153022","n2189153010"]},"w17965749":{"id":"w17965749","tags":{"highway":"residential","name":"S Grant Ave"},"nodes":["n185970614","n185970616","n185964960"]},"w206574482":{"id":"w206574482","tags":{"amenity":"library","area":"yes","building":"yes","name":"Three Rivers Public Library"},"nodes":["n2165942817","n2165942818","n2165942819","n2165942820","n2165942817"]},"w208643097":{"id":"w208643097","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189152994","n2189152995","n2189152996","n2189152997","n2189152998","n2189152999","n2189152994"]},"w17966879":{"id":"w17966879","tags":{"highway":"residential","name":"S Hooker Ave"},"nodes":["n185981482","n185974479","n185964962"]},"w17966325":{"id":"w17966325","tags":{"highway":"residential","name":"S Douglas Ave"},"nodes":["n185976255","n185976257","n185963168","n185969710","n185971980","n185976258","n185954700","n185976259"]},"w17967390":{"id":"w17967390","tags":{"highway":"residential","name":"N Douglas Ave"},"nodes":["n185964959","n185985823","n185975930","n185966960","n185985824","n185949870","n185985825"]},"w208631635":{"id":"w208631635","tags":{"area":"yes","building":"yes"},"nodes":["n2189046007","n2189046009","n2189046011","n2189046012","n2189046007"]},"w208643099":{"id":"w208643099","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153031","n2189153004","n2189153005","n2189153006","n2189153007","n2189153008","n2189153029","n2189153033","n2189153009","n2189153030","n2189153031"]},"w208631658":{"id":"w208631658","tags":{"area":"yes","building":"yes"},"nodes":["n2189046139","n2189046140","n2189046141","n2189046142","n2189046143","n2189046144","n2189046139"]},"w208643104":{"id":"w208643104","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153044","n2189153045"]},"w17966039":{"id":"w17966039","tags":{"highway":"residential","name":"S Lincoln Ave"},"nodes":["n185973633","n185973635","n185973637","n185969289","n185973639","n185949348","n185973641","n185973644","n185973646","n185963165","n185969706","n185971978","n185973648","n185973650"]},"w204003420":{"id":"w204003420","tags":{"amenity":"parking","area":"yes"},"nodes":["n2140155840","n2140155842","n2140155844","n2140155845","n2140155847","n2140155849","n2140155854","n2140155840"]},"w204003419":{"id":"w204003419","tags":{"highway":"service"},"nodes":["n2140155834","n2140155835","n2140155837","n2140155839"]},"w204003418":{"id":"w204003418","tags":{"amenity":"school","area":"yes","name":"Andrews Elementary School"},"nodes":["n2140155828","n2140155829","n2140155830","n2140155831","n2140155832","n2140155833","n2140155828"]},"w17965747":{"id":"w17965747","tags":{"highway":"residential","name":"S Grant Ave"},"nodes":["n185970602","n185970604","n185963167","n185969708","n185970605","n185970606","n185970607"]},"w17967073":{"id":"w17967073","tags":{"highway":"residential","name":"N Lincoln Ave"},"nodes":["n185964961","n185982877","n185975928","n185982879"]},"w204003421":{"id":"w204003421","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2140155851","n2140155852","n2140155854","n2140155856"]},"r1943857":{"id":"r1943857","tags":{"modifier":"Business","name":"US 131 Business (Three Rivers, MI)","network":"US:US","ref":"131","route":"road","type":"route"},"members":[{"id":"w17966509","type":"way","role":"forward"},{"id":"w143497377","type":"way","role":""},{"id":"w134150811","type":"way","role":""},{"id":"w134150800","type":"way","role":""},{"id":"w134150789","type":"way","role":""},{"id":"w134150795","type":"way","role":""},{"id":"w41785752","type":"way","role":""},{"id":"w17965146","type":"way","role":"forward"},{"id":"w17964031","type":"way","role":"forward"}]},"r270277":{"id":"r270277","tags":{"network":"US:MI","ref":"60","route":"road","state_id":"MI","type":"route","url":"http://en.wikipedia.org/wiki/M-60_%28Michigan_highway%29"},"members":[{"id":"w17751087","type":"way","role":"east"},{"id":"w117148312","type":"way","role":"east"},{"id":"w40942155","type":"way","role":"west"},{"id":"w17751017","type":"way","role":""},{"id":"w17751083","type":"way","role":""},{"id":"w17747780","type":"way","role":""},{"id":"w41068082","type":"way","role":""},{"id":"w197025212","type":"way","role":""},{"id":"w17743874","type":"way","role":""},{"id":"w17751044","type":"way","role":""},{"id":"w17752167","type":"way","role":""},{"id":"w17751089","type":"way","role":""},{"id":"w17743879","type":"way","role":""},{"id":"w17751064","type":"way","role":""},{"id":"w197057073","type":"way","role":""},{"id":"w167699963","type":"way","role":""},{"id":"w167699972","type":"way","role":""},{"id":"w17967584","type":"way","role":""},{"id":"w167699964","type":"way","role":""},{"id":"w17967582","type":"way","role":"west"},{"id":"w41260270","type":"way","role":"west"},{"id":"w17965146","type":"way","role":"west"},{"id":"w41785752","type":"way","role":""},{"id":"w134150795","type":"way","role":""},{"id":"w134150789","type":"way","role":""},{"id":"w134150800","type":"way","role":""},{"id":"w134150811","type":"way","role":""},{"id":"w134150836","type":"way","role":""},{"id":"w134150802","type":"way","role":""},{"id":"w41074896","type":"way","role":""},{"id":"w17966773","type":"way","role":""},{"id":"w17967415","type":"way","role":""},{"id":"w41074899","type":"way","role":""},{"id":"w17967581","type":"way","role":""},{"id":"w41074902","type":"way","role":""},{"id":"w41074906","type":"way","role":""},{"id":"w209707997","type":"way","role":""},{"id":"w209707998","type":"way","role":""},{"id":"w17964798","type":"way","role":""},{"id":"w17966034","type":"way","role":""},{"id":"w17967593","type":"way","role":""},{"id":"w41074888","type":"way","role":""},{"id":"w17733772","type":"way","role":""},{"id":"w41074813","type":"way","role":""},{"id":"w17742213","type":"way","role":""},{"id":"w17746863","type":"way","role":""},{"id":"w17745772","type":"way","role":""},{"id":"w17742222","type":"way","role":""},{"id":"w17745922","type":"way","role":""},{"id":"w17742198","type":"way","role":""},{"id":"w17747675","type":"way","role":""},{"id":"w17739927","type":"way","role":""},{"id":"w17745708","type":"way","role":""},{"id":"w41006323","type":"way","role":""},{"id":"w17744233","type":"way","role":""},{"id":"w17739436","type":"way","role":""},{"id":"w17742201","type":"way","role":""},{"id":"w151418616","type":"way","role":""},{"id":"w17750062","type":"way","role":""},{"id":"w17742227","type":"way","role":"east"},{"id":"w41006348","type":"way","role":"east"},{"id":"w41260984","type":"way","role":""},{"id":"w17832427","type":"way","role":""},{"id":"w17838408","type":"way","role":""},{"id":"w17835846","type":"way","role":""},{"id":"w17832923","type":"way","role":""},{"id":"w17839388","type":"way","role":""},{"id":"w17838390","type":"way","role":""},{"id":"w17831272","type":"way","role":""},{"id":"w17828581","type":"way","role":""},{"id":"w38240686","type":"way","role":""},{"id":"w17838405","type":"way","role":"east"},{"id":"w123323711","type":"way","role":"east"},{"id":"w17830167","type":"way","role":"east"},{"id":"w99011909","type":"way","role":"east"},{"id":"w41911361","type":"way","role":"east"},{"id":"w41911355","type":"way","role":"east"},{"id":"w41911356","type":"way","role":"east"},{"id":"w117148326","type":"way","role":"west"},{"id":"w41911352","type":"way","role":"west"},{"id":"w41911353","type":"way","role":"west"},{"id":"w41911354","type":"way","role":"west"},{"id":"w41911360","type":"way","role":"west"},{"id":"w38240676","type":"way","role":"west"},{"id":"w123323710","type":"way","role":"west"},{"id":"w41260271","type":"way","role":"east"},{"id":"w41260273","type":"way","role":"east"},{"id":"w17964031","type":"way","role":"east"},{"id":"w41006344","type":"way","role":"west"},{"id":"w41006351","type":"way","role":"west"}]},"n367813436":{"id":"n367813436","loc":[-85.63605205663384,41.94305506683346],"tags":{"amenity":"fire_station","name":"Three Rivers Fire Department"}},"n185948708":{"id":"n185948708","loc":[-85.6369828,41.9408789]},"n185948710":{"id":"n185948710","loc":[-85.6370184,41.9411346]},"n185954691":{"id":"n185954691","loc":[-85.634476,41.941475]},"n185954692":{"id":"n185954692","loc":[-85.635008,41.941846]},"n185954693":{"id":"n185954693","loc":[-85.635362,41.941962]},"n185954695":{"id":"n185954695","loc":[-85.63578,41.941978]},"n185972903":{"id":"n185972903","loc":[-85.63295,41.9430062]},"n185964971":{"id":"n185964971","loc":[-85.6346811,41.9431023]},"n1819805854":{"id":"n1819805854","loc":[-85.6331275,41.9404837]},"n1819805918":{"id":"n1819805918","loc":[-85.6331168,41.942798]},"n1819805762":{"id":"n1819805762","loc":[-85.6333034,41.9424123]},"n1819805907":{"id":"n1819805907","loc":[-85.6334819,41.9419121]},"n1819805915":{"id":"n1819805915","loc":[-85.6334554,41.9413588]},"n1819848888":{"id":"n1819848888","loc":[-85.6331625,41.942679]},"n1819848930":{"id":"n1819848930","loc":[-85.6338684,41.9431252]},"n1819858505":{"id":"n1819858505","loc":[-85.6346782,41.9429092]},"n1819858507":{"id":"n1819858507","loc":[-85.6339003,41.9414534]},"n1819858508":{"id":"n1819858508","loc":[-85.6345709,41.9427742]},"n1819858509":{"id":"n1819858509","loc":[-85.63419,41.9417322]},"n1819858511":{"id":"n1819858511","loc":[-85.6340666,41.9415652]},"n1819858512":{"id":"n1819858512","loc":[-85.6343295,41.9423027]},"n1819858514":{"id":"n1819858514","loc":[-85.6343241,41.942207]},"n1819858521":{"id":"n1819858521","loc":[-85.633391,41.941231]},"n1819858528":{"id":"n1819858528","loc":[-85.6343027,41.9419716]},"n185954683":{"id":"n185954683","loc":[-85.6335412,41.940147]},"n185954685":{"id":"n185954685","loc":[-85.6334296,41.9403023]},"n185954687":{"id":"n185954687","loc":[-85.6333988,41.9404704]},"n185954689":{"id":"n185954689","loc":[-85.6335511,41.9410225]},"n185954690":{"id":"n185954690","loc":[-85.6336721,41.9411669]},"n1820938802":{"id":"n1820938802","loc":[-85.6330671,41.941845]},"n1821006702":{"id":"n1821006702","loc":[-85.6344047,41.9395496]},"n2130304133":{"id":"n2130304133","loc":[-85.6349025,41.9427659]},"n2130304136":{"id":"n2130304136","loc":[-85.6346027,41.9422017]},"n2130304138":{"id":"n2130304138","loc":[-85.6348577,41.9421517]},"n2130304140":{"id":"n2130304140","loc":[-85.6348419,41.9422694]},"n2130304142":{"id":"n2130304142","loc":[-85.6349071,41.9423135]},"n2130304144":{"id":"n2130304144","loc":[-85.6350495,41.9423312]},"n2130304146":{"id":"n2130304146","loc":[-85.6351009,41.9422812]},"n2130304147":{"id":"n2130304147","loc":[-85.6351227,41.9421532]},"n2130304148":{"id":"n2130304148","loc":[-85.635526,41.9421547]},"n2130304149":{"id":"n2130304149","loc":[-85.6355339,41.9425768]},"n2130304150":{"id":"n2130304150","loc":[-85.6351582,41.9426562]},"n2130304151":{"id":"n2130304151","loc":[-85.6351207,41.9427032]},"n2138493807":{"id":"n2138493807","loc":[-85.6350923,41.9415216]},"n2138493808":{"id":"n2138493808","loc":[-85.6353603,41.9411061]},"n2138493809":{"id":"n2138493809","loc":[-85.6354421,41.9410942]},"n2138493810":{"id":"n2138493810","loc":[-85.6355079,41.9411044]},"n2138493811":{"id":"n2138493811","loc":[-85.6355693,41.9411246]},"n2138493812":{"id":"n2138493812","loc":[-85.6355829,41.9411061]},"n2138493813":{"id":"n2138493813","loc":[-85.6355624,41.9409777]},"n2138493814":{"id":"n2138493814","loc":[-85.6355011,41.9409152]},"n2138493815":{"id":"n2138493815","loc":[-85.635383,41.9409219]},"n2138493816":{"id":"n2138493816","loc":[-85.635299,41.9409658]},"n2138493817":{"id":"n2138493817","loc":[-85.6351695,41.941204]},"n2138493818":{"id":"n2138493818","loc":[-85.6348879,41.9415166]},"n2138493819":{"id":"n2138493819","loc":[-85.634897,41.9415757]},"n2138493820":{"id":"n2138493820","loc":[-85.6349606,41.9416399]},"n2138493821":{"id":"n2138493821","loc":[-85.6350219,41.9416669]},"n2138493822":{"id":"n2138493822","loc":[-85.6351241,41.9416314]},"n2138493823":{"id":"n2138493823","loc":[-85.6350855,41.9415622]},"n2138493824":{"id":"n2138493824","loc":[-85.6350401,41.9413603]},"n2138493825":{"id":"n2138493825","loc":[-85.6352206,41.9410765]},"n2138493826":{"id":"n2138493826","loc":[-85.6343865,41.9415594]},"n2138493827":{"id":"n2138493827","loc":[-85.6343506,41.9415873]},"n2138493828":{"id":"n2138493828","loc":[-85.6344158,41.9417557]},"n2138493829":{"id":"n2138493829","loc":[-85.6344614,41.9417968]},"n2138493830":{"id":"n2138493830","loc":[-85.6345005,41.9418186]},"n2138493831":{"id":"n2138493831","loc":[-85.6345965,41.9418162]},"n2138493832":{"id":"n2138493832","loc":[-85.6347317,41.9417242]},"n2138493833":{"id":"n2138493833","loc":[-85.6346722,41.941775]},"n2139858909":{"id":"n2139858909","loc":[-85.633403,41.9391006]},"n2139858910":{"id":"n2139858910","loc":[-85.6332973,41.9393967]},"n2139858911":{"id":"n2139858911","loc":[-85.633205,41.9396742]},"n2139858912":{"id":"n2139858912","loc":[-85.6332203,41.9397772]},"n2139858913":{"id":"n2139858913","loc":[-85.6333453,41.939936]},"n2139858914":{"id":"n2139858914","loc":[-85.6333761,41.9400018]},"n2139858915":{"id":"n2139858915","loc":[-85.63328,41.9402249]},"n2139858916":{"id":"n2139858916","loc":[-85.6332357,41.9403523]},"n2139858917":{"id":"n2139858917","loc":[-85.6332838,41.9405831]},"n2139858918":{"id":"n2139858918","loc":[-85.6333643,41.9408744]},"n2139858919":{"id":"n2139858919","loc":[-85.6334394,41.9410519]},"n2139858920":{"id":"n2139858920","loc":[-85.6335815,41.9411717]},"n2139858921":{"id":"n2139858921","loc":[-85.6337478,41.9412734]},"n2139858922":{"id":"n2139858922","loc":[-85.6343174,41.9415268]},"n2139858923":{"id":"n2139858923","loc":[-85.6343886,41.9417397]},"n2139858924":{"id":"n2139858924","loc":[-85.6344407,41.9418015]},"n2139858925":{"id":"n2139858925","loc":[-85.6345139,41.9418366]},"n2139858926":{"id":"n2139858926","loc":[-85.6344846,41.942005]},"n2139858927":{"id":"n2139858927","loc":[-85.6345775,41.9422218]},"n2139858928":{"id":"n2139858928","loc":[-85.6348771,41.9427814]},"n2139858929":{"id":"n2139858929","loc":[-85.6349487,41.9427995]},"n2139858930":{"id":"n2139858930","loc":[-85.6350415,41.9427874]},"n2139858931":{"id":"n2139858931","loc":[-85.6351246,41.9428589]},"n2139858978":{"id":"n2139858978","loc":[-85.6349658,41.9431481]},"n2139858979":{"id":"n2139858979","loc":[-85.6350081,41.9431287]},"n2139858980":{"id":"n2139858980","loc":[-85.6349967,41.9430997]},"n2139858981":{"id":"n2139858981","loc":[-85.6352158,41.9430352]},"n2139858982":{"id":"n2139858982","loc":[-85.6348174,41.94267]},"n2139858983":{"id":"n2139858983","loc":[-85.6346142,41.9425989]},"n2139858984":{"id":"n2139858984","loc":[-85.6344938,41.9423809]},"n2139858985":{"id":"n2139858985","loc":[-85.6344856,41.9422997]},"n2139870380":{"id":"n2139870380","loc":[-85.6346707,41.9417955]},"n2139870381":{"id":"n2139870381","loc":[-85.6345949,41.9418311]},"n2139870382":{"id":"n2139870382","loc":[-85.6343322,41.9418659]},"n2139870383":{"id":"n2139870383","loc":[-85.6342072,41.941885]},"n2139870384":{"id":"n2139870384","loc":[-85.6341325,41.9418919]},"n2139870385":{"id":"n2139870385","loc":[-85.6341314,41.9422028]},"n2139870386":{"id":"n2139870386","loc":[-85.6340472,41.9423271]},"n2139870387":{"id":"n2139870387","loc":[-85.6342185,41.9427933]},"n2139870388":{"id":"n2139870388","loc":[-85.6340605,41.9423924]},"n2139870389":{"id":"n2139870389","loc":[-85.6339889,41.9424069]},"n2139870390":{"id":"n2139870390","loc":[-85.633971,41.942356]},"n2139870391":{"id":"n2139870391","loc":[-85.63361,41.9424235]},"n2139870392":{"id":"n2139870392","loc":[-85.6337137,41.9426819]},"n2139870393":{"id":"n2139870393","loc":[-85.6336977,41.9428632]},"n2139870394":{"id":"n2139870394","loc":[-85.6338823,41.9428647]},"n2139870395":{"id":"n2139870395","loc":[-85.6339412,41.9430069]},"n2139870396":{"id":"n2139870396","loc":[-85.6338873,41.9430353]},"n2139870397":{"id":"n2139870397","loc":[-85.6337676,41.942815]},"n2139870398":{"id":"n2139870398","loc":[-85.6336822,41.9423505]},"n2139870399":{"id":"n2139870399","loc":[-85.634037,41.9422725]},"n2139870400":{"id":"n2139870400","loc":[-85.6340294,41.9422518]},"n2139870401":{"id":"n2139870401","loc":[-85.6336726,41.9423312]},"n2139870402":{"id":"n2139870402","loc":[-85.6342188,41.9425715]},"n2139870403":{"id":"n2139870403","loc":[-85.6342524,41.942565]},"n2139870404":{"id":"n2139870404","loc":[-85.6341438,41.942299]},"n2139870405":{"id":"n2139870405","loc":[-85.6341149,41.9423061]},"n2139870407":{"id":"n2139870407","loc":[-85.6340846,41.9431458]},"n2139870408":{"id":"n2139870408","loc":[-85.6339436,41.9429032]},"n2139870409":{"id":"n2139870409","loc":[-85.6343143,41.9428207]},"n2139870410":{"id":"n2139870410","loc":[-85.6343507,41.94277]},"n2139870411":{"id":"n2139870411","loc":[-85.6341527,41.942254]},"n2139870412":{"id":"n2139870412","loc":[-85.6340925,41.9422199]},"n2139870413":{"id":"n2139870413","loc":[-85.6335435,41.9423433]},"n2139870414":{"id":"n2139870414","loc":[-85.6335023,41.9423975]},"n2139870415":{"id":"n2139870415","loc":[-85.6335086,41.9424552]},"n2139870416":{"id":"n2139870416","loc":[-85.6336296,41.942665]},"n2139870417":{"id":"n2139870417","loc":[-85.6341396,41.9428596]},"n2139870418":{"id":"n2139870418","loc":[-85.6339701,41.9424487]},"n2139870419":{"id":"n2139870419","loc":[-85.6335514,41.9425294]},"n2139870420":{"id":"n2139870420","loc":[-85.6337406,41.9424929]},"n2139870421":{"id":"n2139870421","loc":[-85.6338939,41.9428687]},"n2139870422":{"id":"n2139870422","loc":[-85.6341323,41.9419538]},"n2139870423":{"id":"n2139870423","loc":[-85.6340321,41.9420376]},"n2139870424":{"id":"n2139870424","loc":[-85.6337648,41.942238]},"n2139870425":{"id":"n2139870425","loc":[-85.6337604,41.9422685]},"n2139870426":{"id":"n2139870426","loc":[-85.6337682,41.9422928]},"n2139870427":{"id":"n2139870427","loc":[-85.6338086,41.9423862]},"n2139870428":{"id":"n2139870428","loc":[-85.6349465,41.9416631]},"n2139870429":{"id":"n2139870429","loc":[-85.6351097,41.9416973]},"n2139870430":{"id":"n2139870430","loc":[-85.6353371,41.9416798]},"n2139870431":{"id":"n2139870431","loc":[-85.6349627,41.9422506]},"n2139870432":{"id":"n2139870432","loc":[-85.634979,41.9421815]},"n2139870433":{"id":"n2139870433","loc":[-85.634885,41.9421679]},"n2139870434":{"id":"n2139870434","loc":[-85.6348689,41.9422377]},"n2139870435":{"id":"n2139870435","loc":[-85.6349779,41.9419486]},"n2139870436":{"id":"n2139870436","loc":[-85.6349505,41.9418933]},"n2139870437":{"id":"n2139870437","loc":[-85.6347327,41.9419505]},"n2139870438":{"id":"n2139870438","loc":[-85.6347614,41.9420087]},"n2139870439":{"id":"n2139870439","loc":[-85.6351889,41.9416912]},"n2139870440":{"id":"n2139870440","loc":[-85.6351092,41.9418426]},"n2139870441":{"id":"n2139870441","loc":[-85.635086,41.9419659]},"n2139870442":{"id":"n2139870442","loc":[-85.6350584,41.9421466]},"n2139870443":{"id":"n2139870443","loc":[-85.6350993,41.9421606]},"n2139870444":{"id":"n2139870444","loc":[-85.6350993,41.9422132]},"n2139870445":{"id":"n2139870445","loc":[-85.6350794,41.9422855]},"n2139870446":{"id":"n2139870446","loc":[-85.6350474,41.9423159]},"n2139870447":{"id":"n2139870447","loc":[-85.6349251,41.9422998]},"n2139870448":{"id":"n2139870448","loc":[-85.634911,41.9422755]},"n2139870449":{"id":"n2139870449","loc":[-85.6349157,41.9422553]},"n2139870450":{"id":"n2139870450","loc":[-85.6347213,41.9419324]},"n2139870451":{"id":"n2139870451","loc":[-85.6349535,41.9418771]},"n2139870452":{"id":"n2139870452","loc":[-85.6350135,41.9419421]},"n2139870453":{"id":"n2139870453","loc":[-85.6348584,41.9418997]},"n2139870454":{"id":"n2139870454","loc":[-85.6348113,41.9418101]},"n2139870455":{"id":"n2139870455","loc":[-85.6347306,41.9417449]},"n2139870456":{"id":"n2139870456","loc":[-85.6349123,41.941776]},"n2139870457":{"id":"n2139870457","loc":[-85.6349423,41.9421448]},"n2139870458":{"id":"n2139870458","loc":[-85.6349436,41.9420652]},"n2139870459":{"id":"n2139870459","loc":[-85.6349136,41.9419963]},"n2139870460":{"id":"n2139870460","loc":[-85.6349814,41.9419789]},"n2139989328":{"id":"n2139989328","loc":[-85.6334188,41.9421725]},"n2139989330":{"id":"n2139989330","loc":[-85.6335087,41.9416308]},"n2139989335":{"id":"n2139989335","loc":[-85.6336856,41.9429371]},"n2139989337":{"id":"n2139989337","loc":[-85.6333713,41.9427217]},"n2139989339":{"id":"n2139989339","loc":[-85.6332912,41.9425383]},"n2139989341":{"id":"n2139989341","loc":[-85.6339369,41.9409198]},"n2139989344":{"id":"n2139989344","loc":[-85.634097,41.9409469]},"n2139989346":{"id":"n2139989346","loc":[-85.634137,41.9412852]},"n2139989348":{"id":"n2139989348","loc":[-85.6344536,41.9414151]},"n2139989350":{"id":"n2139989350","loc":[-85.6350794,41.9412392]},"n2139989351":{"id":"n2139989351","loc":[-85.6352541,41.9409387]},"n2139989353":{"id":"n2139989353","loc":[-85.6357198,41.9408007]},"n2139989355":{"id":"n2139989355","loc":[-85.6357235,41.9427088]},"n2139989357":{"id":"n2139989357","loc":[-85.6337119,41.9421256]},"n2139989359":{"id":"n2139989359","loc":[-85.6336913,41.9420655]},"n2139989360":{"id":"n2139989360","loc":[-85.633582,41.9420867]},"n2139989362":{"id":"n2139989362","loc":[-85.6336058,41.9421491]},"n2139989364":{"id":"n2139989364","loc":[-85.6339685,41.9410995]},"n2139989366":{"id":"n2139989366","loc":[-85.6339067,41.9411383]},"n2139989368":{"id":"n2139989368","loc":[-85.6339685,41.9411972]},"n2139989370":{"id":"n2139989370","loc":[-85.6340398,41.9411619]},"n2139870379":{"id":"n2139870379","loc":[-85.6348391,41.9416651]},"n2140006363":{"id":"n2140006363","loc":[-85.6353144,41.9430345]},"n2140006364":{"id":"n2140006364","loc":[-85.6349191,41.9431422]},"n2140018997":{"id":"n2140018997","loc":[-85.63645945147184,41.942986488012565],"tags":{"amenity":"townhall","name":"Three Rivers City Hall"}},"n2140018998":{"id":"n2140018998","loc":[-85.6370319,41.9427919]},"n2140018999":{"id":"n2140018999","loc":[-85.6360687,41.9427808]},"n2199856288":{"id":"n2199856288","loc":[-85.6344968,41.9407307]},"n2199856289":{"id":"n2199856289","loc":[-85.634492,41.9406036]},"n2199856290":{"id":"n2199856290","loc":[-85.634891,41.9406001]},"n2199856291":{"id":"n2199856291","loc":[-85.6348894,41.9405288]},"n2199856292":{"id":"n2199856292","loc":[-85.6349166,41.94053]},"n2199856293":{"id":"n2199856293","loc":[-85.6349166,41.9404956]},"n2199856294":{"id":"n2199856294","loc":[-85.6350219,41.9404956]},"n2199856295":{"id":"n2199856295","loc":[-85.6350251,41.94053]},"n2199856296":{"id":"n2199856296","loc":[-85.6350538,41.9405288]},"n2199856297":{"id":"n2199856297","loc":[-85.6350602,41.94079]},"n2199856298":{"id":"n2199856298","loc":[-85.6351703,41.9407912]},"n2199856299":{"id":"n2199856299","loc":[-85.6351688,41.9409171]},"n2199856300":{"id":"n2199856300","loc":[-85.6347889,41.9409135]},"n2199856301":{"id":"n2199856301","loc":[-85.6347921,41.94079]},"n2199856302":{"id":"n2199856302","loc":[-85.6348942,41.9407888]},"n2199856303":{"id":"n2199856303","loc":[-85.6348926,41.9407283]},"n185951869":{"id":"n185951869","loc":[-85.6387639,41.957288]},"n185958643":{"id":"n185958643","loc":[-85.636746,41.929221]},"n185958645":{"id":"n185958645","loc":[-85.636791,41.929363]},"n185958647":{"id":"n185958647","loc":[-85.6375975,41.9314987]},"n185958649":{"id":"n185958649","loc":[-85.637669,41.931667]},"n185958651":{"id":"n185958651","loc":[-85.637728,41.931901]},"n185958653":{"id":"n185958653","loc":[-85.637724,41.932187]},"n185958656":{"id":"n185958656","loc":[-85.637732,41.932761]},"n185958658":{"id":"n185958658","loc":[-85.637688,41.93398]},"n185958660":{"id":"n185958660","loc":[-85.637685,41.934223]},"n185958662":{"id":"n185958662","loc":[-85.6376468,41.9350232]},"n185958664":{"id":"n185958664","loc":[-85.637564,41.937028]},"n185958666":{"id":"n185958666","loc":[-85.637458,41.938197]},"n185958668":{"id":"n185958668","loc":[-85.637424,41.938692]},"n185964972":{"id":"n185964972","loc":[-85.6341901,41.9432732]},"n185971361":{"id":"n185971361","loc":[-85.635762,41.938208]},"n185971364":{"id":"n185971364","loc":[-85.635732,41.9384]},"n185971366":{"id":"n185971366","loc":[-85.635736,41.938697]},"n185972775":{"id":"n185972775","loc":[-85.635638,42.070357]},"n185972777":{"id":"n185972777","loc":[-85.635724,42.069929]},"n185972779":{"id":"n185972779","loc":[-85.635804,42.069248]},"n185972781":{"id":"n185972781","loc":[-85.635869,42.068361]},"n185972783":{"id":"n185972783","loc":[-85.635883,42.067582]},"n185972785":{"id":"n185972785","loc":[-85.635875,42.067114]},"n185972787":{"id":"n185972787","loc":[-85.635778,42.065359]},"n185972788":{"id":"n185972788","loc":[-85.635728,42.063416]},"n185972789":{"id":"n185972789","loc":[-85.635665,42.062491]},"n185972790":{"id":"n185972790","loc":[-85.635617,42.061928]},"n185972791":{"id":"n185972791","loc":[-85.635614,42.061898]},"n185972793":{"id":"n185972793","loc":[-85.635379,42.060288]},"n185972795":{"id":"n185972795","loc":[-85.635092,42.05799]},"n185972797":{"id":"n185972797","loc":[-85.634843,42.055781]},"n185972798":{"id":"n185972798","loc":[-85.634817,42.055549]},"n185972800":{"id":"n185972800","loc":[-85.634708,42.053942]},"n185972802":{"id":"n185972802","loc":[-85.634447,42.051809]},"n185972805":{"id":"n185972805","loc":[-85.634241,42.04946]},"n185972807":{"id":"n185972807","loc":[-85.633787,42.045926]},"n185972809":{"id":"n185972809","loc":[-85.633811,42.045645]},"n185972811":{"id":"n185972811","loc":[-85.63373,42.043626]},"n185972813":{"id":"n185972813","loc":[-85.633698,42.042184]},"n185972814":{"id":"n185972814","loc":[-85.63369,42.04181]},"n185972815":{"id":"n185972815","loc":[-85.633681,42.040714]},"n185972816":{"id":"n185972816","loc":[-85.633571,42.036322]},"n185972817":{"id":"n185972817","loc":[-85.633537,42.034044]},"n185972819":{"id":"n185972819","loc":[-85.633481,42.030785]},"n185972821":{"id":"n185972821","loc":[-85.633452,42.027538]},"n185972824":{"id":"n185972824","loc":[-85.633438,42.027427]},"n185972826":{"id":"n185972826","loc":[-85.633342,42.022656]},"n185972830":{"id":"n185972830","loc":[-85.63327,42.020724]},"n185972832":{"id":"n185972832","loc":[-85.633198,42.019106]},"n185972834":{"id":"n185972834","loc":[-85.633249,42.018363]},"n185972835":{"id":"n185972835","loc":[-85.633139,42.012944]},"n185972836":{"id":"n185972836","loc":[-85.63309,42.008284]},"n185972839":{"id":"n185972839","loc":[-85.63298,42.00005]},"n185972845":{"id":"n185972845","loc":[-85.6325369,41.9764959]},"n185972847":{"id":"n185972847","loc":[-85.6327549,41.9750005]},"n185972849":{"id":"n185972849","loc":[-85.6329374,41.9742527]},"n185972851":{"id":"n185972851","loc":[-85.6331387,41.9736039]},"n185972862":{"id":"n185972862","loc":[-85.6383589,41.9585023]},"n185972868":{"id":"n185972868","loc":[-85.6393633,41.9551716]},"n185972878":{"id":"n185972878","loc":[-85.639377,41.95335]},"n185972882":{"id":"n185972882","loc":[-85.6389179,41.9516944]},"n185972885":{"id":"n185972885","loc":[-85.6387444,41.9512105]},"n185972891":{"id":"n185972891","loc":[-85.636421,41.946392]},"n185972895":{"id":"n185972895","loc":[-85.635965,41.945809]},"n185972897":{"id":"n185972897","loc":[-85.635683,41.945449]},"n185972899":{"id":"n185972899","loc":[-85.635281,41.9450252]},"n185972905":{"id":"n185972905","loc":[-85.6324428,41.9425743]},"n185985217":{"id":"n185985217","loc":[-85.638243,41.943674]},"n185985219":{"id":"n185985219","loc":[-85.638228,41.943747]},"n185985221":{"id":"n185985221","loc":[-85.638163,41.943797]},"n185985222":{"id":"n185985222","loc":[-85.638089,41.943832]},"n185985223":{"id":"n185985223","loc":[-85.637969,41.943841]},"n185985225":{"id":"n185985225","loc":[-85.637841,41.943833]},"n185985227":{"id":"n185985227","loc":[-85.637601,41.943789]},"n185985229":{"id":"n185985229","loc":[-85.637449,41.943754]},"n185985231":{"id":"n185985231","loc":[-85.637342,41.943734]},"n185985233":{"id":"n185985233","loc":[-85.637218,41.943703]},"n185985235":{"id":"n185985235","loc":[-85.637151,41.943663]},"n185985238":{"id":"n185985238","loc":[-85.637118,41.943615]},"n185985240":{"id":"n185985240","loc":[-85.637073,41.943494]},"n185990434":{"id":"n185990434","loc":[-85.6329028,41.9984292],"tags":{"railway":"level_crossing"}},"n1475284023":{"id":"n1475284023","loc":[-85.6336163,41.9435806],"tags":{"railway":"level_crossing"}},"n1475293222":{"id":"n1475293222","loc":[-85.6394045,41.953658],"tags":{"railway":"level_crossing"}},"n1475293226":{"id":"n1475293226","loc":[-85.6364975,41.9638663],"tags":{"railway":"level_crossing"}},"n1475293234":{"id":"n1475293234","loc":[-85.6390449,41.9565145]},"n1475293240":{"id":"n1475293240","loc":[-85.636943,41.9473114]},"n1475293252":{"id":"n1475293252","loc":[-85.6392115,41.9559003]},"n1475293254":{"id":"n1475293254","loc":[-85.6348931,41.9685127],"tags":{"railway":"level_crossing"}},"n1475293260":{"id":"n1475293260","loc":[-85.6375999,41.9485401]},"n1475293261":{"id":"n1475293261","loc":[-85.6391256,41.9523817],"tags":{"railway":"level_crossing"}},"n1475293264":{"id":"n1475293264","loc":[-85.6394155,41.9546493],"tags":{"railway":"level_crossing"}},"n1819805614":{"id":"n1819805614","loc":[-85.6345652,41.9363097]},"n1819805618":{"id":"n1819805618","loc":[-85.6295334,41.9426862]},"n1819805622":{"id":"n1819805622","loc":[-85.6308208,41.9430773]},"n1819805626":{"id":"n1819805626","loc":[-85.6274734,41.9406592]},"n1819805629":{"id":"n1819805629","loc":[-85.6296943,41.9430533]},"n1819805632":{"id":"n1819805632","loc":[-85.6340931,41.9354477]},"n1819805636":{"id":"n1819805636","loc":[-85.6304131,41.9436598]},"n1819805639":{"id":"n1819805639","loc":[-85.6304882,41.9426623]},"n1819805641":{"id":"n1819805641","loc":[-85.6336103,41.9367487]},"n1819805643":{"id":"n1819805643","loc":[-85.6300376,41.9418084]},"n1819805645":{"id":"n1819805645","loc":[-85.6365286,41.9336679]},"n1819805647":{"id":"n1819805647","loc":[-85.632016,41.9429221]},"n1819805666":{"id":"n1819805666","loc":[-85.6314753,41.9442663]},"n1819805669":{"id":"n1819805669","loc":[-85.6268619,41.9402203]},"n1819805673":{"id":"n1819805673","loc":[-85.6296728,41.9412099]},"n1819805676":{"id":"n1819805676","loc":[-85.6354557,41.932766]},"n1819805680":{"id":"n1819805680","loc":[-85.632752,41.9431012]},"n1819805683":{"id":"n1819805683","loc":[-85.631147,41.9432014]},"n1819805687":{"id":"n1819805687","loc":[-85.635284,41.9343942]},"n1819805690":{"id":"n1819805690","loc":[-85.6249736,41.9405794]},"n1819805694":{"id":"n1819805694","loc":[-85.6294153,41.9417925]},"n1819805698":{"id":"n1819805698","loc":[-85.6323486,41.9426986]},"n1819805702":{"id":"n1819805702","loc":[-85.6340287,41.9373871]},"n1819805707":{"id":"n1819805707","loc":[-85.6353698,41.9316326]},"n1819805711":{"id":"n1819805711","loc":[-85.6284176,41.940356]},"n1819805715":{"id":"n1819805715","loc":[-85.6291471,41.9412897]},"n1819805718":{"id":"n1819805718","loc":[-85.6311105,41.943979]},"n1819805722":{"id":"n1819805722","loc":[-85.6320868,41.9400128]},"n1819805724":{"id":"n1819805724","loc":[-85.635166,41.9324627]},"n1819805727":{"id":"n1819805727","loc":[-85.6344686,41.9350567]},"n1819805728":{"id":"n1819805728","loc":[-85.6357132,41.9332369]},"n1819805731":{"id":"n1819805731","loc":[-85.629984,41.9434444]},"n1819805760":{"id":"n1819805760","loc":[-85.6330996,41.9378784]},"n1819805766":{"id":"n1819805766","loc":[-85.625274,41.9411141]},"n1819805770":{"id":"n1819805770","loc":[-85.6326321,41.9412173]},"n1819805774":{"id":"n1819805774","loc":[-85.6347047,41.9312096]},"n1819805777":{"id":"n1819805777","loc":[-85.6363569,41.9339552]},"n1819805780":{"id":"n1819805780","loc":[-85.6327392,41.941926]},"n1819805783":{"id":"n1819805783","loc":[-85.6357239,41.9338435]},"n1819805786":{"id":"n1819805786","loc":[-85.6356595,41.9346576]},"n1819805789":{"id":"n1819805789","loc":[-85.6316469,41.9436598]},"n1819805792":{"id":"n1819805792","loc":[-85.6350587,41.9354557]},"n1819805795":{"id":"n1819805795","loc":[-85.6360028,41.9322791]},"n1819805798":{"id":"n1819805798","loc":[-85.63125,41.9443062]},"n1819805802":{"id":"n1819805802","loc":[-85.6263362,41.9408109]},"n1819805805":{"id":"n1819805805","loc":[-85.6315075,41.9438753]},"n1819805808":{"id":"n1819805808","loc":[-85.6340008,41.9316051]},"n1819805810":{"id":"n1819805810","loc":[-85.6345545,41.9320557]},"n1819805812":{"id":"n1819805812","loc":[-85.6250809,41.9408587]},"n1819805814":{"id":"n1819805814","loc":[-85.6257783,41.9400926]},"n1819805834":{"id":"n1819805834","loc":[-85.6326408,41.9424363]},"n1819805838":{"id":"n1819805838","loc":[-85.6365607,41.9334365]},"n1819805842":{"id":"n1819805842","loc":[-85.6288253,41.9410343]},"n1819805846":{"id":"n1819805846","loc":[-85.6279133,41.9402921]},"n1819805849":{"id":"n1819805849","loc":[-85.6289433,41.9405156]},"n1819805852":{"id":"n1819805852","loc":[-85.6313787,41.9439152]},"n1819805858":{"id":"n1819805858","loc":[-85.6300805,41.9420398]},"n1819805861":{"id":"n1819805861","loc":[-85.6321941,41.9396297]},"n1819805864":{"id":"n1819805864","loc":[-85.6329129,41.9393903]},"n1819805868":{"id":"n1819805868","loc":[-85.632001,41.9434922]},"n1819805870":{"id":"n1819805870","loc":[-85.6314903,41.9431535]},"n1819805873":{"id":"n1819805873","loc":[-85.6251667,41.9401166]},"n1819805876":{"id":"n1819805876","loc":[-85.63287,41.939941]},"n1819805878":{"id":"n1819805878","loc":[-85.6307886,41.9437317]},"n1819805880":{"id":"n1819805880","loc":[-85.6321727,41.940348]},"n1819805883":{"id":"n1819805883","loc":[-85.6265872,41.940113]},"n1819805885":{"id":"n1819805885","loc":[-85.6268404,41.9406672]},"n1819805887":{"id":"n1819805887","loc":[-85.6325267,41.9389035]},"n1819805889":{"id":"n1819805889","loc":[-85.6364964,41.933189]},"n1819805911":{"id":"n1819805911","loc":[-85.6248663,41.9401804]},"n1819805922":{"id":"n1819805922","loc":[-85.633267,41.9387199]},"n1819805925":{"id":"n1819805925","loc":[-85.6293402,41.9408428]},"n1819848849":{"id":"n1819848849","loc":[-85.6464957,41.9695178]},"n1819848850":{"id":"n1819848850","loc":[-85.6497642,41.9611355]},"n1819848851":{"id":"n1819848851","loc":[-85.6480943,41.9624818]},"n1819848854":{"id":"n1819848854","loc":[-85.6500362,41.9657367]},"n1819848855":{"id":"n1819848855","loc":[-85.6493673,41.9783496]},"n1819848856":{"id":"n1819848856","loc":[-85.6457409,41.9548007]},"n1819848857":{"id":"n1819848857","loc":[-85.651313,41.9760426]},"n1819848858":{"id":"n1819848858","loc":[-85.6495819,41.9784772]},"n1819848859":{"id":"n1819848859","loc":[-85.6495105,41.9833722]},"n1819848860":{"id":"n1819848860","loc":[-85.6405053,41.9492792]},"n1819848863":{"id":"n1819848863","loc":[-85.6502293,41.9786826]},"n1819848865":{"id":"n1819848865","loc":[-85.6406877,41.9495106]},"n1819848870":{"id":"n1819848870","loc":[-85.6493136,41.9704611]},"n1819848871":{"id":"n1819848871","loc":[-85.6372249,41.9441284]},"n1819848873":{"id":"n1819848873","loc":[-85.6512379,41.9659441]},"n1819848875":{"id":"n1819848875","loc":[-85.6508087,41.9650187]},"n1819848877":{"id":"n1819848877","loc":[-85.6487166,41.9605352]},"n1819848878":{"id":"n1819848878","loc":[-85.6506478,41.9760665]},"n1819848879":{"id":"n1819848879","loc":[-85.651431,41.9758512]},"n1819848886":{"id":"n1819848886","loc":[-85.6477617,41.9563945]},"n1819848889":{"id":"n1819848889","loc":[-85.6497895,41.9832286]},"n1819848892":{"id":"n1819848892","loc":[-85.6504868,41.9791931]},"n1819848893":{"id":"n1819848893","loc":[-85.6498002,41.9615085]},"n1819848894":{"id":"n1819848894","loc":[-85.6404302,41.9502846]},"n1819848901":{"id":"n1819848901","loc":[-85.6354412,41.9439886]},"n1819848903":{"id":"n1819848903","loc":[-85.6472145,41.9698528]},"n1819848904":{"id":"n1819848904","loc":[-85.6401979,41.9486233]},"n1819848905":{"id":"n1819848905","loc":[-85.6475042,41.963503]},"n1819848909":{"id":"n1819848909","loc":[-85.6343405,41.94358]},"n1819848914":{"id":"n1819848914","loc":[-85.6503474,41.9737773]},"n1819848915":{"id":"n1819848915","loc":[-85.6389533,41.9470992]},"n1819848916":{"id":"n1819848916","loc":[-85.6483625,41.9577907]},"n1819848917":{"id":"n1819848917","loc":[-85.6484768,41.9617419]},"n1819848918":{"id":"n1819848918","loc":[-85.644078,41.9545693]},"n1819848919":{"id":"n1819848919","loc":[-85.6437169,41.9543041]},"n1819848920":{"id":"n1819848920","loc":[-85.6478331,41.9627949]},"n1819848922":{"id":"n1819848922","loc":[-85.6499144,41.9785889]},"n1819848924":{"id":"n1819848924","loc":[-85.647633,41.9720066]},"n1819848926":{"id":"n1819848926","loc":[-85.6487987,41.978868]},"n1819848927":{"id":"n1819848927","loc":[-85.6495105,41.9730355]},"n1819848928":{"id":"n1819848928","loc":[-85.648223,41.9829654]},"n1819848929":{"id":"n1819848929","loc":[-85.6514846,41.9659122]},"n1819848931":{"id":"n1819848931","loc":[-85.6498753,41.9731871]},"n1819848932":{"id":"n1819848932","loc":[-85.640906,41.9508575]},"n1819848933":{"id":"n1819848933","loc":[-85.649775,41.9799767]},"n1819848934":{"id":"n1819848934","loc":[-85.6507014,41.9739927]},"n1819848937":{"id":"n1819848937","loc":[-85.6479763,41.9840899]},"n1819848938":{"id":"n1819848938","loc":[-85.6501113,41.9600884]},"n1819848939":{"id":"n1819848939","loc":[-85.6389962,41.9478253]},"n1819848941":{"id":"n1819848941","loc":[-85.637469,41.9445791]},"n1819848942":{"id":"n1819848942","loc":[-85.6494569,41.9601682]},"n1819848943":{"id":"n1819848943","loc":[-85.6368803,41.9439351]},"n1819848945":{"id":"n1819848945","loc":[-85.6474398,41.9724213]},"n1819848946":{"id":"n1819848946","loc":[-85.6382629,41.9463666]},"n1819848948":{"id":"n1819848948","loc":[-85.6489633,41.9830771]},"n1819848952":{"id":"n1819848952","loc":[-85.6488882,41.9600326]},"n1819848953":{"id":"n1819848953","loc":[-85.6488094,41.9774324]},"n1819848954":{"id":"n1819848954","loc":[-85.6491135,41.9600485]},"n1819848955":{"id":"n1819848955","loc":[-85.6501435,41.9734583]},"n1819848956":{"id":"n1819848956","loc":[-85.6495534,41.960958]},"n1819848958":{"id":"n1819848958","loc":[-85.6474683,41.9561491]},"n1819848959":{"id":"n1819848959","loc":[-85.6401083,41.9485451]},"n1819848960":{"id":"n1819848960","loc":[-85.6481764,41.9678686]},"n1819848961":{"id":"n1819848961","loc":[-85.6484017,41.967382]},"n1819848962":{"id":"n1819848962","loc":[-85.6501328,41.959897]},"n1819848964":{"id":"n1819848964","loc":[-85.6403695,41.9504586]},"n1819848966":{"id":"n1819848966","loc":[-85.6398975,41.9491499]},"n1819848967":{"id":"n1819848967","loc":[-85.6412455,41.9510187]},"n1819848968":{"id":"n1819848968","loc":[-85.6482622,41.9619493]},"n1819848969":{"id":"n1819848969","loc":[-85.6405841,41.9501474]},"n1819848970":{"id":"n1819848970","loc":[-85.6478583,41.9703394]},"n1819848971":{"id":"n1819848971","loc":[-85.6493388,41.9832845]},"n1819848972":{"id":"n1819848972","loc":[-85.6485664,41.9829415]},"n1819848974":{"id":"n1819848974","loc":[-85.6491457,41.9779887]},"n1819848975":{"id":"n1819848975","loc":[-85.6468889,41.9697033]},"n1819848976":{"id":"n1819848976","loc":[-85.6452726,41.9546072]},"n1819848977":{"id":"n1819848977","loc":[-85.6448435,41.9546072]},"n1819848979":{"id":"n1819848979","loc":[-85.6485342,41.9763138]},"n1819848980":{"id":"n1819848980","loc":[-85.6495282,41.9664087]},"n1819848986":{"id":"n1819848986","loc":[-85.6486307,41.9603278]},"n1819848987":{"id":"n1819848987","loc":[-85.6492278,41.9791871]},"n1819848990":{"id":"n1819848990","loc":[-85.6501934,41.9800724]},"n1819848992":{"id":"n1819848992","loc":[-85.6482445,41.9819685]},"n1819848993":{"id":"n1819848993","loc":[-85.6481871,41.9704451]},"n1819848994":{"id":"n1819848994","loc":[-85.6371364,41.9457602]},"n1819848996":{"id":"n1819848996","loc":[-85.6500362,41.9801023]},"n1819849000":{"id":"n1819849000","loc":[-85.639007,41.9485914]},"n1819849001":{"id":"n1819849001","loc":[-85.6488882,41.9669253]},"n1819849002":{"id":"n1819849002","loc":[-85.6484698,41.9565062]},"n1819849004":{"id":"n1819849004","loc":[-85.6510769,41.9761064]},"n1819849005":{"id":"n1819849005","loc":[-85.6503581,41.9799029]},"n1819849006":{"id":"n1819849006","loc":[-85.6489381,41.9703893]},"n1819849008":{"id":"n1819849008","loc":[-85.6497457,41.9833588]},"n1819849011":{"id":"n1819849011","loc":[-85.6497358,41.9717593]},"n1819849012":{"id":"n1819849012","loc":[-85.6494676,41.9796796]},"n1819849019":{"id":"n1819849019","loc":[-85.6486093,41.9771034]},"n1819849021":{"id":"n1819849021","loc":[-85.6504546,41.9796556]},"n1819849022":{"id":"n1819849022","loc":[-85.6371294,41.9454154]},"n1819849023":{"id":"n1819849023","loc":[-85.6503436,41.9759249]},"n1819849025":{"id":"n1819849025","loc":[-85.6462382,41.9693822]},"n1819849026":{"id":"n1819849026","loc":[-85.6497573,41.983093]},"n1819849028":{"id":"n1819849028","loc":[-85.6497465,41.9602799]},"n1819849029":{"id":"n1819849029","loc":[-85.6374728,41.9460698]},"n1819849030":{"id":"n1819849030","loc":[-85.6486592,41.9566039]},"n1819849031":{"id":"n1819849031","loc":[-85.6515989,41.9654993]},"n1819849032":{"id":"n1819849032","loc":[-85.6387028,41.9482658]},"n1819849033":{"id":"n1819849033","loc":[-85.6464742,41.9688398]},"n1819849034":{"id":"n1819849034","loc":[-85.6495212,41.9589236]},"n1819849035":{"id":"n1819849035","loc":[-85.6490599,41.9790096]},"n1819849036":{"id":"n1819849036","loc":[-85.6489918,41.9800724]},"n1819849038":{"id":"n1819849038","loc":[-85.6499182,41.9659042]},"n1819849040":{"id":"n1819849040","loc":[-85.639758,41.9490143]},"n1819849041":{"id":"n1819849041","loc":[-85.6514846,41.9755241]},"n1819849042":{"id":"n1819849042","loc":[-85.6436633,41.9540647]},"n1819849045":{"id":"n1819849045","loc":[-85.6475541,41.9726387]},"n1819849046":{"id":"n1819849046","loc":[-85.6488308,41.9718331]},"n1819849047":{"id":"n1819849047","loc":[-85.6377694,41.9460953]},"n1819849048":{"id":"n1819849048","loc":[-85.6490706,41.9804452]},"n1819849049":{"id":"n1819849049","loc":[-85.6485449,41.9766248]},"n1819849051":{"id":"n1819849051","loc":[-85.6483625,41.9790256]},"n1819849052":{"id":"n1819849052","loc":[-85.6490706,41.9585167]},"n1819849053":{"id":"n1819849053","loc":[-85.6425008,41.9522874]},"n1819849054":{"id":"n1819849054","loc":[-85.6475793,41.9632158]},"n1819849055":{"id":"n1819849055","loc":[-85.6408631,41.9499399]},"n1819849056":{"id":"n1819849056","loc":[-85.6483373,41.9814681]},"n1819849057":{"id":"n1819849057","loc":[-85.6313548,41.9442876]},"n1819849058":{"id":"n1819849058","loc":[-85.6432663,41.9529796]},"n1819849059":{"id":"n1819849059","loc":[-85.6487128,41.9582873]},"n1819849060":{"id":"n1819849060","loc":[-85.6482338,41.9817612]},"n1819849062":{"id":"n1819849062","loc":[-85.6485664,41.9788661]},"n1819849063":{"id":"n1819849063","loc":[-85.6373081,41.9448824]},"n1819849064":{"id":"n1819849064","loc":[-85.6472215,41.9557582]},"n1819849065":{"id":"n1819849065","loc":[-85.6348984,41.9440414]},"n1819849066":{"id":"n1819849066","loc":[-85.6501972,41.9647315]},"n1819849067":{"id":"n1819849067","loc":[-85.6489741,41.9808281]},"n1819849068":{"id":"n1819849068","loc":[-85.6420111,41.9515034]},"n1819849069":{"id":"n1819849069","loc":[-85.6397972,41.9488882]},"n1819849070":{"id":"n1819849070","loc":[-85.6499718,41.9593465]},"n1819849071":{"id":"n1819849071","loc":[-85.6486844,41.9811311]},"n1819849072":{"id":"n1819849072","loc":[-85.6390392,41.9474663]},"n1819849074":{"id":"n1819849074","loc":[-85.6495642,41.9616362]},"n1819849075":{"id":"n1819849075","loc":[-85.6483518,41.9791931]},"n1819849076":{"id":"n1819849076","loc":[-85.6478974,41.9833104]},"n1819849077":{"id":"n1819849077","loc":[-85.640155,41.948719]},"n1819849078":{"id":"n1819849078","loc":[-85.6399366,41.9487845]},"n1819849079":{"id":"n1819849079","loc":[-85.6492959,41.9825348]},"n1819849080":{"id":"n1819849080","loc":[-85.6505083,41.9648352]},"n1819849081":{"id":"n1819849081","loc":[-85.6492959,41.9645241]},"n1819849082":{"id":"n1819849082","loc":[-85.6402049,41.9491835]},"n1819849083":{"id":"n1819849083","loc":[-85.6495175,41.9826963]},"n1819849084":{"id":"n1819849084","loc":[-85.6480836,41.9728361]},"n1819849085":{"id":"n1819849085","loc":[-85.6374349,41.9443425]},"n1819849086":{"id":"n1819849086","loc":[-85.6478331,41.9681238]},"n1819849089":{"id":"n1819849089","loc":[-85.639368,41.9486169]},"n1819849092":{"id":"n1819849092","loc":[-85.6503581,41.9788022]},"n1819849093":{"id":"n1819849093","loc":[-85.64862,41.9568014]},"n1819849094":{"id":"n1819849094","loc":[-85.6496999,41.9828877]},"n1819849095":{"id":"n1819849095","loc":[-85.647472,41.972198]},"n1819849096":{"id":"n1819849096","loc":[-85.6485771,41.9644523]},"n1819849097":{"id":"n1819849097","loc":[-85.6388353,41.9480488]},"n1819849099":{"id":"n1819849099","loc":[-85.6472752,41.9683312]},"n1819849104":{"id":"n1819849104","loc":[-85.6479548,41.9836035]},"n1819849105":{"id":"n1819849105","loc":[-85.6462489,41.9691668]},"n1819849107":{"id":"n1819849107","loc":[-85.6511912,41.9746328]},"n1819849108":{"id":"n1819849108","loc":[-85.6498646,41.9714881]},"n1819849111":{"id":"n1819849111","loc":[-85.6488239,41.961684]},"n1819849112":{"id":"n1819849112","loc":[-85.6469356,41.9553812]},"n1819849114":{"id":"n1819849114","loc":[-85.6479548,41.9640853]},"n1819849119":{"id":"n1819849119","loc":[-85.6491565,41.961692]},"n1819849121":{"id":"n1819849121","loc":[-85.651667,41.9656728]},"n1819849124":{"id":"n1819849124","loc":[-85.6388423,41.9484414]},"n1819849126":{"id":"n1819849126","loc":[-85.6371686,41.9450978]},"n1819849127":{"id":"n1819849127","loc":[-85.6502615,41.9656728]},"n1819849129":{"id":"n1819849129","loc":[-85.6498501,41.9613031]},"n1819849131":{"id":"n1819849131","loc":[-85.6513881,41.9653298]},"n1819849133":{"id":"n1819849133","loc":[-85.639883,41.9485291]},"n1819849139":{"id":"n1819849139","loc":[-85.6508693,41.9658264]},"n1819849140":{"id":"n1819849140","loc":[-85.6486806,41.9761642]},"n1819849141":{"id":"n1819849141","loc":[-85.6483159,41.9717613]},"n1819849144":{"id":"n1819849144","loc":[-85.6443714,41.9546232]},"n1819849146":{"id":"n1819849146","loc":[-85.641775,41.9513359]},"n1819849147":{"id":"n1819849147","loc":[-85.6495604,41.9757335]},"n1819849148":{"id":"n1819849148","loc":[-85.6465671,41.9551678]},"n1819849150":{"id":"n1819849150","loc":[-85.6485127,41.9794084]},"n1819849151":{"id":"n1819849151","loc":[-85.6499144,41.9757096]},"n1819849152":{"id":"n1819849152","loc":[-85.6433736,41.9531072]},"n1819849154":{"id":"n1819849154","loc":[-85.6489741,41.9607426]},"n1819849155":{"id":"n1819849155","loc":[-85.640627,41.9507697]},"n1819849156":{"id":"n1819849156","loc":[-85.6509659,41.9743058]},"n1819849157":{"id":"n1819849157","loc":[-85.6486844,41.9704431]},"n1819849158":{"id":"n1819849158","loc":[-85.6498538,41.9711132]},"n1819849159":{"id":"n1819849159","loc":[-85.6358937,41.943719]},"n1819849160":{"id":"n1819849160","loc":[-85.6497358,41.9707702]},"n1819849161":{"id":"n1819849161","loc":[-85.6480476,41.9564842]},"n1819849162":{"id":"n1819849162","loc":[-85.6482982,41.9574556]},"n1819849163":{"id":"n1819849163","loc":[-85.6501757,41.9757794]},"n1819849164":{"id":"n1819849164","loc":[-85.6372973,41.9459916]},"n1819849165":{"id":"n1819849165","loc":[-85.6513773,41.9750775]},"n1819849166":{"id":"n1819849166","loc":[-85.6436418,41.9537455]},"n1819849167":{"id":"n1819849167","loc":[-85.6483625,41.9571524]},"n1819849169":{"id":"n1819849169","loc":[-85.647751,41.9727962]},"n1819849170":{"id":"n1819849170","loc":[-85.6504546,41.9656808]},"n1819849171":{"id":"n1819849171","loc":[-85.6479977,41.971839]},"n1819849172":{"id":"n1819849172","loc":[-85.6482767,41.9642449]},"n1819849174":{"id":"n1819849174","loc":[-85.6414317,41.9512086]},"n1819849176":{"id":"n1819849176","loc":[-85.6469034,41.9685287]},"n1819849179":{"id":"n1819849179","loc":[-85.6408631,41.9497564]},"n1819849182":{"id":"n1819849182","loc":[-85.6476721,41.96384]},"n1819849183":{"id":"n1819849183","loc":[-85.6479725,41.983111]},"n1819849184":{"id":"n1819849184","loc":[-85.640788,41.9500516]},"n1819849185":{"id":"n1819849185","loc":[-85.6427798,41.9528778]},"n1819849186":{"id":"n1819849186","loc":[-85.6435308,41.9534124]},"n1819849187":{"id":"n1819849187","loc":[-85.6483733,41.9821998]},"n1819849189":{"id":"n1819849189","loc":[-85.6351752,41.9440796]},"n1819849191":{"id":"n1819849191","loc":[-85.6487021,41.9601463]},"n1819849192":{"id":"n1819849192","loc":[-85.6363811,41.9437605]},"n1819849193":{"id":"n1819849193","loc":[-85.6490883,41.9759728]},"n1819849194":{"id":"n1819849194","loc":[-85.6423292,41.9520081]},"n1819849195":{"id":"n1819849195","loc":[-85.6500003,41.960242]},"n1819849196":{"id":"n1819849196","loc":[-85.6385778,41.9466443]},"n1819849197":{"id":"n1819849197","loc":[-85.6494032,41.9718789]},"n1819849198":{"id":"n1819849198","loc":[-85.6404339,41.9506501]},"n1819849199":{"id":"n1819849199","loc":[-85.6426226,41.9527083]},"n1819849200":{"id":"n1819849200","loc":[-85.6439101,41.9545035]},"n1819849201":{"id":"n1819849201","loc":[-85.6516563,41.9657845]},"n1819849202":{"id":"n1819849202","loc":[-85.6473395,41.9699585]},"n1819858501":{"id":"n1819858501","loc":[-85.6361263,41.9437126]},"n1819858503":{"id":"n1819858503","loc":[-85.6350068,41.944034]},"n1819858513":{"id":"n1819858513","loc":[-85.6371402,41.9453282]},"n1819858518":{"id":"n1819858518","loc":[-85.6348713,41.9432923]},"n1819858523":{"id":"n1819858523","loc":[-85.6357047,41.943799]},"n1819858526":{"id":"n1819858526","loc":[-85.6349947,41.9435756]},"n1819858531":{"id":"n1819858531","loc":[-85.6350376,41.943827]},"n1820937508":{"id":"n1820937508","loc":[-85.1026013,42.0881722]},"n1820937509":{"id":"n1820937509","loc":[-85.0558088,42.102493]},"n1820937511":{"id":"n1820937511","loc":[-85.3030116,41.9724451]},"n1820937513":{"id":"n1820937513","loc":[-85.0353221,42.1027398]},"n1820937514":{"id":"n1820937514","loc":[-85.0835468,42.1015469]},"n1820937515":{"id":"n1820937515","loc":[-85.2421298,42.0106305]},"n1820937517":{"id":"n1820937517","loc":[-85.0090632,42.0910452]},"n1820937518":{"id":"n1820937518","loc":[-85.086626,42.0948838]},"n1820937520":{"id":"n1820937520","loc":[-85.2552039,42.0015448]},"n1820937521":{"id":"n1820937521","loc":[-85.3739614,41.9969917]},"n1820937522":{"id":"n1820937522","loc":[-85.4831166,41.993898]},"n1820937523":{"id":"n1820937523","loc":[-85.0341084,42.0977657]},"n1820937524":{"id":"n1820937524","loc":[-85.3272802,41.9710333]},"n1820937525":{"id":"n1820937525","loc":[-85.2125568,42.0414521]},"n1820937526":{"id":"n1820937526","loc":[-85.3798022,41.9992458]},"n1820937527":{"id":"n1820937527","loc":[-85.2652021,41.999768]},"n1820937528":{"id":"n1820937528","loc":[-85.3852739,42.0004896]},"n1820937529":{"id":"n1820937529","loc":[-85.3911919,42.0030513]},"n1820937530":{"id":"n1820937530","loc":[-85.5440349,41.9717109]},"n1820937531":{"id":"n1820937531","loc":[-85.2790155,41.9911764]},"n1820937532":{"id":"n1820937532","loc":[-85.4723277,41.9950518]},"n1820937533":{"id":"n1820937533","loc":[-85.5690546,41.9653931]},"n1820937535":{"id":"n1820937535","loc":[-85.5674882,41.9649623]},"n1820937536":{"id":"n1820937536","loc":[-85.6362815,41.9189165]},"n1820937537":{"id":"n1820937537","loc":[-85.5659003,41.963638]},"n1820937539":{"id":"n1820937539","loc":[-85.6391353,41.9122262]},"n1820937540":{"id":"n1820937540","loc":[-85.4834385,41.9894803]},"n1820937541":{"id":"n1820937541","loc":[-85.6399078,41.9160744]},"n1820937542":{"id":"n1820937542","loc":[-85.632874,41.941031]},"n1820937543":{"id":"n1820937543","loc":[-85.1307591,42.0726961]},"n1820937544":{"id":"n1820937544","loc":[-85.6444397,41.9128378]},"n1820937545":{"id":"n1820937545","loc":[-85.6197204,41.9420365]},"n1820937546":{"id":"n1820937546","loc":[-85.1164857,42.0864631]},"n1820937547":{"id":"n1820937547","loc":[-85.6476111,41.9142222]},"n1820937548":{"id":"n1820937548","loc":[-85.2915747,41.9774223]},"n1820937549":{"id":"n1820937549","loc":[-85.6430192,41.9102461]},"n1820937550":{"id":"n1820937550","loc":[-85.1597495,42.0639017]},"n1820937551":{"id":"n1820937551","loc":[-85.5504079,41.9701793]},"n1820937553":{"id":"n1820937553","loc":[-85.2781317,41.9948951]},"n1820937555":{"id":"n1820937555","loc":[-85.3724594,41.997518]},"n1820937556":{"id":"n1820937556","loc":[-85.5629434,41.9665155]},"n1820937557":{"id":"n1820937557","loc":[-85.3791971,41.9990808]},"n1820937558":{"id":"n1820937558","loc":[-85.001891,42.0878843]},"n1820937560":{"id":"n1820937560","loc":[-85.3140838,41.9709056]},"n1820937561":{"id":"n1820937561","loc":[-85.2468032,42.0146987]},"n1820937563":{"id":"n1820937563","loc":[-85.0877378,42.097255]},"n1820937564":{"id":"n1820937564","loc":[-85.2442498,42.0150654]},"n1820937566":{"id":"n1820937566","loc":[-85.3108973,41.9701478]},"n1820937568":{"id":"n1820937568","loc":[-85.0344584,42.1016572]},"n1820937569":{"id":"n1820937569","loc":[-85.2331025,42.0297387]},"n1820937570":{"id":"n1820937570","loc":[-85.5058446,41.9746996]},"n1820937571":{"id":"n1820937571","loc":[-85.5622739,41.9676427]},"n1820937572":{"id":"n1820937572","loc":[-85.2792687,41.9890337]},"n1820937574":{"id":"n1820937574","loc":[-84.9909302,42.08695]},"n1820937575":{"id":"n1820937575","loc":[-85.6218233,41.9418609]},"n1820937576":{"id":"n1820937576","loc":[-85.3577437,41.9931062]},"n1820937577":{"id":"n1820937577","loc":[-85.639028,41.9165853]},"n1820937578":{"id":"n1820937578","loc":[-84.9956576,42.0865348]},"n1820937579":{"id":"n1820937579","loc":[-85.4828376,41.990198]},"n1820937580":{"id":"n1820937580","loc":[-85.3244478,41.9720543]},"n1820937582":{"id":"n1820937582","loc":[-85.0517479,42.1035159]},"n1820937583":{"id":"n1820937583","loc":[-85.225646,42.0338025]},"n1820937584":{"id":"n1820937584","loc":[-84.9941019,42.0862163]},"n1820937586":{"id":"n1820937586","loc":[-85.1051762,42.0879452]},"n1820937587":{"id":"n1820937587","loc":[-85.1245203,42.0753162]},"n1820937588":{"id":"n1820937588","loc":[-85.3250808,41.9719506]},"n1820937589":{"id":"n1820937589","loc":[-85.2720109,41.997933]},"n1820937590":{"id":"n1820937590","loc":[-85.2556653,42.0027248]},"n1820937591":{"id":"n1820937591","loc":[-85.0872483,42.0943544]},"n1820937592":{"id":"n1820937592","loc":[-85.2778353,41.9955023]},"n1820937593":{"id":"n1820937593","loc":[-85.2984733,41.9735538]},"n1820937594":{"id":"n1820937594","loc":[-85.101578,42.0889552]},"n1820937595":{"id":"n1820937595","loc":[-85.3888745,42.0016959]},"n1820937596":{"id":"n1820937596","loc":[-84.9903508,42.0870654]},"n1820937597":{"id":"n1820937597","loc":[-85.6405558,41.9146261]},"n1820937598":{"id":"n1820937598","loc":[-85.6460704,41.9141311]},"n1820937599":{"id":"n1820937599","loc":[-85.0377468,42.1037428]},"n1820937600":{"id":"n1820937600","loc":[-85.2298345,42.0312899]},"n1820937601":{"id":"n1820937601","loc":[-85.1080958,42.0861964]},"n1820937602":{"id":"n1820937602","loc":[-85.6325307,41.9402329]},"n1820937603":{"id":"n1820937603","loc":[-85.1165984,42.0832184]},"n1820937604":{"id":"n1820937604","loc":[-85.6354446,41.9190602]},"n1820937605":{"id":"n1820937605","loc":[-85.1114592,42.0862959]},"n1820937606":{"id":"n1820937606","loc":[-85.0858763,42.1001646]},"n1820937607":{"id":"n1820937607","loc":[-85.0472083,42.1015151]},"n1820937608":{"id":"n1820937608","loc":[-85.0802477,42.1027609]},"n1820937610":{"id":"n1820937610","loc":[-85.0924585,42.0928564]},"n1820937611":{"id":"n1820937611","loc":[-85.0329617,42.09827]},"n1820937612":{"id":"n1820937612","loc":[-85.2814617,41.993465]},"n1820937613":{"id":"n1820937613","loc":[-85.3097708,41.9700282]},"n1820937614":{"id":"n1820937614","loc":[-85.2809427,41.993695]},"n1820937615":{"id":"n1820937615","loc":[-85.0583233,42.1026494]},"n1820937617":{"id":"n1820937617","loc":[-85.2801592,41.9840021]},"n1820937619":{"id":"n1820937619","loc":[-85.1064154,42.0863449]},"n1820937620":{"id":"n1820937620","loc":[-85.0423173,42.1014662]},"n1820937621":{"id":"n1820937621","loc":[-85.2168913,42.0398107]},"n1820937622":{"id":"n1820937622","loc":[-85.2798481,41.9833401]},"n1820937623":{"id":"n1820937623","loc":[-85.0575468,42.1028672]},"n1820937625":{"id":"n1820937625","loc":[-85.0130369,42.0893067]},"n1820937626":{"id":"n1820937626","loc":[-85.0346985,42.1018256]},"n1820937627":{"id":"n1820937627","loc":[-85.2231569,42.0372768]},"n1820937628":{"id":"n1820937628","loc":[-85.2956195,41.9732268]},"n1820937629":{"id":"n1820937629","loc":[-85.1052312,42.086893]},"n1820937630":{"id":"n1820937630","loc":[-85.4813356,41.9958436]},"n1820937631":{"id":"n1820937631","loc":[-85.0961599,42.0914672]},"n1820937632":{"id":"n1820937632","loc":[-85.308419,41.9704749]},"n1820937633":{"id":"n1820937633","loc":[-85.295952,41.9715119]},"n1820937634":{"id":"n1820937634","loc":[-85.3310933,41.9703923]},"n1820937635":{"id":"n1820937635","loc":[-85.2940745,41.9739686]},"n1820937636":{"id":"n1820937636","loc":[-85.3803343,42.000484]},"n1820937637":{"id":"n1820937637","loc":[-85.1174231,42.0845533]},"n1820937638":{"id":"n1820937638","loc":[-85.0095836,42.089839]},"n1820937639":{"id":"n1820937639","loc":[-85.3179354,41.9705866]},"n1820937640":{"id":"n1820937640","loc":[-85.257708,42.0001189]},"n1820937641":{"id":"n1820937641","loc":[-85.2563522,42.0002771]},"n1820937642":{"id":"n1820937642","loc":[-85.3181929,41.970419]},"n1820937643":{"id":"n1820937643","loc":[-85.2911884,41.9757154]},"n1820937644":{"id":"n1820937644","loc":[-85.2714423,41.9975862]},"n1820937645":{"id":"n1820937645","loc":[-85.0193669,42.089888]},"n1820937646":{"id":"n1820937646","loc":[-85.3889818,42.0039921]},"n1820937647":{"id":"n1820937647","loc":[-85.3408093,41.9853965]},"n1820937648":{"id":"n1820937648","loc":[-85.1258091,42.0748332]},"n1820937649":{"id":"n1820937649","loc":[-85.5722561,41.962782]},"n1820937650":{"id":"n1820937650","loc":[-85.3266902,41.9721819]},"n1820937651":{"id":"n1820937651","loc":[-85.1473255,42.065192]},"n1820937652":{"id":"n1820937652","loc":[-85.1462526,42.0655106]},"n1820937653":{"id":"n1820937653","loc":[-85.4641051,42.0013929]},"n1820937654":{"id":"n1820937654","loc":[-85.5620379,41.9700677]},"n1820937655":{"id":"n1820937655","loc":[-85.3226025,41.971121]},"n1820937656":{"id":"n1820937656","loc":[-85.0200965,42.0899516]},"n1820937657":{"id":"n1820937657","loc":[-85.0624714,42.1044711]},"n1820937658":{"id":"n1820937658","loc":[-85.5649562,41.9637178]},"n1820937659":{"id":"n1820937659","loc":[-85.2360315,42.0253315]},"n1820937660":{"id":"n1820937660","loc":[-85.3881449,41.9994475]},"n1820937661":{"id":"n1820937661","loc":[-85.5032911,41.976263]},"n1820937662":{"id":"n1820937662","loc":[-85.481297,41.9871414]},"n1820937663":{"id":"n1820937663","loc":[-85.1167056,42.0841898]},"n1820937664":{"id":"n1820937664","loc":[-85.2891714,41.9787223]},"n1820937665":{"id":"n1820937665","loc":[-85.4393429,42.0058736]},"n1820937666":{"id":"n1820937666","loc":[-85.0077007,42.0895762]},"n1820937667":{"id":"n1820937667","loc":[-85.2736202,41.9979171]},"n1820937668":{"id":"n1820937668","loc":[-84.9935332,42.0859296]},"n1820937669":{"id":"n1820937669","loc":[-85.0622769,42.1046713]},"n1820937670":{"id":"n1820937670","loc":[-85.2309031,42.0311249]},"n1820937671":{"id":"n1820937671","loc":[-85.0343726,42.10069]},"n1820937672":{"id":"n1820937672","loc":[-85.0596551,42.1048612]},"n1820937673":{"id":"n1820937673","loc":[-85.1338597,42.0707449]},"n1820937674":{"id":"n1820937674","loc":[-85.3117663,41.9689194]},"n1820937675":{"id":"n1820937675","loc":[-85.0705649,42.1057499]},"n1820937676":{"id":"n1820937676","loc":[-85.2441425,42.0180944]},"n1820937677":{"id":"n1820937677","loc":[-85.1171174,42.0862692]},"n1820937678":{"id":"n1820937678","loc":[-85.0346824,42.1005519]},"n1820937680":{"id":"n1820937680","loc":[-85.2389927,42.0229245]},"n1820937681":{"id":"n1820937681","loc":[-85.0834892,42.1018642]},"n1820937682":{"id":"n1820937682","loc":[-85.0619443,42.1049459]},"n1820937683":{"id":"n1820937683","loc":[-85.2845366,41.9811868]},"n1820937684":{"id":"n1820937684","loc":[-85.210411,42.0394123]},"n1820937685":{"id":"n1820937685","loc":[-85.4377383,42.0055942]},"n1820937686":{"id":"n1820937686","loc":[-85.2882058,41.9789138]},"n1820937687":{"id":"n1820937687","loc":[-85.2741191,41.9955808]},"n1820937688":{"id":"n1820937688","loc":[-85.3442211,41.9903575]},"n1820937689":{"id":"n1820937689","loc":[-85.2641413,41.9995237]},"n1820937690":{"id":"n1820937690","loc":[-85.2804489,41.9829174]},"n1820937691":{"id":"n1820937691","loc":[-85.5593342,41.9729074]},"n1820937692":{"id":"n1820937692","loc":[-85.3590912,41.9932601]},"n1820937694":{"id":"n1820937694","loc":[-85.4826445,41.9957479]},"n1820937695":{"id":"n1820937695","loc":[-85.4539127,42.0063041]},"n1820937696":{"id":"n1820937696","loc":[-85.2456767,42.0153683]},"n1820937697":{"id":"n1820937697","loc":[-85.5794015,41.9489631]},"n1820937698":{"id":"n1820937698","loc":[-85.4108686,42.0078507]},"n1820937699":{"id":"n1820937699","loc":[-85.0616386,42.1051529]},"n1820937700":{"id":"n1820937700","loc":[-85.4977979,41.978241]},"n1820937701":{"id":"n1820937701","loc":[-85.2488417,42.0086319]},"n1820937702":{"id":"n1820937702","loc":[-85.5588836,41.9728116]},"n1820937703":{"id":"n1820937703","loc":[-85.4557366,42.0051241]},"n1820937705":{"id":"n1820937705","loc":[-85.0723151,42.1056094]},"n1820937706":{"id":"n1820937706","loc":[-85.0057909,42.0887323]},"n1820937707":{"id":"n1820937707","loc":[-85.0756786,42.105677]},"n1820937708":{"id":"n1820937708","loc":[-85.0901504,42.0940001]},"n1820937709":{"id":"n1820937709","loc":[-85.0979999,42.0910213]},"n1820937710":{"id":"n1820937710","loc":[-85.2376301,42.0239686]},"n1820937711":{"id":"n1820937711","loc":[-85.2780671,41.9902299]},"n1820937712":{"id":"n1820937712","loc":[-85.251481,42.0113188]},"n1820937713":{"id":"n1820937713","loc":[-85.3114767,41.9690311]},"n1820937714":{"id":"n1820937714","loc":[-85.2649621,41.9975662]},"n1820937715":{"id":"n1820937715","loc":[-85.283807,41.9813383]},"n1820937716":{"id":"n1820937716","loc":[-85.5515451,41.9703867]},"n1820937717":{"id":"n1820937717","loc":[-85.1176605,42.0850896]},"n1820937718":{"id":"n1820937718","loc":[-85.1069317,42.0862441]},"n1820937719":{"id":"n1820937719","loc":[-85.2739314,41.9976938]},"n1820937720":{"id":"n1820937720","loc":[-85.5550212,41.9702112]},"n1820937721":{"id":"n1820937721","loc":[-85.3076679,41.9719904]},"n1820937722":{"id":"n1820937722","loc":[-85.592319,41.9440316]},"n1820937723":{"id":"n1820937723","loc":[-85.3139979,41.9704031]},"n1820937724":{"id":"n1820937724","loc":[-85.0421134,42.1013149]},"n1820937725":{"id":"n1820937725","loc":[-85.2508373,42.0102741]},"n1820937726":{"id":"n1820937726","loc":[-85.0830922,42.1038821]},"n1820937727":{"id":"n1820937727","loc":[-85.6342473,41.9360031]},"n1820937730":{"id":"n1820937730","loc":[-85.0500192,42.1024942]},"n1820937731":{"id":"n1820937731","loc":[-85.3498644,41.9926221]},"n1820937732":{"id":"n1820937732","loc":[-85.0234117,42.0918903]},"n1820937733":{"id":"n1820937733","loc":[-85.0464425,42.1009408]},"n1820937734":{"id":"n1820937734","loc":[-85.033938,42.099886]},"n1820937736":{"id":"n1820937736","loc":[-85.0152752,42.0886009]},"n1820937737":{"id":"n1820937737","loc":[-85.0441894,42.1012671]},"n1820937738":{"id":"n1820937738","loc":[-85.4668731,41.9979804]},"n1820937739":{"id":"n1820937739","loc":[-85.4407377,42.006033]},"n1820937740":{"id":"n1820937740","loc":[-85.2262253,42.0344878]},"n1820937741":{"id":"n1820937741","loc":[-85.2550001,42.0033706]},"n1820937742":{"id":"n1820937742","loc":[-85.3071422,41.9722617]},"n1820937743":{"id":"n1820937743","loc":[-85.6147852,41.942228]},"n1820937744":{"id":"n1820937744","loc":[-85.0183853,42.0901825]},"n1820937745":{"id":"n1820937745","loc":[-85.6323161,41.9228489]},"n1820937746":{"id":"n1820937746","loc":[-85.0095568,42.0901376]},"n1820937747":{"id":"n1820937747","loc":[-85.2524037,42.0113826]},"n1820937748":{"id":"n1820937748","loc":[-85.3186864,41.9708578]},"n1820937749":{"id":"n1820937749","loc":[-85.2805669,41.9870883]},"n1820937750":{"id":"n1820937750","loc":[-85.0585768,42.1038144]},"n1820937751":{"id":"n1820937751","loc":[-85.2970786,41.9715358]},"n1820937752":{"id":"n1820937752","loc":[-85.1315758,42.0723445]},"n1820937753":{"id":"n1820937753","loc":[-85.2448291,42.0175444]},"n1820937754":{"id":"n1820937754","loc":[-85.2446468,42.0174248]},"n1820937755":{"id":"n1820937755","loc":[-85.229165,42.032129]},"n1820937756":{"id":"n1820937756","loc":[-85.5612654,41.9724926]},"n1820937757":{"id":"n1820937757","loc":[-85.2331776,42.030854]},"n1820937758":{"id":"n1820937758","loc":[-85.2271909,42.0334519]},"n1820937759":{"id":"n1820937759","loc":[-85.1032396,42.0879214]},"n1820937760":{"id":"n1820937760","loc":[-85.0638447,42.1044154]},"n1820937761":{"id":"n1820937761","loc":[-85.1260706,42.0745556]},"n1820937762":{"id":"n1820937762","loc":[-85.3454485,41.99132]},"n1820937763":{"id":"n1820937763","loc":[-85.2639321,41.9980088]},"n1820937764":{"id":"n1820937764","loc":[-85.0837681,42.1013746]},"n1820937765":{"id":"n1820937765","loc":[-85.2808137,41.9869368]},"n1820937766":{"id":"n1820937766","loc":[-85.6338997,41.9309373]},"n1820937767":{"id":"n1820937767","loc":[-85.2267403,42.0332766]},"n1820937768":{"id":"n1820937768","loc":[-85.0605831,42.1052074]},"n1820937769":{"id":"n1820937769","loc":[-85.0259021,42.0930037]},"n1820937770":{"id":"n1820937770","loc":[-85.232963,42.0313162]},"n1820937771":{"id":"n1820937771","loc":[-85.2404947,42.0125381]},"n1820937772":{"id":"n1820937772","loc":[-85.0910892,42.0935742]},"n1820937773":{"id":"n1820937773","loc":[-85.2554829,42.0019435]},"n1820937774":{"id":"n1820937774","loc":[-85.2799339,41.9867773]},"n1820937775":{"id":"n1820937775","loc":[-85.1075432,42.0852767]},"n1820937776":{"id":"n1820937776","loc":[-85.1176927,42.0854001]},"n1820937777":{"id":"n1820937777","loc":[-85.1067064,42.0863357]},"n1820937778":{"id":"n1820937778","loc":[-85.2517492,42.0106333]},"n1820937779":{"id":"n1820937779","loc":[-85.0987174,42.0909031]},"n1820937780":{"id":"n1820937780","loc":[-85.1160083,42.0863994]},"n1820937781":{"id":"n1820937781","loc":[-85.1268645,42.0739703]},"n1820937782":{"id":"n1820937782","loc":[-85.0454702,42.1002852]},"n1820937783":{"id":"n1820937783","loc":[-85.1334145,42.0705418]},"n1820937784":{"id":"n1820937784","loc":[-85.5866542,41.947431]},"n1820937786":{"id":"n1820937786","loc":[-85.2359886,42.0250366]},"n1820937787":{"id":"n1820937787","loc":[-85.3138048,41.9698527]},"n1820937788":{"id":"n1820937788","loc":[-85.1274291,42.0733081]},"n1820937790":{"id":"n1820937790","loc":[-85.6292905,41.9411267]},"n1820937791":{"id":"n1820937791","loc":[-85.5958809,41.9417333]},"n1820937792":{"id":"n1820937792","loc":[-85.1271019,42.0737581]},"n1820937793":{"id":"n1820937793","loc":[-85.2312679,42.0314437]},"n1820937794":{"id":"n1820937794","loc":[-85.1081387,42.0863516]},"n1820937795":{"id":"n1820937795","loc":[-85.2424473,42.0212109]},"n1820937796":{"id":"n1820937796","loc":[-85.2710654,41.9975236]},"n1820937797":{"id":"n1820937797","loc":[-85.4798408,41.9863223]},"n1820937798":{"id":"n1820937798","loc":[-85.035939,42.104296]},"n1820937799":{"id":"n1820937799","loc":[-85.2178139,42.0395398]},"n1820937800":{"id":"n1820937800","loc":[-85.0630709,42.1042614]},"n1820937801":{"id":"n1820937801","loc":[-85.0440124,42.1014861]},"n1820937802":{"id":"n1820937802","loc":[-85.1321874,42.0720458]},"n1820937804":{"id":"n1820937804","loc":[-85.079427,42.1029121]},"n1820937805":{"id":"n1820937805","loc":[-85.2962632,41.9738968]},"n1820937806":{"id":"n1820937806","loc":[-85.6334748,41.9274627]},"n1820937807":{"id":"n1820937807","loc":[-85.1057341,42.0872804]},"n1820937808":{"id":"n1820937808","loc":[-85.4960169,41.9778263]},"n1820937809":{"id":"n1820937809","loc":[-85.2821226,41.9910273]},"n1820937810":{"id":"n1820937810","loc":[-85.0013868,42.0885054]},"n1820937811":{"id":"n1820937811","loc":[-85.2952547,41.9729795]},"n1820937812":{"id":"n1820937812","loc":[-85.1298375,42.0667842]},"n1820937813":{"id":"n1820937813","loc":[-85.1339201,42.0710025]},"n1820937814":{"id":"n1820937814","loc":[-85.0374356,42.103691]},"n1820937815":{"id":"n1820937815","loc":[-85.0061115,42.0880607]},"n1820937817":{"id":"n1820937817","loc":[-85.2398402,42.0226934]},"n1820937818":{"id":"n1820937818","loc":[-85.123501,42.076236]},"n1820937819":{"id":"n1820937819","loc":[-85.1209489,42.0791294]},"n1820937820":{"id":"n1820937820","loc":[-85.0818624,42.1025778]},"n1820937821":{"id":"n1820937821","loc":[-85.4428835,42.0054749]},"n1820937822":{"id":"n1820937822","loc":[-85.4710359,41.9961147]},"n1820937823":{"id":"n1820937823","loc":[-85.4253354,42.006198]},"n1820937824":{"id":"n1820937824","loc":[-85.5486483,41.9709451]},"n1820937825":{"id":"n1820937825","loc":[-85.2303238,42.0310452]},"n1820937826":{"id":"n1820937826","loc":[-85.6450405,41.9136361]},"n1820937828":{"id":"n1820937828","loc":[-85.2606853,41.9964073]},"n1820937830":{"id":"n1820937830","loc":[-85.097383,42.0911447]},"n1820937831":{"id":"n1820937831","loc":[-85.0498207,42.102136]},"n1820937832":{"id":"n1820937832","loc":[-85.1232435,42.0763793]},"n1820937833":{"id":"n1820937833","loc":[-85.394093,42.0055921]},"n1820937834":{"id":"n1820937834","loc":[-85.3566665,41.9928295]},"n1820937835":{"id":"n1820937835","loc":[-85.3543276,41.9920002]},"n1820937837":{"id":"n1820937837","loc":[-85.084668,42.1034932]},"n1820937838":{"id":"n1820937838","loc":[-85.4400296,42.0060649]},"n1820937839":{"id":"n1820937839","loc":[-85.2362246,42.025714]},"n1820937840":{"id":"n1820937840","loc":[-85.0409225,42.1012791]},"n1820937841":{"id":"n1820937841","loc":[-85.2442283,42.019832]},"n1820937842":{"id":"n1820937842","loc":[-85.1123001,42.084824]},"n1820937843":{"id":"n1820937843","loc":[-85.1603074,42.0638061]},"n1820937844":{"id":"n1820937844","loc":[-85.1359744,42.0650646]},"n1820937845":{"id":"n1820937845","loc":[-85.1757569,42.053849]},"n1820937846":{"id":"n1820937846","loc":[-85.5200925,41.9716686]},"n1820937848":{"id":"n1820937848","loc":[-85.5525322,41.9701315]},"n1820937849":{"id":"n1820937849","loc":[-85.0406489,42.10149]},"n1820937850":{"id":"n1820937850","loc":[-85.0142547,42.088825]},"n1820937851":{"id":"n1820937851","loc":[-85.343749,41.9881884]},"n1820937852":{"id":"n1820937852","loc":[-85.074996,42.1060205]},"n1820937853":{"id":"n1820937853","loc":[-85.2436275,42.0136864]},"n1820937854":{"id":"n1820937854","loc":[-85.2641453,41.9980897]},"n1820937856":{"id":"n1820937856","loc":[-85.2802343,41.9870086]},"n1820937857":{"id":"n1820937857","loc":[-85.0099256,42.0909946]},"n1820937858":{"id":"n1820937858","loc":[-85.493957,41.9786079]},"n1820937859":{"id":"n1820937859","loc":[-85.0739405,42.1059795]},"n1820937860":{"id":"n1820937860","loc":[-85.2331605,42.0301423]},"n1820937862":{"id":"n1820937862","loc":[-85.2035231,42.0438425]},"n1820937863":{"id":"n1820937863","loc":[-85.0884928,42.0986971]},"n1820937864":{"id":"n1820937864","loc":[-85.131597,42.0690142]},"n1820937865":{"id":"n1820937865","loc":[-85.3937454,42.0052677]},"n1820937866":{"id":"n1820937866","loc":[-85.2212729,42.0378561]},"n1820937867":{"id":"n1820937867","loc":[-85.0886068,42.0982421]},"n1820937868":{"id":"n1820937868","loc":[-85.0875004,42.0968064]},"n1820937869":{"id":"n1820937869","loc":[-85.0771323,42.1042642]},"n1820937870":{"id":"n1820937870","loc":[-85.0164554,42.0894887]},"n1820937871":{"id":"n1820937871","loc":[-85.6069102,41.9415577]},"n1820937872":{"id":"n1820937872","loc":[-85.3273875,41.9704908]},"n1820937873":{"id":"n1820937873","loc":[-85.3890891,41.9997983]},"n1820937875":{"id":"n1820937875","loc":[-85.5091276,41.9723705]},"n1820937876":{"id":"n1820937876","loc":[-85.0770626,42.1047696]},"n1820937877":{"id":"n1820937877","loc":[-85.612575,41.9419567]},"n1820937878":{"id":"n1820937878","loc":[-85.3868146,42.0036094]},"n1820937879":{"id":"n1820937879","loc":[-85.2722738,41.9981204]},"n1820937880":{"id":"n1820937880","loc":[-85.3064878,41.9723733]},"n1820937882":{"id":"n1820937882","loc":[-85.1270845,42.0727678]},"n1820937884":{"id":"n1820937884","loc":[-85.3316512,41.97923]},"n1820937885":{"id":"n1820937885","loc":[-85.3932519,42.0042472]},"n1820937886":{"id":"n1820937886","loc":[-85.2457411,42.0175444]},"n1820937887":{"id":"n1820937887","loc":[-85.1397509,42.0648415]},"n1820937891":{"id":"n1820937891","loc":[-85.3196735,41.9719665]},"n1820937892":{"id":"n1820937892","loc":[-85.3372473,41.9845033]},"n1820937894":{"id":"n1820937894","loc":[-85.3254778,41.9719745]},"n1820937897":{"id":"n1820937897","loc":[-85.3185148,41.9691268]},"n1820937899":{"id":"n1820937899","loc":[-85.5419106,41.9714556]},"n1820937901":{"id":"n1820937901","loc":[-85.3293509,41.9748368]},"n1820937903":{"id":"n1820937903","loc":[-85.0798078,42.1028365]},"n1820937905":{"id":"n1820937905","loc":[-85.3954191,42.0056025]},"n1820937909":{"id":"n1820937909","loc":[-85.3417534,41.9857155]},"n1820937913":{"id":"n1820937913","loc":[-84.9927822,42.0857107]},"n1820937915":{"id":"n1820937915","loc":[-85.5444212,41.9712801]},"n1820937917":{"id":"n1820937917","loc":[-85.259088,41.9981682]},"n1820937921":{"id":"n1820937921","loc":[-85.2784576,41.9876358]},"n1820937922":{"id":"n1820937922","loc":[-84.9971918,42.087753]},"n1820937924":{"id":"n1820937924","loc":[-85.5310688,41.966899]},"n1820937928":{"id":"n1820937928","loc":[-85.3766436,41.9979326]},"n1820937930":{"id":"n1820937930","loc":[-85.5494852,41.9704346]},"n1820937933":{"id":"n1820937933","loc":[-85.5548281,41.9695412]},"n1820937935":{"id":"n1820937935","loc":[-85.0768588,42.105088]},"n1820937937":{"id":"n1820937937","loc":[-85.2646885,41.9978054]},"n1820937939":{"id":"n1820937939","loc":[-85.2441532,42.0176082]},"n1820937941":{"id":"n1820937941","loc":[-85.105553,42.0877928]},"n1820937943":{"id":"n1820937943","loc":[-85.0879457,42.0958909]},"n1820937944":{"id":"n1820937944","loc":[-85.3187015,41.9704402]},"n1820937945":{"id":"n1820937945","loc":[-85.5624456,41.970626]},"n1820937946":{"id":"n1820937946","loc":[-85.0580176,42.1028644]},"n1820937948":{"id":"n1820937948","loc":[-85.3016061,41.9726286]},"n1820937949":{"id":"n1820937949","loc":[-85.4310388,42.0069418]},"n1820937950":{"id":"n1820937950","loc":[-85.2945144,41.9740723]},"n1820937951":{"id":"n1820937951","loc":[-85.1170222,42.082657]},"n1820937952":{"id":"n1820937952","loc":[-85.0864503,42.0947632]},"n1820937953":{"id":"n1820937953","loc":[-85.4285926,42.0059533]},"n1820937970":{"id":"n1820937970","loc":[-85.3629965,41.9938023]},"n1820937972":{"id":"n1820937972","loc":[-85.2438099,42.0199755]},"n1820937974":{"id":"n1820937974","loc":[-85.1327654,42.0699285]},"n1820937977":{"id":"n1820937977","loc":[-85.1515956,42.0611935]},"n1820937978":{"id":"n1820937978","loc":[-85.0107369,42.0896638]},"n1820937979":{"id":"n1820937979","loc":[-85.1152626,42.0862083]},"n1820937980":{"id":"n1820937980","loc":[-85.4531831,42.0062881]},"n1820937981":{"id":"n1820937981","loc":[-85.0341473,42.0985924]},"n1820937982":{"id":"n1820937982","loc":[-85.0877485,42.0960171]},"n1820937983":{"id":"n1820937983","loc":[-85.2756373,41.9951742]},"n1820937984":{"id":"n1820937984","loc":[-85.2965421,41.9714401]},"n1820937985":{"id":"n1820937985","loc":[-85.2409775,42.0226934]},"n1820937986":{"id":"n1820937986","loc":[-85.0170723,42.0900579]},"n1820937987":{"id":"n1820937987","loc":[-85.1034663,42.0880555]},"n1820937988":{"id":"n1820937988","loc":[-85.0585071,42.1031577]},"n1820937990":{"id":"n1820937990","loc":[-85.0819174,42.1032373]},"n1820937992":{"id":"n1820937992","loc":[-85.0546608,42.1030542]},"n1820937993":{"id":"n1820937993","loc":[-85.0100811,42.0906125]},"n1820937995":{"id":"n1820937995","loc":[-85.6304278,41.9432655]},"n1820937997":{"id":"n1820937997","loc":[-85.0255628,42.092778]},"n1820938011":{"id":"n1820938011","loc":[-85.2316756,42.0317146]},"n1820938012":{"id":"n1820938012","loc":[-85.4067917,42.008042]},"n1820938013":{"id":"n1820938013","loc":[-85.390398,42.0028759]},"n1820938014":{"id":"n1820938014","loc":[-85.0161604,42.0886527]},"n1820938015":{"id":"n1820938015","loc":[-85.125337,42.0744589]},"n1820938016":{"id":"n1820938016","loc":[-85.2151317,42.0404801]},"n1820938017":{"id":"n1820938017","loc":[-85.3165085,41.9706025]},"n1820938018":{"id":"n1820938018","loc":[-85.5641193,41.9640688]},"n1820938019":{"id":"n1820938019","loc":[-85.147583,42.0642203]},"n1820938022":{"id":"n1820938022","loc":[-85.2803781,41.9947886]},"n1820938024":{"id":"n1820938024","loc":[-85.2692469,41.9982053]},"n1820938026":{"id":"n1820938026","loc":[-85.4321975,42.0067505]},"n1820938028":{"id":"n1820938028","loc":[-85.572535,41.9633405]},"n1820938030":{"id":"n1820938030","loc":[-85.3237505,41.9716475]},"n1820938032":{"id":"n1820938032","loc":[-85.6487698,41.9141583]},"n1820938033":{"id":"n1820938033","loc":[-85.0526371,42.1038315]},"n1820938034":{"id":"n1820938034","loc":[-85.088069,42.0978731]},"n1820938035":{"id":"n1820938035","loc":[-85.2516312,42.0102267]},"n1820938039":{"id":"n1820938039","loc":[-85.2731374,41.9982958]},"n1820938040":{"id":"n1820938040","loc":[-85.5453224,41.9713439]},"n1820938041":{"id":"n1820938041","loc":[-85.4480548,42.0049647]},"n1820938043":{"id":"n1820938043","loc":[-85.2504081,42.010322]},"n1820938045":{"id":"n1820938045","loc":[-85.2663447,41.99919]},"n1820938046":{"id":"n1820938046","loc":[-85.0507287,42.102907]},"n1820938047":{"id":"n1820938047","loc":[-85.0408246,42.1024743]},"n1820938048":{"id":"n1820938048","loc":[-85.2796335,41.9866099]},"n1820938050":{"id":"n1820938050","loc":[-85.452475,42.0061127]},"n1820938051":{"id":"n1820938051","loc":[-85.2410569,42.0128147]},"n1820938052":{"id":"n1820938052","loc":[-85.0413302,42.1011477]},"n1820938053":{"id":"n1820938053","loc":[-85.6327409,41.9197627]},"n1820938056":{"id":"n1820938056","loc":[-85.1072039,42.0857994]},"n1820938057":{"id":"n1820938057","loc":[-85.2001114,42.0448145]},"n1820938058":{"id":"n1820938058","loc":[-85.2655347,41.9978186]},"n1820938059":{"id":"n1820938059","loc":[-85.2330918,42.0304874]},"n1820938060":{"id":"n1820938060","loc":[-85.2601113,41.9966545]},"n1820938061":{"id":"n1820938061","loc":[-85.5397863,41.9708494]},"n1820938062":{"id":"n1820938062","loc":[-85.2702085,41.9977217]},"n1820938063":{"id":"n1820938063","loc":[-85.2219982,42.03699]},"n1820938064":{"id":"n1820938064","loc":[-85.0668957,42.105121]},"n1820938065":{"id":"n1820938065","loc":[-85.2328665,42.0270769]},"n1820938066":{"id":"n1820938066","loc":[-85.3189654,41.9694778]},"n1820938067":{"id":"n1820938067","loc":[-85.3814115,42.0022915]},"n1820938068":{"id":"n1820938068","loc":[-85.2759108,41.9956008]},"n1820938069":{"id":"n1820938069","loc":[-85.0391938,42.1034853]},"n1820938070":{"id":"n1820938070","loc":[-85.2850623,41.9810353]},"n1820938071":{"id":"n1820938071","loc":[-85.538074,41.970855]},"n1820938073":{"id":"n1820938073","loc":[-85.1319661,42.0670932]},"n1820938074":{"id":"n1820938074","loc":[-85.2816763,41.9913678]},"n1820938075":{"id":"n1820938075","loc":[-85.3182144,41.9700282]},"n1820938076":{"id":"n1820938076","loc":[-85.5909028,41.9458989]},"n1820938077":{"id":"n1820938077","loc":[-85.4057617,42.0074361]},"n1820938078":{"id":"n1820938078","loc":[-85.2620438,41.9967729]},"n1820938079":{"id":"n1820938079","loc":[-85.1122143,42.0851107]},"n1820938080":{"id":"n1820938080","loc":[-85.2443785,42.0174567]},"n1820938081":{"id":"n1820938081","loc":[-85.0319733,42.0953853]},"n1820938082":{"id":"n1820938082","loc":[-85.0878276,42.09443]},"n1820938083":{"id":"n1820938083","loc":[-85.0271789,42.0935809]},"n1820938084":{"id":"n1820938084","loc":[-85.0326399,42.0974222]},"n1820938085":{"id":"n1820938085","loc":[-85.3989167,42.0065592]},"n1820938086":{"id":"n1820938086","loc":[-85.3263361,41.9721261]},"n1820938087":{"id":"n1820938087","loc":[-85.2547855,42.0037134]},"n1820938088":{"id":"n1820938088","loc":[-85.4373259,42.005746]},"n1820938089":{"id":"n1820938089","loc":[-85.3094275,41.9699245]},"n1820938090":{"id":"n1820938090","loc":[-85.2783246,41.9872793]},"n1820938092":{"id":"n1820938092","loc":[-85.0815633,42.1025169]},"n1820938093":{"id":"n1820938093","loc":[-85.1788511,42.0522134]},"n1820938095":{"id":"n1820938095","loc":[-85.2830345,41.9816733]},"n1820938096":{"id":"n1820938096","loc":[-85.0744984,42.1059835]},"n1820938097":{"id":"n1820938097","loc":[-85.2788396,41.9879333]},"n1820938098":{"id":"n1820938098","loc":[-85.3640093,41.9946531]},"n1820938099":{"id":"n1820938099","loc":[-85.291167,41.9787463]},"n1820938100":{"id":"n1820938100","loc":[-85.0772436,42.1038156]},"n1820938101":{"id":"n1820938101","loc":[-85.00563,42.0887482]},"n1820938102":{"id":"n1820938102","loc":[-85.0326881,42.0961245]},"n1820938104":{"id":"n1820938104","loc":[-85.0530448,42.1038634]},"n1820938105":{"id":"n1820938105","loc":[-85.2625266,41.9970639]},"n1820938106":{"id":"n1820938106","loc":[-85.2827556,41.9823512]},"n1820938107":{"id":"n1820938107","loc":[-85.2784319,41.9910752]},"n1820938108":{"id":"n1820938108","loc":[-85.0882099,42.094393]},"n1820938109":{"id":"n1820938109","loc":[-85.5718484,41.9645371]},"n1820938110":{"id":"n1820938110","loc":[-85.2559764,42.0099317]},"n1820938111":{"id":"n1820938111","loc":[-85.2969284,41.973179]},"n1820938113":{"id":"n1820938113","loc":[-85.3875055,42.0019726]},"n1820938114":{"id":"n1820938114","loc":[-85.4250779,42.0068199]},"n1820938115":{"id":"n1820938115","loc":[-85.0645367,42.104889]},"n1820938116":{"id":"n1820938116","loc":[-85.1636762,42.0623724]},"n1820938117":{"id":"n1820938117","loc":[-85.0757322,42.1055935]},"n1820938118":{"id":"n1820938118","loc":[-85.3695197,41.9981559]},"n1820938120":{"id":"n1820938120","loc":[-85.1297516,42.0671027]},"n1820938121":{"id":"n1820938121","loc":[-85.1057448,42.0875551]},"n1820938122":{"id":"n1820938122","loc":[-85.2805175,41.9943182]},"n1820938123":{"id":"n1820938123","loc":[-85.2545173,42.0040722]},"n1820938124":{"id":"n1820938124","loc":[-84.9966607,42.0871319]},"n1820938125":{"id":"n1820938125","loc":[-85.0099899,42.0904612]},"n1820938126":{"id":"n1820938126","loc":[-85.2489919,42.0091102]},"n1820938127":{"id":"n1820938127","loc":[-85.0342706,42.0979476]},"n1820938128":{"id":"n1820938128","loc":[-85.1080891,42.0855884]},"n1820938129":{"id":"n1820938129","loc":[-85.0128183,42.0905356]},"n1820938130":{"id":"n1820938130","loc":[-85.631608,41.9434251]},"n1820938131":{"id":"n1820938131","loc":[-85.2551975,42.0008524]},"n1820938132":{"id":"n1820938132","loc":[-85.6421823,41.9096233]},"n1820938133":{"id":"n1820938133","loc":[-85.0125059,42.0906284]},"n1820938134":{"id":"n1820938134","loc":[-85.5499358,41.9701793]},"n1820938135":{"id":"n1820938135","loc":[-85.5472107,41.9712323]},"n1820938136":{"id":"n1820938136","loc":[-85.2760758,41.9958691]},"n1820938137":{"id":"n1820938137","loc":[-85.276678,41.9960433]},"n1820938138":{"id":"n1820938138","loc":[-85.0570319,42.1024731]},"n1820938140":{"id":"n1820938140","loc":[-85.2394325,42.0227492]},"n1820938142":{"id":"n1820938142","loc":[-85.5666341,41.9638829]},"n1820938144":{"id":"n1820938144","loc":[-85.258101,41.9996353]},"n1820938147":{"id":"n1820938147","loc":[-85.2129645,42.0413565]},"n1820938149":{"id":"n1820938149","loc":[-84.9962369,42.0868373]},"n1820938151":{"id":"n1820938151","loc":[-85.2570386,42.0084968]},"n1820938153":{"id":"n1820938153","loc":[-85.3971142,42.0050285]},"n1820938155":{"id":"n1820938155","loc":[-85.1072093,42.0855566]},"n1820938157":{"id":"n1820938157","loc":[-85.2840323,41.9920959]},"n1820938159":{"id":"n1820938159","loc":[-85.1187924,42.0816458]},"n1820938161":{"id":"n1820938161","loc":[-85.2681324,41.9985788]},"n1820938163":{"id":"n1820938163","loc":[-85.0887034,42.0984969]},"n1820938165":{"id":"n1820938165","loc":[-85.4133405,42.0073141]},"n1820938166":{"id":"n1820938166","loc":[-85.0097445,42.0902888]},"n1820938167":{"id":"n1820938167","loc":[-85.0828133,42.1037388]},"n1820938168":{"id":"n1820938168","loc":[-85.0549599,42.1030833]},"n1820938169":{"id":"n1820938169","loc":[-85.4571528,42.0010421]},"n1820938178":{"id":"n1820938178","loc":[-85.2706644,41.9975941]},"n1820938180":{"id":"n1820938180","loc":[-85.2258606,42.0335794]},"n1820938182":{"id":"n1820938182","loc":[-85.2832276,41.9814659]},"n1820938184":{"id":"n1820938184","loc":[-85.1082299,42.0860928]},"n1820938185":{"id":"n1820938185","loc":[-85.3839392,42.0022381]},"n1820938186":{"id":"n1820938186","loc":[-85.2772131,41.995905]},"n1820938187":{"id":"n1820938187","loc":[-85.1044895,42.0879214]},"n1820938188":{"id":"n1820938188","loc":[-85.2135267,42.0407087]},"n1820938189":{"id":"n1820938189","loc":[-85.2543993,42.0044628]},"n1820938190":{"id":"n1820938190","loc":[-85.1501793,42.0617351]},"n1820938191":{"id":"n1820938191","loc":[-85.3350587,41.9820469]},"n1820938192":{"id":"n1820938192","loc":[-85.1350731,42.0655735]},"n1820938193":{"id":"n1820938193","loc":[-85.0404008,42.1028843]},"n1820938194":{"id":"n1820938194","loc":[-85.6323161,41.943042]},"n1820938195":{"id":"n1820938195","loc":[-85.1259593,42.0742837]},"n1820938196":{"id":"n1820938196","loc":[-85.4562988,42.0033758]},"n1820938197":{"id":"n1820938197","loc":[-85.256824,42.0056826]},"n1820938198":{"id":"n1820938198","loc":[-85.2742103,41.9963862]},"n1820938199":{"id":"n1820938199","loc":[-85.0380888,42.1037877]},"n1820938200":{"id":"n1820938200","loc":[-85.47404,41.9944721]},"n1820938201":{"id":"n1820938201","loc":[-85.103021,42.087948]},"n1820938202":{"id":"n1820938202","loc":[-85.4030151,42.0065113]},"n1820938203":{"id":"n1820938203","loc":[-85.2113981,42.040735]},"n1820938204":{"id":"n1820938204","loc":[-85.2603433,41.9965137]},"n1820938206":{"id":"n1820938206","loc":[-85.1669378,42.0607634]},"n1820938207":{"id":"n1820938207","loc":[-85.0642027,42.1046076]},"n1820938208":{"id":"n1820938208","loc":[-85.2812428,41.9915696]},"n1820938209":{"id":"n1820938209","loc":[-85.0839559,42.1038343]},"n1820938210":{"id":"n1820938210","loc":[-85.1239946,42.0769368]},"n1820938211":{"id":"n1820938211","loc":[-85.2311177,42.0283042]},"n1820938212":{"id":"n1820938212","loc":[-85.2791614,41.9882682]},"n1820938213":{"id":"n1820938213","loc":[-85.2674941,41.9987582]},"n1820938214":{"id":"n1820938214","loc":[-85.352787,41.9919579]},"n1820938215":{"id":"n1820938215","loc":[-85.0874146,42.0952182]},"n1820938216":{"id":"n1820938216","loc":[-85.0069711,42.0877092]},"n1820938217":{"id":"n1820938217","loc":[-85.2059049,42.0404004]},"n1820938218":{"id":"n1820938218","loc":[-85.2403552,42.0227332]},"n1820938219":{"id":"n1820938219","loc":[-85.2492923,42.0098915]},"n1820938220":{"id":"n1820938220","loc":[-85.269778,41.9979541]},"n1820938221":{"id":"n1820938221","loc":[-85.2097673,42.0389024]},"n1820938222":{"id":"n1820938222","loc":[-85.0845942,42.1032015]},"n1820938223":{"id":"n1820938223","loc":[-84.993206,42.0858142]},"n1820938224":{"id":"n1820938224","loc":[-85.2108187,42.0402729]},"n1820938225":{"id":"n1820938225","loc":[-84.9893959,42.0873043]},"n1820938226":{"id":"n1820938226","loc":[-85.2952332,41.9719984]},"n1820938227":{"id":"n1820938227","loc":[-85.4100961,42.0081536]},"n1820938228":{"id":"n1820938228","loc":[-85.3299088,41.9785696]},"n1820938229":{"id":"n1820938229","loc":[-85.2258176,42.0340097]},"n1820938230":{"id":"n1820938230","loc":[-85.3146739,41.9711449]},"n1820938231":{"id":"n1820938231","loc":[-85.5447645,41.9712801]},"n1820938232":{"id":"n1820938232","loc":[-85.5510087,41.9705941]},"n1820938233":{"id":"n1820938233","loc":[-85.5122389,41.9703445]},"n1820938234":{"id":"n1820938234","loc":[-85.2792687,41.9865381]},"n1820938235":{"id":"n1820938235","loc":[-85.1475229,42.0630151]},"n1820938237":{"id":"n1820938237","loc":[-85.0332889,42.0996034]},"n1820938238":{"id":"n1820938238","loc":[-85.2588882,41.9986877]},"n1820938239":{"id":"n1820938239","loc":[-85.0656458,42.1050892]},"n1820938240":{"id":"n1820938240","loc":[-84.9913915,42.086098]},"n1820938241":{"id":"n1820938241","loc":[-85.4752416,41.9944402]},"n1820938242":{"id":"n1820938242","loc":[-85.1214304,42.0791147]},"n1820938243":{"id":"n1820938243","loc":[-85.0075183,42.0886925]},"n1820938244":{"id":"n1820938244","loc":[-85.1052888,42.0872087]},"n1820938245":{"id":"n1820938245","loc":[-85.3104252,41.9703393]},"n1820938246":{"id":"n1820938246","loc":[-85.232109,42.0318158]},"n1820938247":{"id":"n1820938247","loc":[-85.0756075,42.1059528]},"n1820938248":{"id":"n1820938248","loc":[-85.0075612,42.0890866]},"n1820938249":{"id":"n1820938249","loc":[-85.1013312,42.0897474]},"n1820938250":{"id":"n1820938250","loc":[-85.1168076,42.0828919]},"n1820938251":{"id":"n1820938251","loc":[-85.2951367,41.9723334]},"n1820938252":{"id":"n1820938252","loc":[-85.0879363,42.0976053]},"n1820938253":{"id":"n1820938253","loc":[-85.0354763,42.1021838]},"n1820938254":{"id":"n1820938254","loc":[-85.2379627,42.0236339]},"n1820938255":{"id":"n1820938255","loc":[-85.1308245,42.0685364]},"n1820938256":{"id":"n1820938256","loc":[-85.0914446,42.0934774]},"n1820938257":{"id":"n1820938257","loc":[-85.2436812,42.014069]},"n1820938258":{"id":"n1820938258","loc":[-85.0682529,42.1056106]},"n1820938259":{"id":"n1820938259","loc":[-85.290652,41.9766805]},"n1820938260":{"id":"n1820938260","loc":[-85.0133494,42.0897434]},"n1820938261":{"id":"n1820938261","loc":[-85.2753047,41.9949429]},"n1820938262":{"id":"n1820938262","loc":[-85.0314691,42.0950788]},"n1820938263":{"id":"n1820938263","loc":[-85.3444786,41.9908359]},"n1820938264":{"id":"n1820938264","loc":[-85.0443115,42.1009061]},"n1820938265":{"id":"n1820938265","loc":[-85.0634853,42.1043159]},"n1820938267":{"id":"n1820938267","loc":[-85.3978223,42.0053952]},"n1820938268":{"id":"n1820938268","loc":[-85.0228659,42.0911885]},"n1820938269":{"id":"n1820938269","loc":[-85.0220237,42.0906272]},"n1820938270":{"id":"n1820938270","loc":[-85.1061525,42.0863369]},"n1820938271":{"id":"n1820938271","loc":[-85.2382309,42.0233708]},"n1820938272":{"id":"n1820938272","loc":[-85.310672,41.9702755]},"n1820938273":{"id":"n1820938273","loc":[-85.1448192,42.0652613]},"n1820938274":{"id":"n1820938274","loc":[-85.6036057,41.9403766]},"n1820938275":{"id":"n1820938275","loc":[-85.0778941,42.1032413]},"n1820938276":{"id":"n1820938276","loc":[-85.1279374,42.0723974]},"n1820938277":{"id":"n1820938277","loc":[-85.2806635,41.9847836]},"n1820938278":{"id":"n1820938278","loc":[-85.2653201,41.9976352]},"n1820938279":{"id":"n1820938279","loc":[-85.0351665,42.1001805]},"n1820938280":{"id":"n1820938280","loc":[-85.0718269,42.1056253]},"n1820938281":{"id":"n1820938281","loc":[-85.2574248,42.0075322]},"n1820938282":{"id":"n1820938282","loc":[-85.126666,42.0740778]},"n1820938283":{"id":"n1820938283","loc":[-85.077705,42.1034733]},"n1820938284":{"id":"n1820938284","loc":[-85.3535552,41.9919045]},"n1820938286":{"id":"n1820938286","loc":[-85.2810711,41.9866657]},"n1820938287":{"id":"n1820938287","loc":[-85.4567494,42.0019885]},"n1820938288":{"id":"n1820938288","loc":[-85.2642419,41.9992936]},"n1820938289":{"id":"n1820938289","loc":[-85.2643344,41.9980925]},"n1820938290":{"id":"n1820938290","loc":[-85.3270335,41.9776125]},"n1820938291":{"id":"n1820938291","loc":[-85.1200584,42.0795077]},"n1820938292":{"id":"n1820938292","loc":[-85.2290792,42.0340256]},"n1820938293":{"id":"n1820938293","loc":[-85.6015887,41.9401372]},"n1820938294":{"id":"n1820938294","loc":[-85.5370869,41.970488]},"n1820938295":{"id":"n1820938295","loc":[-85.3108866,41.9698048]},"n1820938297":{"id":"n1820938297","loc":[-85.1556511,42.0628184]},"n1820938298":{"id":"n1820938298","loc":[-85.0027922,42.0875221]},"n1820938300":{"id":"n1820938300","loc":[-85.3873338,42.0040614]},"n1820938301":{"id":"n1820938301","loc":[-85.0350753,42.1004034]},"n1820938302":{"id":"n1820938302","loc":[-85.6239476,41.9411906]},"n1820938304":{"id":"n1820938304","loc":[-85.0118246,42.0897964]},"n1820938306":{"id":"n1820938306","loc":[-85.4796877,41.995275]},"n1820938307":{"id":"n1820938307","loc":[-85.5388636,41.9707856]},"n1820938309":{"id":"n1820938309","loc":[-85.2971902,41.9727773]},"n1820938310":{"id":"n1820938310","loc":[-85.5426831,41.9715513]},"n1820938311":{"id":"n1820938311","loc":[-85.2798373,41.9836671]},"n1820938312":{"id":"n1820938312","loc":[-85.2432198,42.0104017]},"n1820938313":{"id":"n1820938313","loc":[-85.2650412,41.9987554]},"n1820938317":{"id":"n1820938317","loc":[-85.0015423,42.0882386]},"n1820938318":{"id":"n1820938318","loc":[-85.1409783,42.064879]},"n1820938319":{"id":"n1820938319","loc":[-85.1691908,42.058995]},"n1820938320":{"id":"n1820938320","loc":[-85.1059165,42.0864882]},"n1820938321":{"id":"n1820938321","loc":[-85.3664941,41.9965771]},"n1820938323":{"id":"n1820938323","loc":[-85.3143198,41.9710971]},"n1820938324":{"id":"n1820938324","loc":[-85.0016067,42.0880675]},"n1820938325":{"id":"n1820938325","loc":[-85.0148139,42.0887164]},"n1820938326":{"id":"n1820938326","loc":[-85.0324682,42.0959056]},"n1820938327":{"id":"n1820938327","loc":[-85.0898661,42.0939921]},"n1820938328":{"id":"n1820938328","loc":[-85.2556427,42.0004936]},"n1820938329":{"id":"n1820938329","loc":[-85.6287112,41.9407437]},"n1820938330":{"id":"n1820938330","loc":[-84.9913392,42.0866701]},"n1820938331":{"id":"n1820938331","loc":[-85.2685777,41.9984632]},"n1820938332":{"id":"n1820938332","loc":[-85.0078884,42.0901614]},"n1820938333":{"id":"n1820938333","loc":[-84.999642,42.0878616]},"n1820938334":{"id":"n1820938334","loc":[-85.0188909,42.0899186]},"n1820938335":{"id":"n1820938335","loc":[-85.2830238,41.9819843]},"n1820938336":{"id":"n1820938336","loc":[-85.2491421,42.0096204]},"n1820938337":{"id":"n1820938337","loc":[-85.0585701,42.1034295]},"n1820938338":{"id":"n1820938338","loc":[-85.0651965,42.1051636]},"n1820938339":{"id":"n1820938339","loc":[-85.0583944,42.104292]},"n1820938340":{"id":"n1820938340","loc":[-85.119876,42.0801567]},"n1820938341":{"id":"n1820938341","loc":[-85.0943937,42.0931323]},"n1820938342":{"id":"n1820938342","loc":[-85.1504583,42.0613209]},"n1820938343":{"id":"n1820938343","loc":[-85.0425426,42.1019836]},"n1820938345":{"id":"n1820938345","loc":[-84.9991391,42.0878206]},"n1820938346":{"id":"n1820938346","loc":[-85.2563841,42.0094614]},"n1820938347":{"id":"n1820938347","loc":[-85.0515387,42.103297]},"n1820938348":{"id":"n1820938348","loc":[-85.0857261,42.1003636]},"n1820938349":{"id":"n1820938349","loc":[-85.078971,42.1029241]},"n1820938350":{"id":"n1820938350","loc":[-85.5699558,41.958931]},"n1820938351":{"id":"n1820938351","loc":[-85.3181285,41.9696533]},"n1820938352":{"id":"n1820938352","loc":[-85.5998506,41.9402329]},"n1820938353":{"id":"n1820938353","loc":[-85.2567277,42.000317]},"n1820938354":{"id":"n1820938354","loc":[-85.3082795,41.9708338]},"n1820938355":{"id":"n1820938355","loc":[-85.3127856,41.9692784]},"n1820938356":{"id":"n1820938356","loc":[-85.0340775,42.1010721]},"n1820938357":{"id":"n1820938357","loc":[-85.3158111,41.9706583]},"n1820938359":{"id":"n1820938359","loc":[-85.2312035,42.0280412]},"n1820938360":{"id":"n1820938360","loc":[-85.2448613,42.018477]},"n1820938361":{"id":"n1820938361","loc":[-85.29077,41.9759068]},"n1820938364":{"id":"n1820938364","loc":[-85.3677387,41.9976615]},"n1820938365":{"id":"n1820938365","loc":[-85.0785204,42.1030355]},"n1820938366":{"id":"n1820938366","loc":[-85.2262039,42.0333722]},"n1820938367":{"id":"n1820938367","loc":[-85.1226011,42.0780902]},"n1820938368":{"id":"n1820938368","loc":[-85.3229673,41.971129]},"n1820938369":{"id":"n1820938369","loc":[-85.385334,42.0000056]},"n1820938370":{"id":"n1820938370","loc":[-85.000098,42.0879094]},"n1820938372":{"id":"n1820938372","loc":[-85.3852481,42.0025091]},"n1820938373":{"id":"n1820938373","loc":[-85.3770513,41.9982515]},"n1820938374":{"id":"n1820938374","loc":[-85.6278314,41.9405362]},"n1820938375":{"id":"n1820938375","loc":[-85.6355133,41.9344068]},"n1820938376":{"id":"n1820938376","loc":[-85.635642,41.9324753]},"n1820938377":{"id":"n1820938377","loc":[-85.3154463,41.970778]},"n1820938378":{"id":"n1820938378","loc":[-85.0920334,42.093411]},"n1820938379":{"id":"n1820938379","loc":[-85.3269155,41.9722297]},"n1820938381":{"id":"n1820938381","loc":[-85.1134334,42.0849184]},"n1820938382":{"id":"n1820938382","loc":[-85.005968,42.088585]},"n1820938384":{"id":"n1820938384","loc":[-85.1245203,42.0757183]},"n1820938385":{"id":"n1820938385","loc":[-85.020704,42.0905396]},"n1820938386":{"id":"n1820938386","loc":[-85.119585,42.0808984]},"n1820938387":{"id":"n1820938387","loc":[-85.0072447,42.0880117]},"n1820938388":{"id":"n1820938388","loc":[-85.2742908,41.9960273]},"n1820938389":{"id":"n1820938389","loc":[-85.3275807,41.9696852]},"n1820938390":{"id":"n1820938390","loc":[-85.2385635,42.0231556]},"n1820938392":{"id":"n1820938392","loc":[-85.0202856,42.0900778]},"n1820938393":{"id":"n1820938393","loc":[-85.2067847,42.0395398]},"n1820938394":{"id":"n1820938394","loc":[-85.5183544,41.9713495]},"n1820938396":{"id":"n1820938396","loc":[-85.5073037,41.9736787]},"n1820938397":{"id":"n1820938397","loc":[-85.2519638,42.0114225]},"n1820938398":{"id":"n1820938398","loc":[-85.287487,41.9793285]},"n1820938399":{"id":"n1820938399","loc":[-85.2298088,42.0336431]},"n1820938400":{"id":"n1820938400","loc":[-85.229444,42.0339141]},"n1820938401":{"id":"n1820938401","loc":[-85.2421791,42.0220239]},"n1820938402":{"id":"n1820938402","loc":[-85.2976687,41.9737612]},"n1820938403":{"id":"n1820938403","loc":[-85.3622069,41.993473]},"n1820938404":{"id":"n1820938404","loc":[-85.2465458,42.014906]},"n1820938405":{"id":"n1820938405","loc":[-85.5724663,41.9639412]},"n1820938406":{"id":"n1820938406","loc":[-85.3708501,41.9982037]},"n1820938408":{"id":"n1820938408","loc":[-85.2564592,42.0055311]},"n1820938409":{"id":"n1820938409","loc":[-85.1192846,42.0810856]},"n1820938410":{"id":"n1820938410","loc":[-85.5623812,41.971663]},"n1820938411":{"id":"n1820938411","loc":[-85.3221948,41.9719665]},"n1820938412":{"id":"n1820938412","loc":[-85.5168738,41.9710305]},"n1820938413":{"id":"n1820938413","loc":[-85.4546852,42.0061127]},"n1820938414":{"id":"n1820938414","loc":[-85.5896153,41.9463617]},"n1820938415":{"id":"n1820938415","loc":[-85.2978189,41.9722138]},"n1820938416":{"id":"n1820938416","loc":[-85.1021681,42.0883581]},"n1820938417":{"id":"n1820938417","loc":[-85.2797193,41.9912984]},"n1820938419":{"id":"n1820938419","loc":[-85.2362461,42.0248533]},"n1820938420":{"id":"n1820938420","loc":[-85.4833639,41.9846252]},"n1820938422":{"id":"n1820938422","loc":[-85.3281064,41.9689433]},"n1820938424":{"id":"n1820938424","loc":[-85.2416963,42.0130088]},"n1820938425":{"id":"n1820938425","loc":[-85.5718655,41.9564577]},"n1820938426":{"id":"n1820938426","loc":[-85.0512812,42.1030701]},"n1820938427":{"id":"n1820938427","loc":[-85.1273527,42.0723616]},"n1820938428":{"id":"n1820938428","loc":[-85.0215033,42.0904083]},"n1820938429":{"id":"n1820938429","loc":[-85.6169953,41.942228]},"n1820938430":{"id":"n1820938430","loc":[-85.2829165,41.9907243]},"n1820938431":{"id":"n1820938431","loc":[-85.2240796,42.0374203]},"n1820938432":{"id":"n1820938432","loc":[-85.0167598,42.0898442]},"n1820938433":{"id":"n1820938433","loc":[-85.2132649,42.0411334]},"n1820938434":{"id":"n1820938434","loc":[-85.2293839,42.031513]},"n1820938435":{"id":"n1820938435","loc":[-85.1203374,42.0792608]},"n1820938436":{"id":"n1820938436","loc":[-85.109571,42.086268]},"n1820938437":{"id":"n1820938437","loc":[-85.1079026,42.0853842]},"n1820938438":{"id":"n1820938438","loc":[-85.109237,42.0862413]},"n1820938439":{"id":"n1820938439","loc":[-85.2259936,42.0350831]},"n1820938440":{"id":"n1820938440","loc":[-85.3669705,41.99679]},"n1820938441":{"id":"n1820938441","loc":[-85.2418143,42.0223507]},"n1820938442":{"id":"n1820938442","loc":[-85.3101248,41.9702515]},"n1820938443":{"id":"n1820938443","loc":[-85.069315,42.1059688]},"n1820938444":{"id":"n1820938444","loc":[-85.205862,42.0410378]},"n1820938445":{"id":"n1820938445","loc":[-85.0388076,42.1036604]},"n1820938446":{"id":"n1820938446","loc":[-85.2225389,42.0370115]},"n1820938447":{"id":"n1820938447","loc":[-85.3241474,41.9719346]},"n1820938448":{"id":"n1820938448","loc":[-85.3125496,41.9690789]},"n1820938449":{"id":"n1820938449","loc":[-85.1146497,42.0857039]},"n1820938450":{"id":"n1820938450","loc":[-85.1333944,42.0714963]},"n1820938451":{"id":"n1820938451","loc":[-85.5619306,41.9720937]},"n1820938452":{"id":"n1820938452","loc":[-85.2553651,42.0006479]},"n1820938453":{"id":"n1820938453","loc":[-85.3151137,41.9710093]},"n1820938454":{"id":"n1820938454","loc":[-85.2592315,41.9977947]},"n1820938455":{"id":"n1820938455","loc":[-85.2655723,41.9995966]},"n1820938456":{"id":"n1820938456","loc":[-85.4820652,41.9959233]},"n1820938459":{"id":"n1820938459","loc":[-85.450737,42.0055068]},"n1820938460":{"id":"n1820938460","loc":[-85.2428658,42.0205573]},"n1820938461":{"id":"n1820938461","loc":[-85.0835576,42.1021559]},"n1820938462":{"id":"n1820938462","loc":[-85.244636,42.0194733]},"n1820938463":{"id":"n1820938463","loc":[-85.5702562,41.9581332]},"n1820938465":{"id":"n1820938465","loc":[-85.5680031,41.9659515]},"n1820938467":{"id":"n1820938467","loc":[-85.2798752,41.9948353]},"n1820938468":{"id":"n1820938468","loc":[-85.0477407,42.1015537]},"n1820938469":{"id":"n1820938469","loc":[-85.6403842,41.913732]},"n1820938470":{"id":"n1820938470","loc":[-85.0396029,42.103289]},"n1820938471":{"id":"n1820938471","loc":[-85.2824702,41.9907777]},"n1820938472":{"id":"n1820938472","loc":[-85.2969284,41.9735538]},"n1820938474":{"id":"n1820938474","loc":[-85.401041,42.0070853]},"n1820938475":{"id":"n1820938475","loc":[-85.4116625,42.0073883]},"n1820938476":{"id":"n1820938476","loc":[-85.0437764,42.1016214]},"n1820938477":{"id":"n1820938477","loc":[-85.3643269,41.9958436]},"n1820938478":{"id":"n1820938478","loc":[-85.3895182,42.0009465]},"n1820938479":{"id":"n1820938479","loc":[-85.636157,41.9333373]},"n1820938480":{"id":"n1820938480","loc":[-85.2811355,41.9858044]},"n1820938481":{"id":"n1820938481","loc":[-85.0239052,42.092153]},"n1820938482":{"id":"n1820938482","loc":[-85.2558798,42.0053557]},"n1820938483":{"id":"n1820938483","loc":[-85.2544422,42.0047339]},"n1820938484":{"id":"n1820938484","loc":[-85.4864683,41.9843183]},"n1820938485":{"id":"n1820938485","loc":[-85.2554185,42.0031075]},"n1820938486":{"id":"n1820938486","loc":[-85.3082795,41.9712486]},"n1820938487":{"id":"n1820938487","loc":[-85.2433378,42.0133436]},"n1820938488":{"id":"n1820938488","loc":[-85.0216696,42.0904162]},"n1820938489":{"id":"n1820938489","loc":[-85.2546138,42.0050289]},"n1820938490":{"id":"n1820938490","loc":[-85.2717521,41.9977349]},"n1820938491":{"id":"n1820938491","loc":[-85.0100489,42.0908195]},"n1820938492":{"id":"n1820938492","loc":[-85.207879,42.0392211]},"n1820938493":{"id":"n1820938493","loc":[-85.0007363,42.0882836]},"n1820938494":{"id":"n1820938494","loc":[-85.5775303,41.9504097]},"n1820938495":{"id":"n1820938495","loc":[-85.1131584,42.0847683]},"n1820938496":{"id":"n1820938496","loc":[-85.0887825,42.0941633]},"n1820938497":{"id":"n1820938497","loc":[-85.1185926,42.0818938]},"n1820938498":{"id":"n1820938498","loc":[-85.2748487,41.9948712]},"n1820938499":{"id":"n1820938499","loc":[-85.2566952,42.0090788]},"n1820938500":{"id":"n1820938500","loc":[-85.0774757,42.1036234]},"n1820938501":{"id":"n1820938501","loc":[-85.4190869,42.008903]},"n1820938502":{"id":"n1820938502","loc":[-85.1140395,42.0850577]},"n1820938503":{"id":"n1820938503","loc":[-85.1136104,42.0848627]},"n1820938504":{"id":"n1820938504","loc":[-85.5828089,41.9480638]},"n1820938505":{"id":"n1820938505","loc":[-85.625514,41.9405202]},"n1820938506":{"id":"n1820938506","loc":[-85.2063384,42.0398322]},"n1820938507":{"id":"n1820938507","loc":[-85.3395476,41.9851636]},"n1820938508":{"id":"n1820938508","loc":[-85.0328853,42.0963606]},"n1820938510":{"id":"n1820938510","loc":[-85.1170369,42.0843702]},"n1820938511":{"id":"n1820938511","loc":[-85.2784748,41.9868487]},"n1820938512":{"id":"n1820938512","loc":[-85.6310501,41.9435528]},"n1820938514":{"id":"n1820938514","loc":[-85.0334284,42.0981028]},"n1820938515":{"id":"n1820938515","loc":[-84.9912091,42.0868226]},"n1820938516":{"id":"n1820938516","loc":[-85.2806141,41.9940351]},"n1820938517":{"id":"n1820938517","loc":[-85.1233025,42.0776734]},"n1820938518":{"id":"n1820938518","loc":[-85.2047891,42.0429023]},"n1820938519":{"id":"n1820938519","loc":[-85.1475443,42.0648312]},"n1820938520":{"id":"n1820938520","loc":[-85.2644685,41.9990891]},"n1820938521":{"id":"n1820938521","loc":[-85.1056281,42.0872553]},"n1820938522":{"id":"n1820938522","loc":[-85.4813184,41.9930105]},"n1820938523":{"id":"n1820938523","loc":[-85.321551,41.9722936]},"n1820938524":{"id":"n1820938524","loc":[-85.1564664,42.0631211]},"n1820938525":{"id":"n1820938525","loc":[-85.4149885,42.0079144]},"n1820938527":{"id":"n1820938527","loc":[-85.2861888,41.9803653]},"n1820938528":{"id":"n1820938528","loc":[-85.1301379,42.0682178]},"n1820938529":{"id":"n1820938529","loc":[-85.4156537,42.0084247]},"n1820938530":{"id":"n1820938530","loc":[-85.245151,42.0176082]},"n1820938531":{"id":"n1820938531","loc":[-85.457818,42.0001651]},"n1820938532":{"id":"n1820938532","loc":[-85.310951,41.9694538]},"n1820938533":{"id":"n1820938533","loc":[-85.1509089,42.0611298]},"n1820938534":{"id":"n1820938534","loc":[-85.1108249,42.086321]},"n1820938535":{"id":"n1820938535","loc":[-85.1260344,42.0740687]},"n1820938536":{"id":"n1820938536","loc":[-85.4561228,42.0042791]},"n1820938537":{"id":"n1820938537","loc":[-85.2805082,41.9945761]},"n1820938538":{"id":"n1820938538","loc":[-85.273352,41.9981921]},"n1820938539":{"id":"n1820938539","loc":[-85.1084216,42.0864364]},"n1820938540":{"id":"n1820938540","loc":[-85.5009737,41.9773637]},"n1820938541":{"id":"n1820938541","loc":[-85.3960843,42.0051879]},"n1820938542":{"id":"n1820938542","loc":[-85.3425088,41.9865034]},"n1820938545":{"id":"n1820938545","loc":[-84.9937907,42.0860849]},"n1820938546":{"id":"n1820938546","loc":[-85.1084176,42.086065]},"n1820938547":{"id":"n1820938547","loc":[-85.3492851,41.9924786]},"n1820938548":{"id":"n1820938548","loc":[-85.2512235,42.0101147]},"n1820938549":{"id":"n1820938549","loc":[-85.3717298,41.9979326]},"n1820938551":{"id":"n1820938551","loc":[-85.2573712,42.0064081]},"n1820938552":{"id":"n1820938552","loc":[-85.2514596,42.010139]},"n1820938553":{"id":"n1820938553","loc":[-85.416512,42.0088073]},"n1820938554":{"id":"n1820938554","loc":[-85.4365964,42.0061606]},"n1820938555":{"id":"n1820938555","loc":[-85.4552431,42.0057301]},"n1820938556":{"id":"n1820938556","loc":[-85.2916283,41.9778769]},"n1820938557":{"id":"n1820938557","loc":[-85.100709,42.0902968]},"n1820938558":{"id":"n1820938558","loc":[-85.4703064,41.9965771]},"n1820938559":{"id":"n1820938559","loc":[-85.3134722,41.9696134]},"n1820938560":{"id":"n1820938560","loc":[-85.4834213,41.9885768]},"n1820938561":{"id":"n1820938561","loc":[-85.2740641,41.9975236]},"n1820938562":{"id":"n1820938562","loc":[-85.148334,42.0623405]},"n1820938563":{"id":"n1820938563","loc":[-85.2358598,42.0263675]},"n1820938565":{"id":"n1820938565","loc":[-85.2902979,41.9790892]},"n1820938566":{"id":"n1820938566","loc":[-85.2528865,42.0112869]},"n1820938567":{"id":"n1820938567","loc":[-85.2595319,41.9973003]},"n1820938568":{"id":"n1820938568","loc":[-85.071151,42.105689]},"n1820938570":{"id":"n1820938570","loc":[-85.299278,41.9732188]},"n1820938571":{"id":"n1820938571","loc":[-85.0354669,42.1024771]},"n1820938583":{"id":"n1820938583","loc":[-85.3313937,41.972562]},"n1820938585":{"id":"n1820938585","loc":[-85.0756933,42.1058334]},"n1820938587":{"id":"n1820938587","loc":[-85.3130324,41.9694219]},"n1820938590":{"id":"n1820938590","loc":[-85.0934227,42.0931681]},"n1820938592":{"id":"n1820938592","loc":[-85.3517956,41.9922553]},"n1820938593":{"id":"n1820938593","loc":[-85.4023971,42.0065169]},"n1820938594":{"id":"n1820938594","loc":[-85.3506798,41.9925583]},"n1820938595":{"id":"n1820938595","loc":[-85.3673524,41.9971193]},"n1820938596":{"id":"n1820938596","loc":[-85.1073608,42.0853523]},"n1820938597":{"id":"n1820938597","loc":[-85.2976579,41.972477]},"n1820938598":{"id":"n1820938598","loc":[-85.5616517,41.9694295]},"n1820938599":{"id":"n1820938599","loc":[-85.3552074,41.9921915]},"n1820938600":{"id":"n1820938600","loc":[-85.4665126,41.9999953]},"n1820938601":{"id":"n1820938601","loc":[-85.2740695,41.9966226]},"n1820938602":{"id":"n1820938602","loc":[-85.279376,41.9886669]},"n1820938603":{"id":"n1820938603","loc":[-85.0771109,42.1040413]},"n1820938604":{"id":"n1820938604","loc":[-85.2636049,41.9977895]},"n1820938605":{"id":"n1820938605","loc":[-85.3762145,41.9976456]},"n1820938606":{"id":"n1820938606","loc":[-85.2321369,42.0289577]},"n1820938620":{"id":"n1820938620","loc":[-85.4947724,41.9776189]},"n1820938622":{"id":"n1820938622","loc":[-85.1547069,42.0622768]},"n1820938624":{"id":"n1820938624","loc":[-85.0005056,42.0880249]},"n1820938626":{"id":"n1820938626","loc":[-85.0735596,42.1059357]},"n1820938628":{"id":"n1820938628","loc":[-85.4665298,41.99932]},"n1820938629":{"id":"n1820938629","loc":[-85.434515,42.0065273]},"n1820938630":{"id":"n1820938630","loc":[-85.117462,42.0823823]},"n1820938631":{"id":"n1820938631","loc":[-85.0131777,42.0890707]},"n1820938632":{"id":"n1820938632","loc":[-85.0875326,42.0961934]},"n1820938634":{"id":"n1820938634","loc":[-85.6433839,41.9112042]},"n1820938635":{"id":"n1820938635","loc":[-85.1366181,42.064969]},"n1820938636":{"id":"n1820938636","loc":[-85.073109,42.1057925]},"n1820938638":{"id":"n1820938638","loc":[-85.161406,42.0632541]},"n1820938640":{"id":"n1820938640","loc":[-85.6343932,41.9188845]},"n1820938642":{"id":"n1820938642","loc":[-85.2500004,42.010306]},"n1820938644":{"id":"n1820938644","loc":[-85.291918,41.9753166]},"n1820938663":{"id":"n1820938663","loc":[-85.2841611,41.9916812]},"n1820938664":{"id":"n1820938664","loc":[-85.1052955,42.0868134]},"n1820938665":{"id":"n1820938665","loc":[-85.4606118,42.0005534]},"n1820938666":{"id":"n1820938666","loc":[-85.5672736,41.9642922]},"n1820938667":{"id":"n1820938667","loc":[-85.6348481,41.9316932]},"n1820938668":{"id":"n1820938668","loc":[-85.0224904,42.0909576]},"n1820938669":{"id":"n1820938669","loc":[-85.0133856,42.0899755]},"n1820938670":{"id":"n1820938670","loc":[-85.344779,41.991139]},"n1820938671":{"id":"n1820938671","loc":[-85.632874,41.9425313]},"n1820938673":{"id":"n1820938673","loc":[-85.4941501,41.9779698]},"n1820938675":{"id":"n1820938675","loc":[-85.0862559,42.0997519]},"n1820938676":{"id":"n1820938676","loc":[-85.0097874,42.0898032]},"n1820938678":{"id":"n1820938678","loc":[-84.9913553,42.0863675]},"n1820938680":{"id":"n1820938680","loc":[-85.0533666,42.1038315]},"n1820938682":{"id":"n1820938682","loc":[-85.2950294,41.9743914]},"n1820938684":{"id":"n1820938684","loc":[-85.2517385,42.0104499]},"n1820938686":{"id":"n1820938686","loc":[-85.0247971,42.0922514]},"n1820938688":{"id":"n1820938688","loc":[-85.0807037,42.1026017]},"n1820938690":{"id":"n1820938690","loc":[-85.52462,41.9722748]},"n1820938694":{"id":"n1820938694","loc":[-85.2586535,41.9988818]},"n1820938695":{"id":"n1820938695","loc":[-85.0931612,42.092948]},"n1820938697":{"id":"n1820938697","loc":[-85.2470822,42.016564]},"n1820938698":{"id":"n1820938698","loc":[-85.4143018,42.0075158]},"n1820938699":{"id":"n1820938699","loc":[-85.0771484,42.104487]},"n1820938700":{"id":"n1820938700","loc":[-85.0291208,42.0942775]},"n1820938701":{"id":"n1820938701","loc":[-85.6367964,41.9185971]},"n1820938702":{"id":"n1820938702","loc":[-85.085419,42.1010693]},"n1820938703":{"id":"n1820938703","loc":[-85.0583877,42.1040584]},"n1820938705":{"id":"n1820938705","loc":[-85.2573379,42.0003182]},"n1820938706":{"id":"n1820938706","loc":[-85.2655937,41.9981575]},"n1820938707":{"id":"n1820938707","loc":[-85.023181,42.0915758]},"n1820938708":{"id":"n1820938708","loc":[-85.2318687,42.0274674]},"n1820938709":{"id":"n1820938709","loc":[-85.1056389,42.0866184]},"n1820938710":{"id":"n1820938710","loc":[-85.5276265,41.9700978]},"n1820938711":{"id":"n1820938711","loc":[-85.0864128,42.0945761]},"n1820938712":{"id":"n1820938712","loc":[-84.9897071,42.0871888]},"n1820938714":{"id":"n1820938714","loc":[-85.1328845,42.0665611]},"n1820938715":{"id":"n1820938715","loc":[-85.0336537,42.0991377]},"n1820938716":{"id":"n1820938716","loc":[-85.087597,42.0986692]},"n1820938717":{"id":"n1820938717","loc":[-85.1241394,42.0761882]},"n1820938718":{"id":"n1820938718","loc":[-85.1176002,42.0847723]},"n1820938719":{"id":"n1820938719","loc":[-85.2423615,42.0216572]},"n1820938721":{"id":"n1820938721","loc":[-85.2196378,42.0387908]},"n1820938722":{"id":"n1820938722","loc":[-85.0164272,42.0890082]},"n1820938723":{"id":"n1820938723","loc":[-85.5917182,41.9451807]},"n1820938724":{"id":"n1820938724","loc":[-85.2458806,42.0086638]},"n1820938725":{"id":"n1820938725","loc":[-85.1264474,42.0740527]},"n1820938726":{"id":"n1820938726","loc":[-85.1961631,42.04738]},"n1820938727":{"id":"n1820938727","loc":[-85.2784643,41.9943648]},"n1820938728":{"id":"n1820938728","loc":[-85.2905554,41.9763216]},"n1820938729":{"id":"n1820938729","loc":[-85.2913386,41.9771511]},"n1820938730":{"id":"n1820938730","loc":[-85.0112519,42.0895683]},"n1820938732":{"id":"n1820938732","loc":[-85.4290261,42.0064531]},"n1820938733":{"id":"n1820938733","loc":[-85.3867073,42.0031629]},"n1820938734":{"id":"n1820938734","loc":[-85.4943647,41.9836005]},"n1820938735":{"id":"n1820938735","loc":[-85.4900303,41.9860728]},"n1820938736":{"id":"n1820938736","loc":[-85.0866153,42.0944539]},"n1820938737":{"id":"n1820938737","loc":[-85.0869532,42.0990911]},"n1820938738":{"id":"n1820938738","loc":[-85.6321659,41.9208851]},"n1820938739":{"id":"n1820938739","loc":[-85.5930485,41.9433453]},"n1820938740":{"id":"n1820938740","loc":[-85.0406851,42.102733]},"n1820938741":{"id":"n1820938741","loc":[-85.1051131,42.0869846]},"n1820938742":{"id":"n1820938742","loc":[-85.1377554,42.0648893]},"n1820938743":{"id":"n1820938743","loc":[-85.2795694,41.994604]},"n1820938745":{"id":"n1820938745","loc":[-85.4948153,41.9826594]},"n1820938746":{"id":"n1820938746","loc":[-85.4488916,42.0050923]},"n1820938747":{"id":"n1820938747","loc":[-85.1052526,42.0866144]},"n1820938748":{"id":"n1820938748","loc":[-85.1468749,42.0653991]},"n1820938749":{"id":"n1820938749","loc":[-85.0856886,42.1006104]},"n1820938750":{"id":"n1820938750","loc":[-85.2144022,42.0404004]},"n1820938751":{"id":"n1820938751","loc":[-85.277771,41.9907458]},"n1820938752":{"id":"n1820938752","loc":[-85.1474542,42.0636149]},"n1820938753":{"id":"n1820938753","loc":[-85.0820515,42.1028075]},"n1820938754":{"id":"n1820938754","loc":[-85.1122948,42.08525]},"n1820938756":{"id":"n1820938756","loc":[-85.0173352,42.0901933]},"n1820938757":{"id":"n1820938757","loc":[-85.2259721,42.0354018]},"n1820938758":{"id":"n1820938758","loc":[-85.0872389,42.0987795]},"n1820938759":{"id":"n1820938759","loc":[-85.2291436,42.031874]},"n1820938760":{"id":"n1820938760","loc":[-85.3802485,42.0016002]},"n1820938761":{"id":"n1820938761","loc":[-85.3945822,42.0057938]},"n1820938762":{"id":"n1820938762","loc":[-85.5273237,41.9713017]},"n1820938763":{"id":"n1820938763","loc":[-85.2868862,41.9798629]},"n1820938764":{"id":"n1820938764","loc":[-85.2516677,42.0107899]},"n1820938766":{"id":"n1820938766","loc":[-85.3183002,41.9693103]},"n1820938768":{"id":"n1820938768","loc":[-85.2159042,42.0401932]},"n1820938770":{"id":"n1820938770","loc":[-85.0094481,42.0911141]},"n1820938771":{"id":"n1820938771","loc":[-85.0244538,42.0922155]},"n1820938772":{"id":"n1820938772","loc":[-85.231697,42.028862]},"n1820938773":{"id":"n1820938773","loc":[-85.2102394,42.0390617]},"n1820938774":{"id":"n1820938774","loc":[-85.2463419,42.0151212]},"n1820938775":{"id":"n1820938775","loc":[-85.0726195,42.1056424]},"n1820938776":{"id":"n1820938776","loc":[-85.0060431,42.0883262]},"n1820938778":{"id":"n1820938778","loc":[-85.425889,42.0056982]},"n1820938779":{"id":"n1820938779","loc":[-85.1183042,42.0820638]},"n1820938780":{"id":"n1820938780","loc":[-85.441596,42.0058257]},"n1820938781":{"id":"n1820938781","loc":[-85.1124879,42.0847086]},"n1820938782":{"id":"n1820938782","loc":[-85.2452733,42.0153894]},"n1820938783":{"id":"n1820938783","loc":[-85.2741191,41.9969244]},"n1820938784":{"id":"n1820938784","loc":[-85.2829487,41.9822236]},"n1820938785":{"id":"n1820938785","loc":[-85.3202743,41.972142]},"n1820938786":{"id":"n1820938786","loc":[-85.2345402,42.0266465]},"n1820938787":{"id":"n1820938787","loc":[-85.3037626,41.9724611]},"n1820938789":{"id":"n1820938789","loc":[-85.2474792,42.0161973]},"n1820938790":{"id":"n1820938790","loc":[-85.2951045,41.9727323]},"n1820938791":{"id":"n1820938791","loc":[-85.322345,41.9712726]},"n1820938792":{"id":"n1820938792","loc":[-85.2402372,42.0110394]},"n1820938793":{"id":"n1820938793","loc":[-85.5135693,41.9698659]},"n1820938794":{"id":"n1820938794","loc":[-85.4695339,41.9967366]},"n1820938796":{"id":"n1820938796","loc":[-85.0418492,42.1011131]},"n1820938797":{"id":"n1820938797","loc":[-85.3334107,41.9806337]},"n1820938798":{"id":"n1820938798","loc":[-85.5625314,41.9711685]},"n1820938799":{"id":"n1820938799","loc":[-85.3755707,41.9973585]},"n1820938800":{"id":"n1820938800","loc":[-85.5227532,41.9722429]},"n1820938801":{"id":"n1820938801","loc":[-85.4267687,42.0052836]},"n1820938803":{"id":"n1820938803","loc":[-85.0284704,42.0940837]},"n1820938804":{"id":"n1820938804","loc":[-85.015585,42.0885305]},"n1820938805":{"id":"n1820938805","loc":[-85.0765905,42.1053865]},"n1820938806":{"id":"n1820938806","loc":[-85.2614953,41.9964551]},"n1820938808":{"id":"n1820938808","loc":[-85.0307355,42.0947313]},"n1820938810":{"id":"n1820938810","loc":[-85.3894753,42.0003565]},"n1820938812":{"id":"n1820938812","loc":[-85.0868848,42.095006]},"n1820938813":{"id":"n1820938813","loc":[-85.3854198,42.0009465]},"n1820938814":{"id":"n1820938814","loc":[-85.2659692,41.9993534]},"n1820938815":{"id":"n1820938815","loc":[-85.1234259,42.0765266]},"n1820938816":{"id":"n1820938816","loc":[-85.1426906,42.0648893]},"n1820938818":{"id":"n1820938818","loc":[-85.1014533,42.0893067]},"n1820938819":{"id":"n1820938819","loc":[-85.0883064,42.098067]},"n1820938820":{"id":"n1820938820","loc":[-85.0503156,42.102704]},"n1820938821":{"id":"n1820938821","loc":[-85.1179649,42.0821884]},"n1820938822":{"id":"n1820938822","loc":[-85.3484697,41.9921596]},"n1820938823":{"id":"n1820938823","loc":[-85.3732962,41.9970874]},"n1820938824":{"id":"n1820938824","loc":[-85.2784104,41.9898312]},"n1820938825":{"id":"n1820938825","loc":[-85.4441709,42.0052198]},"n1820938826":{"id":"n1820938826","loc":[-85.3925438,42.0038326]},"n1820938829":{"id":"n1820938829","loc":[-85.5717582,41.9621861]},"n1820938830":{"id":"n1820938830","loc":[-85.0866314,42.0995051]},"n1820938831":{"id":"n1820938831","loc":[-85.576672,41.9522769]},"n1820938832":{"id":"n1820938832","loc":[-85.1587238,42.0636205]},"n1820938833":{"id":"n1820938833","loc":[-85.3804245,41.9999155]},"n1820938834":{"id":"n1820938834","loc":[-85.280083,41.9948843]},"n1820938836":{"id":"n1820938836","loc":[-85.561892,41.9686693]},"n1820938837":{"id":"n1820938837","loc":[-85.0158975,42.0885253]},"n1820938838":{"id":"n1820938838","loc":[-85.4248204,42.007633]},"n1820938839":{"id":"n1820938839","loc":[-85.0352738,42.1039657]},"n1820938840":{"id":"n1820938840","loc":[-85.211956,42.0411812]},"n1820938841":{"id":"n1820938841","loc":[-85.4816575,41.9908997]},"n1820938842":{"id":"n1820938842","loc":[-85.3807635,42.0020308]},"n1820938843":{"id":"n1820938843","loc":[-85.0100865,42.0898521]},"n1820938844":{"id":"n1820938844","loc":[-85.0103936,42.0897434]},"n1820938848":{"id":"n1820938848","loc":[-85.2430052,42.0131363]},"n1820938849":{"id":"n1820938849","loc":[-85.112559,42.0853723]},"n1820938851":{"id":"n1820938851","loc":[-85.3641553,41.9952535]},"n1820938852":{"id":"n1820938852","loc":[-85.2087373,42.0390777]},"n1820938853":{"id":"n1820938853","loc":[-85.2473933,42.0148263]},"n1820938854":{"id":"n1820938854","loc":[-85.0213464,42.090509]},"n1820938855":{"id":"n1820938855","loc":[-85.0673208,42.1052353]},"n1820938856":{"id":"n1820938856","loc":[-85.1003053,42.0905528]},"n1820938857":{"id":"n1820938857","loc":[-85.2617367,41.9965389]},"n1820938858":{"id":"n1820938858","loc":[-85.280363,41.9916015]},"n1820938859":{"id":"n1820938859","loc":[-85.0038866,42.0873469]},"n1820938860":{"id":"n1820938860","loc":[-85.2476401,42.0151451]},"n1820938861":{"id":"n1820938861","loc":[-85.193717,42.0499294]},"n1820938862":{"id":"n1820938862","loc":[-85.3478689,41.9917609]},"n1820938863":{"id":"n1820938863","loc":[-85.5638017,41.9648881]},"n1820938864":{"id":"n1820938864","loc":[-85.4356308,42.0064476]},"n1820938865":{"id":"n1820938865","loc":[-85.0561722,42.1023509]},"n1820938867":{"id":"n1820938867","loc":[-85.2256031,42.0356034]},"n1820938868":{"id":"n1820938868","loc":[-85.6102576,41.9420844]},"n1820938869":{"id":"n1820938869","loc":[-85.2285213,42.0339938]},"n1820938870":{"id":"n1820938870","loc":[-85.0326238,42.0978003]},"n1820938871":{"id":"n1820938871","loc":[-85.0131389,42.0903736]},"n1820938872":{"id":"n1820938872","loc":[-85.2550859,42.0012259]},"n1820938873":{"id":"n1820938873","loc":[-85.1130029,42.0846966]},"n1820938874":{"id":"n1820938874","loc":[-85.1579041,42.06336]},"n1820938875":{"id":"n1820938875","loc":[-85.0430522,42.1020234]},"n1820938876":{"id":"n1820938876","loc":[-85.2786679,41.9865935]},"n1820938877":{"id":"n1820938877","loc":[-85.1221666,42.0788706]},"n1820938878":{"id":"n1820938878","loc":[-85.2554614,42.0103303]},"n1820938879":{"id":"n1820938879","loc":[-85.2349801,42.0265748]},"n1820938880":{"id":"n1820938880","loc":[-85.0997434,42.0907864]},"n1820938881":{"id":"n1820938881","loc":[-85.0045464,42.0878167]},"n1820938882":{"id":"n1820938882","loc":[-85.2728048,41.9982519]},"n1820938883":{"id":"n1820938883","loc":[-85.3111333,41.9691587]},"n1820938884":{"id":"n1820938884","loc":[-85.3219802,41.9721899]},"n1820938885":{"id":"n1820938885","loc":[-85.3091378,41.9699325]},"n1820938887":{"id":"n1820938887","loc":[-85.4242367,42.0085203]},"n1820938888":{"id":"n1820938888","loc":[-84.9968377,42.0874504]},"n1820938890":{"id":"n1820938890","loc":[-85.5443139,41.9714078]},"n1820938891":{"id":"n1820938891","loc":[-85.6404013,41.9154676]},"n1820938892":{"id":"n1820938892","loc":[-85.3644986,41.9962582]},"n1820938893":{"id":"n1820938893","loc":[-85.0496772,42.1018323]},"n1820938894":{"id":"n1820938894","loc":[-85.297261,41.9737373]},"n1820938895":{"id":"n1820938895","loc":[-85.0327096,42.098071]},"n1820938896":{"id":"n1820938896","loc":[-85.3856773,41.9996867]},"n1820938897":{"id":"n1820938897","loc":[-85.0493862,42.1015509]},"n1820938898":{"id":"n1820938898","loc":[-84.9969879,42.0876614]},"n1820938899":{"id":"n1820938899","loc":[-85.0848625,42.1013587]},"n1820938900":{"id":"n1820938900","loc":[-85.5853195,41.9479201]},"n1820938901":{"id":"n1820938901","loc":[-85.6329169,41.9387964]},"n1820938902":{"id":"n1820938902","loc":[-85.0843046,42.1029468]},"n1820938903":{"id":"n1820938903","loc":[-85.1228747,42.0778474]},"n1820938904":{"id":"n1820938904","loc":[-85.4855456,41.984095]},"n1820938905":{"id":"n1820938905","loc":[-85.0573269,42.1026801]},"n1820938906":{"id":"n1820938906","loc":[-85.2425868,42.0131523]},"n1820938907":{"id":"n1820938907","loc":[-85.1149622,42.0860053]},"n1820938908":{"id":"n1820938908","loc":[-85.4833097,41.9951578]},"n1820938909":{"id":"n1820938909","loc":[-85.075979,42.1056372]},"n1820938910":{"id":"n1820938910","loc":[-85.0338509,42.0977139]},"n1820938911":{"id":"n1820938911","loc":[-85.6384272,41.9115715]},"n1820938912":{"id":"n1820938912","loc":[-85.0458363,42.1004074]},"n1820938913":{"id":"n1820938913","loc":[-85.0592138,42.1048305]},"n1820938914":{"id":"n1820938914","loc":[-85.2807493,41.9916653]},"n1820938915":{"id":"n1820938915","loc":[-85.1103274,42.0864193]},"n1820938916":{"id":"n1820938916","loc":[-85.6267156,41.9404404]},"n1820938918":{"id":"n1820938918","loc":[-85.0331374,42.0982911]},"n1820938919":{"id":"n1820938919","loc":[-85.5637331,41.965409]},"n1820938920":{"id":"n1820938920","loc":[-85.5457515,41.9714237]},"n1820938922":{"id":"n1820938922","loc":[-85.082073,42.1030104]},"n1820938923":{"id":"n1820938923","loc":[-85.0780765,42.103102]},"n1820938924":{"id":"n1820938924","loc":[-85.4208035,42.0089508]},"n1820938925":{"id":"n1820938925","loc":[-85.3469934,41.9914795]},"n1820938926":{"id":"n1820938926","loc":[-85.0322,42.095619]},"n1820938927":{"id":"n1820938927","loc":[-85.4784431,41.9949401]},"n1820938928":{"id":"n1820938928","loc":[-85.1303095,42.0667523]},"n1820938929":{"id":"n1820938929","loc":[-85.2463784,42.0084781]},"n1820938930":{"id":"n1820938930","loc":[-85.6299986,41.9427707]},"n1820938931":{"id":"n1820938931","loc":[-85.6325907,41.9238499]},"n1820938932":{"id":"n1820938932","loc":[-85.4808464,41.9914476]},"n1820938934":{"id":"n1820938934","loc":[-85.2411599,42.0105292]},"n1820938935":{"id":"n1820938935","loc":[-85.0163213,42.0892379]},"n1820938936":{"id":"n1820938936","loc":[-85.3290934,41.9682322]},"n1820938937":{"id":"n1820938937","loc":[-85.4925623,41.9853231]},"n1820938938":{"id":"n1820938938","loc":[-85.0338294,42.09892]},"n1820938940":{"id":"n1820938940","loc":[-85.4174561,42.008903]},"n1820938941":{"id":"n1820938941","loc":[-85.1165595,42.0838845]},"n1820938942":{"id":"n1820938942","loc":[-85.2954585,41.9717192]},"n1820938943":{"id":"n1820938943","loc":[-85.6330199,41.9257338]},"n1820938944":{"id":"n1820938944","loc":[-85.2294654,42.0324478]},"n1820938945":{"id":"n1820938945","loc":[-85.5601282,41.9728914]},"n1820938946":{"id":"n1820938946","loc":[-85.1176324,42.08568]},"n1820938947":{"id":"n1820938947","loc":[-85.0210245,42.0906005]},"n1820938948":{"id":"n1820938948","loc":[-85.0251887,42.09253]},"n1820938949":{"id":"n1820938949","loc":[-85.0895832,42.0939551]},"n1820938950":{"id":"n1820938950","loc":[-84.9915109,42.085842]},"n1820938951":{"id":"n1820938951","loc":[-85.2187366,42.0393486]},"n1820938952":{"id":"n1820938952","loc":[-85.006605,42.087579]},"n1820938953":{"id":"n1820938953","loc":[-85.046641,42.1012393]},"n1820938954":{"id":"n1820938954","loc":[-85.052102,42.103695]},"n1820938955":{"id":"n1820938955","loc":[-85.283925,41.9912825]},"n1820938956":{"id":"n1820938956","loc":[-85.2326626,42.0316349]},"n1820938957":{"id":"n1820938957","loc":[-85.1174298,42.0859694]},"n1820938958":{"id":"n1820938958","loc":[-85.3802056,41.9994794]},"n1820938959":{"id":"n1820938959","loc":[-85.4586334,41.9999737]},"n1820938960":{"id":"n1820938960","loc":[-85.4302234,42.0069418]},"n1820938961":{"id":"n1820938961","loc":[-85.092201,42.0930674]},"n1820938962":{"id":"n1820938962","loc":[-85.3684511,41.9979382]},"n1820938963":{"id":"n1820938963","loc":[-85.4618735,42.0011856]},"n1820938964":{"id":"n1820938964","loc":[-85.4828205,41.9877793]},"n1820938965":{"id":"n1820938965","loc":[-85.0837789,42.1025726]},"n1820938966":{"id":"n1820938966","loc":[-85.0176195,42.090253]},"n1820938967":{"id":"n1820938967","loc":[-85.3801627,42.001074]},"n1820938968":{"id":"n1820938968","loc":[-85.4767007,41.994488]},"n1820938969":{"id":"n1820938969","loc":[-85.274268,41.9957495]},"n1820938970":{"id":"n1820938970","loc":[-85.2977438,41.9719506]},"n1820938971":{"id":"n1820938971","loc":[-85.2425546,42.0208682]},"n1820938972":{"id":"n1820938972","loc":[-85.2557082,42.002382]},"n1820938973":{"id":"n1820938973","loc":[-85.3187937,41.9691986]},"n1820938975":{"id":"n1820938975","loc":[-85.2448077,42.0153045]},"n1820938977":{"id":"n1820938977","loc":[-85.0343015,42.0997718]},"n1820938978":{"id":"n1820938978","loc":[-85.2449364,42.01874]},"n1820938979":{"id":"n1820938979","loc":[-85.2598391,41.9969602]},"n1820938980":{"id":"n1820938980","loc":[-85.4294724,42.0067665]},"n1820938981":{"id":"n1820938981","loc":[-85.428082,42.0055124]},"n1820938983":{"id":"n1820938983","loc":[-85.5436315,41.9717484]},"n1820938985":{"id":"n1820938985","loc":[-85.5978336,41.9407437]},"n1820938986":{"id":"n1820938986","loc":[-85.491661,41.9860249]},"n1820938987":{"id":"n1820938987","loc":[-85.4942789,41.9801392]},"n1820938988":{"id":"n1820938988","loc":[-85.0416306,42.1010841]},"n1820938989":{"id":"n1820938989","loc":[-85.2653644,41.9984433]},"n1820938990":{"id":"n1820938990","loc":[-85.1028266,42.0881124]},"n1820938991":{"id":"n1820938991","loc":[-85.0163146,42.0887932]},"n1820938992":{"id":"n1820938992","loc":[-85.5282209,41.9678112]},"n1820938993":{"id":"n1820938993","loc":[-85.5442752,41.9715888]},"n1820938994":{"id":"n1820938994","loc":[-85.5634327,41.9658558]},"n1820938995":{"id":"n1820938995","loc":[-85.0384227,42.1037627]},"n1820938996":{"id":"n1820938996","loc":[-85.1144258,42.0854439]},"n1820938997":{"id":"n1820938997","loc":[-85.1870651,42.0506305]},"n1820938998":{"id":"n1820938998","loc":[-85.1256159,42.0747376]},"n1820938999":{"id":"n1820938999","loc":[-85.3272695,41.9715836]},"n1820939000":{"id":"n1820939000","loc":[-85.0543067,42.103098]},"n1820939001":{"id":"n1820939001","loc":[-85.4678173,41.9973585]},"n1820939003":{"id":"n1820939003","loc":[-85.0266626,42.0933154]},"n1820939004":{"id":"n1820939004","loc":[-85.0353046,42.1019728]},"n1820939005":{"id":"n1820939005","loc":[-85.1237961,42.0762798]},"n1820939006":{"id":"n1820939006","loc":[-85.2812214,41.9826702]},"n1820939007":{"id":"n1820939007","loc":[-85.2927763,41.9747343]},"n1820939008":{"id":"n1820939008","loc":[-85.3270979,41.9720862]},"n1820939009":{"id":"n1820939009","loc":[-85.488657,41.9856581]},"n1820939010":{"id":"n1820939010","loc":[-85.3087301,41.9701399]},"n1820939011":{"id":"n1820939011","loc":[-85.0276939,42.093768]},"n1820939012":{"id":"n1820939012","loc":[-85.2956516,41.9748779]},"n1820939013":{"id":"n1820939013","loc":[-85.1298579,42.0726443]},"n1820939014":{"id":"n1820939014","loc":[-85.105144,42.0870893]},"n1820939015":{"id":"n1820939015","loc":[-85.0677486,42.1053917]},"n1820939016":{"id":"n1820939016","loc":[-85.0333681,42.0993459]},"n1820939017":{"id":"n1820939017","loc":[-85.6384272,41.910805]},"n1820939018":{"id":"n1820939018","loc":[-85.399496,42.006894]},"n1820939019":{"id":"n1820939019","loc":[-85.2648427,41.9998318]},"n1820939020":{"id":"n1820939020","loc":[-85.1237424,42.0766779]},"n1820939021":{"id":"n1820939021","loc":[-85.2515025,42.0109442]},"n1820939022":{"id":"n1820939022","loc":[-85.5566306,41.9718385]},"n1820939023":{"id":"n1820939023","loc":[-85.090644,42.0938369]},"n1820939024":{"id":"n1820939024","loc":[-85.1245525,42.074914]},"n1820939025":{"id":"n1820939025","loc":[-85.1099934,42.0863926]},"n1820939026":{"id":"n1820939026","loc":[-85.1251653,42.0744589]},"n1820939027":{"id":"n1820939027","loc":[-85.401792,42.0068143]},"n1820939028":{"id":"n1820939028","loc":[-85.0094763,42.0899584]},"n1820939029":{"id":"n1820939029","loc":[-85.1330779,42.0705605]},"n1820939030":{"id":"n1820939030","loc":[-85.4935064,41.984398]},"n1820939031":{"id":"n1820939031","loc":[-85.5713334,41.9613939]},"n1820939032":{"id":"n1820939032","loc":[-85.0873945,42.0964669]},"n1820939033":{"id":"n1820939033","loc":[-85.0886497,42.0986481]},"n1820939034":{"id":"n1820939034","loc":[-85.3276343,41.9758897]},"n1820939035":{"id":"n1820939035","loc":[-85.1304386,42.0727387]},"n1820939036":{"id":"n1820939036","loc":[-85.2551932,42.0052999]},"n1820939037":{"id":"n1820939037","loc":[-85.2206936,42.0384458]},"n1820939038":{"id":"n1820939038","loc":[-85.2313645,42.0286389]},"n1820939039":{"id":"n1820939039","loc":[-85.0754586,42.1059835]},"n1820939040":{"id":"n1820939040","loc":[-85.0663324,42.1050812]},"n1820939041":{"id":"n1820939041","loc":[-85.2406234,42.0106887]},"n1820939042":{"id":"n1820939042","loc":[-85.0685962,42.1058175]},"n1820939043":{"id":"n1820939043","loc":[-85.0689462,42.1059437]},"n1820939044":{"id":"n1820939044","loc":[-85.0586144,42.1046144]},"n1820939045":{"id":"n1820939045","loc":[-85.3650565,41.9965452]},"n1820939047":{"id":"n1820939047","loc":[-85.5752558,41.9536014]},"n1820939048":{"id":"n1820939048","loc":[-85.5110159,41.9710624]},"n1820939050":{"id":"n1820939050","loc":[-85.2832641,41.9926477]},"n1820939051":{"id":"n1820939051","loc":[-85.0078402,42.0898947]},"n1820939052":{"id":"n1820939052","loc":[-85.3882737,42.0017916]},"n1820939053":{"id":"n1820939053","loc":[-85.1718945,42.0564937]},"n1820939054":{"id":"n1820939054","loc":[-85.0947048,42.0929293]},"n1820939055":{"id":"n1820939055","loc":[-85.4456944,42.0051082]},"n1820939056":{"id":"n1820939056","loc":[-85.3139872,41.9706903]},"n1820939057":{"id":"n1820939057","loc":[-85.3893895,42.0034021]},"n1820939058":{"id":"n1820939058","loc":[-85.2425332,42.0106089]},"n1820939059":{"id":"n1820939059","loc":[-85.6085624,41.9420844]},"n1820939060":{"id":"n1820939060","loc":[-85.210411,42.0397789]},"n1820939061":{"id":"n1820939061","loc":[-85.2762542,41.9960473]},"n1820939062":{"id":"n1820939062","loc":[-85.4686584,41.9969973]},"n1820939063":{"id":"n1820939063","loc":[-85.3860421,42.0018394]},"n1820939064":{"id":"n1820939064","loc":[-85.5636944,41.9644414]},"n1820939065":{"id":"n1820939065","loc":[-85.3267331,41.9766554]},"n1820939066":{"id":"n1820939066","loc":[-85.0868996,42.0943822]},"n1820939067":{"id":"n1820939067","loc":[-85.104861,42.0880038]},"n1820939068":{"id":"n1820939068","loc":[-85.5537123,41.9695093]},"n1820939069":{"id":"n1820939069","loc":[-85.6325092,41.9396743]},"n1820939070":{"id":"n1820939070","loc":[-85.3869648,42.0024454]},"n1820939071":{"id":"n1820939071","loc":[-85.2775349,41.9957335]},"n1820939072":{"id":"n1820939072","loc":[-85.2055616,42.0421533]},"n1820939073":{"id":"n1820939073","loc":[-85.4731431,41.9946531]},"n1820939074":{"id":"n1820939074","loc":[-85.0399609,42.1030833]},"n1820939075":{"id":"n1820939075","loc":[-85.3055758,41.9725169]},"n1820939076":{"id":"n1820939076","loc":[-85.4834599,41.994488]},"n1820939077":{"id":"n1820939077","loc":[-85.3819866,42.0023018]},"n1820939078":{"id":"n1820939078","loc":[-85.1218756,42.0789992]},"n1820939079":{"id":"n1820939079","loc":[-85.2793159,41.9944458]},"n1820939080":{"id":"n1820939080","loc":[-85.2495498,42.0101466]},"n1820939081":{"id":"n1820939081","loc":[-85.0035969,42.0872434]},"n1820939082":{"id":"n1820939082","loc":[-85.1054243,42.0865626]},"n1820939083":{"id":"n1820939083","loc":[-85.0917665,42.0934774]},"n1820939084":{"id":"n1820939084","loc":[-85.3442211,41.988938]},"n1820939086":{"id":"n1820939086","loc":[-85.273989,41.9953588]},"n1820939087":{"id":"n1820939087","loc":[-85.1142541,42.0852488]},"n1820939089":{"id":"n1820939089","loc":[-85.1526684,42.0615758]},"n1820939090":{"id":"n1820939090","loc":[-85.2538843,42.0110159]},"n1820939091":{"id":"n1820939091","loc":[-85.28341,41.9909635]},"n1820939092":{"id":"n1820939092","loc":[-85.3963178,42.0050217]},"n1820939093":{"id":"n1820939093","loc":[-85.0851682,42.1012472]},"n1820939095":{"id":"n1820939095","loc":[-85.2811784,41.986243]},"n1820939096":{"id":"n1820939096","loc":[-85.4274125,42.0052995]},"n1820939097":{"id":"n1820939097","loc":[-85.0871262,42.0951652]},"n1820939099":{"id":"n1820939099","loc":[-85.1314253,42.0671665]},"n1820939100":{"id":"n1820939100","loc":[-85.2778997,41.991001]},"n1820939101":{"id":"n1820939101","loc":[-85.112107,42.0862812]},"n1820939102":{"id":"n1820939102","loc":[-85.299911,41.9729955]},"n1820939103":{"id":"n1820939103","loc":[-85.639822,41.9094796]},"n1820939104":{"id":"n1820939104","loc":[-85.122294,42.0785334]},"n1820939105":{"id":"n1820939105","loc":[-85.2476294,42.015719]},"n1820939106":{"id":"n1820939106","loc":[-85.4946007,41.9814631]},"n1820939107":{"id":"n1820939107","loc":[-85.0879524,42.0986919]},"n1820939108":{"id":"n1820939108","loc":[-85.0342814,42.098274]},"n1820939109":{"id":"n1820939109","loc":[-85.2450695,42.0095463]},"n1820939110":{"id":"n1820939110","loc":[-85.3847546,42.0024135]},"n1820939111":{"id":"n1820939111","loc":[-85.2961344,41.9742558]},"n1820939112":{"id":"n1820939112","loc":[-85.27899,41.994317]},"n1820939114":{"id":"n1820939114","loc":[-85.1017644,42.0886618]},"n1820939115":{"id":"n1820939115","loc":[-85.076215,42.1056333]},"n1820939116":{"id":"n1820939116","loc":[-85.1198009,42.0805349]},"n1820939117":{"id":"n1820939117","loc":[-85.11988,42.0798513]},"n1820939118":{"id":"n1820939118","loc":[-85.147819,42.0625476]},"n1820939119":{"id":"n1820939119","loc":[-85.0585969,42.1029042]},"n1820939120":{"id":"n1820939120","loc":[-85.1248596,42.0745744]},"n1820939121":{"id":"n1820939121","loc":[-85.3023786,41.9725249]},"n1820939123":{"id":"n1820939123","loc":[-85.0119332,42.0900699]},"n1820939124":{"id":"n1820939124","loc":[-85.2466852,42.0170343]},"n1820939125":{"id":"n1820939125","loc":[-85.0033019,42.0872792]},"n1820939126":{"id":"n1820939126","loc":[-85.0042084,42.0875778]},"n1820939128":{"id":"n1820939128","loc":[-85.0052961,42.0885424]},"n1820939130":{"id":"n1820939130","loc":[-85.0647942,42.10508]},"n1820939131":{"id":"n1820939131","loc":[-85.2824123,41.9825107]},"n1820939132":{"id":"n1820939132","loc":[-85.3210039,41.9723255]},"n1820939133":{"id":"n1820939133","loc":[-85.0491033,42.1014184]},"n1820939134":{"id":"n1820939134","loc":[-85.1127776,42.0855168]},"n1820939135":{"id":"n1820939135","loc":[-85.1243768,42.0759322]},"n1820939137":{"id":"n1820939137","loc":[-85.125974,42.0747547]},"n1820939138":{"id":"n1820939138","loc":[-85.1071248,42.0859973]},"n1820939139":{"id":"n1820939139","loc":[-85.5326175,41.9674833]},"n1820939140":{"id":"n1820939140","loc":[-85.1338715,42.0660833]},"n1820939142":{"id":"n1820939142","loc":[-85.649671,41.9135675]},"n1820939144":{"id":"n1820939144","loc":[-85.0236545,42.0920444]},"n1820939145":{"id":"n1820939145","loc":[-85.1084391,42.0859376]},"n1820939146":{"id":"n1820939146","loc":[-85.1539988,42.0618626]},"n1820939147":{"id":"n1820939147","loc":[-85.2354521,42.026511]},"n1820939148":{"id":"n1820939148","loc":[-85.2362246,42.0260408]},"n1820939149":{"id":"n1820939149","loc":[-85.2401342,42.0115233]},"n1820939150":{"id":"n1820939150","loc":[-85.295319,41.9747423]},"n1820939151":{"id":"n1820939151","loc":[-85.1164696,42.0835409]},"n1820939152":{"id":"n1820939152","loc":[-85.3232891,41.9712885]},"n1820939153":{"id":"n1820939153","loc":[-85.2574463,42.0068944]},"n1820939155":{"id":"n1820939155","loc":[-85.5704064,41.9598246]},"n1820939156":{"id":"n1820939156","loc":[-85.0349077,42.0998116]},"n1820939157":{"id":"n1820939157","loc":[-85.0949529,42.0925619]},"n1820939159":{"id":"n1820939159","loc":[-85.0179829,42.0902343]},"n1820939160":{"id":"n1820939160","loc":[-85.0405832,42.1016942]},"n1820939161":{"id":"n1820939161","loc":[-85.2534015,42.0111833]},"n1820939162":{"id":"n1820939162","loc":[-85.0839881,42.102708]},"n1820939163":{"id":"n1820939163","loc":[-85.0341996,42.1008385]},"n1820939164":{"id":"n1820939164","loc":[-85.1037761,42.0879731]},"n1820939173":{"id":"n1820939173","loc":[-85.0460616,42.1005786]},"n1820939177":{"id":"n1820939177","loc":[-85.0061651,42.0878059]},"n1820939181":{"id":"n1820939181","loc":[-85.1456775,42.0654684]},"n1820939183":{"id":"n1820939183","loc":[-85.1325508,42.0718439]},"n1820939185":{"id":"n1820939185","loc":[-85.2485842,42.008329]},"n1820939187":{"id":"n1820939187","loc":[-85.2744128,41.9949322]},"n1820939189":{"id":"n1820939189","loc":[-85.2579025,41.9999542]},"n1820939191":{"id":"n1820939191","loc":[-85.3358998,41.9828987]},"n1820939193":{"id":"n1820939193","loc":[-85.3192658,41.9716714]},"n1820939194":{"id":"n1820939194","loc":[-85.6400795,41.9130725]},"n1820939195":{"id":"n1820939195","loc":[-85.3278489,41.9780591]},"n1820939196":{"id":"n1820939196","loc":[-85.2800197,41.983061]},"n1820939197":{"id":"n1820939197","loc":[-85.3278167,41.9692943]},"n1820939198":{"id":"n1820939198","loc":[-85.3366894,41.9838653]},"n1820939199":{"id":"n1820939199","loc":[-85.0328383,42.0969923]},"n1820939201":{"id":"n1820939201","loc":[-85.3259284,41.9720383]},"n1820939217":{"id":"n1820939217","loc":[-85.1840181,42.0503277]},"n1820939220":{"id":"n1820939220","loc":[-85.422563,42.0089986]},"n1820939222":{"id":"n1820939222","loc":[-85.555386,41.9707856]},"n1820939224":{"id":"n1820939224","loc":[-85.3830809,42.002254]},"n1820939226":{"id":"n1820939226","loc":[-84.9917938,42.0857517]},"n1820939227":{"id":"n1820939227","loc":[-85.2936775,41.9740484]},"n1820939228":{"id":"n1820939228","loc":[-85.2632133,41.9975024]},"n1820939229":{"id":"n1820939229","loc":[-85.2809424,41.9853259]},"n1820939230":{"id":"n1820939230","loc":[-85.242104,42.0131204]},"n1820939232":{"id":"n1820939232","loc":[-85.2610246,41.9963901]},"n1820939233":{"id":"n1820939233","loc":[-85.2335531,42.0268378]},"n1820939234":{"id":"n1820939234","loc":[-85.3188839,41.9713575]},"n1820939235":{"id":"n1820939235","loc":[-85.2413637,42.0225658]},"n1820939237":{"id":"n1820939237","loc":[-85.0010796,42.0887215]},"n1820939239":{"id":"n1820939239","loc":[-85.2241697,42.0362624]},"n1820939243":{"id":"n1820939243","loc":[-85.0368456,42.1040134]},"n1820939244":{"id":"n1820939244","loc":[-85.1327986,42.069524]},"n1820939260":{"id":"n1820939260","loc":[-85.5408163,41.9711206]},"n1820939261":{"id":"n1820939261","loc":[-85.2959199,41.9746546]},"n1820939262":{"id":"n1820939262","loc":[-85.3298659,41.9683598]},"n1820939263":{"id":"n1820939263","loc":[-85.2240581,42.0358425]},"n1820939264":{"id":"n1820939264","loc":[-85.2438206,42.0101944]},"n1820939265":{"id":"n1820939265","loc":[-85.3984489,42.0059589]},"n1820939266":{"id":"n1820939266","loc":[-85.2330811,42.0294279]},"n1820939268":{"id":"n1820939268","loc":[-85.1126877,42.0857704]},"n1820939271":{"id":"n1820939271","loc":[-85.254925,42.0106253]},"n1820939273":{"id":"n1820939273","loc":[-85.4328046,42.0064662]},"n1820939277":{"id":"n1820939277","loc":[-85.289622,41.9789616]},"n1820939279":{"id":"n1820939279","loc":[-85.4574532,42.0004043]},"n1820939281":{"id":"n1820939281","loc":[-85.4803486,41.9867211]},"n1820939283":{"id":"n1820939283","loc":[-85.157475,42.0631848]},"n1820939285":{"id":"n1820939285","loc":[-85.2571458,42.0059935]},"n1820939287":{"id":"n1820939287","loc":[-85.2818544,41.9825984]},"n1820939289":{"id":"n1820939289","loc":[-85.2298302,42.0328781]},"n1820939291":{"id":"n1820939291","loc":[-85.4819523,41.984821]},"n1820939301":{"id":"n1820939301","loc":[-85.3139765,41.9701159]},"n1820939304":{"id":"n1820939304","loc":[-85.0424447,42.101742]},"n1820939306":{"id":"n1820939306","loc":[-85.6360283,41.9338482]},"n1820939310":{"id":"n1820939310","loc":[-85.3463025,41.9913622]},"n1820939312":{"id":"n1820939312","loc":[-85.4664869,41.9988097]},"n1820939314":{"id":"n1820939314","loc":[-85.149364,42.0622449]},"n1820939316":{"id":"n1820939316","loc":[-85.2460415,42.0153125]},"n1820939318":{"id":"n1820939318","loc":[-85.4806103,41.9924523]},"n1820939320":{"id":"n1820939320","loc":[-85.2449042,42.0190987]},"n1820939322":{"id":"n1820939322","loc":[-85.5280165,41.9689263]},"n1820939324":{"id":"n1820939324","loc":[-85.0051204,42.0882625]},"n1820939326":{"id":"n1820939326","loc":[-85.1240925,42.0771546]},"n1820939329":{"id":"n1820939329","loc":[-85.2261653,42.0342225]},"n1820939331":{"id":"n1820939331","loc":[-85.5259933,41.972211]},"n1820939333":{"id":"n1820939333","loc":[-85.0074754,42.0883183]},"n1820939335":{"id":"n1820939335","loc":[-85.0764014,42.1055549]},"n1820939336":{"id":"n1820939336","loc":[-85.2908773,41.9769597]},"n1820939337":{"id":"n1820939337","loc":[-85.4095382,42.0083449]},"n1820939346":{"id":"n1820939346","loc":[-85.2514166,42.0111753]},"n1820939348":{"id":"n1820939348","loc":[-85.0030377,42.0873799]},"n1820939350":{"id":"n1820939350","loc":[-85.3659362,41.9964974]},"n1820939352":{"id":"n1820939352","loc":[-85.226058,42.0348281]},"n1820939355":{"id":"n1820939355","loc":[-85.1902408,42.0507101]},"n1820939357":{"id":"n1820939357","loc":[-85.2781854,41.9946001]},"n1820939359":{"id":"n1820939359","loc":[-85.2139988,42.0405175]},"n1820939361":{"id":"n1820939361","loc":[-85.0086609,42.0908262]},"n1820939363":{"id":"n1820939363","loc":[-85.0627128,42.1043398]},"n1820939365":{"id":"n1820939365","loc":[-85.1311346,42.072501]},"n1820939369":{"id":"n1820939369","loc":[-85.248198,42.0082652]},"n1820939370":{"id":"n1820939370","loc":[-84.99792,42.087794]},"n1820939371":{"id":"n1820939371","loc":[-85.2786775,41.9942783]},"n1820939372":{"id":"n1820939372","loc":[-85.0342103,42.1013957]},"n1820939373":{"id":"n1820939373","loc":[-85.2022357,42.0444799]},"n1820939374":{"id":"n1820939374","loc":[-85.2279205,42.0337388]},"n1820939375":{"id":"n1820939375","loc":[-85.1337699,42.0712614]},"n1820939376":{"id":"n1820939376","loc":[-85.317517,41.9707062]},"n1820939377":{"id":"n1820939377","loc":[-85.1326326,42.070218]},"n1820939394":{"id":"n1820939394","loc":[-85.0197746,42.0899118]},"n1820939397":{"id":"n1820939397","loc":[-85.2590076,41.9984632]},"n1820939399":{"id":"n1820939399","loc":[-85.2469964,42.0083449]},"n1820939400":{"id":"n1820939400","loc":[-85.2470929,42.0146668]},"n1820939401":{"id":"n1820939401","loc":[-84.9984095,42.0878087]},"n1820939402":{"id":"n1820939402","loc":[-85.2372653,42.0243273]},"n1820939403":{"id":"n1820939403","loc":[-85.2454986,42.0091955]},"n1820939404":{"id":"n1820939404","loc":[-85.0539205,42.1035995]},"n1820939405":{"id":"n1820939405","loc":[-85.550601,41.9706101]},"n1820939406":{"id":"n1820939406","loc":[-85.0351343,42.0999656]},"n1820939407":{"id":"n1820939407","loc":[-85.0082908,42.0905755]},"n1820939408":{"id":"n1820939408","loc":[-85.0132904,42.0902251]},"n1820939410":{"id":"n1820939410","loc":[-85.0892546,42.094012]},"n1820939412":{"id":"n1820939412","loc":[-85.0350793,42.1030315]},"n1820939416":{"id":"n1820939416","loc":[-85.0012406,42.0886777]},"n1820939418":{"id":"n1820939418","loc":[-85.0577453,42.1029229]},"n1820939420":{"id":"n1820939420","loc":[-85.1230786,42.0776722]},"n1820939422":{"id":"n1820939422","loc":[-85.571136,41.9649304]},"n1820939436":{"id":"n1820939436","loc":[-85.1137968,42.0848997]},"n1820939437":{"id":"n1820939437","loc":[-85.3559584,41.9925105]},"n1820939438":{"id":"n1820939438","loc":[-85.0080172,42.0903565]},"n1820939439":{"id":"n1820939439","loc":[-85.0048897,42.0880913]},"n1820939441":{"id":"n1820939441","loc":[-85.0406959,42.1018574]},"n1820939443":{"id":"n1820939443","loc":[-85.3897328,42.0029078]},"n1820939445":{"id":"n1820939445","loc":[-85.122349,42.0782814]},"n1820939448":{"id":"n1820939448","loc":[-85.4872193,41.985036]},"n1820939450":{"id":"n1820939450","loc":[-85.0120459,42.0904919]},"n1820939452":{"id":"n1820939452","loc":[-85.6320543,41.921982]},"n1820939456":{"id":"n1820939456","loc":[-85.0844749,42.1036843]},"n1820939458":{"id":"n1820939458","loc":[-85.0968037,42.091296]},"n1820939463":{"id":"n1820939463","loc":[-85.5339747,41.9681841]},"n1820939465":{"id":"n1820939465","loc":[-85.4125423,42.0072129]},"n1820939467":{"id":"n1820939467","loc":[-85.6335563,41.9303626]},"n1820939469":{"id":"n1820939469","loc":[-85.2821014,41.9932126]},"n1820939471":{"id":"n1820939471","loc":[-85.374691,41.9969917]},"n1820939485":{"id":"n1820939485","loc":[-85.4471321,42.0049806]},"n1820939487":{"id":"n1820939487","loc":[-85.3752532,41.9972206]},"n1820939489":{"id":"n1820939489","loc":[-85.4517283,42.005927]},"n1820939492":{"id":"n1820939492","loc":[-85.4662552,42.0005693]},"n1820939494":{"id":"n1820939494","loc":[-85.0120083,42.0902928]},"n1820939496":{"id":"n1820939496","loc":[-85.044463,42.1004631]},"n1820939498":{"id":"n1820939498","loc":[-85.418293,42.0089667]},"n1820939500":{"id":"n1820939500","loc":[-85.0554762,42.1027358]},"n1820939504":{"id":"n1820939504","loc":[-85.1246289,42.0746858]},"n1820939507":{"id":"n1820939507","loc":[-85.0408139,42.1021838]},"n1820939508":{"id":"n1820939508","loc":[-85.1236204,42.0775169]},"n1820939509":{"id":"n1820939509","loc":[-85.0350109,42.1037428]},"n1820939510":{"id":"n1820939510","loc":[-85.0551583,42.1029878]},"n1820939511":{"id":"n1820939511","loc":[-85.0956771,42.0916662]},"n1820939512":{"id":"n1820939512","loc":[-85.2323408,42.0273638]},"n1820939513":{"id":"n1820939513","loc":[-85.1232771,42.0762388]},"n1820939531":{"id":"n1820939531","loc":[-85.264608,41.9997828]},"n1820939533":{"id":"n1820939533","loc":[-85.4198808,42.0087914]},"n1820939535":{"id":"n1820939535","loc":[-85.3080864,41.9715677]},"n1820939536":{"id":"n1820939536","loc":[-85.1189426,42.0812596]},"n1820939537":{"id":"n1820939537","loc":[-85.2642741,41.9996764]},"n1820939538":{"id":"n1820939538","loc":[-85.2572531,42.0079627]},"n1820939539":{"id":"n1820939539","loc":[-85.2907807,41.9790174]},"n1820939540":{"id":"n1820939540","loc":[-85.3171415,41.9707301]},"n1820939541":{"id":"n1820939541","loc":[-85.08777,42.0953841]},"n1820939542":{"id":"n1820939542","loc":[-85.1239262,42.0773218]},"n1820939543":{"id":"n1820939543","loc":[-84.9973956,42.0877968]},"n1820939544":{"id":"n1820939544","loc":[-85.011606,42.0896161]},"n1820939545":{"id":"n1820939545","loc":[-85.4077358,42.0082971]},"n1820939546":{"id":"n1820939546","loc":[-85.3614945,41.9933717]},"n1820939547":{"id":"n1820939547","loc":[-85.3189118,41.9697649]},"n1820939550":{"id":"n1820939550","loc":[-85.1262691,42.0740221]},"n1820939551":{"id":"n1820939551","loc":[-85.3863639,41.9994635]},"n1820939552":{"id":"n1820939552","loc":[-85.2836034,41.9923953]},"n1820939554":{"id":"n1820939554","loc":[-85.3222377,41.9715916]},"n1820939555":{"id":"n1820939555","loc":[-85.0122658,42.0906312]},"n1820939556":{"id":"n1820939556","loc":[-85.0022652,42.0877581]},"n1820939557":{"id":"n1820939557","loc":[-85.1011314,42.0899954]},"n1820939559":{"id":"n1820939559","loc":[-85.0008181,42.0885293]},"n1820939561":{"id":"n1820939561","loc":[-85.3637046,41.9942488]},"n1820939562":{"id":"n1820939562","loc":[-85.4500117,42.0052892]},"n1820939563":{"id":"n1820939563","loc":[-85.0537636,42.1036365]},"n1820939565":{"id":"n1820939565","loc":[-85.2367503,42.0246939]},"n1820939566":{"id":"n1820939566","loc":[-85.0448479,42.1002653]},"n1820939567":{"id":"n1820939567","loc":[-85.6337065,41.9295006]},"n1820939568":{"id":"n1820939568","loc":[-85.0879792,42.095623]},"n1820939569":{"id":"n1820939569","loc":[-85.6347623,41.9352369]},"n1820939570":{"id":"n1820939570","loc":[-85.1497931,42.0620378]},"n1820939571":{"id":"n1820939571","loc":[-85.5676169,41.9656324]},"n1820939572":{"id":"n1820939572","loc":[-85.638041,41.9166971]},"n1820939573":{"id":"n1820939573","loc":[-85.4993429,41.9781293]},"n1820939574":{"id":"n1820939574","loc":[-85.5352831,41.9692127]},"n1820939575":{"id":"n1820939575","loc":[-84.9924429,42.0857118]},"n1820939577":{"id":"n1820939577","loc":[-85.0581101,42.1026721]},"n1820939578":{"id":"n1820939578","loc":[-85.641088,41.9094477]},"n1820939579":{"id":"n1820939579","loc":[-85.2548821,42.0052282]},"n1820939580":{"id":"n1820939580","loc":[-85.1124463,42.0859734]},"n1820939581":{"id":"n1820939581","loc":[-85.1083479,42.0857624]},"n1820939583":{"id":"n1820939583","loc":[-85.1387424,42.0648893]},"n1820939584":{"id":"n1820939584","loc":[-85.5152645,41.9700892]},"n1820939585":{"id":"n1820939585","loc":[-85.5463738,41.9713439]},"n1820939586":{"id":"n1820939586","loc":[-85.360207,41.9933717]},"n1820939587":{"id":"n1820939587","loc":[-85.2402372,42.0120917]},"n1820939588":{"id":"n1820939588","loc":[-85.3936381,42.0047255]},"n1820939589":{"id":"n1820939589","loc":[-85.3310246,41.973784]},"n1820939590":{"id":"n1820939590","loc":[-85.0329403,42.096642]},"n1820939591":{"id":"n1820939591","loc":[-85.0097271,42.0910981]},"n1820939593":{"id":"n1820939593","loc":[-85.0446562,42.1003437]},"n1820939595":{"id":"n1820939595","loc":[-85.0856671,42.1008452]},"n1820939596":{"id":"n1820939596","loc":[-85.4087228,42.0083449]},"n1820939597":{"id":"n1820939597","loc":[-85.0609519,42.1052564]},"n1820939598":{"id":"n1820939598","loc":[-85.3432126,41.9874548]},"n1820939599":{"id":"n1820939599","loc":[-85.4041738,42.0067027]},"n1820939600":{"id":"n1820939600","loc":[-85.0825437,42.1035768]},"n1820939601":{"id":"n1820939601","loc":[-85.048422,42.101498]},"n1820939602":{"id":"n1820939602","loc":[-85.0336256,42.0999031]},"n1820939603":{"id":"n1820939603","loc":[-85.046818,42.1014104]},"n1820939605":{"id":"n1820939605","loc":[-85.2856524,41.98078]},"n1820939607":{"id":"n1820939607","loc":[-85.1118173,42.0864245]},"n1820939609":{"id":"n1820939609","loc":[-85.0443397,42.1006263]},"n1820939610":{"id":"n1820939610","loc":[-85.0336698,42.0978361]},"n1820939611":{"id":"n1820939611","loc":[-85.4630322,42.0014248]},"n1820939612":{"id":"n1820939612","loc":[-85.0613127,42.1052353]},"n1820939613":{"id":"n1820939613","loc":[-85.0137571,42.0887801]},"n1820939614":{"id":"n1820939614","loc":[-85.272487,41.9982013]},"n1820939616":{"id":"n1820939616","loc":[-85.4665727,41.9983791]},"n1820939617":{"id":"n1820939617","loc":[-85.1288078,42.0725476]},"n1820939618":{"id":"n1820939618","loc":[-85.4653282,42.00109]},"n1820939619":{"id":"n1820939619","loc":[-85.2314717,42.0276746]},"n1820939620":{"id":"n1820939620","loc":[-85.255982,42.0003569]},"n1820939621":{"id":"n1820939621","loc":[-85.2886779,41.9787223]},"n1820939622":{"id":"n1820939622","loc":[-85.22438,42.0367509]},"n1820939623":{"id":"n1820939623","loc":[-85.0334713,42.0998382]},"n1820939624":{"id":"n1820939624","loc":[-85.2236504,42.037484]},"n1820939625":{"id":"n1820939625","loc":[-85.636908,41.9175162]},"n1820939627":{"id":"n1820939627","loc":[-85.2669187,41.9989707]},"n1820939628":{"id":"n1820939628","loc":[-85.3247268,41.9720702]},"n1820939629":{"id":"n1820939629","loc":[-85.3785104,41.9987299]},"n1820939630":{"id":"n1820939630","loc":[-85.5267658,41.9720515]},"n1820939631":{"id":"n1820939631","loc":[-85.2445116,42.0098811]},"n1820939632":{"id":"n1820939632","loc":[-85.1271448,42.0725077]},"n1820939633":{"id":"n1820939633","loc":[-85.0345751,42.099724]},"n1820939634":{"id":"n1820939634","loc":[-85.4217476,42.0089986]},"n1820939635":{"id":"n1820939635","loc":[-85.3121848,41.9689433]},"n1820939636":{"id":"n1820939636","loc":[-85.2826419,41.9929985]},"n1820939637":{"id":"n1820939637","loc":[-85.3160257,41.9706344]},"n1820939638":{"id":"n1820939638","loc":[-85.5684967,41.9657919]},"n1820939640":{"id":"n1820939640","loc":[-85.225131,42.0356194]},"n1820939642":{"id":"n1820939642","loc":[-85.1324124,42.0693328]},"n1820939644":{"id":"n1820939644","loc":[-84.9994073,42.0878843]},"n1820939645":{"id":"n1820939645","loc":[-85.1087596,42.0863329]},"n1820939646":{"id":"n1820939646","loc":[-85.2915532,41.9782996]},"n1820939647":{"id":"n1820939647","loc":[-84.9988708,42.0877808]},"n1820939648":{"id":"n1820939648","loc":[-85.2243628,42.0356728]},"n1820939649":{"id":"n1820939649","loc":[-85.0427397,42.1020524]},"n1820939650":{"id":"n1820939650","loc":[-85.6388392,41.9100752]},"n1820939651":{"id":"n1820939651","loc":[-85.0133709,42.0888557]},"n1820939652":{"id":"n1820939652","loc":[-85.318798,41.9701211]},"n1820939653":{"id":"n1820939653","loc":[-85.6335778,41.9190602]},"n1820939654":{"id":"n1820939654","loc":[-85.6338396,41.9370247]},"n1820939655":{"id":"n1820939655","loc":[-85.0939069,42.0931988]},"n1820939656":{"id":"n1820939656","loc":[-85.5702347,41.9651378]},"n1820939657":{"id":"n1820939657","loc":[-85.4235286,42.0088392]},"n1820939658":{"id":"n1820939658","loc":[-85.2740856,41.9972206]},"n1820939659":{"id":"n1820939659","loc":[-85.4824299,41.9934195]},"n1820939660":{"id":"n1820939660","loc":[-85.3857846,42.0014408]},"n1820939661":{"id":"n1820939661","loc":[-85.0451658,42.10028]},"n1820939662":{"id":"n1820939662","loc":[-85.3893036,42.001377]},"n1820939664":{"id":"n1820939664","loc":[-85.2455845,42.0088607]},"n1820939665":{"id":"n1820939665","loc":[-85.2741071,41.9951116]},"n1820939666":{"id":"n1820939666","loc":[-85.1298375,42.0677718]},"n1820939667":{"id":"n1820939667","loc":[-85.5491848,41.9707377]},"n1820939669":{"id":"n1820939669","loc":[-85.2780298,41.995238]},"n1820939670":{"id":"n1820939670","loc":[-85.1330068,42.0716926]},"n1820939671":{"id":"n1820939671","loc":[-85.0811342,42.1025129]},"n1820939672":{"id":"n1820939672","loc":[-85.2325124,42.0290135]},"n1820939673":{"id":"n1820939673","loc":[-85.2975077,41.9716953]},"n1820939674":{"id":"n1820939674","loc":[-85.0951729,42.0922394]},"n1820939676":{"id":"n1820939676","loc":[-85.0363252,42.1043119]},"n1820939677":{"id":"n1820939677","loc":[-85.2960057,41.97349]},"n1820939678":{"id":"n1820939678","loc":[-85.3701849,41.9982515]},"n1820939679":{"id":"n1820939679","loc":[-85.3381486,41.9848861]},"n1820939680":{"id":"n1820939680","loc":[-85.2058448,42.0417286]},"n1820939682":{"id":"n1820939682","loc":[-85.0819335,42.1034443]},"n1820939683":{"id":"n1820939683","loc":[-85.3872223,41.9993359]},"n1820939684":{"id":"n1820939684","loc":[-85.095366,42.091909]},"n1820939685":{"id":"n1820939685","loc":[-85.2327914,42.0291888]},"n1820939686":{"id":"n1820939686","loc":[-85.0433459,42.1018773]},"n1820939687":{"id":"n1820939687","loc":[-85.0585339,42.1027318]},"n1820939688":{"id":"n1820939688","loc":[-85.0062885,42.0876347]},"n1820939689":{"id":"n1820939689","loc":[-85.246299,42.017377]},"n1820939690":{"id":"n1820939690","loc":[-85.2932376,41.9742877]},"n1820939691":{"id":"n1820939691","loc":[-85.2962846,41.9736815]},"n1820939692":{"id":"n1820939692","loc":[-85.6052365,41.9409193]},"n1820939693":{"id":"n1820939693","loc":[-85.2570536,42.0003341]},"n1820939694":{"id":"n1820939694","loc":[-85.0488458,42.1014064]},"n1820939695":{"id":"n1820939695","loc":[-85.4050321,42.0069578]},"n1820939696":{"id":"n1820939696","loc":[-85.4847517,41.9845894]},"n1820939697":{"id":"n1820939697","loc":[-85.0844655,42.1013826]},"n1820939698":{"id":"n1820939698","loc":[-85.1437206,42.0650008]},"n1820939699":{"id":"n1820939699","loc":[-85.1168183,42.0864034]},"n1820939700":{"id":"n1820939700","loc":[-85.5479831,41.9711366]},"n1820939701":{"id":"n1820939701","loc":[-85.0349948,42.1034124]},"n1820939702":{"id":"n1820939702","loc":[-85.0835589,42.1038821]},"n1820939703":{"id":"n1820939703","loc":[-85.0203875,42.0902649]},"n1820939704":{"id":"n1820939704","loc":[-85.0371191,42.1038184]},"n1820939705":{"id":"n1820939705","loc":[-85.1273312,42.0735681]},"n1820939707":{"id":"n1820939707","loc":[-85.1272239,42.0730226]},"n1820939710":{"id":"n1820939710","loc":[-85.0349881,42.1019012]},"n1820939712":{"id":"n1820939712","loc":[-85.2440459,42.0178313]},"n1820939713":{"id":"n1820939713","loc":[-85.2444751,42.0182618]},"n1820939714":{"id":"n1820939714","loc":[-85.0539996,42.1032863]},"n1820939715":{"id":"n1820939715","loc":[-85.2215905,42.0373246]},"n1820939716":{"id":"n1820939716","loc":[-85.0649712,42.1051994]},"n1820939717":{"id":"n1820939717","loc":[-85.0927146,42.0927581]},"n1820939718":{"id":"n1820939718","loc":[-85.3884668,42.0042312]},"n1820939719":{"id":"n1820939719","loc":[-85.0840672,42.1013241]},"n1820939720":{"id":"n1820939720","loc":[-85.304739,41.9725408]},"n1820939721":{"id":"n1820939721","loc":[-85.2243585,42.0371334]},"n1820939722":{"id":"n1820939722","loc":[-85.0599823,42.1049686]},"n1820939723":{"id":"n1820939723","loc":[-85.0298825,42.0944288]},"n1820939724":{"id":"n1820939724","loc":[-85.0366095,42.1042443]},"n1820939725":{"id":"n1820939725","loc":[-85.0698783,42.1058135]},"n1820939726":{"id":"n1820939726","loc":[-85.1054551,42.0873361]},"n1820939727":{"id":"n1820939727","loc":[-84.9952324,42.0864285]},"n1820939728":{"id":"n1820939728","loc":[-85.3442211,41.9897993]},"n1820939729":{"id":"n1820939729","loc":[-85.4386134,42.0056822]},"n1820939730":{"id":"n1820939730","loc":[-85.2438528,42.0146589]},"n1820939731":{"id":"n1820939731","loc":[-85.0355581,42.1041846]},"n1820939732":{"id":"n1820939732","loc":[-85.557682,41.9724447]},"n1820939734":{"id":"n1820939734","loc":[-85.2299418,42.033314]},"n1820939735":{"id":"n1820939735","loc":[-85.6297412,41.9419088]},"n1820939736":{"id":"n1820939736","loc":[-85.2645101,41.9980259]},"n1820939738":{"id":"n1820939738","loc":[-85.082195,42.1035649]},"n1820939739":{"id":"n1820939739","loc":[-85.234272,42.0267102]},"n1820939740":{"id":"n1820939740","loc":[-85.0130758,42.0895006]},"n1820939741":{"id":"n1820939741","loc":[-85.4594702,42.0000375]},"n1820939742":{"id":"n1820939742","loc":[-84.9946745,42.0863687]},"n1820939743":{"id":"n1820939743","loc":[-85.6438775,41.9120186]},"n1820939744":{"id":"n1820939744","loc":[-85.6372685,41.9168089]},"n1820939745":{"id":"n1820939745","loc":[-85.2789468,41.9893208]},"n1820939747":{"id":"n1820939747","loc":[-85.3775019,41.998427]},"n1820939749":{"id":"n1820939749","loc":[-85.0993571,42.0909178]},"n1820939750":{"id":"n1820939750","loc":[-85.1308503,42.0669339]},"n1820939751":{"id":"n1820939751","loc":[-85.4802566,41.9856659]},"n1820939752":{"id":"n1820939752","loc":[-85.2543563,42.0108804]},"n1820939753":{"id":"n1820939753","loc":[-85.1041033,42.0878815]},"n1820939755":{"id":"n1820939755","loc":[-85.4000969,42.0071651]},"n1820939757":{"id":"n1820939757","loc":[-85.3858275,42.0022381]},"n1820939758":{"id":"n1820939758","loc":[-85.3653998,41.996609]},"n1820939759":{"id":"n1820939759","loc":[-85.2432949,42.0202305]},"n1820939760":{"id":"n1820939760","loc":[-85.3878874,42.0042472]},"n1820939761":{"id":"n1820939761","loc":[-85.2516741,42.0114145]},"n1820939762":{"id":"n1820939762","loc":[-85.2788825,41.9865142]},"n1820939763":{"id":"n1820939763","loc":[-85.0009147,42.0886686]},"n1820939764":{"id":"n1820939764","loc":[-85.3918142,42.003434]},"n1820939765":{"id":"n1820939765","loc":[-85.5532832,41.9696848]},"n1820939766":{"id":"n1820939766","loc":[-85.5545063,41.969254]},"n1820939768":{"id":"n1820939768","loc":[-85.1327989,42.0704769]},"n1820939770":{"id":"n1820939770","loc":[-85.0588558,42.1047696]},"n1820939772":{"id":"n1820939772","loc":[-85.555798,41.9713017]},"n1820939773":{"id":"n1820939773","loc":[-85.0565853,42.1023589]},"n1820939774":{"id":"n1820939774","loc":[-85.2582941,41.9992765]},"n1820939775":{"id":"n1820939775","loc":[-85.3007264,41.9727642]},"n1820939776":{"id":"n1820939776","loc":[-85.2477045,42.0082652]},"n1820939777":{"id":"n1820939777","loc":[-85.2415247,42.0104973]},"n1821006698":{"id":"n1821006698","loc":[-85.6345227,41.9382009]},"n1821006700":{"id":"n1821006700","loc":[-85.6344894,41.938975]},"n1821006704":{"id":"n1821006704","loc":[-85.6351181,41.9370157]},"n1821006706":{"id":"n1821006706","loc":[-85.6357554,41.9361657]},"n1821006708":{"id":"n1821006708","loc":[-85.6351235,41.9368481]},"n1821006710":{"id":"n1821006710","loc":[-85.6352844,41.9364211]},"n1821006712":{"id":"n1821006712","loc":[-85.6351503,41.937307]},"n1821006716":{"id":"n1821006716","loc":[-85.6350366,41.9379774]},"n1821006725":{"id":"n1821006725","loc":[-85.6352147,41.9375903]},"n1821137607":{"id":"n1821137607","loc":[-85.5297057,41.9669915]},"n1821137608":{"id":"n1821137608","loc":[-85.5288598,41.9673094]},"n1821139530":{"id":"n1821139530","loc":[-85.4832228,41.9881686]},"n1821139531":{"id":"n1821139531","loc":[-85.4812101,41.9851258]},"n1821139532":{"id":"n1821139532","loc":[-85.4799127,41.9860244]},"n1821139533":{"id":"n1821139533","loc":[-85.4800313,41.9865555]},"n1841425201":{"id":"n1841425201","loc":[-85.4334577,42.0063713]},"n1841425222":{"id":"n1841425222","loc":[-85.4382449,42.0055785]},"n1914861007":{"id":"n1914861007","loc":[-85.394959,42.0057472]},"n1914861057":{"id":"n1914861057","loc":[-85.3967185,42.0049695]},"n1914861112":{"id":"n1914861112","loc":[-85.394179,42.0056906]},"n1914861306":{"id":"n1914861306","loc":[-85.3900226,42.0028488]},"n2114807565":{"id":"n2114807565","loc":[-85.6385979,41.9577824]},"n2114807568":{"id":"n2114807568","loc":[-85.6325097,41.9775713]},"n2114807572":{"id":"n2114807572","loc":[-85.6328996,41.9980965]},"n2114807578":{"id":"n2114807578","loc":[-85.6344818,41.9696956]},"n2114807583":{"id":"n2114807583","loc":[-85.6326289,41.9757853]},"n2114807593":{"id":"n2114807593","loc":[-85.6360828,41.9650674]},"n2130304159":{"id":"n2130304159","loc":[-85.6352537,41.9450015],"tags":{"railway":"level_crossing"}},"n2139795852":{"id":"n2139795852","loc":[-85.6374708,41.9311633]},"n2139858882":{"id":"n2139858882","loc":[-85.635178,41.9356158]},"n2139858883":{"id":"n2139858883","loc":[-85.63533,41.9355886]},"n2139858884":{"id":"n2139858884","loc":[-85.6353819,41.93556]},"n2139858885":{"id":"n2139858885","loc":[-85.6353665,41.9355157]},"n2139858886":{"id":"n2139858886","loc":[-85.6353165,41.9354971]},"n2139858887":{"id":"n2139858887","loc":[-85.6352454,41.9355328]},"n2139858888":{"id":"n2139858888","loc":[-85.6350184,41.9357846]},"n2139858889":{"id":"n2139858889","loc":[-85.634978,41.9359448]},"n2139858890":{"id":"n2139858890","loc":[-85.6347723,41.9361523]},"n2139858891":{"id":"n2139858891","loc":[-85.6347165,41.9362667]},"n2139858892":{"id":"n2139858892","loc":[-85.6346992,41.9364312]},"n2139858893":{"id":"n2139858893","loc":[-85.634603,41.9366329]},"n2139858894":{"id":"n2139858894","loc":[-85.6345973,41.9367488]},"n2139858895":{"id":"n2139858895","loc":[-85.6345127,41.9369734]},"n2139858896":{"id":"n2139858896","loc":[-85.634478,41.9371923]},"n2139858897":{"id":"n2139858897","loc":[-85.6344838,41.9373768]},"n2139858898":{"id":"n2139858898","loc":[-85.6346242,41.9375299]},"n2139858899":{"id":"n2139858899","loc":[-85.6347723,41.9376357]},"n2139858900":{"id":"n2139858900","loc":[-85.6347607,41.9377788]},"n2139858901":{"id":"n2139858901","loc":[-85.6346204,41.9379533]},"n2139858902":{"id":"n2139858902","loc":[-85.6344184,41.9380105]},"n2139858903":{"id":"n2139858903","loc":[-85.6341627,41.9380406]},"n2139858904":{"id":"n2139858904","loc":[-85.634005,41.9381679]},"n2139858905":{"id":"n2139858905","loc":[-85.63393,41.9383353]},"n2139858906":{"id":"n2139858906","loc":[-85.6338588,41.9384597]},"n2139858907":{"id":"n2139858907","loc":[-85.6336627,41.9387759]},"n2139858908":{"id":"n2139858908","loc":[-85.6335127,41.9389361]},"n2139858933":{"id":"n2139858933","loc":[-85.6353118,41.9432646]},"n2139858934":{"id":"n2139858934","loc":[-85.6353952,41.9433002]},"n2139858935":{"id":"n2139858935","loc":[-85.6356496,41.9433255]},"n2139858936":{"id":"n2139858936","loc":[-85.6363128,41.9433373]},"n2139858937":{"id":"n2139858937","loc":[-85.6365467,41.9433779]},"n2139858938":{"id":"n2139858938","loc":[-85.6368692,41.9435265]},"n2139858939":{"id":"n2139858939","loc":[-85.6370986,41.9437039]},"n2139858940":{"id":"n2139858940","loc":[-85.6372371,41.9437732]},"n2139858941":{"id":"n2139858941","loc":[-85.6374756,41.9438171]},"n2139858942":{"id":"n2139858942","loc":[-85.6376164,41.9439286]},"n2139858943":{"id":"n2139858943","loc":[-85.6377504,41.944138]},"n2139858944":{"id":"n2139858944","loc":[-85.6384204,41.9443137]},"n2139858945":{"id":"n2139858945","loc":[-85.6385726,41.9444506]},"n2139858946":{"id":"n2139858946","loc":[-85.638702,41.9445739]},"n2139858947":{"id":"n2139858947","loc":[-85.6387179,41.9446516]},"n2139858948":{"id":"n2139858948","loc":[-85.6387088,41.9447985]},"n2139858949":{"id":"n2139858949","loc":[-85.6387656,41.9449877]},"n2139858950":{"id":"n2139858950","loc":[-85.638777,41.9451448]},"n2139858951":{"id":"n2139858951","loc":[-85.6387088,41.9452631]},"n2139858964":{"id":"n2139858964","loc":[-85.6383346,41.9442912]},"n2139858966":{"id":"n2139858966","loc":[-85.6384724,41.9443605]},"n2139858967":{"id":"n2139858967","loc":[-85.6354078,41.9434285]},"n2139858968":{"id":"n2139858968","loc":[-85.635271,41.943654]},"n2139858969":{"id":"n2139858969","loc":[-85.6352657,41.9437437]},"n2139858970":{"id":"n2139858970","loc":[-85.635271,41.9438195]},"n2139858971":{"id":"n2139858971","loc":[-85.6351563,41.9438906]},"n2139858972":{"id":"n2139858972","loc":[-85.6351384,41.9438882]},"n2139858973":{"id":"n2139858973","loc":[-85.6351514,41.9438034]},"n2139858974":{"id":"n2139858974","loc":[-85.6351237,41.9436641]},"n2139858975":{"id":"n2139858975","loc":[-85.6351498,41.9436108]},"n2139858976":{"id":"n2139858976","loc":[-85.6351058,41.9435345]},"n2139858977":{"id":"n2139858977","loc":[-85.6349641,41.9432051]},"n2139858986":{"id":"n2139858986","loc":[-85.6341205,41.9380746]},"n2139858990":{"id":"n2139858990","loc":[-85.6345671,41.9381816]},"n2139858995":{"id":"n2139858995","loc":[-85.6339783,41.9382273]},"n2139859003":{"id":"n2139859003","loc":[-85.6340477,41.9373489]},"n2139859004":{"id":"n2139859004","loc":[-85.6339784,41.9374752]},"n2139870406":{"id":"n2139870406","loc":[-85.6342265,41.9432605]},"n2139877106":{"id":"n2139877106","loc":[-85.6346323,41.9438746]},"n2139982399":{"id":"n2139982399","loc":[-85.6324055,41.9408537]},"n2139982400":{"id":"n2139982400","loc":[-85.632488,41.941063],"tags":{"leisure":"slipway"}},"n2139982401":{"id":"n2139982401","loc":[-85.6327261,41.9415366]},"n2139982402":{"id":"n2139982402","loc":[-85.6326391,41.9413598]},"n2139982403":{"id":"n2139982403","loc":[-85.6327041,41.9414391]},"n2139982405":{"id":"n2139982405","loc":[-85.6322891,41.9406009]},"n2139982406":{"id":"n2139982406","loc":[-85.6325412,41.9425257]},"n2139989333":{"id":"n2139989333","loc":[-85.6340584,41.9431731]},"n2140006331":{"id":"n2140006331","loc":[-85.6361751,41.9459744]},"n2140006334":{"id":"n2140006334","loc":[-85.636528,41.9459751]},"n2140006336":{"id":"n2140006336","loc":[-85.6370918,41.9458926]},"n2140006338":{"id":"n2140006338","loc":[-85.6378806,41.9456474]},"n2140006340":{"id":"n2140006340","loc":[-85.6385831,41.9454343]},"n2140006342":{"id":"n2140006342","loc":[-85.639341,41.945157]},"n2140006344":{"id":"n2140006344","loc":[-85.6393497,41.9450232]},"n2140006346":{"id":"n2140006346","loc":[-85.6388245,41.9450145]},"n2140006348":{"id":"n2140006348","loc":[-85.6388167,41.9441739]},"n2140006351":{"id":"n2140006351","loc":[-85.6382915,41.9441797]},"n2140006353":{"id":"n2140006353","loc":[-85.63828,41.9438109]},"n2140006355":{"id":"n2140006355","loc":[-85.6381949,41.9436009]},"n2140006357":{"id":"n2140006357","loc":[-85.6371904,41.9435918]},"n2140006359":{"id":"n2140006359","loc":[-85.6366966,41.9432727]},"n2140006361":{"id":"n2140006361","loc":[-85.6353755,41.9432744]},"n2140006365":{"id":"n2140006365","loc":[-85.6350906,41.9435472]},"n2140006366":{"id":"n2140006366","loc":[-85.6343461,41.9441573]},"n2140006395":{"id":"n2140006395","loc":[-85.6351171,41.9437175]},"n2140006397":{"id":"n2140006397","loc":[-85.635352,41.9450206]},"n2140006399":{"id":"n2140006399","loc":[-85.6358194,41.9454937]},"n2140006401":{"id":"n2140006401","loc":[-85.6348693,41.9445739]},"n2140006431":{"id":"n2140006431","loc":[-85.6376737,41.9438023]},"n2140006437":{"id":"n2140006437","loc":[-85.6382631,41.9442724]},"n2189123379":{"id":"n2189123379","loc":[-85.6342671,41.9352665]},"w203974076":{"id":"w203974076","tags":{"highway":"footway"},"nodes":["n2139870442","n2139870457","n2139870458","n2139870459","n2139870460","n2139870452"]},"w170989131":{"id":"w170989131","tags":{"name":"St Joseph River","waterway":"river"},"nodes":["n1820938225","n1820938712","n1820937596","n1820937574","n1820938515","n1820938330","n1820938678","n1820938240","n1820938950","n1820939226","n1820939575","n1820937913","n1820938223","n1820937668","n1820938545","n1820937584","n1820939742","n1820939727","n1820937578","n1820938149","n1820938124","n1820938888","n1820938898","n1820937922","n1820939543","n1820939370","n1820939401","n1820939647","n1820938345","n1820939644","n1820938333","n1820938370","n1820938624","n1820938493","n1820939559","n1820939763","n1820939237","n1820939416","n1820937810","n1820938317","n1820938324","n1820937558","n1820939556","n1820938298","n1820939348","n1820939125","n1820939081","n1820938859","n1820939126","n1820938881","n1820939439","n1820939324","n1820939128","n1820938101","n1820937706","n1820938382","n1820938776","n1820937815","n1820939177","n1820939688","n1820938952","n1820938216","n1820938387","n1820939333","n1820938243","n1820938248","n1820937666","n1820939051","n1820938332","n1820939438","n1820939407","n1820939361","n1820937517","n1820938770","n1820939591","n1820937857","n1820938491","n1820937993","n1820938125","n1820938166","n1820937746","n1820939028","n1820937638","n1820938676","n1820938843","n1820938844","n1820937978","n1820938730","n1820939544","n1820938304","n1820939123","n1820939494","n1820939450","n1820939555","n1820938133","n1820938129","n1820938871","n1820939408","n1820938669","n1820938260","n1820939740","n1820937625","n1820938631","n1820939651","n1820939613","n1820937850","n1820938325","n1820937736","n1820938804","n1820938837","n1820938014","n1820938991","n1820938722","n1820938935","n1820937870","n1820938432","n1820937986","n1820938756","n1820938966","n1820939159","n1820937744","n1820938334","n1820937645","n1820939394","n1820937656","n1820938392","n1820939703","n1820938385","n1820938947","n1820938854","n1820938428","n1820938488","n1820938269","n1820938668","n1820938268","n1820938707","n1820937732","n1820939144","n1820938481","n1820938771","n1820938686","n1820938948","n1820937997","n1820937769","n1820939003","n1820938083","n1820939011","n1820938803","n1820938700","n1820939723","n1820938808","n1820938262","n1820938081","n1820938926","n1820938326","n1820938102","n1820938508","n1820939590","n1820939199","n1820938084","n1820938870","n1820938895","n1820937611","n1820938918","n1820938514","n1820939610","n1820938910","n1820937523","n1820938127","n1820939108","n1820937981","n1820938938","n1820938715","n1820939016","n1820938237","n1820939623","n1820939602","n1820937734","n1820938977","n1820939633","n1820939156","n1820939406","n1820938279","n1820938301","n1820937678","n1820937671","n1820939163","n1820938356","n1820939372","n1820937568","n1820937626","n1820939710","n1820939004","n1820938253","n1820938571","n1820937513","n1820939412","n1820939701","n1820939509","n1820938839","n1820939731","n1820937798","n1820939676","n1820939724","n1820939243","n1820939704","n1820937814","n1820937599","n1820938199","n1820938995","n1820938445","n1820938069","n1820938470","n1820939074","n1820938193","n1820938740","n1820938047","n1820939507","n1820939441","n1820939160","n1820937849","n1820937840","n1820938052","n1820938988","n1820938796","n1820937724","n1820937620","n1820939304","n1820938343","n1820939649","n1820938875","n1820939686","n1820938476","n1820937801","n1820937737","n1820938264","n1820939609","n1820939496","n1820939593","n1820939566","n1820939661","n1820937782","n1820938912","n1820939173","n1820937733","n1820938953","n1820939603","n1820937607","n1820938468","n1820939601","n1820939694","n1820939133","n1820938897","n1820938893","n1820937831","n1820937730","n1820938820","n1820938046","n1820938426","n1820938347","n1820937582","n1820938954","n1820938033","n1820938104","n1820938680","n1820939563","n1820939404","n1820939714","n1820939000","n1820937992","n1820938168","n1820939510","n1820939500","n1820937509","n1820938865","n1820939773","n1820938138","n1820938905","n1820937623","n1820939418","n1820937946","n1820939577","n1820937615","n1820939687","n1820939119","n1820937988","n1820938337","n1820937750","n1820938703","n1820938339","n1820939044","n1820939770","n1820938913","n1820937672","n1820939722","n1820937768","n1820939597","n1820939612","n1820937699","n1820937682","n1820937669","n1820937657","n1820939363","n1820937800","n1820938265","n1820937760","n1820938207","n1820938115","n1820939130","n1820939716","n1820938338","n1820938239","n1820939040","n1820938064","n1820938855","n1820939015","n1820938258","n1820939042","n1820939043","n1820938443","n1820939725","n1820937675","n1820938568","n1820938280","n1820937705","n1820938775","n1820938636","n1820938626","n1820937859","n1820938096","n1820937852","n1820939039","n1820938247","n1820938585","n1820937707","n1820938117","n1820938909","n1820939115","n1820939335","n1820938805","n1820937935","n1820937876","n1820938699","n1820937869","n1820938603","n1820938100","n1820938500","n1820938283","n1820938275","n1820938923","n1820938365","n1820938349","n1820937804","n1820937903","n1820937608","n1820938688","n1820939671","n1820938092","n1820937820","n1820938753","n1820938922","n1820937990","n1820939682","n1820939738","n1820939600","n1820938167","n1820937726","n1820939702","n1820938209","n1820939456","n1820937837","n1820938222","n1820938902","n1820939162","n1820938965","n1820938461","n1820937681","n1820937514","n1820937764","n1820939719","n1820939697","n1820938899","n1820939093","n1820938702","n1820939595","n1820938749","n1820938348","n1820937606","n1820938675","n1820938830","n1820938737","n1820938758","n1820938716","n1820939107","n1820937863","n1820939033","n1820938163","n1820937867","n1820938819","n1820938034","n1820938252","n1820937563","n1820937868","n1820939032","n1820938632","n1820937982","n1820937943","n1820939568","n1820939541","n1820938215","n1820939097","n1820938812","n1820937518","n1820937952","n1820938711","n1820938736","n1820939066","n1820937591","n1820938082","n1820938108","n1820938496","n1820939410","n1820938949","n1820938327","n1820937708","n1820939023","n1820937772","n1820938256","n1820939083","n1820938378","n1820938961","n1820937610","n1820939717","n1820938695","n1820938590","n1820939655","n1820938341","n1820939054","n1820939157","n1820939674","n1820939684","n1820939511","n1820937631","n1820939458","n1820937830","n1820937709","n1820937779","n1820939749","n1820938880","n1820938856","n1820938557","n1820939557","n1820938249","n1820938818","n1820937594","n1820939114","n1820938416","n1820937508","n1820938990","n1820938201","n1820937759","n1820937987","n1820939164","n1820939753","n1820938187","n1820939067","n1820937586","n1820937941","n1820938121","n1820937807","n1820938521","n1820939726","n1820938244","n1820939014","n1820938741","n1820937629","n1820938664","n1820938747","n1820939082","n1820938709","n1820938320","n1820938270","n1820937619","n1820937777","n1820937718","n1820939138","n1820938056","n1820938155","n1820938596","n1820937775","n1820938437","n1820938128","n1820939581","n1820939145","n1820938546","n1820938184","n1820937601","n1820937794","n1820938539","n1820939645","n1820938438","n1820938436","n1820939025","n1820938915","n1820938534","n1820937605","n1820939607","n1820939101","n1820939580","n1820939268","n1820939134","n1820938849","n1820938754","n1820938079","n1820937842","n1820938781","n1820938873","n1820938495","n1820938381","n1820938503","n1820939436","n1820938502","n1820939087","n1820938996","n1820938449","n1820938907","n1820937979","n1820937780","n1820937546","n1820939699","n1820937677","n1820938957","n1820938946","n1820937776","n1820937717","n1820938718","n1820937637","n1820938510","n1820937663","n1820938941","n1820939151","n1820937603","n1820938250","n1820937951","n1820938630","n1820938821","n1820938779","n1820938497","n1820938159","n1820939536","n1820938409","n1820938386","n1820939116","n1820938340","n1820939117","n1820938291","n1820938435","n1820937819","n1820938242","n1820939078","n1820938877","n1820939104","n1820939445","n1820938367","n1820938903","n1820939420","n1820938517","n1820939508","n1820939542","n1820939326","n1820938210","n1820939020","n1820938815","n1820937832","n1820939513","n1820937818","n1820939005","n1820938717","n1820939135","n1820938384","n1820937587","n1820939024","n1820939504","n1820939120","n1820939026","n1820938015","n1820938998","n1820937648","n1820939137","n1820937761","n1820938195","n1820938535","n1820939550","n1820938725","n1820938282","n1820937781","n1820937792","n1820939705","n1820937788","n1820939707","n1820937882","n1820939632","n1820938427","n1820938276","n1820939617","n1820939013","n1820939035","n1820937543","n1820939365","n1820937752","n1820937802","n1820939183","n1820939670","n1820938450","n1820939375","n1820937813","n1820937673","n1820937783","n1820939029","n1820939768","n1820939377","n1820937974","n1820939244","n1820939642","n1820937864","n1820938255","n1820938528","n1820939666","n1820938120","n1820937812","n1820938928","n1820939750","n1820939099","n1820938073","n1820938714","n1820939140","n1820938192","n1820937844","n1820938635","n1820938742","n1820939583","n1820937887","n1820938318","n1820938816","n1820939698","n1820938273","n1820939181","n1820937652","n1820938748","n1820937651","n1820938519","n1820938019","n1820938752","n1820938235","n1820939118","n1820938562","n1820939314","n1820939570","n1820938190","n1820938342","n1820938533","n1820937977","n1820939089","n1820939146","n1820938622","n1820938297","n1820938524","n1820939283","n1820938874","n1820938832","n1820937550","n1820937843","n1820938638","n1820938116","n1820938206","n1820938319","n1820939053","n1820937845","n1820938093","n1820939217","n1820938997","n1820939355","n1820938861","n1820938726","n1820938057","n1820939373","n1820937862","n1820938518","n1820939072","n1820939680","n1820938444","n1820938217","n1820938506","n1820938393","n1820938492","n1820938852","n1820938221","n1820938773","n1820937684","n1820939060","n1820938224","n1820938203","n1820938840","n1820937525","n1820938147","n1820938433","n1820938188","n1820939359","n1820938750","n1820938016","n1820938768","n1820937621","n1820937799","n1820938951","n1820938721","n1820939037","n1820937866","n1820939715","n1820938063","n1820938446","n1820937627","n1820939624","n1820938431","n1820939721","n1820939622","n1820939239","n1820939263","n1820939648","n1820939640","n1820938867","n1820938757","n1820938439","n1820939352","n1820937740","n1820939329","n1820938229","n1820937583","n1820938180","n1820938366","n1820937767","n1820937758","n1820939374","n1820938869","n1820938292","n1820938400","n1820938399","n1820939734","n1820939289","n1820938944","n1820937755","n1820938759","n1820938434","n1820937600","n1820937825","n1820937670","n1820937793","n1820938011","n1820938246","n1820938956","n1820937770","n1820937757","n1820938059","n1820937860","n1820937569","n1820939266","n1820939685","n1820939672","n1820938606","n1820938772","n1820939038","n1820938211","n1820938359","n1820939619","n1820938708","n1820939512","n1820938065","n1820939233","n1820939739","n1820938786","n1820938879","n1820939147","n1820938563","n1820939148","n1820937839","n1820937659","n1820937786","n1820938419","n1820939565","n1820939402","n1820937710","n1820938254","n1820938271","n1820938390","n1820937680","n1820938140","n1820937817","n1820938218","n1820937985","n1820939235","n1820938441","n1820938401","n1820938719","n1820937795","n1820938971","n1820938460","n1820939759","n1820937972","n1820937841","n1820938462","n1820939320","n1820938978","n1820938360","n1820939713","n1820937676","n1820939712","n1820937939","n1820938080","n1820937754","n1820937753","n1820938530","n1820937886","n1820939689","n1820939124","n1820938697","n1820938789","n1820939105","n1820938860","n1820938853","n1820939400","n1820937561","n1820938404","n1820938774","n1820939316","n1820937696","n1820938782","n1820938975","n1820937564","n1820939730","n1820938257","n1820937853","n1820938487","n1820938848","n1820938906","n1820939230","n1820938424","n1820938051","n1820937771","n1820939587","n1820939149","n1820938792","n1820939041","n1820938934","n1820939777","n1820937515","n1820939058","n1820938312","n1820939264","n1820939631","n1820939109","n1820939403","n1820939664","n1820938724","n1820938929","n1820939399","n1820939776","n1820939369","n1820939185","n1820937701","n1820938126","n1820938336","n1820938219","n1820939080","n1820938642","n1820938043","n1820937725","n1820938548","n1820938552","n1820938035","n1820938684","n1820937778","n1820938764","n1820939021","n1820939346","n1820937712","n1820939761","n1820938397","n1820937747","n1820938566","n1820939161","n1820939090","n1820939752","n1820939271","n1820938878","n1820938110","n1820938346","n1820938499","n1820938151","n1820939538","n1820938281","n1820939153","n1820938551","n1820939285","n1820938197","n1820938408","n1820938482","n1820939036","n1820939579","n1820938489","n1820938483","n1820938189","n1820938123","n1820938087","n1820937741","n1820938485","n1820937590","n1820938972","n1820937773","n1820937520","n1820938872","n1820938131","n1820938452","n1820938328","n1820939620","n1820937641","n1820938353","n1820939693","n1820938705","n1820937640","n1820939189","n1820938144","n1820939774","n1820938694","n1820938238","n1820939397","n1820937917","n1820938454","n1820938567","n1820938979","n1820938060","n1820938204","n1820937828","n1820939232","n1820938806","n1820938857","n1820938078","n1820938105","n1820939228","n1820938604","n1820937763","n1820937854","n1820938289","n1820939736","n1820937937","n1820937714","n1820938278","n1820938058","n1820938706","n1820938989","n1820938313","n1820938520","n1820938288","n1820937689","n1820939537","n1820939531","n1820939019","n1820937527","n1820938455","n1820938814","n1820938045","n1820939627","n1820938213","n1820938161","n1820938331","n1820938024","n1820938220","n1820938062","n1820938178","n1820937796","n1820937644","n1820938490","n1820937589","n1820937879","n1820939614","n1820938882","n1820938039","n1820938538","n1820937667","n1820937719","n1820938561","n1820939658","n1820938783","n1820938601","n1820938198","n1820938388","n1820938969","n1820937687","n1820939086","n1820939665","n1820939187","n1820938498","n1820938261","n1820937983","n1820938068","n1820938136","n1820939061","n1820938137","n1820938186","n1820939071","n1820937592","n1820939669","n1820937553","n1820939357","n1820938727","n1820939371","n1820939112","n1820939079","n1820938743","n1820938467","n1820938834","n1820938022","n1820938537","n1820938122","n1820938516","n1820937614","n1820937612","n1820939469","n1820939636","n1820939050","n1820939552","n1820938157","n1820938663","n1820938955","n1820939091","n1820938430","n1820938471","n1820937809","n1820938074","n1820938208","n1820938914","n1820938858","n1820938417","n1820937531","n1820938107","n1820939100","n1820938751","n1820937711","n1820938824","n1820939745","n1820937572","n1820938602","n1820938212","n1820938097","n1820937921","n1820938090","n1820938511","n1820938876","n1820939762","n1820938234","n1820938048","n1820937774","n1820937856","n1820937749","n1820937765","n1820938286","n1820939095","n1820938480","n1820939229","n1820938277","n1820937617","n1820938311","n1820937622","n1820939196","n1820937690","n1820939006","n1820939287","n1820939131","n1820938106","n1820938784","n1820938335","n1820938095","n1820938182","n1820937715","n1820937683","n1820938070","n1820939605","n1820938527","n1820938763","n1820938398","n1820937686","n1820939621","n1820937664","n1820939277","n1820938565","n1820939539","n1820938099","n1820939646","n1820938556","n1820937548","n1820938729","n1820939336","n1820938259","n1820938728","n1820938361","n1820937643","n1820938644","n1820939007","n1820939690","n1820939227","n1820937635","n1820937950","n1820938682","n1820939150","n1820939012","n1820939261","n1820939111","n1820937805","n1820939691","n1820939677","n1820937628","n1820937811","n1820938790","n1820938251","n1820938226","n1820938942","n1820937633","n1820937984","n1820937751","n1820939673","n1820938970","n1820938415","n1820938597","n1820938309","n1820938111","n1820938472","n1820938894","n1820938402","n1820937593","n1820938570","n1820939102","n1820939775","n1820937948","n1820939121","n1820937511","n1820938787","n1820939720","n1820939075","n1820937880","n1820937742","n1820937721","n1820939535","n1820938486","n1820938354","n1820937632","n1820939010","n1820938885","n1820938089","n1820937613","n1820938442","n1820938245","n1820938272","n1820937566","n1820938295","n1820938532","n1820938883","n1820937713","n1820937674","n1820939635","n1820938448","n1820938355","n1820938587","n1820938559","n1820937787","n1820939301","n1820937723","n1820939056","n1820937560","n1820938323","n1820938230","n1820938453","n1820938377","n1820938357","n1820939637","n1820938017","n1820939540","n1820939376","n1820937639","n1820937642","n1820938075","n1820938351","n1820938766","n1820937897","n1820938973","n1820938066","n1820939547","n1820939652","n1820937944","n1820937748","n1820939234","n1820939193","n1820937891","n1820938785","n1820939132","n1820938523","n1820938884","n1820938411","n1820939554","n1820938791","n1820937655","n1820938368","n1820939152","n1820938030","n1820938447","n1820937580","n1820939628","n1820937588","n1820937894","n1820939201","n1820938086","n1820937650","n1820938379","n1820939008","n1820938999","n1820937524","n1820937872","n1820938389","n1820939197","n1820938422","n1820938936","n1820939262","n1820937634","n1820938583","n1820939589","n1820937901","n1820939034","n1820939065","n1820938290","n1820939195","n1820938228","n1820937884","n1820938797","n1820938191","n1820939191","n1820939198","n1820937892","n1820939679","n1820938507","n1820937647","n1820937909","n1820938542","n1820939598","n1820937851","n1820939084","n1820939728","n1820937688","n1820938263","n1820938670","n1820937762","n1820939310","n1820938925","n1820938862","n1820938822","n1820938547","n1820937731","n1820938594","n1820938592","n1820938214","n1820938284","n1820937835","n1820938599","n1820939437","n1820937834","n1820937576","n1820937692","n1820939586","n1820939546","n1820938403","n1820937970","n1820939561","n1820938098","n1820938851","n1820938477","n1820938892","n1820939045","n1820939758","n1820939350","n1820938321","n1820938440","n1820938595","n1820938364","n1820938962","n1820938118","n1820939678","n1820938406","n1820938549","n1820937555","n1820938823","n1820937521","n1820939471","n1820939487","n1820938799","n1820938605","n1820937928","n1820938373","n1820939747","n1820939629","n1820937557","n1820937526","n1820938958","n1820938833","n1820937636","n1820938967","n1820938760","n1820938842","n1820938067","n1820939077","n1820939224","n1820938185","n1820939110","n1820938372","n1820939757","n1820939063","n1820939660","n1820938813","n1820937528","n1820938369","n1820938896","n1820939551","n1820939683","n1820937660","n1820937873","n1820938810","n1820938478","n1820939662","n1820937595","n1820939052","n1820938113","n1820939070","n1820938733","n1820937878","n1820938300","n1820939760","n1820939718","n1820937646","n1820939057","n1820939443","n1914861306","n1820938013","n1820937529","n1820939764","n1820938826","n1820937885","n1820939588","n1820937865","n1820937833","n1914861112","n1820938761","n1914861007","n1820937905","n1820938541","n1820939092","n1914861057","n1820938153","n1820938267","n1820939265","n1820938085","n1820939018","n1820939755","n1820938474","n1820939027","n1820938593","n1820938202","n1820939599","n1820939695","n1820938077","n1820938012","n1820939545","n1820939596","n1820939337","n1820938227","n1820937698","n1820938475","n1820939465","n1820938165","n1820938698","n1820938525","n1820938529","n1820938553","n1820938940","n1820939498","n1820938501","n1820939533","n1820938924","n1820939634","n1820939220","n1820939657","n1820938887","n1820938838","n1820938114","n1820937823","n1820938778","n1820938801","n1820939096","n1820938981","n1820937953","n1820938732","n1820938980","n1820938960","n1820937949","n1820938026","n1820939273","n1841425201","n1820938629","n1820938864","n1820938554","n1820938088","n1820937685","n1841425222","n1820939729","n1820937665","n1820937838","n1820937739","n1820938780","n1820937821","n1820938825","n1820939055","n1820939485","n1820938041","n1820938746","n1820939562","n1820938459","n1820939489","n1820938050","n1820937980","n1820937695","n1820938413","n1820938555","n1820937703","n1820938536","n1820938196","n1820938287","n1820938169","n1820939279","n1820938531","n1820938959","n1820939741","n1820938665","n1820938963","n1820939611","n1820937653","n1820939618","n1820939492","n1820938600","n1820938628","n1820939312","n1820939616","n1820937738","n1820939001","n1820939062","n1820938794","n1820938558","n1820937822","n1820937532","n1820939073","n1820938200","n1820938241","n1820938968","n1820938927","n1820938306","n1820937630","n1820938456","n1820937694","n1820938908","n1820939076","n1820937522","n1820939659","n1820938522","n1820939318","n1820938932","n1820938841","n1820937579","n1820937540","n1820938560","n1821139530","n1820938964","n1820937662","n1820939281","n1821139533","n1820937797","n1821139532","n1820939751","n1821139531","n1820939291","n1820938420","n1820939696","n1820938904","n1820938484","n1820939448","n1820939009","n1820938735","n1820938986","n1820938937","n1820939030","n1820938734","n1820938745","n1820939106","n1820938987","n1820937858","n1820938673","n1820938620","n1820937808","n1820937700","n1820939573","n1820938540","n1820937661","n1820937570","n1820938396","n1820937875","n1820939048","n1820938233","n1820938793","n1820939584","n1820938412","n1820938394","n1820937846","n1820938800","n1820938690","n1820939331","n1820939630","n1820938762","n1820938710","n1820939322","n1820938992","n1821137608","n1821137607","n1820937924","n1820939139","n1820939463","n1820939574","n1820938294","n1820938071","n1820938307","n1820938061","n1820939260","n1820937899","n1820938310","n1820938983","n1820937530","n1820938993","n1820938890","n1820937915","n1820938231","n1820938040","n1820938920","n1820939585","n1820938135","n1820939700","n1820937824","n1820939667","n1820937930","n1820938134","n1820937551","n1820939405","n1820938232","n1820937716","n1820937848","n1820939765","n1820939068","n1820939766","n1820937933","n1820937720","n1820939222","n1820939772","n1820939022","n1820939732","n1820937702","n1820937691","n1820938945","n1820937756","n1820938451","n1820938410","n1820938798","n1820937945","n1820937654","n1820938598","n1820938836","n1820937571","n1820937556","n1820938994","n1820938919","n1820938863","n1820939064","n1820938018","n1820937658","n1820937537","n1820938142","n1820938666","n1820937535","n1820939571","n1820938465","n1820939638","n1820937533","n1820939656","n1820939422","n1820938109","n1820938405","n1820938028","n1820937649","n1820938829","n1820939031","n1820939155","n1820938350","n1820938463","n1820938425","n1820939047","n1820938831","n1820938494","n1820937697","n1820938504","n1820938900","n1820937784","n1820938414","n1820938076","n1820938723","n1820937722","n1820938739","n1820937791","n1820938985","n1820938352","n1820938293","n1820938274","n1820939692","n1820937871","n1820939059","n1820938868","n1820937877","n1820937743","n1820938429","n1820937545","n1820937575","n1820938302","n1820938505","n1820938916","n1820938374","n1820938329","n1820937790","n1820939735","n1820938930","n1820937995","n1820938512","n1820938130","n1820938194","n1820938671","n1820938802","n1820937542","n1820937602","n1820939069","n1820938901","n1820939654","n1820937727","n1820939569","n1820938375","n1820939306","n1820938479","n1820938376","n1820938667","n1820937766","n1820939467","n1820939567","n1820937806","n1820938943","n1820938931","n1820937745","n1820939452","n1820938738","n1820938053","n1820939653","n1820938640","n1820937604","n1820937536","n1820938701","n1820939625","n1820939744","n1820939572","n1820937577","n1820937541","n1820938891","n1820937597","n1820938469","n1820939194","n1820937539","n1820938911","n1820939017","n1820939650","n1820939103","n1820939578","n1820938132","n1820937549","n1820938634","n1820939743","n1820937544","n1820937826","n1820937598","n1820937547","n1820938032","n1820939142"]},"w17963021":{"id":"w17963021","tags":{"highway":"residential"},"nodes":["n185948706","n185948708","n185948710"]},"w203974069":{"id":"w203974069","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139870431","n2139870432","n2139870433","n2139870434","n2139870431"]},"w209816575":{"id":"w209816575","tags":{"area":"yes","building":"yes"},"nodes":["n2199856288","n2199856289","n2199856290","n2199856291","n2199856292","n2199856293","n2199856294","n2199856295","n2199856296","n2199856297","n2199856298","n2199856299","n2199856300","n2199856301","n2199856302","n2199856303","n2199856288"]},"w203841838":{"id":"w203841838","tags":{"area":"yes","natural":"water"},"nodes":["n2138493826","n2138493827","n2138493828","n2138493829","n2138493830","n2138493831","n2138493833","n2138493832","n2138493826"]},"w203972937":{"id":"w203972937","tags":{"highway":"path","name":"Riverwalk Trail","surface":"asphalt","width":"3"},"nodes":["n2139858882","n2139858883","n2139858884","n2139858885","n2139858886","n2139858887","n2139858882","n2139858888","n2139858889","n2139858890","n2139858891","n2139858892","n2139858893","n2139858894","n2139858895","n2139858896","n2139858897","n2139858898","n2139858899","n2139858900","n2139858901","n2139858902","n2139858903","n2139858986","n2139858904","n2139858995","n2139858905","n2139858906","n2139858907","n2139858908","n2139858909","n2139858910","n2139858911","n2139858912","n2139858913","n2139858914","n2139858915","n2139858916","n2139858917","n2139858918","n2139858919","n2139858920","n2139858921","n2139858922","n2139858923","n2139858924","n2139858925","n2139858926","n2139858927","n2139858982","n2139858928","n2139858929","n2139858930","n2139858931","n2139858932","n2139858981","n2139858933","n2139858934","n2139858935","n2139858936","n2139858937","n2139858938","n2139858939","n2139858940","n2139858941","n2139858942","n2139858943","n2140006437","n2139858964","n2139858944","n2139858966","n2139858945","n2139858946","n2139858947","n2139858948","n2139858949","n2139858950","n2139858951"]},"w17964015":{"id":"w17964015","tags":{"highway":"residential"},"nodes":["n185954680","n185954683","n185954685","n185954687","n185954689","n185954690","n185954691","n2139870379","n2139870456","n185954692","n185954693","n185954695"]},"w17967315":{"id":"w17967315","tags":{"highway":"residential","name":"South Andrews Street"},"nodes":["n185981999","n185974477","n185964963"]},"w203974071":{"id":"w203974071","tags":{"highway":"footway"},"nodes":["n2139870439","n2139870440","n2139870441","n2139870442","n2139870443","n2139870444","n2139870445","n2139870446","n2139870447","n2139870448","n2139870449"]},"w170848824":{"id":"w170848824","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819858503","n1819858531","n1819858526","n1819858518","n1819858505","n1819858508","n1819858512","n1819858514","n1819858528","n1819858509","n1819858511","n1819858507","n1819858521"]},"w203986458":{"id":"w203986458","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139989357","n2139989359","n2139989360","n2139989362","n2139989357"]},"w170844917":{"id":"w170844917","tags":{"waterway":"riverbank"},"nodes":["n1819805911","n1819805690","n1819805812","n1819805766","n1819805802","n1819805885","n1819805626","n1819805842","n1819805715","n1819805694","n1819805618","n1819805629","n1819805731","n1819805636","n1819805878","n1819805718","n1819805798","n1819849057","n1819805666","n1819805852","n1819805805","n1819805789","n1819805868","n1819805680","n1819805918","n1819848888","n1819805762","n2139989328","n1819805907","n2139989330","n1819805915","n1819858521","n1819805854","n1819805876","n1819805864","n1819805922","n2139859004","n1819805702","n2139859003","n1819805614","n1819805792","n1819805786","n1819805777","n1819805645","n1819805838","n1819805889","n1819805795","n1819805707","n1819805774","n1819805808","n1819805810","n1819805724","n1819805676","n1819805728","n1819805783","n1819805687","n1819805727","n2189123379","n1819805632","n1819805641","n1819805760","n1819805887","n1819805861","n1819805722","n1819805880","n2139982405","n2139982399","n2139982400","n1819805770","n2139982402","n2139982403","n2139982401","n1819805780","n1819805834","n2139982406","n1819805698","n1819805647","n1819805870","n1819805683","n1819805622","n1819805639","n1819805858","n1819805643","n1819805673","n1819805925","n1819805849","n1819805711","n1819805846","n1819805669","n1819805883","n1819805814","n1819805873","n1819805911"]},"w17967326":{"id":"w17967326","tags":{"highway":"residential","name":"North Constantine Street"},"nodes":["n185985217","n185985219","n185985221","n185985222","n185985223","n185985225","n2140006431","n185985227","n185985229","n185985231","n185985233","n185985235","n185985238","n185985240","n2140018998","n185964965"]},"w134150789":{"id":"w134150789","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964971","n2139870406","n185964972"]},"w17966400":{"id":"w17966400","tags":{"highway":"tertiary","name":"South Constantine Street"},"nodes":["n185958672","n185964965"]},"w203974066":{"id":"w203974066","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139870417","n2139870418","n2139870420","n2139870419"]},"w17965998":{"id":"w17965998","tags":{"name":"Conrail Railroad","railway":"rail"},"nodes":["n185972775","n185972777","n185972779","n185972781","n185972783","n185972785","n185972787","n185972788","n185972789","n185972790","n185972791","n185972793","n185972795","n185972797","n185972798","n185972800","n185972802","n185972805","n185972807","n185972809","n185972811","n185972813","n185972814","n185972815","n185972816","n185972817","n185972819","n185972821","n185972824","n185972826","n185972830","n185972832","n185972834","n185972835","n185972836","n185972839","n185990434","n2114807572","n2114807568","n185972845","n2114807583","n185972847","n185972849","n185972851","n2114807578","n1475293254","n2114807593","n1475293226","n185972862","n2114807565","n185951869","n1475293234","n1475293252","n185972868","n1475293264","n1475293222","n185972878","n1475293261","n185972882","n185972885","n1475293260","n1475293240","n185972891","n185972895","n185972897","n185972899","n2130304159","n1475284023","n185972903"]},"w134150795":{"id":"w134150795","tags":{"bridge":"yes","highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964970","n185964971"]},"w203974067":{"id":"w203974067","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139870420","n2139870421"]},"w170995908":{"id":"w170995908","tags":{"highway":"residential","name":"Thomas Street"},"nodes":["n1821006702","n1821006700","n1821006698","n2139858990","n1821006716","n1821006725","n1821006712","n1821006704","n1821006708","n1821006710","n1821006706"]},"w17965834":{"id":"w17965834","tags":{"highway":"residential","name":"Spring Street"},"nodes":["n185971361","n185971364","n185971366","n185971368","n185954695","n185964968"]},"w203974070":{"id":"w203974070","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139870435","n2139870436","n2139870437","n2139870438","n2139870435"]},"w203989879":{"id":"w203989879","tags":{"highway":"service"},"nodes":["n2140018998","n2140018999","n2140019000"]},"w203974062":{"id":"w203974062","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870387","n2139870388","n2139870389","n2139870390","n2139870391","n2139870392","n2139870397","n2139870393","n2139870396","n2139870395","n2139870394","n2139870387"]},"w203974061":{"id":"w203974061","tags":{"bridge":"yes","highway":"footway"},"nodes":["n2139870382","n2139870383"]},"w203049587":{"id":"w203049587","tags":{"area":"yes","name":"Scidmore Park Petting Zoo","tourism":"zoo","zoo":"petting_zoo"},"nodes":["n2130304133","n2130304136","n2130304138","n2130304140","n2130304142","n2130304144","n2130304146","n2130304147","n2130304148","n2130304149","n2130304150","n2130304151","n2130304133"]},"w203972941":{"id":"w203972941","tags":{"highway":"path"},"nodes":["n2139858982","n2139858983","n2139858984","n2139858985","n2139858927"]},"w203974065":{"id":"w203974065","tags":{"highway":"service"},"nodes":["n2139870406","n2139870407","n2139870408","n2139870417","n2139870409","n2139870410","n2139870411","n2139870412","n2139870426","n2139870413","n2139870414","n2139870415","n2139870419","n2139870416","n2139870421","n2139870408"]},"w203972940":{"id":"w203972940","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858934","n2139858967","n2139858968","n2139858969","n2139858970","n2139858971","n2139858972","n2139858973","n2139858974","n2139858975","n2139858976","n2139858977","n2139858978","n2139858979","n2139858980","n2139858981"]},"w203974072":{"id":"w203974072","tags":{"highway":"footway"},"nodes":["n2139858925","n2139870450","n2139870453","n2139870451","n2139870452","n2139870441"]},"w203974074":{"id":"w203974074","tags":{"highway":"footway"},"nodes":["n2139870454","n2139870456","n2139870429"]},"w203974060":{"id":"w203974060","tags":{"highway":"footway"},"nodes":["n2139870383","n2139870384","n2139870422","n2139870385","n2139870386","n2139870388"]},"w203841837":{"id":"w203841837","tags":{"area":"yes","natural":"water"},"nodes":["n2138493807","n2138493808","n2138493809","n2138493810","n2138493811","n2138493812","n2138493813","n2138493814","n2138493815","n2138493816","n2138493825","n2138493817","n2138493824","n2138493818","n2138493819","n2138493820","n2138493821","n2138493822","n2138493823","n2138493807"]},"w134150845":{"id":"w134150845","tags":{"bridge":"yes","name":"Conrail Railroad","railway":"rail"},"nodes":["n185972903","n185972905"]},"w203974059":{"id":"w203974059","tags":{"highway":"footway"},"nodes":["n2139870430","n2139870439","n2139870429","n2139870428","n2139870379","n2139870455","n2139870380","n2139870381","n2139858925","n2139870382"]},"w203986457":{"id":"w203986457","tags":{"area":"yes","leisure":"park","name":"Scidmore Park"},"nodes":["n2139989333","n2139989335","n2139989337","n2139989339","n1819805762","n2139989328","n1819805907","n2139989330","n1819805915","n2139989341","n2139989344","n2139989346","n2139989348","n2139989350","n2139989351","n2139989353","n2139989355","n2139989333"]},"w170848331":{"id":"w170848331","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819848937","n1819849104","n1819849076","n1819849183","n1819848928","n1819848972","n1819848948","n1819848971","n1819848859","n1819849008","n1819848889","n1819849026","n1819849094","n1819849083","n1819849079","n1819849187","n1819848992","n1819849060","n1819849056","n1819849071","n1819849067","n1819849048","n1819849036","n1819849150","n1819849075","n1819849051","n1819849062","n1819848926","n1819849035","n1819848987","n1819849012","n1819848933","n1819848996","n1819848990","n1819849005","n1819849021","n1819848892","n1819849092","n1819848863","n1819848922","n1819848858","n1819848855","n1819848974","n1819848953","n1819849019","n1819849049","n1819848979","n1819849140","n1819849193","n1819849147","n1819849151","n1819849163","n1819849023","n1819848878","n1819849004","n1819848857","n1819848879","n1819849041","n1819849165","n1819849107","n1819849156","n1819848934","n1819848914","n1819848955","n1819848931","n1819848927","n1819849084","n1819849169","n1819849045","n1819848945","n1819849095","n1819848924","n1819849171","n1819849141","n1819849046","n1819849197","n1819849011","n1819849108","n1819849158","n1819849160","n1819848870","n1819849006","n1819849157","n1819848993","n1819848970","n1819849202","n1819848903","n1819848975","n1819848849","n1819849025","n1819849105","n1819849033","n1819849176","n1819849099","n1819849086","n1819848960","n1819848961","n1819849001","n1819848980","n1819849038","n1819848854","n1819849127","n1819849170","n1819849139","n1819848873","n1819848929","n1819849201","n1819849121","n1819849031","n1819849131","n1819848875","n1819849080","n1819849066","n1819849081","n1819849096","n1819849172","n1819849114","n1819849182","n1819848905","n1819849054","n1819848920","n1819848851","n1819848968","n1819848917","n1819849111","n1819849119","n1819849074","n1819848893","n1819849129","n1819848850","n1819848956","n1819849154","n1819848877","n1819848986","n1819849191","n1819848952","n1819848954","n1819848942","n1819849028","n1819849195","n1819848938","n1819848962","n1819849070","n1819849034","n1819849052","n1819849059","n1819848916","n1819849162","n1819849167","n1819849093","n1819849030","n1819849002","n1819849161","n1819848886","n1819848958","n1819849064","n1819849112","n1819849148","n1819848856","n1819848976","n1819848977","n1819849144","n1819848918","n1819849200","n1819848919","n1819849042","n1819849166","n1819849186","n1819849152","n1819849058","n1819849185","n1819849199","n1819849053","n1819849194","n1819849068","n1819849146","n1819849174","n1819848967","n1819848932","n1819849155","n1819849198","n1819848964","n1819848894","n1819848969","n1819849184","n1819849055","n1819849179","n1819848865","n1819848860","n1819849082","n1819848966","n1819849040","n1819849069","n1819849078","n1819849077","n1819848904","n1819848959","n1819849133","n1819849089","n1819849000","n1819849124","n1819849032","n1819849097","n1819848939","n1819849072","n1819848915","n1819849196","n1819848946","n1819849047","n1819849029","n1819849164","n1819848994","n1819849022","n1819858513","n1819849126","n1819849063","n1819848941","n1819849085","n1819848871","n1819848943","n1819849192","n1819858501","n1819849159","n1819858523","n1819848901","n1819849189","n1819858503","n1819849065","n2139877106","n1819848909","n1819848930","n1819848888"]},"w17967397":{"id":"w17967397","tags":{"highway":"residential","name":"North Andrews Street"},"nodes":["n185964963","n185985217"]},"w17964497":{"id":"w17964497","tags":{"highway":"tertiary","name":"Constantine St"},"nodes":["n185958643","n185958645","n2139795852","n185958647","n185958649","n185958651","n185958653","n185958656","n185958658","n185958660","n185958662","n185958664","n185958666","n185958668","n185958670","n185948710","n185958672"]},"w203974068":{"id":"w203974068","tags":{"highway":"footway"},"nodes":["n2139870422","n2139870423","n2139870424","n2139870425","n2139870426","n2139870427"]},"w203974063":{"id":"w203974063","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870398","n2139870399","n2139870400","n2139870401","n2139870398"]},"w203986459":{"id":"w203986459","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139989364","n2139989366","n2139989368","n2139989370","n2139989364"]},"w203988286":{"id":"w203988286","tags":{"area":"yes","leisure":"park","name":"Memory Isle Park"},"nodes":["n2140006331","n2140006334","n2140006336","n2140006338","n2140006340","n2140006342","n2140006344","n2140006346","n2140006348","n2140006351","n2140006353","n2140006355","n2140006357","n2140006359","n2140006361","n2140006363","n2140006364","n2140006365","n2140006395","n2140006366","n2140006401","n2140006397","n2140006399","n2140006331"]},"w203974073":{"id":"w203974073","tags":{"highway":"footway"},"nodes":["n2139870453","n2139870454","n2139870455"]},"w203974064":{"id":"w203974064","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870402","n2139870403","n2139870404","n2139870405","n2139870402"]},"n185966959":{"id":"n185966959","loc":[-85.642185,41.946411]},"n1475283980":{"id":"n1475283980","loc":[-85.6398249,41.9451425]},"n1475284013":{"id":"n1475284013","loc":[-85.6396448,41.9451666]},"n1475284042":{"id":"n1475284042","loc":[-85.6386382,41.9454789]},"n185975925":{"id":"n185975925","loc":[-85.6393332,41.9452388]},"n185975919":{"id":"n185975919","loc":[-85.6391279,41.9453044]},"n185975917":{"id":"n185975917","loc":[-85.6389034,41.9453872]},"n2140006369":{"id":"n2140006369","loc":[-85.6386163,41.9451631]},"n2140006370":{"id":"n2140006370","loc":[-85.6385144,41.9449357]},"n2140006417":{"id":"n2140006417","loc":[-85.6385785,41.9450299]},"n2140006419":{"id":"n2140006419","loc":[-85.6385781,41.9452152]},"n2189123361":{"id":"n2189123361","loc":[-85.6404948,41.947015]},"n2189123363":{"id":"n2189123363","loc":[-85.6395765,41.946495]},"n2189123365":{"id":"n2189123365","loc":[-85.6389347,41.9460875]},"n185966962":{"id":"n185966962","loc":[-85.644417,41.946364]},"n185975911":{"id":"n185975911","loc":[-85.637532,41.9458276]},"n185975913":{"id":"n185975913","loc":[-85.6376323,41.9457936]},"n185975915":{"id":"n185975915","loc":[-85.6383596,41.9455425]},"n185975932":{"id":"n185975932","loc":[-85.644403,41.945088]},"n185975934":{"id":"n185975934","loc":[-85.645486,41.945084]},"n185979974":{"id":"n185979974","loc":[-85.644381,41.943831]},"n2139795809":{"id":"n2139795809","loc":[-85.6464756,41.9450813]},"n2139795810":{"id":"n2139795810","loc":[-85.6466646,41.945174]},"n2139858952":{"id":"n2139858952","loc":[-85.6383567,41.9454039]},"n2139858953":{"id":"n2139858953","loc":[-85.6380506,41.9455301]},"n2139858954":{"id":"n2139858954","loc":[-85.6377321,41.9455546]},"n2139858955":{"id":"n2139858955","loc":[-85.6376571,41.9455245]},"n2139858956":{"id":"n2139858956","loc":[-85.6375859,41.9454544]},"n2139858957":{"id":"n2139858957","loc":[-85.6376686,41.9453185]},"n2139858958":{"id":"n2139858958","loc":[-85.6378936,41.9451712]},"n2139858959":{"id":"n2139858959","loc":[-85.6379225,41.9450825]},"n2139858960":{"id":"n2139858960","loc":[-85.6379302,41.9447564]},"n2139858961":{"id":"n2139858961","loc":[-85.6379763,41.9446963]},"n2139858962":{"id":"n2139858962","loc":[-85.6380436,41.9446706]},"n2139858963":{"id":"n2139858963","loc":[-85.6381286,41.9445969]},"n2139858965":{"id":"n2139858965","loc":[-85.6382523,41.9444134]},"n2140006367":{"id":"n2140006367","loc":[-85.6380923,41.9454418]},"n2140006368":{"id":"n2140006368","loc":[-85.6384089,41.9453146]},"n2140006372":{"id":"n2140006372","loc":[-85.6383252,41.9447706]},"n2140006374":{"id":"n2140006374","loc":[-85.6381033,41.9447436]},"n2140006376":{"id":"n2140006376","loc":[-85.6379759,41.9447815]},"n2140006378":{"id":"n2140006378","loc":[-85.6379832,41.9448654]},"n2140006380":{"id":"n2140006380","loc":[-85.6380632,41.9450738]},"n2140006382":{"id":"n2140006382","loc":[-85.6380414,41.9452064]},"n2140006389":{"id":"n2140006389","loc":[-85.6379068,41.9453092]},"n2140006391":{"id":"n2140006391","loc":[-85.637925,41.9453904]},"n2140006393":{"id":"n2140006393","loc":[-85.6379977,41.94545]},"n2189123275":{"id":"n2189123275","loc":[-85.6371346,41.9462544]},"n2189123278":{"id":"n2189123278","loc":[-85.6368371,41.9466153]},"n2189123280":{"id":"n2189123280","loc":[-85.6379537,41.9489088]},"n2189123282":{"id":"n2189123282","loc":[-85.6383816,41.9497858]},"n2189123285":{"id":"n2189123285","loc":[-85.6393673,41.9512417]},"n2189123287":{"id":"n2189123287","loc":[-85.640554,41.9517766]},"n2189123289":{"id":"n2189123289","loc":[-85.6411,41.9522344]},"n2189123291":{"id":"n2189123291","loc":[-85.6417418,41.9526574]},"n2189123293":{"id":"n2189123293","loc":[-85.642321,41.9529407]},"n2189123295":{"id":"n2189123295","loc":[-85.6427697,41.9532278]},"n2189123297":{"id":"n2189123297","loc":[-85.6433332,41.9538254]},"n2189123300":{"id":"n2189123300","loc":[-85.6435785,41.9543648]},"n2189123301":{"id":"n2189123301","loc":[-85.6444394,41.9541048]},"n2189123303":{"id":"n2189123303","loc":[-85.6450603,41.954]},"n2189123312":{"id":"n2189123312","loc":[-85.6454829,41.9539108]},"n2189123314":{"id":"n2189123314","loc":[-85.6460464,41.9538526]},"n2189123315":{"id":"n2189123315","loc":[-85.6463178,41.9537167]},"n2189123316":{"id":"n2189123316","loc":[-85.646276,41.9534141]},"n2189123317":{"id":"n2189123317","loc":[-85.6459995,41.9531541]},"n2189123318":{"id":"n2189123318","loc":[-85.645222,41.9531929]},"n2189123319":{"id":"n2189123319","loc":[-85.6447316,41.9531813]},"n2189123320":{"id":"n2189123320","loc":[-85.6440637,41.9532977]},"n2189123321":{"id":"n2189123321","loc":[-85.6438185,41.9531774]},"n2189123322":{"id":"n2189123322","loc":[-85.6440011,41.9528398]},"n2189123323":{"id":"n2189123323","loc":[-85.6442672,41.9525914]},"n2189123324":{"id":"n2189123324","loc":[-85.6442881,41.9523276]},"n2189123326":{"id":"n2189123326","loc":[-85.644262,41.952153]},"n2189123328":{"id":"n2189123328","loc":[-85.6441681,41.9520404]},"n2189123330":{"id":"n2189123330","loc":[-85.6442098,41.9517494]},"n2189123333":{"id":"n2189123333","loc":[-85.6438498,41.9515864]},"n2189123336":{"id":"n2189123336","loc":[-85.6435889,41.9513225]},"n2189123339":{"id":"n2189123339","loc":[-85.6425349,41.9510315]},"n2189123342":{"id":"n2189123342","loc":[-85.6422688,41.9508802]},"n2189123345":{"id":"n2189123345","loc":[-85.6418775,41.9508142]},"n2189123348":{"id":"n2189123348","loc":[-85.6415488,41.9508064]},"n2189123351":{"id":"n2189123351","loc":[-85.6411027,41.9505488]},"n2189123353":{"id":"n2189123353","loc":[-85.6410374,41.9498208]},"n2189123355":{"id":"n2189123355","loc":[-85.6410061,41.9494327]},"n2189123357":{"id":"n2189123357","loc":[-85.6411522,41.9482569]},"n2189123359":{"id":"n2189123359","loc":[-85.6410548,41.9473036]},"n2189123368":{"id":"n2189123368","loc":[-85.6380216,41.9458974]},"n2189123370":{"id":"n2189123370","loc":[-85.6386721,41.9507782]},"w17968193":{"id":"w17968193","tags":{"highway":"residential","name":"French St"},"nodes":["n185970906","n185982877","n185967774","n185985823","n185979974"]},"w203972939":{"id":"w203972939","tags":{"highway":"path"},"nodes":["n2139858965","n2139858966"]},"w203988289":{"id":"w203988289","tags":{"area":"yes","natural":"water"},"nodes":["n2140006367","n2140006368","n2140006419","n2140006369","n2140006417","n2140006370","n2140006372","n2140006374","n2140006376","n2140006378","n2140006380","n2140006382","n2140006389","n2140006391","n2140006393","n2140006367"]},"w208640157":{"id":"w208640157","tags":{"area":"yes","natural":"wetland"},"nodes":["n1819849029","n2189123275","n2189123278","n2189123280","n2189123282","n2189123370","n2189123285","n2189123287","n2189123289","n2189123291","n2189123293","n2189123295","n2189123297","n2189123300","n2189123301","n2189123303","n2189123312","n2189123314","n2189123315","n2189123316","n2189123317","n2189123318","n2189123319","n2189123320","n2189123321","n2189123322","n2189123323","n2189123324","n2189123326","n2189123328","n2189123330","n2189123333","n2189123336","n2189123339","n2189123342","n2189123345","n2189123348","n2189123351","n2189123353","n2189123355","n2189123357","n2189123359","n2189123361","n2189123363","n2189123365","n2189123368","n1819849029"]},"w17966281":{"id":"w17966281","tags":{"highway":"residential","name":"Pealer St"},"nodes":["n185975911","n185975913","n185975915","n1475284042","n185975917","n185975919","n185975925","n185970909","n1475284013","n1475283980","n185975928","n185967775","n185975930","n185975932","n185975934","n2139795809","n2139795810"]},"w17965353":{"id":"w17965353","tags":{"highway":"residential","name":"Yauney St"},"nodes":["n185966958","n185966959","n185966960","n185966962"]},"w203972938":{"id":"w203972938","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858964","n2139858965","n2139858963","n2139858962","n2139858961","n2139858960","n2139858959","n2139858958","n2139858957","n2139858956","n2139858955","n2139858954","n2139858953","n2139858952","n2139858951"]},"n354002665":{"id":"n354002665","loc":[-85.6366599,41.9444923],"tags":{"name":"Memory Isle","place":"island"}},"n354031301":{"id":"n354031301","loc":[-85.635,41.9463889],"tags":{"amenity":"post_office","name":"Three Rivers Post Office"}},"n185963454":{"id":"n185963454","loc":[-85.633686,41.946072]},"n185963455":{"id":"n185963455","loc":[-85.633815,41.946131]},"n185963456":{"id":"n185963456","loc":[-85.633951,41.946174]},"n185978375":{"id":"n185978375","loc":[-85.634385,41.94559]},"n185978377":{"id":"n185978377","loc":[-85.634544,41.945725]},"n185978379":{"id":"n185978379","loc":[-85.634573,41.945764]},"n185978381":{"id":"n185978381","loc":[-85.634616,41.945849]},"n185978383":{"id":"n185978383","loc":[-85.634629,41.945893]},"n185984011":{"id":"n185984011","loc":[-85.636058,41.946201]},"n185984013":{"id":"n185984013","loc":[-85.636112,41.946366]},"n185984015":{"id":"n185984015","loc":[-85.636143,41.946551]},"n185988237":{"id":"n185988237","loc":[-85.6354162,41.946044]},"n185988969":{"id":"n185988969","loc":[-85.635374,41.945325]},"n185988971":{"id":"n185988971","loc":[-85.635643,41.945585]},"n185988972":{"id":"n185988972","loc":[-85.635853,41.94586]},"n1475283992":{"id":"n1475283992","loc":[-85.6372968,41.9459007]},"n1475284011":{"id":"n1475284011","loc":[-85.6359415,41.9459797]},"n1475284019":{"id":"n1475284019","loc":[-85.6364433,41.9460423]},"n185984009":{"id":"n185984009","loc":[-85.6360524,41.9460485]},"n185988239":{"id":"n185988239","loc":[-85.6358187,41.9460423]},"n185988243":{"id":"n185988243","loc":[-85.6366156,41.9460282]},"n185988244":{"id":"n185988244","loc":[-85.6368316,41.9460046]},"n185988245":{"id":"n185988245","loc":[-85.6370133,41.9459704]},"n185988241":{"id":"n185988241","loc":[-85.636291,41.9460461]},"n185964976":{"id":"n185964976","loc":[-85.633923,41.9434157]},"n185964980":{"id":"n185964980","loc":[-85.6333656,41.9437293]},"n185978388":{"id":"n185978388","loc":[-85.6346449,41.9460571]},"n1819858504":{"id":"n1819858504","loc":[-85.6365343,41.9447926]},"n1819858506":{"id":"n1819858506","loc":[-85.6370546,41.9451882]},"n1819858516":{"id":"n1819858516","loc":[-85.6358369,41.9444654]},"n1819858519":{"id":"n1819858519","loc":[-85.6361534,41.9446176]},"n1819858525":{"id":"n1819858525","loc":[-85.6368025,41.9449442]},"n1819858527":{"id":"n1819858527","loc":[-85.6334199,41.9457495]},"n185963452":{"id":"n185963452","loc":[-85.633564,41.9458519]},"n185963453":{"id":"n185963453","loc":[-85.6336152,41.9459804]},"n185963451":{"id":"n185963451","loc":[-85.6332888,41.9456871]},"n2130304152":{"id":"n2130304152","loc":[-85.6359466,41.9454599]},"n2130304153":{"id":"n2130304153","loc":[-85.6362773,41.9452683]},"n2130304154":{"id":"n2130304154","loc":[-85.6352028,41.9442868]},"n2130304155":{"id":"n2130304155","loc":[-85.6348756,41.9444769]},"n2130304156":{"id":"n2130304156","loc":[-85.6349723,41.9444207]},"n2130304157":{"id":"n2130304157","loc":[-85.6338698,41.9434443]},"n2130304158":{"id":"n2130304158","loc":[-85.635094,41.9451026]},"n2130304160":{"id":"n2130304160","loc":[-85.6353716,41.9449322]},"n2130304162":{"id":"n2130304162","loc":[-85.6365942,41.9459352]},"n2130304163":{"id":"n2130304163","loc":[-85.6369006,41.9457469]},"n2130304164":{"id":"n2130304164","loc":[-85.6363292,41.9452278]},"n2130304165":{"id":"n2130304165","loc":[-85.6360248,41.9454175]},"n2139824683":{"id":"n2139824683","loc":[-85.6339825,41.9446441]},"n2139824689":{"id":"n2139824689","loc":[-85.6340437,41.9446925]},"n2139824702":{"id":"n2139824702","loc":[-85.6340961,41.9447551]},"n2139824705":{"id":"n2139824705","loc":[-85.6337467,41.944809]},"n2139824707":{"id":"n2139824707","loc":[-85.6341598,41.9448129]},"n2139824710":{"id":"n2139824710","loc":[-85.6342771,41.9448223]},"n2139824712":{"id":"n2139824712","loc":[-85.6346058,41.944841]},"n2139824713":{"id":"n2139824713","loc":[-85.633808,41.9448574]},"n2139824714":{"id":"n2139824714","loc":[-85.6340889,41.9448589]},"n2139824716":{"id":"n2139824716","loc":[-85.6343335,41.944871]},"n2139824717":{"id":"n2139824717","loc":[-85.6343341,41.9448717]},"n2139824720":{"id":"n2139824720","loc":[-85.6338757,41.9449069]},"n2139824721":{"id":"n2139824721","loc":[-85.6341445,41.9449071]},"n2139824724":{"id":"n2139824724","loc":[-85.6334787,41.9449262]},"n2139824726":{"id":"n2139824726","loc":[-85.6347119,41.9449332]},"n2139824727":{"id":"n2139824727","loc":[-85.6347175,41.9449418]},"n2139824728":{"id":"n2139824728","loc":[-85.6344284,41.9449538]},"n2139824729":{"id":"n2139824729","loc":[-85.6339339,41.9449573]},"n2139824730":{"id":"n2139824730","loc":[-85.6339179,41.9449682]},"n2139824732":{"id":"n2139824732","loc":[-85.6335472,41.9449895]},"n2139824733":{"id":"n2139824733","loc":[-85.6339736,41.9450164]},"n2139824735":{"id":"n2139824735","loc":[-85.6336034,41.9450415]},"n2139824736":{"id":"n2139824736","loc":[-85.6348317,41.945043]},"n2139824737":{"id":"n2139824737","loc":[-85.63403,41.9450651]},"n2139824738":{"id":"n2139824738","loc":[-85.6336611,41.9450949]},"n2139824740":{"id":"n2139824740","loc":[-85.6336582,41.9450966]},"n2139824744":{"id":"n2139824744","loc":[-85.6331702,41.9451107]},"n2139824745":{"id":"n2139824745","loc":[-85.6333388,41.9451142]},"n2139824746":{"id":"n2139824746","loc":[-85.6337131,41.9451341]},"n2139824747":{"id":"n2139824747","loc":[-85.6337021,41.9451372]},"n2139824748":{"id":"n2139824748","loc":[-85.6341244,41.9451472]},"n2139824749":{"id":"n2139824749","loc":[-85.6333952,41.945166]},"n2139824750":{"id":"n2139824750","loc":[-85.633395,41.9451661]},"n2139824751":{"id":"n2139824751","loc":[-85.6346258,41.9451725]},"n2139824752":{"id":"n2139824752","loc":[-85.6332387,41.9451741]},"n2139824753":{"id":"n2139824753","loc":[-85.6346901,41.9451853]},"n2139824754":{"id":"n2139824754","loc":[-85.6346611,41.9452035]},"n2139824755":{"id":"n2139824755","loc":[-85.6346574,41.9452059]},"n2139824756":{"id":"n2139824756","loc":[-85.6345611,41.9452133]},"n2139824757":{"id":"n2139824757","loc":[-85.633453,41.9452194]},"n2139824758":{"id":"n2139824758","loc":[-85.6335508,41.9452283]},"n2139824759":{"id":"n2139824759","loc":[-85.6347424,41.9452312]},"n2139824760":{"id":"n2139824760","loc":[-85.6342305,41.9452395]},"n2139824761":{"id":"n2139824761","loc":[-85.6342319,41.9452449]},"n2139824762":{"id":"n2139824762","loc":[-85.6334969,41.94526]},"n2139824763":{"id":"n2139824763","loc":[-85.63468,41.9452706]},"n2139824764":{"id":"n2139824764","loc":[-85.6346772,41.9452724]},"n2139824765":{"id":"n2139824765","loc":[-85.6338611,41.9452763]},"n2139824766":{"id":"n2139824766","loc":[-85.6347811,41.9452939]},"n2139824767":{"id":"n2139824767","loc":[-85.6347375,41.9453211]},"n2139824768":{"id":"n2139824768","loc":[-85.6339171,41.9453301]},"n2139824769":{"id":"n2139824769","loc":[-85.6348307,41.9453377]},"n2139824770":{"id":"n2139824770","loc":[-85.6347067,41.9453405]},"n2139824771":{"id":"n2139824771","loc":[-85.6343461,41.9453461]},"n2139824772":{"id":"n2139824772","loc":[-85.6343481,41.9453475]},"n2139824773":{"id":"n2139824773","loc":[-85.634805,41.9453538]},"n2139824774":{"id":"n2139824774","loc":[-85.6336997,41.9453692]},"n2139824775":{"id":"n2139824775","loc":[-85.6339709,41.9453818]},"n2139824776":{"id":"n2139824776","loc":[-85.6336229,41.9454134]},"n2139824777":{"id":"n2139824777","loc":[-85.6349022,41.9454141]},"n2139824778":{"id":"n2139824778","loc":[-85.6348854,41.9454246]},"n2139824779":{"id":"n2139824779","loc":[-85.6340286,41.9454373]},"n2139824780":{"id":"n2139824780","loc":[-85.6336963,41.9454572]},"n2139824781":{"id":"n2139824781","loc":[-85.6336789,41.9454672]},"n2139824782":{"id":"n2139824782","loc":[-85.6344933,41.945475]},"n2139824783":{"id":"n2139824783","loc":[-85.6340854,41.9454918]},"n2139824784":{"id":"n2139824784","loc":[-85.6350036,41.9455034]},"n2139824785":{"id":"n2139824785","loc":[-85.6337501,41.9455089]},"n2139824786":{"id":"n2139824786","loc":[-85.6337497,41.9455091]},"n2139824787":{"id":"n2139824787","loc":[-85.6345425,41.9455186]},"n2139824788":{"id":"n2139824788","loc":[-85.6341459,41.9455372]},"n2139824789":{"id":"n2139824789","loc":[-85.6341376,41.945542]},"n2139824790":{"id":"n2139824790","loc":[-85.6338394,41.9455462]},"n2139824791":{"id":"n2139824791","loc":[-85.6349171,41.9455588]},"n2139824792":{"id":"n2139824792","loc":[-85.6338074,41.9455646]},"n2139824793":{"id":"n2139824793","loc":[-85.6346229,41.9455894]},"n2139824794":{"id":"n2139824794","loc":[-85.6338983,41.9455995]},"n2139824795":{"id":"n2139824795","loc":[-85.6338962,41.9456007]},"n2139824796":{"id":"n2139824796","loc":[-85.6342475,41.9456348]},"n2139824797":{"id":"n2139824797","loc":[-85.6339505,41.9456497]},"n2139824798":{"id":"n2139824798","loc":[-85.6347243,41.9456788]},"n2139824799":{"id":"n2139824799","loc":[-85.635057,41.9456831]},"n2139824800":{"id":"n2139824800","loc":[-85.635287,41.9457056]},"n2139824801":{"id":"n2139824801","loc":[-85.6350753,41.9457068]},"n2139824802":{"id":"n2139824802","loc":[-85.6347753,41.9457252]},"n2139824803":{"id":"n2139824803","loc":[-85.6340521,41.9457473]},"n2139824804":{"id":"n2139824804","loc":[-85.6352875,41.9457611]},"n2139824805":{"id":"n2139824805","loc":[-85.6352941,41.9457611]},"n2139824806":{"id":"n2139824806","loc":[-85.6350758,41.9457623]},"n2139824807":{"id":"n2139824807","loc":[-85.6348194,41.9457638]},"n2139824808":{"id":"n2139824808","loc":[-85.635296,41.9459428]},"n2139824809":{"id":"n2139824809","loc":[-85.6348212,41.9459455]},"n2139832635":{"id":"n2139832635","loc":[-85.6354612,41.9448791]},"n2139832636":{"id":"n2139832636","loc":[-85.6360241,41.9453844]},"n2139832637":{"id":"n2139832637","loc":[-85.6361452,41.9453121]},"n2139832639":{"id":"n2139832639","loc":[-85.6355997,41.944797]},"n2139832641":{"id":"n2139832641","loc":[-85.6351346,41.9443541]},"n2139832647":{"id":"n2139832647","loc":[-85.6329883,41.9453692]},"n2139832653":{"id":"n2139832653","loc":[-85.6333643,41.9456293]},"n2139832663":{"id":"n2139832663","loc":[-85.6335394,41.9455339]},"n2139832665":{"id":"n2139832665","loc":[-85.6332375,41.9452476]},"n2139832667":{"id":"n2139832667","loc":[-85.6331664,41.9452161]},"n2139832669":{"id":"n2139832669","loc":[-85.6331144,41.9451875]},"n2139832671":{"id":"n2139832671","loc":[-85.6330779,41.9451274]},"n2139832673":{"id":"n2139832673","loc":[-85.6330664,41.9450802]},"n2139832678":{"id":"n2139832678","loc":[-85.6332218,41.9453585]},"n2139832686":{"id":"n2139832686","loc":[-85.6334246,41.945541]},"n2139832691":{"id":"n2139832691","loc":[-85.6329898,41.9454997]},"n2139832693":{"id":"n2139832693","loc":[-85.6343554,41.9443274]},"n2139832694":{"id":"n2139832694","loc":[-85.6336339,41.9437089]},"n2139832696":{"id":"n2139832696","loc":[-85.633532,41.9437708]},"n2139832697":{"id":"n2139832697","loc":[-85.6338316,41.9440868]},"n2139832698":{"id":"n2139832698","loc":[-85.6342258,41.9444141]},"n2139832699":{"id":"n2139832699","loc":[-85.6339164,41.9442166]},"n2139832700":{"id":"n2139832700","loc":[-85.6341389,41.944384]},"n2139832701":{"id":"n2139832701","loc":[-85.634235,41.9443259]},"n2139832702":{"id":"n2139832702","loc":[-85.633613,41.9437875]},"n2139832703":{"id":"n2139832703","loc":[-85.633915,41.9436132]},"n2139832704":{"id":"n2139832704","loc":[-85.6340019,41.9435613]},"n2139832706":{"id":"n2139832706","loc":[-85.6343197,41.9438427]},"n2139832708":{"id":"n2139832708","loc":[-85.6342361,41.9438936]},"n2139832709":{"id":"n2139832709","loc":[-85.6353839,41.9460401]},"n2139832710":{"id":"n2139832710","loc":[-85.6354032,41.9456763]},"n2139832711":{"id":"n2139832711","loc":[-85.6356839,41.9459252]},"n2139832712":{"id":"n2139832712","loc":[-85.6356109,41.945735]},"n2139832713":{"id":"n2139832713","loc":[-85.6353997,41.9457421]},"n2139832714":{"id":"n2139832714","loc":[-85.6353895,41.9459347]},"n2139832715":{"id":"n2139832715","loc":[-85.6334777,41.9436628]},"n2139832716":{"id":"n2139832716","loc":[-85.6333137,41.9435382]},"n2139832717":{"id":"n2139832717","loc":[-85.6330938,41.9435406]},"n2139832721":{"id":"n2139832721","loc":[-85.6333023,41.9434922]},"n2139832722":{"id":"n2139832722","loc":[-85.6330466,41.943623]},"n2139832723":{"id":"n2139832723","loc":[-85.6332746,41.9435624]},"n2139832724":{"id":"n2139832724","loc":[-85.6333511,41.9435176]},"n2139832725":{"id":"n2139832725","loc":[-85.6332241,41.9434001]},"n2139832726":{"id":"n2139832726","loc":[-85.6332355,41.9433686]},"n2139870373":{"id":"n2139870373","loc":[-85.6351783,41.9439117]},"n2139870374":{"id":"n2139870374","loc":[-85.6351431,41.9439217]},"n2139870375":{"id":"n2139870375","loc":[-85.6348853,41.9439117]},"n2139870376":{"id":"n2139870376","loc":[-85.6348317,41.9439105]},"n2139870377":{"id":"n2139870377","loc":[-85.6346384,41.944007]},"n2139870378":{"id":"n2139870378","loc":[-85.6345563,41.9440523]},"n2140006403":{"id":"n2140006403","loc":[-85.6359942,41.9450097]},"n2140006405":{"id":"n2140006405","loc":[-85.6363884,41.9446079]},"n2140006407":{"id":"n2140006407","loc":[-85.6362148,41.9447874]},"n2140006409":{"id":"n2140006409","loc":[-85.6379476,41.9445869]},"n2140006411":{"id":"n2140006411","loc":[-85.6378485,41.9445674]},"n2140006413":{"id":"n2140006413","loc":[-85.6378952,41.9444547]},"n2140006415":{"id":"n2140006415","loc":[-85.6379962,41.944477]},"n2140006421":{"id":"n2140006421","loc":[-85.6355248,41.9433702]},"n2140006423":{"id":"n2140006423","loc":[-85.6378471,41.9439233]},"n2140006425":{"id":"n2140006425","loc":[-85.6378913,41.9441238]},"n2140006426":{"id":"n2140006426","loc":[-85.6381674,41.9442289]},"n2140006427":{"id":"n2140006427","loc":[-85.6382359,41.9440975]},"n2140006428":{"id":"n2140006428","loc":[-85.6382071,41.9440252]},"n2140006429":{"id":"n2140006429","loc":[-85.6381409,41.9439973]},"n2140006430":{"id":"n2140006430","loc":[-85.6380569,41.9440153]},"n2140006433":{"id":"n2140006433","loc":[-85.6379071,41.9442467]},"n2140006435":{"id":"n2140006435","loc":[-85.6381634,41.9443125]},"n2140006436":{"id":"n2140006436","loc":[-85.6382407,41.944301]},"n2140006438":{"id":"n2140006438","loc":[-85.6382761,41.9442188]},"n2140006439":{"id":"n2140006439","loc":[-85.6382429,41.9441761]},"n2140006440":{"id":"n2140006440","loc":[-85.6382016,41.9441632]},"n2140006441":{"id":"n2140006441","loc":[-85.6378185,41.9439835]},"n2166205688":{"id":"n2166205688","loc":[-85.6349963,41.9444392]},"n2168544780":{"id":"n2168544780","loc":[-85.633944,41.945807]},"n2168544781":{"id":"n2168544781","loc":[-85.6340783,41.9458621]},"n2168544782":{"id":"n2168544782","loc":[-85.6338184,41.9457548]},"n2168544783":{"id":"n2168544783","loc":[-85.6339925,41.9459777]},"n2168544784":{"id":"n2168544784","loc":[-85.6337317,41.9458698]},"n2168544785":{"id":"n2168544785","loc":[-85.6337297,41.9460042]},"n2168544786":{"id":"n2168544786","loc":[-85.633919,41.9460797]},"n2168544787":{"id":"n2168544787","loc":[-85.6338672,41.9459263]},"n2168544788":{"id":"n2168544788","loc":[-85.6338246,41.9459853]},"n2168544789":{"id":"n2168544789","loc":[-85.6337615,41.9459601]},"n2168544790":{"id":"n2168544790","loc":[-85.6342079,41.9460399]},"n2168544791":{"id":"n2168544791","loc":[-85.6343346,41.9458503]},"n2168544792":{"id":"n2168544792","loc":[-85.6343759,41.9458116]},"n2168544793":{"id":"n2168544793","loc":[-85.6344394,41.9458109]},"n2168544795":{"id":"n2168544795","loc":[-85.6344827,41.945851]},"n2168544797":{"id":"n2168544797","loc":[-85.6344807,41.945969]},"n2168544798":{"id":"n2168544798","loc":[-85.6344404,41.9459697]},"n2168544799":{"id":"n2168544799","loc":[-85.6344413,41.9460333]},"n2168544800":{"id":"n2168544800","loc":[-85.6342173,41.9460705]},"n2168544801":{"id":"n2168544801","loc":[-85.6342162,41.9460392]},"n2168544802":{"id":"n2168544802","loc":[-85.6344251,41.9460351]},"n2168544805":{"id":"n2168544805","loc":[-85.6344257,41.9460507]},"n2168544807":{"id":"n2168544807","loc":[-85.6344721,41.9460498]},"n2168544809":{"id":"n2168544809","loc":[-85.6344754,41.9461427]},"n2168544811":{"id":"n2168544811","loc":[-85.6344311,41.9461435]},"n2168544813":{"id":"n2168544813","loc":[-85.6344317,41.9461592]},"n2168544815":{"id":"n2168544815","loc":[-85.6343708,41.9461604]},"n2168544817":{"id":"n2168544817","loc":[-85.6343715,41.9461786]},"n2168544819":{"id":"n2168544819","loc":[-85.6343229,41.9461795]},"n2168544821":{"id":"n2168544821","loc":[-85.6343222,41.9461606]},"n2168544823":{"id":"n2168544823","loc":[-85.6342476,41.9461621]},"n2168544825":{"id":"n2168544825","loc":[-85.6342444,41.94607]},"n2168544827":{"id":"n2168544827","loc":[-85.634138,41.9461632]},"n2168544829":{"id":"n2168544829","loc":[-85.6342016,41.9460703]},"n2168544830":{"id":"n2168544830","loc":[-85.6332929,41.9463092]},"n2168544831":{"id":"n2168544831","loc":[-85.633122,41.946239]},"n2168544832":{"id":"n2168544832","loc":[-85.6332954,41.9460055]},"n2168544833":{"id":"n2168544833","loc":[-85.6333954,41.9460466]},"n2168544834":{"id":"n2168544834","loc":[-85.6334044,41.9460345]},"n2168544835":{"id":"n2168544835","loc":[-85.6334594,41.9460571]},"n2168544836":{"id":"n2168544836","loc":[-85.6333871,41.9461544]},"n2168544837":{"id":"n2168544837","loc":[-85.633403,41.9461609]},"n2168544838":{"id":"n2168544838","loc":[-85.6341683,41.9464167]},"n2168544839":{"id":"n2168544839","loc":[-85.6341711,41.9463411]},"n2168544840":{"id":"n2168544840","loc":[-85.6344471,41.9463469]},"n2168544841":{"id":"n2168544841","loc":[-85.6344441,41.9464243]},"n2168544842":{"id":"n2168544842","loc":[-85.6343622,41.9464226]},"n2168544843":{"id":"n2168544843","loc":[-85.6343593,41.9464989]},"n2168544844":{"id":"n2168544844","loc":[-85.6342812,41.9464973]},"n2168544845":{"id":"n2168544845","loc":[-85.634283,41.9464504]},"n2168544846":{"id":"n2168544846","loc":[-85.6342609,41.9464499]},"n2168544847":{"id":"n2168544847","loc":[-85.6342621,41.9464187]},"n2168544848":{"id":"n2168544848","loc":[-85.6348414,41.9463396]},"n2168544849":{"id":"n2168544849","loc":[-85.6348387,41.9461872]},"n2168544850":{"id":"n2168544850","loc":[-85.6351186,41.9461844]},"n2168544851":{"id":"n2168544851","loc":[-85.635119,41.9462112]},"n2168544852":{"id":"n2168544852","loc":[-85.6351918,41.9462104]},"n2168544853":{"id":"n2168544853","loc":[-85.6351944,41.9463515]},"n2168544854":{"id":"n2168544854","loc":[-85.6351049,41.9463524]},"n2168544855":{"id":"n2168544855","loc":[-85.6351046,41.946337]},"n2189153180":{"id":"n2189153180","loc":[-85.6340369,41.9469572]},"n2189153181":{"id":"n2189153181","loc":[-85.6342531,41.946953]},"n2189153183":{"id":"n2189153183","loc":[-85.6348115,41.9465468]},"n2189153184":{"id":"n2189153184","loc":[-85.6348105,41.9464569]},"n2189153185":{"id":"n2189153185","loc":[-85.6351431,41.9464549]},"n2189153186":{"id":"n2189153186","loc":[-85.6351441,41.9465448]},"n2189153187":{"id":"n2189153187","loc":[-85.6350077,41.9465456]},"n2189153188":{"id":"n2189153188","loc":[-85.635008,41.9465721]},"n2189153189":{"id":"n2189153189","loc":[-85.6348965,41.9465727]},"n2189153190":{"id":"n2189153190","loc":[-85.6348962,41.9465463]},"n2189153191":{"id":"n2189153191","loc":[-85.6348963,41.9471586]},"n2189153192":{"id":"n2189153192","loc":[-85.6348944,41.947032]},"n2189153193":{"id":"n2189153193","loc":[-85.6350241,41.947031]},"n2189153194":{"id":"n2189153194","loc":[-85.635026,41.9471575]},"n2189153195":{"id":"n2189153195","loc":[-85.6352328,41.9471053]},"n2189153196":{"id":"n2189153196","loc":[-85.6352359,41.9469906]},"n2189153197":{"id":"n2189153197","loc":[-85.6353694,41.9469925]},"n2189153198":{"id":"n2189153198","loc":[-85.6353664,41.9471072]},"n2189153199":{"id":"n2189153199","loc":[-85.6348241,41.9469287]},"n2189153200":{"id":"n2189153200","loc":[-85.6348248,41.9468185]},"n2189153201":{"id":"n2189153201","loc":[-85.6351199,41.9468195]},"n2189153202":{"id":"n2189153202","loc":[-85.6351192,41.9469298]},"n2189153203":{"id":"n2189153203","loc":[-85.6347965,41.9468057]},"n2189153204":{"id":"n2189153204","loc":[-85.634792,41.9466044]},"n2189153205":{"id":"n2189153205","loc":[-85.6349483,41.9466025]},"n2189153206":{"id":"n2189153206","loc":[-85.6349493,41.9466448]},"n2189153207":{"id":"n2189153207","loc":[-85.6349753,41.9466445]},"n2189153208":{"id":"n2189153208","loc":[-85.6349743,41.9465995]},"n2189153209":{"id":"n2189153209","loc":[-85.6351173,41.9465977]},"n2189153210":{"id":"n2189153210","loc":[-85.6351219,41.9468015]},"n2189153211":{"id":"n2189153211","loc":[-85.6349806,41.9468032]},"n2189153212":{"id":"n2189153212","loc":[-85.6349794,41.9467519]},"n2189153213":{"id":"n2189153213","loc":[-85.6349521,41.9467523]},"n2189153214":{"id":"n2189153214","loc":[-85.6349532,41.9468037]},"n2189153215":{"id":"n2189153215","loc":[-85.6346302,41.9468381]},"n2189153216":{"id":"n2189153216","loc":[-85.6343028,41.9468449]},"n2189153217":{"id":"n2189153217","loc":[-85.6342006,41.9468297]},"n2189153218":{"id":"n2189153218","loc":[-85.6336698,41.9465918]},"n2189153219":{"id":"n2189153219","loc":[-85.6344663,41.9466639]},"n2189153220":{"id":"n2189153220","loc":[-85.6344639,41.9466015]},"n2189153221":{"id":"n2189153221","loc":[-85.6342283,41.9466065]},"n2189153222":{"id":"n2189153222","loc":[-85.6342303,41.9466587]},"n2189153223":{"id":"n2189153223","loc":[-85.6342843,41.9466575]},"n2189153224":{"id":"n2189153224","loc":[-85.6342851,41.9466794]},"n2189153225":{"id":"n2189153225","loc":[-85.6343475,41.9466781]},"n2189153226":{"id":"n2189153226","loc":[-85.634347,41.9466664]},"n2189153227":{"id":"n2189153227","loc":[-85.6354428,41.9470148]},"n2189153228":{"id":"n2189153228","loc":[-85.6354432,41.9468005]},"n2189153229":{"id":"n2189153229","loc":[-85.6360277,41.9468011]},"n2189153230":{"id":"n2189153230","loc":[-85.6360273,41.9470154]},"n2189153231":{"id":"n2189153231","loc":[-85.6354565,41.9465823]},"n2189153232":{"id":"n2189153232","loc":[-85.6354496,41.946218]},"n2189153233":{"id":"n2189153233","loc":[-85.6356355,41.9465788]},"n2189153234":{"id":"n2189153234","loc":[-85.6357155,41.9468008]},"n2189153235":{"id":"n2189153235","loc":[-85.6359539,41.9467969]},"n2189153236":{"id":"n2189153236","loc":[-85.6359561,41.9463036]},"n2189153237":{"id":"n2189153237","loc":[-85.6360129,41.9464793]},"n2189153238":{"id":"n2189153238","loc":[-85.6360152,41.9463898]},"n2189153239":{"id":"n2189153239","loc":[-85.6359607,41.9464928]},"n2189153240":{"id":"n2189153240","loc":[-85.6356903,41.9462227]},"n2189153242":{"id":"n2189153242","loc":[-85.6354163,41.946142]},"n2189153243":{"id":"n2189153243","loc":[-85.6357546,41.9462214]},"n2189153244":{"id":"n2189153244","loc":[-85.6357937,41.9462542]},"n2189153245":{"id":"n2189153245","loc":[-85.6358723,41.9467048]},"n2189153246":{"id":"n2189153246","loc":[-85.6361494,41.946757]},"n2189153247":{"id":"n2189153247","loc":[-85.6354173,41.9469082]},"n2189153248":{"id":"n2189153248","loc":[-85.635443,41.9469079]},"n2189153249":{"id":"n2189153249","loc":[-85.6360275,41.9469093]},"n2189153250":{"id":"n2189153250","loc":[-85.6361542,41.946915]},"n2189153251":{"id":"n2189153251","loc":[-85.6358654,41.9464843]},"n2189153252":{"id":"n2189153252","loc":[-85.6359549,41.9467499]},"n2189153253":{"id":"n2189153253","loc":[-85.6357172,41.9466335]},"n2189153254":{"id":"n2189153254","loc":[-85.6355644,41.9461768]},"n2189153255":{"id":"n2189153255","loc":[-85.6355655,41.946528]},"n2189153256":{"id":"n2189153256","loc":[-85.6357055,41.9465971]},"n2189153257":{"id":"n2189153257","loc":[-85.635869,41.9465971]},"n2189153259":{"id":"n2189153259","loc":[-85.6354561,41.9470278]},"n2189153260":{"id":"n2189153260","loc":[-85.6357961,41.9470233]},"n2189153261":{"id":"n2189153261","loc":[-85.6357977,41.9470907]},"n2189153262":{"id":"n2189153262","loc":[-85.6357297,41.9470916]},"n2189153263":{"id":"n2189153263","loc":[-85.635733,41.947233]},"n2189153264":{"id":"n2189153264","loc":[-85.6362674,41.9468637]},"n2189153265":{"id":"n2189153265","loc":[-85.6362646,41.9467047]},"n2189153266":{"id":"n2189153266","loc":[-85.6363267,41.9467047]},"n2189153267":{"id":"n2189153267","loc":[-85.6362633,41.9465848]},"n2189153268":{"id":"n2189153268","loc":[-85.6363805,41.9465468]},"n2189153269":{"id":"n2189153269","loc":[-85.6364604,41.9466842]},"n2189153270":{"id":"n2189153270","loc":[-85.6364604,41.9468647]},"n2199109756":{"id":"n2199109756","loc":[-85.6337134,41.9471841]},"n2199109757":{"id":"n2199109757","loc":[-85.6336514,41.94716]},"n2199109758":{"id":"n2199109758","loc":[-85.6337043,41.9470847]},"n2199109759":{"id":"n2199109759","loc":[-85.6335997,41.9470441]},"n2199109760":{"id":"n2199109760","loc":[-85.6335064,41.9471771]},"n185960195":{"id":"n185960195","loc":[-85.6295992,41.9524346]},"n185960796":{"id":"n185960796","loc":[-85.634723,41.953681]},"n185961396":{"id":"n185961396","loc":[-85.634767,41.959009]},"n185962625":{"id":"n185962625","loc":[-85.635175,41.97201]},"n185964982":{"id":"n185964982","loc":[-85.632799,41.9440543]},"n185965289":{"id":"n185965289","loc":[-85.634621,41.947323]},"n185965291":{"id":"n185965291","loc":[-85.636166,41.947296]},"n185965399":{"id":"n185965399","loc":[-85.634776,41.959834]},"n185966937":{"id":"n185966937","loc":[-85.633183,41.947315]},"n185966948":{"id":"n185966948","loc":[-85.626406,41.957188]},"n185967422":{"id":"n185967422","loc":[-85.6320229,41.9490123]},"n185967917":{"id":"n185967917","loc":[-85.634763,41.958292]},"n185967918":{"id":"n185967918","loc":[-85.636271,41.958311]},"n185968100":{"id":"n185968100","loc":[-85.630835,41.950656]},"n185970515":{"id":"n185970515","loc":[-85.634832,41.963866]},"n185971578":{"id":"n185971578","loc":[-85.634641,41.948627]},"n185971580":{"id":"n185971580","loc":[-85.6361818,41.9486135]},"n185971631":{"id":"n185971631","loc":[-85.634729,41.954667]},"n185971632":{"id":"n185971632","loc":[-85.636236,41.954656]},"n185972155":{"id":"n185972155","loc":[-85.623333,41.961987]},"n185974583":{"id":"n185974583","loc":[-85.634686,41.951158]},"n185974585":{"id":"n185974585","loc":[-85.6362059,41.9511457]},"n185975064":{"id":"n185975064","loc":[-85.636218,41.953667]},"n185975735":{"id":"n185975735","loc":[-85.634923,41.969269]},"n185978390":{"id":"n185978390","loc":[-85.634668,41.949875]},"n185978392":{"id":"n185978392","loc":[-85.634686,41.952415]},"n185978394":{"id":"n185978394","loc":[-85.634726,41.955921]},"n185978399":{"id":"n185978399","loc":[-85.6347861,41.9606613]},"n185978402":{"id":"n185978402","loc":[-85.634806,41.961485]},"n185978406":{"id":"n185978406","loc":[-85.6348298,41.964783]},"n185978410":{"id":"n185978410","loc":[-85.6348766,41.9677088]},"n185978414":{"id":"n185978414","loc":[-85.634938,41.971566]},"n185978415":{"id":"n185978415","loc":[-85.634942,41.971611]},"n185978417":{"id":"n185978417","loc":[-85.634952,41.971655]},"n185978419":{"id":"n185978419","loc":[-85.634989,41.971741]},"n185978420":{"id":"n185978420","loc":[-85.635063,41.971864]},"n185978787":{"id":"n185978787","loc":[-85.627936,41.954693]},"n185978790":{"id":"n185978790","loc":[-85.626832,41.954677]},"n185978967":{"id":"n185978967","loc":[-85.632278,41.948613]},"n185980735":{"id":"n185980735","loc":[-85.628639,41.953725]},"n185982163":{"id":"n185982163","loc":[-85.636233,41.952398]},"n185982193":{"id":"n185982193","loc":[-85.6313855,41.9499125]},"n185982195":{"id":"n185982195","loc":[-85.6304857,41.9511945]},"n185982196":{"id":"n185982196","loc":[-85.626336,41.957291]},"n185982197":{"id":"n185982197","loc":[-85.625578,41.958664]},"n185982198":{"id":"n185982198","loc":[-85.624619,41.960145]},"n185982200":{"id":"n185982200","loc":[-85.624494,41.960338]},"n185984017":{"id":"n185984017","loc":[-85.636163,41.947382]},"n185984020":{"id":"n185984020","loc":[-85.636188,41.9498803]},"n185984022":{"id":"n185984022","loc":[-85.636276,41.955919]},"n185984024":{"id":"n185984024","loc":[-85.636279,41.956901]},"n185988036":{"id":"n185988036","loc":[-85.631422,41.948294]},"n185988867":{"id":"n185988867","loc":[-85.63102,41.948805]},"n185988869":{"id":"n185988869","loc":[-85.630773,41.949209]},"n185988871":{"id":"n185988871","loc":[-85.63005,41.95016]},"n185988872":{"id":"n185988872","loc":[-85.629423,41.951016]},"n185988873":{"id":"n185988873","loc":[-85.629252,41.951256]},"n185988875":{"id":"n185988875","loc":[-85.629126,41.951489]},"n185988877":{"id":"n185988877","loc":[-85.628991,41.951704]},"n185988878":{"id":"n185988878","loc":[-85.628689,41.952112]},"n185988879":{"id":"n185988879","loc":[-85.628313,41.952666]},"n185988880":{"id":"n185988880","loc":[-85.627687,41.953529]},"n185988882":{"id":"n185988882","loc":[-85.627394,41.953947]},"n185988884":{"id":"n185988884","loc":[-85.627287,41.954128]},"n1819858502":{"id":"n1819858502","loc":[-85.6328435,41.9455473]},"n1819858510":{"id":"n1819858510","loc":[-85.6324841,41.9453438]},"n1819858515":{"id":"n1819858515","loc":[-85.6318511,41.9446409]},"n1819858520":{"id":"n1819858520","loc":[-85.6326558,41.9454708]},"n1819858522":{"id":"n1819858522","loc":[-85.6319048,41.9447407]},"n1819858524":{"id":"n1819858524","loc":[-85.6317718,41.9443666]},"n1819858530":{"id":"n1819858530","loc":[-85.632055,41.9449128]},"n2139795768":{"id":"n2139795768","loc":[-85.6243023,41.9606102]},"n2139832645":{"id":"n2139832645","loc":[-85.6324455,41.9448607]},"n2139832649":{"id":"n2139832649","loc":[-85.6328043,41.9454773]},"n2139832651":{"id":"n2139832651","loc":[-85.6322547,41.9449621]},"n2139832675":{"id":"n2139832675","loc":[-85.6327356,41.944757]},"n2139832677":{"id":"n2139832677","loc":[-85.6325433,41.9448599]},"n2139832680":{"id":"n2139832680","loc":[-85.6328885,41.9455614]},"n2139832682":{"id":"n2139832682","loc":[-85.6320913,41.9449492]},"n2139832684":{"id":"n2139832684","loc":[-85.6325366,41.9447133]},"n2139832688":{"id":"n2139832688","loc":[-85.6322786,41.94485]},"n2139832718":{"id":"n2139832718","loc":[-85.6327486,41.9432475]},"n2139832719":{"id":"n2139832719","loc":[-85.6327926,41.9431773]},"n2139832720":{"id":"n2139832720","loc":[-85.6329033,41.943153]},"n2139832727":{"id":"n2139832727","loc":[-85.6328975,41.9430783]},"n2139844839":{"id":"n2139844839","loc":[-85.6326261,41.9432308]},"n2189015992":{"id":"n2189015992","loc":[-85.6347706,41.9593383]},"n2189153179":{"id":"n2189153179","loc":[-85.6340476,41.9472565]},"n2189153182":{"id":"n2189153182","loc":[-85.6342638,41.9472522]},"n2189153241":{"id":"n2189153241","loc":[-85.6354184,41.9473091]},"n2189153258":{"id":"n2189153258","loc":[-85.6354611,41.9472366]},"n2189153277":{"id":"n2189153277","loc":[-85.6328948,41.9462374]},"n2199109755":{"id":"n2199109755","loc":[-85.6336729,41.9472417]},"w203970139":{"id":"w203970139","tags":{"building":"yes"},"nodes":["n2139824793","n2139824787","n2139824773","n2139824778","n2139824793"]},"w203970098":{"id":"w203970098","tags":{"building":"yes"},"nodes":["n2139824748","n2139824712","n2139824726","n2139824760","n2139824748"]},"w208643132":{"id":"w208643132","tags":{"area":"yes","building":"yes"},"nodes":["n2189153195","n2189153196","n2189153197","n2189153198","n2189153195"]},"w203970094":{"id":"w203970094","tags":{"building":"yes"},"nodes":["n2139824755","n2139824753","n2139824759","n2139824764","n2139824763","n2139824767","n2139824770","n2139824782","n2139824772","n2139824756","n2139824751","n2139824754","n2139824755"]},"w208643138":{"id":"w208643138","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153231","n2189153232","n2189153240","n2189153244","n2189153236","n2189153238","n2189153237","n2189153239","n2189153252","n2189153235","n2189153234","n2189153253","n2189153233","n2189153231"]},"w203970125":{"id":"w203970125","tags":{"building":"yes"},"nodes":["n2139824735","n2139824738","n2139824757","n2139824749","n2139824735"]},"w170848823":{"id":"w170848823","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819849189","n1819858516","n1819858519","n1819858504","n1819858525","n1819858506","n1819858513"]},"w203970898":{"id":"w203970898","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832645","n2139832647","n2139832649","n2139832651","n2139832645"]},"w203970134":{"id":"w203970134","tags":{"building":"yes"},"nodes":["n2139824796","n2139824803","n2139824797","n2139824788","n2139824796"]},"w203970104":{"id":"w203970104","tags":{"building":"yes"},"nodes":["n2139824733","n2139824730","n2139824714","n2139824721","n2139824733"]},"w206805245":{"id":"w206805245","tags":{"area":"yes","building":"yes"},"nodes":["n2168544780","n2168544781","n2139824796","n2139824803","n2168544780"]},"w206805252":{"id":"w206805252","tags":{"area":"yes","building":"yes"},"nodes":["n2168544838","n2168544839","n2168544840","n2168544841","n2168544842","n2168544843","n2168544844","n2168544845","n2168544846","n2168544847","n2168544838"]},"w203970099":{"id":"w203970099","tags":{"building":"yes"},"nodes":["n2139824783","n2139824795","n2139824790","n2139824779","n2139824783"]},"w17967730":{"id":"w17967730","tags":{"highway":"residential","name":"Water St"},"nodes":["n185963451","n2189153277","n185988036","n185988867","n185988869","n185988871","n185988872","n185988873","n185988875","n185988877","n185988878","n185988879","n185988880","n185988882","n185988884","n185978790"]},"w208643133":{"id":"w208643133","tags":{"area":"yes","building":"yes"},"nodes":["n2189153199","n2189153200","n2189153201","n2189153202","n2189153199"]},"w203970127":{"id":"w203970127","tags":{"building":"yes"},"nodes":["n2139824794","n2139824783","n2139824789","n2139824797","n2139824794"]},"w208643139":{"id":"w208643139","tags":{"highway":"service"},"nodes":["n185988237","n2189153242","n2189153247","n2189153241"]},"w203988297":{"id":"w203988297","tags":{"amenity":"parking","area":"yes"},"nodes":["n2140006423","n2140006441","n2140006425","n2140006426","n2140006440","n2140006427","n2140006428","n2140006429","n2140006430","n2140006423"]},"w206805250":{"id":"w206805250","tags":{"area":"yes","building":"yes"},"nodes":["n2168544827","n2168544823","n2168544825","n2168544800","n2168544829","n2168544827"]},"w208643140":{"id":"w208643140","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153242","n2189153254","n2189153243","n2189153244","n2189153251","n2189153257","n2189153245","n2189153252","n2189153246"]},"w203974055":{"id":"w203974055","tags":{"bridge":"yes","highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870376","n2139870377"]},"w206805247":{"id":"w206805247","tags":{"area":"yes","building":"yes"},"nodes":["n2168544785","n2168544786","n2168544783","n2168544787","n2168544788","n2168544789","n2168544785"]},"w17964996":{"id":"w17964996","tags":{"highway":"residential","name":"Foster St"},"nodes":["n1819858524","n1819858515","n1819858522","n1819858530","n2139832682","n1819858510","n1819858520","n1819858502","n2139832680","n185963451","n1819858527","n185963452","n185963453","n185963454","n185963455","n185963456"]},"w208643144":{"id":"w208643144","tags":{"area":"yes","building":"yes"},"nodes":["n2189153264","n2189153265","n2189153266","n2189153267","n2189153268","n2189153269","n2189153270","n2189153264"]},"w203970914":{"id":"w203970914","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832722","n2139832723","n2139832724","n2139832725","n2139832726","n2139832727","n2139844839","n2139832722"]},"w208643143":{"id":"w208643143","tags":{"area":"yes","building":"yes"},"nodes":["n2189153258","n2189153259","n2189153260","n2189153261","n2189153262","n2189153263","n2189153258"]},"w203049590":{"id":"w203049590","tags":{"amenity":"parking","area":"yes"},"nodes":["n2130304152","n2130304153","n2140006403","n2130304154","n2130304156","n2130304155","n2130304160","n2130304152"]},"w203974054":{"id":"w203974054","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858971","n2139870373","n2139870374"]},"w203049595":{"id":"w203049595","tags":{"highway":"service"},"nodes":["n2130304158","n2130304159","n2130304160","n2139832635","n2139832639"]},"w203970913":{"id":"w203970913","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832715","n2139832716","n2139832717","n2139832718","n2139832719","n2139832720","n2139832721","n2139832716"]},"w208643134":{"id":"w208643134","tags":{"area":"yes","building":"yes"},"nodes":["n2189153203","n2189153204","n2189153205","n2189153206","n2189153207","n2189153208","n2189153209","n2189153210","n2189153211","n2189153212","n2189153213","n2189153214","n2189153203"]},"w134150808":{"id":"w134150808","tags":{"bridge":"yes","highway":"residential","name":"Moore St"},"nodes":["n185988239","n185984009","n185988241","n1475284019"]},"w203970115":{"id":"w203970115","tags":{"building":"yes"},"nodes":["n2139824761","n2139824727","n2139824736","n2139824771","n2139824761"]},"w208643130":{"id":"w208643130","tags":{"area":"yes","building":"yes"},"nodes":["n2189153183","n2189153184","n2189153185","n2189153186","n2189153187","n2189153188","n2189153189","n2189153190","n2189153183"]},"w206805246":{"id":"w206805246","tags":{"area":"yes","building":"yes"},"nodes":["n2168544782","n2168544780","n2168544781","n2168544783","n2168544787","n2168544784","n2168544782"]},"w203970138":{"id":"w203970138","tags":{"building":"yes"},"nodes":["n2139824729","n2139824720","n2139824702","n2139824707","n2139824729"]},"w203970133":{"id":"w203970133","tags":{"building":"yes"},"nodes":["n2139824748","n2139824737","n2139824717","n2139824728","n2139824748"]},"w203970907":{"id":"w203970907","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832700","n2139832701","n2139832702"]},"w203974056":{"id":"w203974056","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870377","n2139870378"]},"w203970897":{"id":"w203970897","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2130304156","n2166205688","n2139832635","n2139832636","n2139832637","n2139832639","n2139832641","n2166205688"]},"w203974057":{"id":"w203974057","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870375","n2139870376"]},"w203049594":{"id":"w203049594","tags":{"highway":"service"},"nodes":["n2130304156","n2139870378","n2139832706","n2139832704","n2130304157"]},"w203970122":{"id":"w203970122","tags":{"building":"yes"},"nodes":["n2139824757","n2139824740","n2139824747","n2139824762","n2139824757"]},"w208643136":{"id":"w208643136","tags":{"area":"yes","building":"yes"},"nodes":["n2189153219","n2189153220","n2189153221","n2189153222","n2189153223","n2189153224","n2189153225","n2189153226","n2189153219"]},"w203970128":{"id":"w203970128","tags":{"building":"yes"},"nodes":["n2139824732","n2139824752","n2139824744","n2139824724","n2139824732"]},"w203970097":{"id":"w203970097","tags":{"building":"yes"},"nodes":["n2139824737","n2139824733","n2139824710","n2139824716","n2139824737"]},"w203970137":{"id":"w203970137","tags":{"building":"yes"},"nodes":["n2139824765","n2139824774","n2139824758","n2139824746","n2139824765"]},"w134150840":{"id":"w134150840","tags":{"highway":"residential","name":"Moore St"},"nodes":["n1475284019","n185988243","n185988244","n185988245"]},"w17967628":{"id":"w17967628","tags":{"highway":"residential","name":"Moore St"},"nodes":["n185978388","n2139832709","n185988237","n185988239"]},"w203988292":{"id":"w203988292","tags":{"bridge":"yes","highway":"footway"},"nodes":["n2140006407","n2140006405"]},"w203970118":{"id":"w203970118","tags":{"building":"yes"},"nodes":["n2139824775","n2139824785","n2139824780","n2139824768","n2139824775"]},"w203970121":{"id":"w203970121","tags":{"building":"yes"},"nodes":["n2139824768","n2139824781","n2139824776","n2139824765","n2139824768"]},"w17967752":{"id":"w17967752","tags":{"highway":"residential","name":"Railroad Drive"},"nodes":["n185964980","n2139832699","n2139832700","n2130304158","n185988969","n185988971","n185988972","n1475284011"]},"w203970136":{"id":"w203970136","tags":{"building":"yes"},"nodes":["n2139824798","n2139824793","n2139824777","n2139824784","n2139824798"]},"w203970142":{"id":"w203970142","tags":{"building":"yes"},"nodes":["n2139824808","n2139824809","n2139824807","n2139824806","n2139824801","n2139824800","n2139824804","n2139824805","n2139824808"]},"w208643137":{"id":"w208643137","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153227","n2189153248","n2189153228","n2189153234","n2189153235","n2189153229","n2189153249","n2189153230","n2189153227"]},"w208643129":{"id":"w208643129","tags":{"area":"yes","building":"yes"},"nodes":["n2189153179","n2189153180","n2189153181","n2189153182","n2189153179"]},"w203970909":{"id":"w203970909","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832703","n2139832704","n2139832706","n2139832708","n2139832703"]},"w203970905":{"id":"w203970905","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832688","n2139832691"]},"w203988298":{"id":"w203988298","tags":{"highway":"service"},"nodes":["n2140006431","n2140006433","n2140006435","n2140006436","n2140006437","n2140006438","n2140006439","n2140006440"]},"w203970106":{"id":"w203970106","tags":{"building":"yes"},"nodes":["n2139824798","n2139824791","n2139824799","n2139824802","n2139824798"]},"w203970129":{"id":"w203970129","tags":{"building":"yes"},"nodes":["n2139824787","n2139824782","n2139824766","n2139824769","n2139824787"]},"w208643131":{"id":"w208643131","tags":{"area":"yes","building":"yes"},"nodes":["n2189153191","n2189153192","n2189153193","n2189153194","n2189153191"]},"w206805249":{"id":"w206805249","tags":{"area":"yes","building":"yes"},"nodes":["n2168544800","n2168544801","n2168544802","n2168544805","n2168544807","n2168544809","n2168544811","n2168544813","n2168544815","n2168544817","n2168544819","n2168544821","n2168544823","n2168544825","n2168544800"]},"w134150800":{"id":"w134150800","tags":{"bridge":"yes","highway":"primary","name":"W Michigan Ave","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964972","n185964976"]},"w17966984":{"id":"w17966984","tags":{"highway":"residential","name":"Portage Avenue"},"nodes":["n185978375","n185963456","n2189153218","n185966937","n185978967","n185967422","n185982193","n185968100","n185982195","n185960195","n185980735","n185978787","n185966948","n185982196","n185982197","n185982198","n185982200","n2139795768","n185972155"]},"w203988294":{"id":"w203988294","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2140006409","n2140006411","n2140006413","n2140006415","n2140006409"]},"w203970912":{"id":"w203970912","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832711","n2139832712","n2139832713","n2139832714","n2139832711"]},"w203970119":{"id":"w203970119","tags":{"building":"yes"},"nodes":["n2139824713","n2139824705","n2139824683","n2139824689","n2139824713"]},"w203970114":{"id":"w203970114","tags":{"building":"yes"},"nodes":["n2139824735","n2139824750","n2139824745","n2139824732","n2139824735"]},"w208643142":{"id":"w208643142","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153254","n2189153255","n2189153256","n2189153257"]},"w206805253":{"id":"w206805253","tags":{"area":"yes","building":"yes"},"nodes":["n2168544848","n2168544849","n2168544850","n2168544851","n2168544852","n2168544853","n2168544854","n2168544855","n2168544848"]},"w143497377":{"id":"w143497377","tags":{"highway":"primary","name":"North Main Street","old_ref":"US 131","ref":"US 131 Business"},"nodes":["n185962625","n185978420","n185978419","n185978417","n185978415","n185978414","n185975735","n1475293254","n185978410","n185978406","n185970515","n185978402","n185978399","n185965399","n2189015992","n185961396","n185967917","n185978394","n185971631","n185960796","n185978392","n185974583","n185978390","n185971578","n185965289","n2189153215","n185978388","n185978383","n185978381","n185978379","n185978377","n185978375","n185964982"]},"w134150811":{"id":"w134150811","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964976","n2130304157","n1475284023","n2139832715","n185964980","n185964982"]},"w208643135":{"id":"w208643135","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153215","n2189153216","n2189153217","n2189153218"]},"w17967183":{"id":"w17967183","tags":{"highway":"residential","name":"West Street"},"nodes":["n1475284011","n185984011","n185984013","n185984015","n2189153246","n2189153250","n185965291","n185984017","n185971580","n185984020","n185974585","n185982163","n185975064","n185971632","n185984022","n185984024","n185967918"]},"w134150778":{"id":"w134150778","tags":{"bridge":"yes","highway":"residential","name":"Moore St"},"nodes":["n185988245","n1475283992","n185975911"]},"w206805248":{"id":"w206805248","tags":{"area":"yes","building":"yes"},"nodes":["n2168544790","n2168544791","n2168544792","n2168544793","n2168544795","n2168544797","n2168544798","n2168544799","n2168544802","n2168544801","n2168544790"]},"w203974058":{"id":"w203974058","tags":{"bridge":"yes","highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870374","n2139870375"]},"w203970902":{"id":"w203970902","tags":{"highway":"service"},"nodes":["n2139832678","n2139832691","n2139832680"]},"w203988296":{"id":"w203988296","tags":{"highway":"path"},"nodes":["n2139858967","n2140006421","n2139858935"]},"w206805251":{"id":"w206805251","tags":{"area":"yes","building":"yes"},"nodes":["n2168544830","n2168544831","n2168544832","n2168544833","n2168544834","n2168544835","n2168544836","n2168544837","n2168544830"]},"w203970906":{"id":"w203970906","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832693","n2139832694","n2139832696","n2139832697","n2139832698","n2139832693"]},"w203049598":{"id":"w203049598","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2130304162","n2130304163","n2130304164","n2130304165","n2130304162"]},"w203970911":{"id":"w203970911","tags":{"highway":"service"},"nodes":["n2139832709","n2139832714","n2139832713","n2139832710","n185988971"]},"w203970105":{"id":"w203970105","tags":{"building":"yes"},"nodes":["n2139824779","n2139824792","n2139824786","n2139824775","n2139824779"]},"w203988290":{"id":"w203988290","tags":{"highway":"footway"},"nodes":["n2140006403","n2140006407"]},"w203970900":{"id":"w203970900","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832653","n2139832663","n2139832665","n2139832667","n2139832669","n2139832671","n2139832673","n2139832675","n2139832677","n2139832653"]},"w209717048":{"id":"w209717048","tags":{"area":"yes","building":"yes"},"nodes":["n2199109755","n2199109756","n2199109757","n2199109758","n2199109759","n2199109760","n2199109755"]},"w208643141":{"id":"w208643141","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153247","n2189153248","n2189153249","n2189153250"]},"w203970903":{"id":"w203970903","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832682","n2139832688","n2139832684","n2139832678","n2139832686"]},"n354002527":{"id":"n354002527","loc":[-85.6236039,41.9458813],"tags":{"amenity":"school","name":"Barrows School"}},"n185963396":{"id":"n185963396","loc":[-85.627401,41.943496]},"n185963397":{"id":"n185963397","loc":[-85.627403,41.943625]},"n185965101":{"id":"n185965101","loc":[-85.626409,41.943215]},"n185971474":{"id":"n185971474","loc":[-85.624884,41.943508]},"n185971475":{"id":"n185971475","loc":[-85.625191,41.943509]},"n185971482":{"id":"n185971482","loc":[-85.624882,41.94382]},"n185983135":{"id":"n185983135","loc":[-85.624893,41.945616]},"n185983137":{"id":"n185983137","loc":[-85.624912,41.946524]},"n185988027":{"id":"n185988027","loc":[-85.622721,41.946535]},"n185963398":{"id":"n185963398","loc":[-85.6273993,41.9446899]},"n185983238":{"id":"n185983238","loc":[-85.6227157,41.9456321]},"n185980374":{"id":"n185980374","loc":[-85.6248856,41.9447242]},"n185980373":{"id":"n185980373","loc":[-85.6226744,41.9447371]},"n2196831342":{"id":"n2196831342","loc":[-85.6250924,41.945063]},"n2196831343":{"id":"n2196831343","loc":[-85.6252335,41.9450636]},"n2196831344":{"id":"n2196831344","loc":[-85.6252286,41.9448707]},"n2196831345":{"id":"n2196831345","loc":[-85.6250661,41.9448707]},"n2196831346":{"id":"n2196831346","loc":[-85.6250243,41.9449012]},"n2196831347":{"id":"n2196831347","loc":[-85.6250251,41.9449244]},"n2196831348":{"id":"n2196831348","loc":[-85.6250867,41.9449257]},"n2196831349":{"id":"n2196831349","loc":[-85.625349,41.9445058]},"n2196831350":{"id":"n2196831350","loc":[-85.6253471,41.9443882]},"n2196831351":{"id":"n2196831351","loc":[-85.6251516,41.94439]},"n2196831352":{"id":"n2196831352","loc":[-85.6251522,41.9444308]},"n2196831353":{"id":"n2196831353","loc":[-85.6251344,41.9444309]},"n2196831354":{"id":"n2196831354","loc":[-85.6251356,41.9445077]},"n2196831355":{"id":"n2196831355","loc":[-85.6232357,41.9463406]},"n2196831356":{"id":"n2196831356","loc":[-85.6232409,41.9460668]},"n2196831357":{"id":"n2196831357","loc":[-85.6232072,41.9460665]},"n2196831358":{"id":"n2196831358","loc":[-85.6232117,41.9458272]},"n2196831359":{"id":"n2196831359","loc":[-85.6229808,41.9458248]},"n2196831360":{"id":"n2196831360","loc":[-85.6229763,41.9460627]},"n2196831361":{"id":"n2196831361","loc":[-85.623006,41.946063]},"n2196831362":{"id":"n2196831362","loc":[-85.6230023,41.9462557]},"n2196831363":{"id":"n2196831363","loc":[-85.6230755,41.9462565]},"n2196831364":{"id":"n2196831364","loc":[-85.6230739,41.9463389]},"n185947349":{"id":"n185947349","loc":[-85.618327,41.945607]},"n185947359":{"id":"n185947359","loc":[-85.615453,41.945597]},"n185947378":{"id":"n185947378","loc":[-85.617231,41.945603]},"n185947474":{"id":"n185947474","loc":[-85.616136,41.945602]},"n185948972":{"id":"n185948972","loc":[-85.615273,41.945637]},"n185955019":{"id":"n185955019","loc":[-85.620172,41.945627]},"n185960682":{"id":"n185960682","loc":[-85.622759,41.951845]},"n185961369":{"id":"n185961369","loc":[-85.622758,41.947444]},"n185961371":{"id":"n185961371","loc":[-85.624908,41.947416]},"n185963392":{"id":"n185963392","loc":[-85.6270462,41.9409953]},"n185963393":{"id":"n185963393","loc":[-85.627295,41.941304]},"n185963394":{"id":"n185963394","loc":[-85.627352,41.94148]},"n185963395":{"id":"n185963395","loc":[-85.62737,41.942261]},"n185965099":{"id":"n185965099","loc":[-85.6264,41.942263]},"n185965108":{"id":"n185965108","loc":[-85.622769,41.949224]},"n185965110":{"id":"n185965110","loc":[-85.624937,41.949237]},"n185966295":{"id":"n185966295","loc":[-85.6299942,41.9446689]},"n185966342":{"id":"n185966342","loc":[-85.624873,41.942022]},"n185970222":{"id":"n185970222","loc":[-85.622761,41.948357]},"n185970224":{"id":"n185970224","loc":[-85.624924,41.9483338]},"n185971477":{"id":"n185971477","loc":[-85.620051,41.94383]},"n185971478":{"id":"n185971478","loc":[-85.621219,41.943801]},"n185971481":{"id":"n185971481","loc":[-85.621812,41.943807]},"n185973866":{"id":"n185973866","loc":[-85.627629,41.946498]},"n185974699":{"id":"n185974699","loc":[-85.6227688,41.950119]},"n185978800":{"id":"n185978800","loc":[-85.623953,41.954684]},"n185980372":{"id":"n185980372","loc":[-85.621459,41.944756]},"n185980378":{"id":"n185980378","loc":[-85.6286375,41.9446764]},"n185980380":{"id":"n185980380","loc":[-85.630139,41.944661]},"n185980382":{"id":"n185980382","loc":[-85.630298,41.944635]},"n185980384":{"id":"n185980384","loc":[-85.630759,41.94454]},"n185980386":{"id":"n185980386","loc":[-85.6312369,41.9444548]},"n185983133":{"id":"n185983133","loc":[-85.6248672,41.9415903]},"n185983139":{"id":"n185983139","loc":[-85.624951,41.950239]},"n185983140":{"id":"n185983140","loc":[-85.624934,41.950681]},"n185983141":{"id":"n185983141","loc":[-85.624813,41.950983]},"n185983143":{"id":"n185983143","loc":[-85.6246225,41.951591]},"n185983144":{"id":"n185983144","loc":[-85.623908,41.9539165]},"n185983145":{"id":"n185983145","loc":[-85.6238903,41.9540956]},"n185983146":{"id":"n185983146","loc":[-85.623898,41.95431]},"n185983236":{"id":"n185983236","loc":[-85.628481,41.945611]},"n185985914":{"id":"n185985914","loc":[-85.620072,41.946538]},"n185986812":{"id":"n185986812","loc":[-85.6227785,41.9510005]},"n185988028":{"id":"n185988028","loc":[-85.6281401,41.9469632]},"n185988030":{"id":"n185988030","loc":[-85.6282451,41.9470314]},"n185988032":{"id":"n185988032","loc":[-85.6283312,41.9470656]},"w17964989":{"id":"w17964989","tags":{"highway":"residential","name":"Middle St"},"nodes":["n185963392","n185963393","n185963394","n185963395","n185963396","n185963397","n185963398"]},"w17965158":{"id":"w17965158","tags":{"access":"private","highway":"service","name":"Battle St"},"nodes":["n185965099","n185965101"]},"w41074896":{"id":"w41074896","tags":{"highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980372","n185980373","n185980374","n185963398","n185980378","n185966295","n185980380","n185980382","n185980384","n185980386"]},"w17965846":{"id":"w17965846","tags":{"highway":"residential","name":"2nd Ave"},"nodes":["n185971477","n185971478","n185971481","n185971482"]},"w209470306":{"id":"w209470306","tags":{"area":"yes","building":"yes"},"nodes":["n2196831349","n2196831350","n2196831351","n2196831352","n2196831353","n2196831354","n2196831349"]},"w17965845":{"id":"w17965845","tags":{"highway":"residential","name":"2nd Ave"},"nodes":["n185971474","n185971475","n185963396"]},"w209470307":{"id":"w209470307","tags":{"area":"yes","building":"yes"},"nodes":["n2196831355","n2196831356","n2196831357","n2196831358","n2196831359","n2196831360","n2196831361","n2196831362","n2196831363","n2196831364","n2196831355"]},"w17968192":{"id":"w17968192","tags":{"highway":"residential","name":"Washington St"},"nodes":["n185980373","n185983238","n185988027","n185961369","n185970222","n185965108","n185974699","n185986812","n185960682"]},"w17967603":{"id":"w17967603","tags":{"highway":"residential","name":"5th Ave"},"nodes":["n185985914","n185988027","n185983137","n185973866","n185988028","n185988030","n185988032"]},"w209470305":{"id":"w209470305","tags":{"area":"yes","building":"yes"},"nodes":["n2196831342","n2196831343","n2196831344","n2196831345","n2196831346","n2196831347","n2196831348","n2196831342"]},"w17967092":{"id":"w17967092","tags":{"highway":"residential","name":"Wood St"},"nodes":["n185983133","n185966342","n185971474","n185971482","n185980374","n185983135","n185983137","n185961371","n185970224","n185965110","n185983139","n185983140","n185983141","n185983143","n185983144","n185983145","n185983146","n185978800"]},"w17967107":{"id":"w17967107","tags":{"highway":"residential","name":"4th Ave"},"nodes":["n185983236","n185983135","n185983238","n185955019","n185947349","n185947378","n185947474","n185947359","n185948972"]},"n354030330":{"id":"n354030330","loc":[-85.6297222,41.9444444],"tags":{"leisure":"park","name":"Scouter Park"}},"n185966296":{"id":"n185966296","loc":[-85.629998,41.944078]},"n185966298":{"id":"n185966298","loc":[-85.629972,41.943927]},"n185966300":{"id":"n185966300","loc":[-85.629948,41.943783]},"n185980391":{"id":"n185980391","loc":[-85.6322992,41.9442766]},"n185980393":{"id":"n185980393","loc":[-85.6324925,41.9442136]},"n185980389":{"id":"n185980389","loc":[-85.6320272,41.9443281]},"n185980388":{"id":"n185980388","loc":[-85.6315778,41.9443959]},"n354031320":{"id":"n354031320","loc":[-85.6280556,41.9447222],"tags":{"amenity":"place_of_worship","name":"Riverside Church","religion":"christian"}},"n185987309":{"id":"n185987309","loc":[-85.6286497,41.9453531]},"n185987311":{"id":"n185987311","loc":[-85.6285942,41.9454805]},"n185988034":{"id":"n185988034","loc":[-85.6285815,41.9471692]},"n185988896":{"id":"n185988896","loc":[-85.6318433,41.9437929]},"n185977764":{"id":"n185977764","loc":[-85.6322988,41.943472]},"n1819848852":{"id":"n1819848852","loc":[-85.6315188,41.9448808]},"n1819848912":{"id":"n1819848912","loc":[-85.6284289,41.9472189]},"n1819848925":{"id":"n1819848925","loc":[-85.6314501,41.9451617]},"n1819848949":{"id":"n1819848949","loc":[-85.6309394,41.9455192]},"n1819848951":{"id":"n1819848951","loc":[-85.6290297,41.9457187]},"n1819848963":{"id":"n1819848963","loc":[-85.630521,41.9455591]},"n1819848981":{"id":"n1819848981","loc":[-85.6292936,41.9455846]},"n1819848989":{"id":"n1819848989","loc":[-85.6298451,41.9455431]},"n1819848998":{"id":"n1819848998","loc":[-85.6314973,41.9446254]},"n1819849018":{"id":"n1819849018","loc":[-85.6302807,41.9455527]},"n1819849043":{"id":"n1819849043","loc":[-85.6285533,41.9469731]},"n1819849087":{"id":"n1819849087","loc":[-85.6314501,41.9453532]},"n1819849090":{"id":"n1819849090","loc":[-85.628843,41.9461033]},"n1819849109":{"id":"n1819849109","loc":[-85.6311926,41.9454729]},"n1819849116":{"id":"n1819849116","loc":[-85.6288967,41.9459437]},"n1819849177":{"id":"n1819849177","loc":[-85.6287894,41.9464544]},"n1819858529":{"id":"n1819858529","loc":[-85.6325485,41.9445625]},"n2189112797":{"id":"n2189112797","loc":[-85.6275271,41.944555]},"n2189112798":{"id":"n2189112798","loc":[-85.6275196,41.9437258]},"n2189112799":{"id":"n2189112799","loc":[-85.6278937,41.943723]},"n2189112800":{"id":"n2189112800","loc":[-85.6278969,41.9439191]},"n2189112801":{"id":"n2189112801","loc":[-85.6279907,41.9439345]},"n2189112802":{"id":"n2189112802","loc":[-85.6280817,41.9439663]},"n2189112803":{"id":"n2189112803","loc":[-85.6281768,41.9440145]},"n2189112804":{"id":"n2189112804","loc":[-85.6281933,41.9440483]},"n2189112805":{"id":"n2189112805","loc":[-85.6281671,41.9440535]},"n2189112806":{"id":"n2189112806","loc":[-85.6281933,41.9440935]},"n2189112807":{"id":"n2189112807","loc":[-85.6282126,41.9441437]},"n2189112808":{"id":"n2189112808","loc":[-85.628214,41.9441991]},"n2189112809":{"id":"n2189112809","loc":[-85.6283298,41.944196]},"n2189112810":{"id":"n2189112810","loc":[-85.6283285,41.9442616]},"n2189112811":{"id":"n2189112811","loc":[-85.6281727,41.9442616]},"n2189112812":{"id":"n2189112812","loc":[-85.6281713,41.9442934]},"n2189112813":{"id":"n2189112813","loc":[-85.6280386,41.9442963]},"n2189112814":{"id":"n2189112814","loc":[-85.6280405,41.9443292]},"n2189112815":{"id":"n2189112815","loc":[-85.627829,41.9443349]},"n2189112816":{"id":"n2189112816","loc":[-85.6278347,41.9445495]},"n2189153271":{"id":"n2189153271","loc":[-85.6321053,41.9460342]},"n2189153272":{"id":"n2189153272","loc":[-85.632278,41.9457841]},"n2189153273":{"id":"n2189153273","loc":[-85.632823,41.9459936]},"n2189153274":{"id":"n2189153274","loc":[-85.6326845,41.9461963]},"n2189153275":{"id":"n2189153275","loc":[-85.6325664,41.9461507]},"n2189153276":{"id":"n2189153276","loc":[-85.6325323,41.946198]},"n2189153278":{"id":"n2189153278","loc":[-85.6321916,41.9459733]},"n2189153279":{"id":"n2189153279","loc":[-85.6322598,41.9458703]},"n2189153280":{"id":"n2189153280","loc":[-85.6327208,41.9460358]},"n2189153281":{"id":"n2189153281","loc":[-85.6326413,41.9461422]},"n185959079":{"id":"n185959079","loc":[-85.6293702,41.9474668]},"n185966301":{"id":"n185966301","loc":[-85.629692,41.943136]},"n185966304":{"id":"n185966304","loc":[-85.629565,41.942916]},"n185966308":{"id":"n185966308","loc":[-85.629501,41.942751]},"n185966315":{"id":"n185966315","loc":[-85.629472,41.942578]},"n185966319":{"id":"n185966319","loc":[-85.629444,41.942414]},"n185966321":{"id":"n185966321","loc":[-85.629391,41.94205]},"n185966323":{"id":"n185966323","loc":[-85.629369,41.941858]},"n185966327":{"id":"n185966327","loc":[-85.629297,41.941604]},"n185966331":{"id":"n185966331","loc":[-85.629233,41.941549]},"n185966336":{"id":"n185966336","loc":[-85.628504,41.941364]},"n185966338":{"id":"n185966338","loc":[-85.628275,41.941303]},"n185966340":{"id":"n185966340","loc":[-85.6269038,41.9410983]},"n185973867":{"id":"n185973867","loc":[-85.626843,41.947333]},"n185977762":{"id":"n185977762","loc":[-85.6318441,41.9429453]},"n1819848853":{"id":"n1819848853","loc":[-85.625854,41.9492218]},"n1819848861":{"id":"n1819848861","loc":[-85.6251459,41.9552376]},"n1819848874":{"id":"n1819848874","loc":[-85.6267445,41.9482961]},"n1819848882":{"id":"n1819848882","loc":[-85.6257209,41.9552396]},"n1819848883":{"id":"n1819848883","loc":[-85.624706,41.9523173]},"n1819848907":{"id":"n1819848907","loc":[-85.62609,41.9561471]},"n1819848908":{"id":"n1819848908","loc":[-85.6244013,41.9549284]},"n1819848911":{"id":"n1819848911","loc":[-85.6265578,41.9553672]},"n1819848923":{"id":"n1819848923","loc":[-85.6246802,41.9550959]},"n1819848936":{"id":"n1819848936","loc":[-85.6241588,41.9539291]},"n1819848940":{"id":"n1819848940","loc":[-85.62506,41.9511129]},"n1819848944":{"id":"n1819848944","loc":[-85.624942,41.9515912]},"n1819848950":{"id":"n1819848950","loc":[-85.6273989,41.9475461]},"n1819848957":{"id":"n1819848957","loc":[-85.627695,41.947404]},"n1819849009":{"id":"n1819849009","loc":[-85.6259248,41.94896]},"n1819849037":{"id":"n1819849037","loc":[-85.6257252,41.9502112]},"n1819849061":{"id":"n1819849061","loc":[-85.6270084,41.9479626]},"n1819849073":{"id":"n1819849073","loc":[-85.6243734,41.9534583]},"n1819849091":{"id":"n1819849091","loc":[-85.6241373,41.9543918]},"n1819849130":{"id":"n1819849130","loc":[-85.6282572,41.9473067]},"n1819849143":{"id":"n1819849143","loc":[-85.625281,41.9506596]},"n1819849153":{"id":"n1819849153","loc":[-85.6258647,41.9498043]},"n1819849168":{"id":"n1819849168","loc":[-85.6265084,41.9559317]},"n1819849173":{"id":"n1819849173","loc":[-85.6263325,41.9552156]},"n1819849175":{"id":"n1819849175","loc":[-85.6266372,41.9556764]},"n1819849178":{"id":"n1819849178","loc":[-85.6242232,41.9545993]},"n1819849181":{"id":"n1819849181","loc":[-85.6262187,41.9486712]},"n1819849188":{"id":"n1819849188","loc":[-85.6245558,41.9530434]},"n1819849190":{"id":"n1819849190","loc":[-85.6255982,41.9563017]},"n2168544738":{"id":"n2168544738","loc":[-85.6245707,41.9529711]},"w208643145":{"id":"w208643145","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153271","n2189153272","n2189153273","n2189153274","n2189153275","n2189153276","n2189153271"]},"w17967561":{"id":"w17967561","tags":{"highway":"residential","name":"Garden St"},"nodes":["n185980378","n185987309","n185987311","n185983236","n185973866"]},"w134150802":{"id":"w134150802","tags":{"bridge":"yes","highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980386","n185980388"]},"w208639462":{"id":"w208639462","tags":{"area":"yes","building":"yes"},"nodes":["n2189112797","n2189112798","n2189112799","n2189112800","n2189112801","n2189112802","n2189112803","n2189112804","n2189112805","n2189112806","n2189112807","n2189112808","n2189112809","n2189112810","n2189112811","n2189112812","n2189112813","n2189112814","n2189112815","n2189112816","n2189112797"]},"w134150830":{"id":"w134150830","tags":{"bridge":"yes","highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977762","n185977764"]},"w134150801":{"id":"w134150801","tags":{"highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977764","n185964982"]},"w208643146":{"id":"w208643146","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153277","n2189153281","n2189153278","n2189153279","n2189153280","n2189153281"]},"w17966061":{"id":"w17966061","tags":{"highway":"residential","name":"John Glenn Ct"},"nodes":["n185973866","n185973867"]},"w134150772":{"id":"w134150772","tags":{"highway":"residential","name":"5th Ave"},"nodes":["n185988034","n185959079","n185988036","n185978967"]},"w134150836":{"id":"w134150836","tags":{"highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980388","n1819858524","n185980389","n185980391","n185980393","n185964982"]},"w17967734":{"id":"w17967734","tags":{"highway":"residential","name":"Water Street"},"nodes":["n185988896","n185980391","n1819858529"]},"w17965305":{"id":"w17965305","tags":{"highway":"residential","name":"River Dr"},"nodes":["n185966295","n185966296","n185966298","n185966300","n185966301","n185966304","n185966308","n185966315","n185966319","n185966321","n185966323","n185966327","n185966331","n185966336","n185966338","n185963392","n185966340","n185966342"]},"w134150826":{"id":"w134150826","tags":{"bridge":"yes","highway":"residential","name":"5th Ave"},"nodes":["n185988032","n185988034"]},"w170848330":{"id":"w170848330","tags":{"name":"Portage River","waterway":"river"},"nodes":["n1819849190","n1819848907","n1819849168","n1819849175","n1819848911","n1819849173","n1819848882","n1819848861","n1819848923","n1819848908","n1819849178","n1819849091","n1819848936","n1819849073","n1819849188","n2168544738","n1819848883","n1819848944","n1819848940","n1819849143","n1819849037","n1819849153","n1819848853","n1819849009","n1819849181","n1819848874","n1819849061","n1819848950","n1819848957","n1819849130","n1819848912","n1819849043","n1819849177","n1819849090","n1819849116","n1819848951","n1819848981","n1819848989","n1819849018","n1819848963","n1819848949","n1819849109","n1819849087","n1819848925","n1819848852","n1819848998","n1819849057"]},"r270264":{"id":"r270264","tags":{"network":"US:MI","ref":"86","route":"road","state_id":"MI","type":"route","url":"http://en.wikipedia.org/wiki/M-86_%28Michigan_highway%29"},"members":[{"id":"w17737723","type":"way","role":""},{"id":"w17735949","type":"way","role":""},{"id":"w17740404","type":"way","role":""},{"id":"w17966273","type":"way","role":""},{"id":"w17964745","type":"way","role":""},{"id":"w151538068","type":"way","role":""},{"id":"w151538067","type":"way","role":""},{"id":"w17964960","type":"way","role":""},{"id":"w17966099","type":"way","role":""},{"id":"w17968009","type":"way","role":""},{"id":"w41259499","type":"way","role":""},{"id":"w151540401","type":"way","role":""},{"id":"w151540418","type":"way","role":""},{"id":"w17967997","type":"way","role":""},{"id":"w17966029","type":"way","role":""},{"id":"w17964801","type":"way","role":""},{"id":"w41259496","type":"way","role":""},{"id":"w151540399","type":"way","role":""},{"id":"w17968004","type":"way","role":""},{"id":"w17966462","type":"way","role":""},{"id":"w134150830","type":"way","role":""},{"id":"w134150801","type":"way","role":""},{"id":"w17732295","type":"way","role":""}]},"n185980093":{"id":"n185980093","loc":[-85.6271414,41.9407274]},"n185964330":{"id":"n185964330","loc":[-85.6235688,41.9399111]},"n185964328":{"id":"n185964328","loc":[-85.6235609,41.9391301]},"n185958034":{"id":"n185958034","loc":[-85.627102,41.939125]},"n185964331":{"id":"n185964331","loc":[-85.623571,41.940124]},"n185964329":{"id":"n185964329","loc":[-85.623562,41.9392411]},"n185972756":{"id":"n185972756","loc":[-85.623802,41.939102]},"n185972757":{"id":"n185972757","loc":[-85.623584,41.93913]},"n185975325":{"id":"n185975325","loc":[-85.624835,41.939318]},"n185975326":{"id":"n185975326","loc":[-85.624811,41.939435]},"n185975327":{"id":"n185975327","loc":[-85.624635,41.939703]},"n185975328":{"id":"n185975328","loc":[-85.624366,41.940055]},"n185975330":{"id":"n185975330","loc":[-85.624287,41.940113]},"n185975332":{"id":"n185975332","loc":[-85.624215,41.940134]},"n185980088":{"id":"n185980088","loc":[-85.627127,41.940086]},"n185988943":{"id":"n185988943","loc":[-85.622643,41.940128]},"n185988961":{"id":"n185988961","loc":[-85.627263,41.940082]},"n185990192":{"id":"n185990192","loc":[-85.622933,41.939224]},"n185990194":{"id":"n185990194","loc":[-85.621976,41.939203]},"n185991378":{"id":"n185991378","loc":[-85.622643,41.940635]},"n1475283999":{"id":"n1475283999","loc":[-85.6271165,41.9408429]},"n185980090":{"id":"n185980090","loc":[-85.6271315,41.9402001]},"n185958036":{"id":"n185958036","loc":[-85.6248366,41.9391615]},"n1819800188":{"id":"n1819800188","loc":[-85.6246947,41.9401644]},"n1819800199":{"id":"n1819800199","loc":[-85.6233686,41.9430896]},"n1819800204":{"id":"n1819800204","loc":[-85.6223236,41.9408587]},"n1819800213":{"id":"n1819800213","loc":[-85.6247526,41.9414138]},"n1819800216":{"id":"n1819800216","loc":[-85.6230961,41.9407151]},"n1819800218":{"id":"n1819800218","loc":[-85.621991,41.9429336]},"n1819800221":{"id":"n1819800221","loc":[-85.6246088,41.9424708]},"n1819800227":{"id":"n1819800227","loc":[-85.6241368,41.9403081]},"n1819800230":{"id":"n1819800230","loc":[-85.6226776,41.9431012]},"n1819800231":{"id":"n1819800231","loc":[-85.6243728,41.9401644]},"n1819800232":{"id":"n1819800232","loc":[-85.6249629,41.9408907]},"n1819800248":{"id":"n1819800248","loc":[-85.6238685,41.9405555]},"n1819800266":{"id":"n1819800266","loc":[-85.6246882,41.9418367]},"n1819800271":{"id":"n1819800271","loc":[-85.62492,41.9413695]},"n1819800294":{"id":"n1819800294","loc":[-85.6243556,41.9427465]},"n1819800304":{"id":"n1819800304","loc":[-85.6251453,41.94117]},"n1819800325":{"id":"n1819800325","loc":[-85.6248234,41.9405714]},"n1819800362":{"id":"n1819800362","loc":[-85.6239544,41.9429416]},"n1819800368":{"id":"n1819800368","loc":[-85.6243406,41.9402283]},"n1819800375":{"id":"n1819800375","loc":[-85.6226562,41.940755]},"n1819800377":{"id":"n1819800377","loc":[-85.6232033,41.9406512]},"n185945133":{"id":"n185945133","loc":[-85.623501,41.933232]},"n185945135":{"id":"n185945135","loc":[-85.624776,41.933205]},"n185945395":{"id":"n185945395","loc":[-85.624741,41.93019]},"n185952239":{"id":"n185952239","loc":[-85.615166,41.9382]},"n185954490":{"id":"n185954490","loc":[-85.624721,41.929278]},"n185957831":{"id":"n185957831","loc":[-85.625041,41.938662]},"n185958030":{"id":"n185958030","loc":[-85.629033,41.93913]},"n185958032":{"id":"n185958032","loc":[-85.628429,41.939143]},"n185958498":{"id":"n185958498","loc":[-85.621605,41.940143]},"n185961186":{"id":"n185961186","loc":[-85.624792,41.935214]},"n185963099":{"id":"n185963099","loc":[-85.6204461,41.9401485]},"n185963698":{"id":"n185963698","loc":[-85.6297342,41.9400783]},"n185964320":{"id":"n185964320","loc":[-85.623511,41.934216]},"n185964322":{"id":"n185964322","loc":[-85.6235312,41.9362084]},"n185964324":{"id":"n185964324","loc":[-85.6235488,41.9371726]},"n185964326":{"id":"n185964326","loc":[-85.6235512,41.9381718]},"n185967077":{"id":"n185967077","loc":[-85.617359,41.940161]},"n185967634":{"id":"n185967634","loc":[-85.6248039,41.9362012]},"n185970833":{"id":"n185970833","loc":[-85.6248019,41.9381684]},"n185972752":{"id":"n185972752","loc":[-85.624582,41.938848]},"n185972754":{"id":"n185972754","loc":[-85.6242,41.939008]},"n185973251":{"id":"n185973251","loc":[-85.602727,41.936012]},"n185974509":{"id":"n185974509","loc":[-85.62478,41.93217]},"n185975315":{"id":"n185975315","loc":[-85.624703,41.925597]},"n185975316":{"id":"n185975316","loc":[-85.624716,41.927359]},"n185975317":{"id":"n185975317","loc":[-85.62475,41.93119]},"n185975318":{"id":"n185975318","loc":[-85.624782,41.934218]},"n185975320":{"id":"n185975320","loc":[-85.6247949,41.9371708]},"n185977754":{"id":"n185977754","loc":[-85.6276,41.937412]},"n185980075":{"id":"n185980075","loc":[-85.627451,41.937549]},"n185980077":{"id":"n185980077","loc":[-85.627375,41.937618]},"n185980078":{"id":"n185980078","loc":[-85.627278,41.937728]},"n185980079":{"id":"n185980079","loc":[-85.627199,41.937842]},"n185980081":{"id":"n185980081","loc":[-85.627141,41.937981]},"n185980083":{"id":"n185980083","loc":[-85.627109,41.938153]},"n185980085":{"id":"n185980085","loc":[-85.627101,41.938699]},"n185981173":{"id":"n185981173","loc":[-85.61433,41.940167]},"n185987021":{"id":"n185987021","loc":[-85.628311,41.942261]},"n185988963":{"id":"n185988963","loc":[-85.628439,41.940086]},"n185990195":{"id":"n185990195","loc":[-85.621225,41.939143]},"n185990196":{"id":"n185990196","loc":[-85.620576,41.939033]},"n185990198":{"id":"n185990198","loc":[-85.619081,41.938804]},"n185990200":{"id":"n185990200","loc":[-85.617593,41.938552]},"n185990202":{"id":"n185990202","loc":[-85.617372,41.938535]},"n185990204":{"id":"n185990204","loc":[-85.616087,41.93832]},"n185990206":{"id":"n185990206","loc":[-85.615754,41.938289]},"n185990209":{"id":"n185990209","loc":[-85.615438,41.938251]},"n185990211":{"id":"n185990211","loc":[-85.613469,41.937867]},"n185990212":{"id":"n185990212","loc":[-85.610172,41.937298]},"n185990213":{"id":"n185990213","loc":[-85.605537,41.936497]},"n185990214":{"id":"n185990214","loc":[-85.604014,41.936234]},"n1819800180":{"id":"n1819800180","loc":[-85.588775,41.9455032]},"n1819800181":{"id":"n1819800181","loc":[-85.6074212,41.9408827]},"n1819800182":{"id":"n1819800182","loc":[-85.6131397,41.9427022]},"n1819800183":{"id":"n1819800183","loc":[-85.6171523,41.9416807]},"n1819800184":{"id":"n1819800184","loc":[-85.602465,41.9397415]},"n1819800185":{"id":"n1819800185","loc":[-85.6109296,41.9410583]},"n1819800186":{"id":"n1819800186","loc":[-85.6165729,41.9418004]},"n1819800189":{"id":"n1819800189","loc":[-85.5866293,41.9458224]},"n1819800191":{"id":"n1819800191","loc":[-85.5853311,41.9466603]},"n1819800201":{"id":"n1819800201","loc":[-85.6101142,41.9433406]},"n1819800202":{"id":"n1819800202","loc":[-85.600963,41.9428618]},"n1819800206":{"id":"n1819800206","loc":[-85.6154357,41.9427501]},"n1819800207":{"id":"n1819800207","loc":[-85.6040309,41.9414094]},"n1819800209":{"id":"n1819800209","loc":[-85.6113694,41.943189]},"n1819800211":{"id":"n1819800211","loc":[-85.618032,41.9416408]},"n1819800214":{"id":"n1819800214","loc":[-85.5959419,41.9402602]},"n1819800219":{"id":"n1819800219","loc":[-85.5972117,41.9420043]},"n1819800223":{"id":"n1819800223","loc":[-85.6117171,41.9430019]},"n1819800224":{"id":"n1819800224","loc":[-85.5977873,41.9395579]},"n1819800226":{"id":"n1819800226","loc":[-85.5917362,41.9432209]},"n1819800228":{"id":"n1819800228","loc":[-85.6055759,41.9419122]},"n1819800229":{"id":"n1819800229","loc":[-85.6203395,41.9425595]},"n1819800233":{"id":"n1819800233","loc":[-85.6107579,41.9433007]},"n1819800234":{"id":"n1819800234","loc":[-85.6039773,41.9412498]},"n1819800235":{"id":"n1819800235","loc":[-85.6000977,41.9412861]},"n1819800236":{"id":"n1819800236","loc":[-85.6026689,41.9407231]},"n1819800237":{"id":"n1819800237","loc":[-85.615161,41.9428662]},"n1819800238":{"id":"n1819800238","loc":[-85.5878953,41.9454314]},"n1819800239":{"id":"n1819800239","loc":[-85.6035267,41.941569]},"n1819800240":{"id":"n1819800240","loc":[-85.5929738,41.9450208]},"n1819800241":{"id":"n1819800241","loc":[-85.6186329,41.9416488]},"n1819800242":{"id":"n1819800242","loc":[-85.5881136,41.9483963]},"n1819800243":{"id":"n1819800243","loc":[-85.5909208,41.9466922]},"n1819800244":{"id":"n1819800244","loc":[-85.5997721,41.9394941]},"n1819800245":{"id":"n1819800245","loc":[-85.6202064,41.9425712]},"n1819800246":{"id":"n1819800246","loc":[-85.591071,41.9448808]},"n1819800247":{"id":"n1819800247","loc":[-85.5866078,41.9490622]},"n1819800250":{"id":"n1819800250","loc":[-85.602383,41.9420841]},"n1819800251":{"id":"n1819800251","loc":[-85.5957418,41.9426906]},"n1819800255":{"id":"n1819800255","loc":[-85.6157039,41.9416727]},"n1819800256":{"id":"n1819800256","loc":[-85.6080328,41.9410982]},"n1819800258":{"id":"n1819800258","loc":[-85.6192551,41.9414892]},"n1819800260":{"id":"n1819800260","loc":[-85.6104253,41.94117]},"n1819800261":{"id":"n1819800261","loc":[-85.6204503,41.9425709]},"n1819800263":{"id":"n1819800263","loc":[-85.5872194,41.9455431]},"n1819800264":{"id":"n1819800264","loc":[-85.616176,41.9418244]},"n1819800268":{"id":"n1819800268","loc":[-85.6120883,41.9426703]},"n1819800269":{"id":"n1819800269","loc":[-85.5894547,41.9474946]},"n1819800272":{"id":"n1819800272","loc":[-85.6209181,41.9425027]},"n1819800274":{"id":"n1819800274","loc":[-85.6122814,41.9412817]},"n1819800276":{"id":"n1819800276","loc":[-85.5895153,41.9452798]},"n1819800277":{"id":"n1819800277","loc":[-85.5884317,41.9455272]},"n1819800279":{"id":"n1819800279","loc":[-85.5884103,41.9480966]},"n1819800287":{"id":"n1819800287","loc":[-85.5904917,41.9453915]},"n1819800288":{"id":"n1819800288","loc":[-85.6212292,41.9412977]},"n1819800289":{"id":"n1819800289","loc":[-85.5954377,41.9406832]},"n1819800290":{"id":"n1819800290","loc":[-85.593721,41.9420957]},"n1819800291":{"id":"n1819800291","loc":[-85.6162832,41.9427102]},"n1819800292":{"id":"n1819800292","loc":[-85.605018,41.9401804]},"n1819800293":{"id":"n1819800293","loc":[-85.6086443,41.941146]},"n1819800296":{"id":"n1819800296","loc":[-85.6204675,41.9413775]},"n1819800297":{"id":"n1819800297","loc":[-85.612496,41.9424947]},"n1819800299":{"id":"n1819800299","loc":[-85.6065629,41.9423431]},"n1819800301":{"id":"n1819800301","loc":[-85.6036125,41.9398452]},"n1819800303":{"id":"n1819800303","loc":[-85.6114767,41.94117]},"n1819800306":{"id":"n1819800306","loc":[-85.592616,41.9428139]},"n1819800308":{"id":"n1819800308","loc":[-85.6023041,41.9419521]},"n1819800310":{"id":"n1819800310","loc":[-85.6218944,41.9411061]},"n1819800311":{"id":"n1819800311","loc":[-85.6097816,41.941162]},"n1819800312":{"id":"n1819800312","loc":[-85.5922549,41.9457869]},"n1819800313":{"id":"n1819800313","loc":[-85.5986027,41.9417206]},"n1819800314":{"id":"n1819800314","loc":[-85.5918687,41.946138]},"n1819800315":{"id":"n1819800315","loc":[-85.5872875,41.948883]},"n1819800316":{"id":"n1819800316","loc":[-85.594272,41.9436642]},"n1819800317":{"id":"n1819800317","loc":[-85.6176351,41.941577]},"n1819800318":{"id":"n1819800318","loc":[-85.6137834,41.9430853]},"n1819800319":{"id":"n1819800319","loc":[-85.6195383,41.942622],"tags":{"leisure":"slipway"}},"n1819800320":{"id":"n1819800320","loc":[-85.5971006,41.9398053]},"n1819800321":{"id":"n1819800321","loc":[-85.601714,41.9406752]},"n1819800322":{"id":"n1819800322","loc":[-85.5908028,41.9453117]},"n1819800323":{"id":"n1819800323","loc":[-85.6062732,41.9404597]},"n1819800324":{"id":"n1819800324","loc":[-85.62124,41.9425905]},"n1819800327":{"id":"n1819800327","loc":[-85.6008664,41.942766]},"n1819800328":{"id":"n1819800328","loc":[-85.6179355,41.9428538]},"n1819800330":{"id":"n1819800330","loc":[-85.6045566,41.9415131]},"n1819800331":{"id":"n1819800331","loc":[-85.5944935,41.9414653]},"n1819800333":{"id":"n1819800333","loc":[-85.6088911,41.943181]},"n1819800334":{"id":"n1819800334","loc":[-85.5946367,41.943369]},"n1819800336":{"id":"n1819800336","loc":[-85.6150494,41.9429656]},"n1819800343":{"id":"n1819800343","loc":[-85.6096099,41.9433326]},"n1819800345":{"id":"n1819800345","loc":[-85.5915216,41.9435401]},"n1819800347":{"id":"n1819800347","loc":[-85.607786,41.9428698]},"n1819800349":{"id":"n1819800349","loc":[-85.6187616,41.9426623]},"n1819800350":{"id":"n1819800350","loc":[-85.6012527,41.9426064]},"n1819800352":{"id":"n1819800352","loc":[-85.6214867,41.9428379]},"n1819800354":{"id":"n1819800354","loc":[-85.61338,41.94293]},"n1819800355":{"id":"n1819800355","loc":[-85.5923156,41.9428139]},"n1819800357":{"id":"n1819800357","loc":[-85.5901591,41.9453197]},"n1819800359":{"id":"n1819800359","loc":[-85.6033979,41.9408827]},"n1819800360":{"id":"n1819800360","loc":[-85.6186543,41.9414653]},"n1819800363":{"id":"n1819800363","loc":[-85.6128607,41.9425665]},"n1819800365":{"id":"n1819800365","loc":[-85.614234,41.9412977]},"n1819800367":{"id":"n1819800367","loc":[-85.6089662,41.9410902]},"n1819800369":{"id":"n1819800369","loc":[-85.6197379,41.9413695]},"n1819800370":{"id":"n1819800370","loc":[-85.6037348,41.941733]},"n1819800371":{"id":"n1819800371","loc":[-85.5993467,41.9415654]},"n1819800372":{"id":"n1819800372","loc":[-85.598077,41.94196]},"n1819800373":{"id":"n1819800373","loc":[-85.5984203,41.9394781]},"n1819800374":{"id":"n1819800374","loc":[-85.6013315,41.9427066]},"n1819800376":{"id":"n1819800376","loc":[-85.5934673,41.944167]},"n1819800378":{"id":"n1819800378","loc":[-85.6011062,41.9407753]},"n1819800379":{"id":"n1819800379","loc":[-85.6150602,41.9415131]},"n1819800380":{"id":"n1819800380","loc":[-85.6132148,41.9412338]},"n1819800381":{"id":"n1819800381","loc":[-85.5889038,41.9453835]},"n2139966621":{"id":"n2139966621","loc":[-85.6198719,41.9426184]},"n2139966622":{"id":"n2139966622","loc":[-85.6197551,41.9426123]},"n2139966623":{"id":"n2139966623","loc":[-85.6196467,41.9426279]},"n2139966624":{"id":"n2139966624","loc":[-85.6191519,41.9426221]},"n2139966625":{"id":"n2139966625","loc":[-85.6194153,41.9426256]},"n2139966626":{"id":"n2139966626","loc":[-85.6200497,41.9425812]},"n2139966629":{"id":"n2139966629","loc":[-85.6192123,41.9426229]},"n2203933101":{"id":"n2203933101","loc":[-85.6030009,41.9360592]},"w17967539":{"id":"w17967539","tags":{"highway":"residential","name":"1st Ave"},"nodes":["n185965099","n185963395","n185987021"]},"w17967751":{"id":"w17967751","tags":{"highway":"residential","name":"River St"},"nodes":["n185980088","n185988961","n185988963","n185963698"]},"w17965088":{"id":"w17965088","tags":{"highway":"residential","name":"9th St"},"nodes":["n185945133","n185964320","n185964322","n185964324","n185964326","n185964328","n185964329","n185964330","n185964331"]},"w17964467":{"id":"w17964467","tags":{"highway":"residential","name":"Mechanic St"},"nodes":["n185958030","n185958032","n185958034","n185958036"]},"w134150842":{"id":"w134150842","tags":{"bridge":"yes","highway":"residential","name":"6th St"},"nodes":["n185980090","n185980093"]},"w17966740":{"id":"w17966740","tags":{"highway":"residential","name":"6th St"},"nodes":["n185977754","n185980075","n185980077","n185980078","n185980079","n185980081","n185980083","n185980085","n185958034","n185980088","n185980090"]},"w170844765":{"id":"w170844765","tags":{"waterway":"dam"},"nodes":["n1819800304","n1819800232","n1819800325","n1819800188"]},"w17967745":{"id":"w17967745","tags":{"highway":"residential","name":"River St"},"nodes":["n185981173","n185967077","n185963099","n185958498","n185988943","n185964331","n185975332"]},"w17968113":{"id":"w17968113","tags":{"highway":"residential","name":"Green St"},"nodes":["n185988943","n185991378"]},"w134150833":{"id":"w134150833","tags":{"highway":"residential","name":"6th St"},"nodes":["n185980093","n1475283999","n185963392"]},"w17967935":{"id":"w17967935","tags":{"name":"Michigan Central Railroad","railway":"abandoned"},"nodes":["n185972757","n185990192","n185990194","n185990195","n185990196","n185990198","n185990200","n185990202","n185990204","n185990206","n185990209","n185952239","n185990211","n185990212","n185990213","n185990214","n2203933101","n185973251"]},"w17965993":{"id":"w17965993","tags":{"name":"Conrail Railroad","railway":"abandoned"},"nodes":["n185957831","n185972752","n185972754","n185972756","n185972757"]},"w17966211":{"id":"w17966211","tags":{"highway":"residential","name":"8th St"},"nodes":["n185975315","n185975316","n185954490","n185945395","n185975317","n185974509","n185945135","n185975318","n185961186","n185967634","n185975320","n185970833","n185958036","n185975325","n185975326","n185975327","n185975328","n185975330","n185975332"]},"w170844766":{"id":"w170844766","tags":{"waterway":"riverbank"},"nodes":["n1819800229","n1819800245","n2139966626","n2139966621","n2139966622","n2139966623","n1819800319","n2139966625","n2139966629","n2139966624","n1819800349","n1819800328","n1819800291","n1819800206","n1819800237","n1819800336","n1819800318","n1819800354","n1819800182","n1819800363","n1819800297","n1819800268","n1819800223","n1819800209","n1819800233","n1819800201","n1819800343","n1819800333","n1819800347","n1819800299","n1819800228","n1819800330","n1819800370","n1819800250","n1819800374","n1819800202","n1819800327","n1819800350","n1819800308","n1819800239","n1819800207","n1819800234","n1819800359","n1819800236","n1819800321","n1819800378","n1819800235","n1819800371","n1819800313","n1819800372","n1819800219","n1819800251","n1819800334","n1819800316","n1819800376","n1819800240","n1819800312","n1819800314","n1819800243","n1819800269","n1819800279","n1819800242","n1819800315","n1819800247","n1819800191","n1819800189","n1819800263","n1819800238","n1819800277","n1819800180","n1819800381","n1819800276","n1819800357","n1819800287","n1819800322","n1819800246","n1819800345","n1819800226","n1819800355","n1819800306","n1819800290","n1819800331","n1819800289","n1819800214","n1819800320","n1819800224","n1819800373","n1819800244","n1819800184","n1819800301","n1819800292","n1819800323","n1819800181","n1819800256","n1819800293","n1819800367","n1819800311","n1819800260","n1819800185","n1819800303","n1819800274","n1819800380","n1819800365","n1819800379","n1819800255","n1819800264","n1819800186","n1819800183","n1819800317","n1819800211","n1819800241","n1819800360","n1819800258","n1819800369","n1819800296","n1819800288","n1819800310","n1819800204","n1819800375","n1819800216","n1819800377","n1819800248","n1819800227","n1819800368","n1819800231","n1819800188","n1819800325","n1819800232","n1819800304","n1819800271","n1819800213","n1819800266","n1819800221","n1819800294","n1819800362","n1819800199","n1819800230","n1819800218","n1819800352","n1819800324","n1819800272","n1819800261","n1819800229"]},"n1875654132":{"id":"n1875654132","loc":[-85.6297439,41.939808]},"n1475293263":{"id":"n1475293263","loc":[-85.6296235,41.939922]},"n185947850":{"id":"n185947850","loc":[-85.631594,41.942613]},"n185952745":{"id":"n185952745","loc":[-85.630986,41.941786]},"n185972907":{"id":"n185972907","loc":[-85.631797,41.9420055]},"n185972911":{"id":"n185972911","loc":[-85.6309723,41.9411623]},"n185972915":{"id":"n185972915","loc":[-85.6295971,41.939267]},"n1475293223":{"id":"n1475293223","loc":[-85.6313962,41.9416114],"tags":{"railway":"level_crossing"}},"n1475293231":{"id":"n1475293231","loc":[-85.6318779,41.9415447]},"n1475293241":{"id":"n1475293241","loc":[-85.6304613,41.9405499]},"n1475293246":{"id":"n1475293246","loc":[-85.6297512,41.9395393],"tags":{"railway":"level_crossing"}},"n1475293251":{"id":"n1475293251","loc":[-85.6316633,41.9415128]},"n2139982404":{"id":"n2139982404","loc":[-85.6313283,41.9413748]},"n2139982407":{"id":"n2139982407","loc":[-85.6325545,41.9417787]},"n2139982408":{"id":"n2139982408","loc":[-85.6324499,41.9417693]},"n2139982409":{"id":"n2139982409","loc":[-85.6324753,41.9416444]},"n2139982410":{"id":"n2139982410","loc":[-85.6325814,41.9416538]},"n2139982411":{"id":"n2139982411","loc":[-85.6319572,41.9413515]},"n2139982412":{"id":"n2139982412","loc":[-85.6322925,41.941139]},"n2139982413":{"id":"n2139982413","loc":[-85.6323153,41.941153]},"n2139982414":{"id":"n2139982414","loc":[-85.6323019,41.9412617]},"n2139982415":{"id":"n2139982415","loc":[-85.6323703,41.9412667]},"n2139982416":{"id":"n2139982416","loc":[-85.6323555,41.941538]},"n2139982417":{"id":"n2139982417","loc":[-85.6321343,41.9416777]},"n2139982418":{"id":"n2139982418","loc":[-85.6319425,41.9416866]},"n2139982419":{"id":"n2139982419","loc":[-85.6320303,41.9416941]},"n2139982420":{"id":"n2139982420","loc":[-85.6321665,41.9415554]},"n2139982421":{"id":"n2139982421","loc":[-85.632412,41.9414164]},"n2139982422":{"id":"n2139982422","loc":[-85.6324801,41.9413421]},"n2139982423":{"id":"n2139982423","loc":[-85.6325023,41.9412585]},"n2139982424":{"id":"n2139982424","loc":[-85.6324532,41.9411607]},"n2139982425":{"id":"n2139982425","loc":[-85.6323502,41.941103]},"n2139982426":{"id":"n2139982426","loc":[-85.6322362,41.9411183]},"n2139982427":{"id":"n2139982427","loc":[-85.6318941,41.9413551]},"n2139982428":{"id":"n2139982428","loc":[-85.6318592,41.9414105]},"n2139982429":{"id":"n2139982429","loc":[-85.6320111,41.9415866]},"n2139982430":{"id":"n2139982430","loc":[-85.632446,41.9413792]},"n2139982431":{"id":"n2139982431","loc":[-85.6325112,41.941416]},"n2139982432":{"id":"n2139982432","loc":[-85.6325449,41.9416345]},"n2139982433":{"id":"n2139982433","loc":[-85.6326122,41.94164]},"n2139982434":{"id":"n2139982434","loc":[-85.6325954,41.9421966]},"n2139982435":{"id":"n2139982435","loc":[-85.6325655,41.9422411]},"n2139982436":{"id":"n2139982436","loc":[-85.632515,41.9422564]},"n2139982437":{"id":"n2139982437","loc":[-85.6324495,41.94223]},"n2139982438":{"id":"n2139982438","loc":[-85.6324009,41.9421743]},"n2139982439":{"id":"n2139982439","loc":[-85.6323915,41.9421145]},"n2139982440":{"id":"n2139982440","loc":[-85.6320287,41.9418585]},"n2139982441":{"id":"n2139982441","loc":[-85.6318285,41.9416387]},"n1475293258":{"id":"n1475293258","loc":[-85.6318289,41.9415077]},"n2168544754":{"id":"n2168544754","loc":[-85.6312814,41.9431198]},"n2168544755":{"id":"n2168544755","loc":[-85.6314212,41.9430646]},"n2168544756":{"id":"n2168544756","loc":[-85.6313387,41.942949]},"n2168544757":{"id":"n2168544757","loc":[-85.6311989,41.9430041]},"n2168544758":{"id":"n2168544758","loc":[-85.6311024,41.9429313]},"n2168544759":{"id":"n2168544759","loc":[-85.6310087,41.9428087]},"n2168544760":{"id":"n2168544760","loc":[-85.6313831,41.9426504]},"n2168544761":{"id":"n2168544761","loc":[-85.6314768,41.9427729]},"n2168544762":{"id":"n2168544762","loc":[-85.6306376,41.942809]},"n2168544763":{"id":"n2168544763","loc":[-85.6307378,41.9429427]},"n2168544764":{"id":"n2168544764","loc":[-85.630841,41.9428998]},"n2168544765":{"id":"n2168544765","loc":[-85.6307408,41.9427662]},"n2168544766":{"id":"n2168544766","loc":[-85.6305404,41.9426029]},"n2168544767":{"id":"n2168544767","loc":[-85.6304976,41.9426194]},"n2168544768":{"id":"n2168544768","loc":[-85.6305673,41.9427184]},"n2168544769":{"id":"n2168544769","loc":[-85.6306164,41.9426984]},"n2168544770":{"id":"n2168544770","loc":[-85.6306418,41.9427302]},"n2168544771":{"id":"n2168544771","loc":[-85.6306861,41.9427137]},"n2168544772":{"id":"n2168544772","loc":[-85.6307146,41.9427537]},"n2168544773":{"id":"n2168544773","loc":[-85.6308999,41.9426807]},"n2168544774":{"id":"n2168544774","loc":[-85.6308429,41.9426053]},"n2168544775":{"id":"n2168544775","loc":[-85.6308999,41.9425806]},"n2168544776":{"id":"n2168544776","loc":[-85.6308318,41.9424875]},"n2168544777":{"id":"n2168544777","loc":[-85.6307732,41.9425087]},"n2168544778":{"id":"n2168544778","loc":[-85.6307178,41.9424357]},"n2168544779":{"id":"n2168544779","loc":[-85.630485,41.942524]},"n2189099387":{"id":"n2189099387","loc":[-85.631203,41.9393371]},"n2189099404":{"id":"n2189099404","loc":[-85.6301963,41.9391363]},"n2189099405":{"id":"n2189099405","loc":[-85.6304447,41.9391352]},"n2189099406":{"id":"n2189099406","loc":[-85.6304463,41.9393391]},"n2189099407":{"id":"n2189099407","loc":[-85.6308435,41.9393373]},"n2189099408":{"id":"n2189099408","loc":[-85.6308418,41.9391251]},"n2189099409":{"id":"n2189099409","loc":[-85.6310929,41.939124]},"n2189099410":{"id":"n2189099410","loc":[-85.6310946,41.9393376]},"n2189112720":{"id":"n2189112720","loc":[-85.6314677,41.9412327]},"n2189112721":{"id":"n2189112721","loc":[-85.6313337,41.9411397]},"n2189112722":{"id":"n2189112722","loc":[-85.6320521,41.9405678]},"n2189112723":{"id":"n2189112723","loc":[-85.6321899,41.9406633]},"n2189112724":{"id":"n2189112724","loc":[-85.6313229,41.9408344]},"n2189112725":{"id":"n2189112725","loc":[-85.6311223,41.9410018]},"n2189112726":{"id":"n2189112726","loc":[-85.6313205,41.9411333]},"n2189112727":{"id":"n2189112727","loc":[-85.6315211,41.9409659]},"n2189112728":{"id":"n2189112728","loc":[-85.6311035,41.9402529]},"n2189112729":{"id":"n2189112729","loc":[-85.631226,41.9402107]},"n2189112730":{"id":"n2189112730","loc":[-85.6315966,41.9408051]},"n2189112731":{"id":"n2189112731","loc":[-85.6314741,41.9408473]},"n2189112732":{"id":"n2189112732","loc":[-85.6318114,41.940534]},"n2189112733":{"id":"n2189112733","loc":[-85.631588,41.94061]},"n2189112734":{"id":"n2189112734","loc":[-85.6314379,41.940366]},"n2189112735":{"id":"n2189112735","loc":[-85.6316613,41.94029]},"n2189112736":{"id":"n2189112736","loc":[-85.6306214,41.9400415]},"n2189112737":{"id":"n2189112737","loc":[-85.6304362,41.9397728]},"n2189112738":{"id":"n2189112738","loc":[-85.6305899,41.9397142]},"n2189112739":{"id":"n2189112739","loc":[-85.6307751,41.9399828]},"n2189112740":{"id":"n2189112740","loc":[-85.6304695,41.9401673]},"n2189112741":{"id":"n2189112741","loc":[-85.6301298,41.9396855]},"n2189112742":{"id":"n2189112742","loc":[-85.6303016,41.9396184]},"n2189112743":{"id":"n2189112743","loc":[-85.6306413,41.9401003]},"n2189112744":{"id":"n2189112744","loc":[-85.6309656,41.9406189]},"n2189112745":{"id":"n2189112745","loc":[-85.6308738,41.940493]},"n2189112746":{"id":"n2189112746","loc":[-85.6309333,41.940469]},"n2189112747":{"id":"n2189112747","loc":[-85.6307634,41.9402358]},"n2189112748":{"id":"n2189112748","loc":[-85.6308798,41.9401889]},"n2189112749":{"id":"n2189112749","loc":[-85.6311416,41.940548]},"n2189112750":{"id":"n2189112750","loc":[-85.6309577,41.9408708]},"n2189112751":{"id":"n2189112751","loc":[-85.630874,41.9407777]},"n2189112752":{"id":"n2189112752","loc":[-85.6310622,41.9406841]},"n2189112753":{"id":"n2189112753","loc":[-85.6311459,41.9407772]},"n2189112754":{"id":"n2189112754","loc":[-85.6320308,41.9405747]},"n2189112755":{"id":"n2189112755","loc":[-85.6317769,41.9401857]},"n2189112756":{"id":"n2189112756","loc":[-85.6313462,41.9401785]},"n2189112757":{"id":"n2189112757","loc":[-85.6313423,41.9401199]},"n2189112758":{"id":"n2189112758","loc":[-85.6318308,41.9401184]},"n2189112759":{"id":"n2189112759","loc":[-85.6321154,41.9405433]},"n2189112760":{"id":"n2189112760","loc":[-85.6310307,41.941683]},"n2189112761":{"id":"n2189112761","loc":[-85.6309808,41.9416078]},"n2189112762":{"id":"n2189112762","loc":[-85.6312094,41.9415156]},"n2189112763":{"id":"n2189112763","loc":[-85.6312636,41.9415865]},"n2189112764":{"id":"n2189112764","loc":[-85.6309384,41.94155]},"n2189112765":{"id":"n2189112765","loc":[-85.631156,41.9414619]},"n2189112766":{"id":"n2189112766","loc":[-85.6311968,41.94152]},"n2189112767":{"id":"n2189112767","loc":[-85.6308946,41.9414851]},"n2189112768":{"id":"n2189112768","loc":[-85.6308237,41.9413888]},"n2189112769":{"id":"n2189112769","loc":[-85.6309858,41.9413228]},"n2189112770":{"id":"n2189112770","loc":[-85.6310567,41.9414192]},"n2189112771":{"id":"n2189112771","loc":[-85.6307774,41.9413276]},"n2189112772":{"id":"n2189112772","loc":[-85.6309068,41.9412735]},"n2189112773":{"id":"n2189112773","loc":[-85.6309531,41.9413347]},"n2189112774":{"id":"n2189112774","loc":[-85.6307975,41.9412466]},"n2189112775":{"id":"n2189112775","loc":[-85.6307006,41.9411699]},"n2189112776":{"id":"n2189112776","loc":[-85.6308289,41.941113]},"n2189112777":{"id":"n2189112777","loc":[-85.6308997,41.9412012]},"n2189112778":{"id":"n2189112778","loc":[-85.630765,41.9412062]},"n2189112779":{"id":"n2189112779","loc":[-85.630739,41.9412177]},"n2189112780":{"id":"n2189112780","loc":[-85.6305822,41.9410391]},"n2189112781":{"id":"n2189112781","loc":[-85.6304117,41.9408177]},"n2189112782":{"id":"n2189112782","loc":[-85.6305086,41.9407769]},"n2189112783":{"id":"n2189112783","loc":[-85.6306779,41.9410044]},"n2189112784":{"id":"n2189112784","loc":[-85.6307734,41.9421663]},"n2189112785":{"id":"n2189112785","loc":[-85.630708,41.9420741]},"n2189112786":{"id":"n2189112786","loc":[-85.630863,41.9420133]},"n2189112787":{"id":"n2189112787","loc":[-85.6309285,41.9421055]},"n2189112788":{"id":"n2189112788","loc":[-85.6307014,41.9420263]},"n2189112789":{"id":"n2189112789","loc":[-85.6306648,41.941971]},"n2189112790":{"id":"n2189112790","loc":[-85.6307927,41.9419178]},"n2189112791":{"id":"n2189112791","loc":[-85.6308366,41.9419696]},"n2189112792":{"id":"n2189112792","loc":[-85.6307574,41.9418708]},"n2189112793":{"id":"n2189112793","loc":[-85.6306288,41.9419231]},"n2189112794":{"id":"n2189112794","loc":[-85.6306943,41.9417835]},"n2189112795":{"id":"n2189112795","loc":[-85.6305344,41.9418474]},"n2189112796":{"id":"n2189112796","loc":[-85.6305981,41.9419355]},"n2189123410":{"id":"n2189123410","loc":[-85.6315476,41.9393801]},"n2189123412":{"id":"n2189123412","loc":[-85.6315247,41.9399188]},"n2189123415":{"id":"n2189123415","loc":[-85.6316484,41.9400433]},"n185945138":{"id":"n185945138","loc":[-85.627073,41.93319]},"n185945142":{"id":"n185945142","loc":[-85.6296891,41.9331674]},"n185945401":{"id":"n185945401","loc":[-85.6269,41.930199]},"n185945405":{"id":"n185945405","loc":[-85.6296598,41.9301676]},"n185956891":{"id":"n185956891","loc":[-85.6251617,41.9255049]},"n185959979":{"id":"n185959979","loc":[-85.626333,41.928347]},"n185959983":{"id":"n185959983","loc":[-85.6296419,41.9283288]},"n185961192":{"id":"n185961192","loc":[-85.627053,41.9352031]},"n185961200":{"id":"n185961200","loc":[-85.6297088,41.9351902]},"n185963655":{"id":"n185963655","loc":[-85.6296112,41.9273948]},"n185963665":{"id":"n185963665","loc":[-85.626047,41.92737]},"n185963688":{"id":"n185963688","loc":[-85.6296503,41.9292199]},"n185963689":{"id":"n185963689","loc":[-85.6296694,41.931157]},"n185963690":{"id":"n185963690","loc":[-85.6296791,41.9321485]},"n185963691":{"id":"n185963691","loc":[-85.6296991,41.9341973]},"n185967638":{"id":"n185967638","loc":[-85.627089,41.9361884]},"n185972917":{"id":"n185972917","loc":[-85.6293759,41.9388605]},"n185972919":{"id":"n185972919","loc":[-85.6290337,41.9380234]},"n185972921":{"id":"n185972921","loc":[-85.628424,41.936212]},"n185972923":{"id":"n185972923","loc":[-85.628367,41.936029]},"n185974511":{"id":"n185974511","loc":[-85.627064,41.932169]},"n185977728":{"id":"n185977728","loc":[-85.625605,41.925842]},"n185977729":{"id":"n185977729","loc":[-85.625685,41.926163]},"n185977731":{"id":"n185977731","loc":[-85.6257845,41.9264872]},"n185977733":{"id":"n185977733","loc":[-85.62663,41.929251]},"n185977734":{"id":"n185977734","loc":[-85.627008,41.930642]},"n185977736":{"id":"n185977736","loc":[-85.627029,41.930775]},"n185977738":{"id":"n185977738","loc":[-85.627041,41.930946]},"n185977739":{"id":"n185977739","loc":[-85.6270379,41.9311746]},"n185977742":{"id":"n185977742","loc":[-85.627055,41.934206]},"n185977744":{"id":"n185977744","loc":[-85.627084,41.936804]},"n185977746":{"id":"n185977746","loc":[-85.627104,41.936914]},"n185977748":{"id":"n185977748","loc":[-85.627156,41.937026]},"n185977750":{"id":"n185977750","loc":[-85.6272406,41.9371672]},"n185977752":{"id":"n185977752","loc":[-85.627317,41.93723]},"n185977753":{"id":"n185977753","loc":[-85.627422,41.937312]},"n185977755":{"id":"n185977755","loc":[-85.627754,41.937504]},"n185977757":{"id":"n185977757","loc":[-85.627883,41.937623]},"n185977761":{"id":"n185977761","loc":[-85.627984,41.93773]},"n1475283996":{"id":"n1475283996","loc":[-85.6270514,41.9317122],"tags":{"railway":"level_crossing"}},"n1475284004":{"id":"n1475284004","loc":[-85.6278177,41.9342117],"tags":{"railway":"level_crossing"}},"n1475284014":{"id":"n1475284014","loc":[-85.6251877,41.9255913],"tags":{"railway":"level_crossing"}},"n1475284017":{"id":"n1475284017","loc":[-85.6274992,41.9331816],"tags":{"railway":"level_crossing"}},"n1475284021":{"id":"n1475284021","loc":[-85.6297108,41.9353939],"tags":{"railway":"level_crossing"}},"n1475284027":{"id":"n1475284027","loc":[-85.62811,41.935198],"tags":{"railway":"level_crossing"}},"n1475284035":{"id":"n1475284035","loc":[-85.626888,41.9311757],"tags":{"railway":"level_crossing"}},"n1475293245":{"id":"n1475293245","loc":[-85.6286047,41.9367881]},"n1875654302":{"id":"n1875654302","loc":[-85.6296367,41.927491]},"n2189099388":{"id":"n2189099388","loc":[-85.6312007,41.9389988]},"n2189099389":{"id":"n2189099389","loc":[-85.6311003,41.9389992]},"n2189099390":{"id":"n2189099390","loc":[-85.6310988,41.9387847]},"n2189099391":{"id":"n2189099391","loc":[-85.6312165,41.9387843]},"n2189099392":{"id":"n2189099392","loc":[-85.6312152,41.9385857]},"n2189099393":{"id":"n2189099393","loc":[-85.6310877,41.9385862]},"n2189099394":{"id":"n2189099394","loc":[-85.6310858,41.9383161]},"n2189099395":{"id":"n2189099395","loc":[-85.6302002,41.9383196]},"n2189099396":{"id":"n2189099396","loc":[-85.6302011,41.9384472]},"n2189099397":{"id":"n2189099397","loc":[-85.6301018,41.9384476]},"n2189099398":{"id":"n2189099398","loc":[-85.6301025,41.9385419]},"n2189099399":{"id":"n2189099399","loc":[-85.6299275,41.9385427]},"n2189099400":{"id":"n2189099400","loc":[-85.62993,41.9388653]},"n2189099401":{"id":"n2189099401","loc":[-85.630107,41.9388645]},"n2189099402":{"id":"n2189099402","loc":[-85.6301079,41.9389908]},"n2189099403":{"id":"n2189099403","loc":[-85.6301951,41.9389904]},"n2189123382":{"id":"n2189123382","loc":[-85.6336279,41.9354365]},"n2189123384":{"id":"n2189123384","loc":[-85.6328492,41.9355177]},"n2189123387":{"id":"n2189123387","loc":[-85.6323762,41.9357396]},"n2189123388":{"id":"n2189123388","loc":[-85.6315174,41.9358966]},"n2189123389":{"id":"n2189123389","loc":[-85.6304331,41.936124]},"n2189123390":{"id":"n2189123390","loc":[-85.6302075,41.9364271]},"n2189123391":{"id":"n2189123391","loc":[-85.6303458,41.9367953]},"n2189123392":{"id":"n2189123392","loc":[-85.6299601,41.9369739]},"n2189123393":{"id":"n2189123393","loc":[-85.6299164,41.9374882]},"n2189123394":{"id":"n2189123394","loc":[-85.6299455,41.9378022]},"n2189123395":{"id":"n2189123395","loc":[-85.6299771,41.9379053]},"n2189123396":{"id":"n2189123396","loc":[-85.6301597,41.9379091]},"n2189123397":{"id":"n2189123397","loc":[-85.6308042,41.9377913]},"n2189123398":{"id":"n2189123398","loc":[-85.6316885,41.9378082]},"n2189123399":{"id":"n2189123399","loc":[-85.6316848,41.9380079]},"n2189123400":{"id":"n2189123400","loc":[-85.6318449,41.9381161]},"n2189123401":{"id":"n2189123401","loc":[-85.6320705,41.9381811]},"n2189123402":{"id":"n2189123402","loc":[-85.6321433,41.9383706]},"n2189123404":{"id":"n2189123404","loc":[-85.632056,41.9384355]},"n2189123406":{"id":"n2189123406","loc":[-85.6317867,41.9384572]},"n2189123409":{"id":"n2189123409","loc":[-85.6316572,41.9387281]},"n2189123417":{"id":"n2189123417","loc":[-85.6315946,41.93775]},"n2189123419":{"id":"n2189123419","loc":[-85.6302641,41.9378393]},"w208640158":{"id":"w208640158","tags":{"area":"yes","natural":"wetland"},"nodes":["n2189123379","n2189123382","n2189123384","n2189123387","n2189123388","n2189123389","n2189123390","n2189123391","n2189123392","n2189123393","n2189123394","n2189123395","n2189123396","n2189123419","n2189123397","n2189123417","n2189123398","n2189123399","n2189123400","n2189123401","n2189123402","n2189123404","n2189123406","n2189123409","n2189123410","n2189123412","n2189123415","n1819805722","n1819805861","n1819805887","n1819805760","n1819805641","n1819805632","n2189123379"]},"w134150787":{"id":"w134150787","tags":{"name":"Conrail Railroad","railway":"rail"},"nodes":["n185972905","n185972907","n1475293223","n185972911","n1475293241","n1475293246","n185972915","n185972917","n185972919","n1475293245","n185972921","n185972923","n1475284027","n1475284004","n1475284017","n1475283996","n1475284035","n1475284014","n185956891"]},"w208639443":{"id":"w208639443","tags":{"area":"yes","building":"yes"},"nodes":["n2189112720","n2189112721","n2189112722","n2189112723","n2189112720"]},"w17966462":{"id":"w17966462","tags":{"highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977728","n185977729","n185977731","n185963665","n185959979","n185977733","n185945401","n185977734","n185977736","n185977738","n185977739","n1475283996","n185974511","n185945138","n185977742","n185961192","n185967638","n185977744","n185977746","n185977748","n185977750","n185977752","n185977753","n185977754","n185977755","n185977757","n185977761","n185958030","n1475293263","n185963698","n185952745","n185947850","n185977762"]},"w203985741":{"id":"w203985741","tags":{"area":"yes","leisure":"park","name":"Conservation Park"},"nodes":["n2139982404","n2139982405","n2139982399","n2139982400","n1819805770","n2139982402","n2139982403","n2139982401","n1819805780","n1819805834","n2139982406","n2139982404"]},"w17963676":{"id":"w17963676","tags":{"highway":"service"},"nodes":["n1475293258","n2139982428","n2139982427","n2139982426","n2139982425","n2139982424","n2139982423","n2139982422","n2139982430","n2139982421","n2139982420","n2139982429","n1475293231","n1475293258","n1475293251","n1475293223","n185952745"]},"w203985745":{"id":"w203985745","tags":{"highway":"footway"},"nodes":["n2139982430","n2139982431","n2139982432","n2139982433","n2139982434","n2139982435","n2139982436","n2139982437","n2139982438","n2139982439","n2139982440","n2139982441","n1475293231"]},"w208639451":{"id":"w208639451","tags":{"area":"yes","building":"yes"},"nodes":["n2189112754","n2189112755","n2189112756","n2189112757","n2189112758","n2189112759","n2189112754"]},"w208639452":{"id":"w208639452","tags":{"area":"yes","building":"yes"},"nodes":["n2189112760","n2189112761","n2189112766","n2189112762","n2189112763","n2189112760"]},"w206805244":{"id":"w206805244","tags":{"area":"yes","building":"yes"},"nodes":["n2168544766","n2168544767","n2168544768","n2168544769","n2168544770","n2168544771","n2168544772","n2168544773","n2168544774","n2168544775","n2168544776","n2168544777","n2168544778","n2168544779","n2168544766"]},"w208639444":{"id":"w208639444","tags":{"area":"yes","building":"yes"},"nodes":["n2189112724","n2189112725","n2189112726","n2189112727","n2189112724"]},"w208639450":{"id":"w208639450","tags":{"area":"yes","building":"yes"},"nodes":["n2189112750","n2189112751","n2189112752","n2189112753","n2189112750"]},"w208639448":{"id":"w208639448","tags":{"area":"yes","building":"yes"},"nodes":["n2189112740","n2189112741","n2189112742","n2189112743","n2189112740"]},"w208637859":{"id":"w208637859","tags":{"area":"yes","building":"yes"},"nodes":["n2189099387","n2189099388","n2189099389","n2189099390","n2189099391","n2189099392","n2189099393","n2189099394","n2189099395","n2189099396","n2189099397","n2189099398","n2189099399","n2189099400","n2189099401","n2189099402","n2189099403","n2189099404","n2189099405","n2189099406","n2189099407","n2189099408","n2189099409","n2189099410","n2189099387"]},"w208639453":{"id":"w208639453","tags":{"area":"yes","building":"yes"},"nodes":["n2189112764","n2189112765","n2189112766","n2189112761","n2189112764"]},"w208639456":{"id":"w208639456","tags":{"area":"yes","building":"yes"},"nodes":["n2189112774","n2189112778","n2189112779","n2189112775","n2189112776","n2189112777","n2189112774"]},"w208639445":{"id":"w208639445","tags":{"area":"yes","building":"yes"},"nodes":["n2189112728","n2189112729","n2189112730","n2189112731","n2189112728"]},"w17967776":{"id":"w17967776","tags":{"highway":"residential","name":"5th St"},"nodes":["n185958032","n185988963"]},"w208639461":{"id":"w208639461","tags":{"area":"yes","building":"yes"},"nodes":["n2189112792","n2189112794","n2189112795","n2189112796","n2189112793","n2189112792"]},"w206805241":{"id":"w206805241","tags":{"area":"yes","building":"yes"},"nodes":["n2168544754","n2168544755","n2168544756","n2168544757","n2168544754"]},"w208639449":{"id":"w208639449","tags":{"area":"yes","building":"yes"},"nodes":["n2189112744","n2189112745","n2189112746","n2189112747","n2189112748","n2189112749","n2189112744"]},"w208639455":{"id":"w208639455","tags":{"area":"yes","building":"yes"},"nodes":["n2189112771","n2189112772","n2189112773","n2189112768","n2189112771"]},"w208639457":{"id":"w208639457","tags":{"area":"yes","building":"yes"},"nodes":["n2189112780","n2189112781","n2189112782","n2189112783","n2189112780"]},"w208639446":{"id":"w208639446","tags":{"area":"yes","building":"yes"},"nodes":["n2189112732","n2189112733","n2189112734","n2189112735","n2189112732"]},"w208639454":{"id":"w208639454","tags":{"area":"yes","building":"yes"},"nodes":["n2189112767","n2189112768","n2189112773","n2189112769","n2189112770","n2189112767"]},"w203985743":{"id":"w203985743","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139982411","n2139982412","n2139982413","n2139982414","n2139982415","n2139982416","n2139982417","n2139982419","n2139982418","n2139982411"]},"w17965023":{"id":"w17965023","tags":{"highway":"residential","name":"4th St"},"nodes":["n185963655","n1875654302","n185959983","n185963688","n185945405","n185963689","n185963690","n185945142","n185963691","n185961200","n1475284021","n1475293246","n1875654132","n1475293263"]},"w206805242":{"id":"w206805242","tags":{"area":"yes","building":"yes"},"nodes":["n2168544758","n2168544759","n2168544760","n2168544761","n2168544758"]},"w208639460":{"id":"w208639460","tags":{"area":"yes","building":"yes"},"nodes":["n2189112792","n2189112793","n2189112789","n2189112790","n2189112792"]},"w208639447":{"id":"w208639447","tags":{"area":"yes","building":"yes"},"nodes":["n2189112736","n2189112737","n2189112738","n2189112739","n2189112736"]},"w208639458":{"id":"w208639458","tags":{"area":"yes","building":"yes"},"nodes":["n2189112784","n2189112785","n2189112786","n2189112787","n2189112784"]},"w203985744":{"id":"w203985744","tags":{"highway":"service"},"nodes":["n2139982425","n2139982400"]},"w208639459":{"id":"w208639459","tags":{"area":"yes","building":"yes"},"nodes":["n2189112788","n2189112789","n2189112790","n2189112791","n2189112788"]},"w203985742":{"id":"w203985742","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139982407","n2139982408","n2139982409","n2139982410","n2139982407"]},"w206805243":{"id":"w206805243","tags":{"area":"yes","building":"yes"},"nodes":["n2168544762","n2168544763","n2168544764","n2168544765","n2168544762"]},"n185959081":{"id":"n185959081","loc":[-85.628469,41.948674]},"n185967427":{"id":"n185967427","loc":[-85.632054,41.951174]},"n185967424":{"id":"n185967424","loc":[-85.6320391,41.9499109]},"n185968101":{"id":"n185968101","loc":[-85.6308395,41.9511969]},"n185960792":{"id":"n185960792","loc":[-85.632074,41.953707]},"n185961389":{"id":"n185961389","loc":[-85.630935,41.959037]},"n185961391":{"id":"n185961391","loc":[-85.632169,41.959025]},"n185965395":{"id":"n185965395","loc":[-85.63216,41.959859]},"n185966953":{"id":"n185966953","loc":[-85.630894,41.957428]},"n185966955":{"id":"n185966955","loc":[-85.632122,41.957427]},"n185967430":{"id":"n185967430","loc":[-85.632077,41.952453]},"n185967432":{"id":"n185967432","loc":[-85.632095,41.954685]},"n185967434":{"id":"n185967434","loc":[-85.632121,41.955914]},"n185967436":{"id":"n185967436","loc":[-85.632128,41.9583]},"n185967438":{"id":"n185967438","loc":[-85.632187,41.960681]},"n185967440":{"id":"n185967440","loc":[-85.632182,41.961493]},"n185968102":{"id":"n185968102","loc":[-85.630855,41.952452]},"n185968104":{"id":"n185968104","loc":[-85.630887,41.953714]},"n185968106":{"id":"n185968106","loc":[-85.630883,41.954692]},"n185968108":{"id":"n185968108","loc":[-85.630904,41.955913]},"n185968110":{"id":"n185968110","loc":[-85.630904,41.958058]},"n185968112":{"id":"n185968112","loc":[-85.630952,41.960667]},"n185968114":{"id":"n185968114","loc":[-85.630972,41.961495]},"n185968116":{"id":"n185968116","loc":[-85.630962,41.961967]},"n185978969":{"id":"n185978969","loc":[-85.633214,41.948618]},"n185985812":{"id":"n185985812","loc":[-85.633274,41.951159]},"n185986155":{"id":"n185986155","loc":[-85.633258,41.949893]},"n2208608826":{"id":"n2208608826","loc":[-85.6339222,41.9486225]},"w17964531":{"id":"w17964531","tags":{"highway":"residential","name":"Willow Dr"},"nodes":["n185959079","n185959081"]},"w17967386":{"id":"w17967386","tags":{"highway":"residential","name":"East Armitage Street"},"nodes":["n185982195","n185968101","n185967427","n185985812","n185974583"]},"w17965502":{"id":"w17965502","tags":{"highway":"residential","name":"Elm Street"},"nodes":["n185968100","n185968101","n185968102","n185968104","n185968106","n185968108","n185966953","n185968110","n185961389","n185968112","n185968114","n185968116"]},"w17967844":{"id":"w17967844","tags":{"highway":"residential","name":"East Bennett Street"},"nodes":["n185982193","n185967424","n185986155","n185978390"]},"w17966581":{"id":"w17966581","tags":{"highway":"residential","name":"E Kelsey St"},"nodes":["n185978967","n185978969","n2208608826","n185971578"]},"w17965402":{"id":"w17965402","tags":{"highway":"residential","name":"Walnut Street"},"nodes":["n185967422","n185967424","n185967427","n185967430","n185960792","n185967432","n185967434","n185966955","n185967436","n185961391","n185965395","n185967438","n185967440"]},"n2199093506":{"id":"n2199093506","loc":[-85.6251879,41.9478322]},"n2199093505":{"id":"n2199093505","loc":[-85.6252076,41.9477749]},"n2199093504":{"id":"n2199093504","loc":[-85.6252289,41.9477602]},"n2199093503":{"id":"n2199093503","loc":[-85.625201,41.9477492]},"n2199093502":{"id":"n2199093502","loc":[-85.6251682,41.9477066]},"n2199093501":{"id":"n2199093501","loc":[-85.6251715,41.947609]},"n2199093500":{"id":"n2199093500","loc":[-85.6252125,41.9475639]},"n2199093499":{"id":"n2199093499","loc":[-85.6252896,41.9475602]},"n2199093498":{"id":"n2199093498","loc":[-85.6253027,41.9475334]},"n2199093497":{"id":"n2199093497","loc":[-85.6253437,41.9474822]},"n2199093496":{"id":"n2199093496","loc":[-85.6254421,41.9474675]},"n2199093495":{"id":"n2199093495","loc":[-85.6256503,41.9474944]},"n2199093494":{"id":"n2199093494","loc":[-85.6257257,41.9476127]},"n2199093493":{"id":"n2199093493","loc":[-85.6257028,41.9477285]},"n2199093492":{"id":"n2199093492","loc":[-85.6255339,41.9478102]},"n2199093491":{"id":"n2199093491","loc":[-85.6253912,41.9478224]},"n2199093490":{"id":"n2199093490","loc":[-85.6253043,41.947859]},"n2199093489":{"id":"n2199093489","loc":[-85.6252027,41.9478846]},"n2199093458":{"id":"n2199093458","loc":[-85.6246876,41.9486617]},"n2199093457":{"id":"n2199093457","loc":[-85.6243127,41.9486583]},"n2199093456":{"id":"n2199093456","loc":[-85.624306,41.9490569]},"n2199093455":{"id":"n2199093455","loc":[-85.624681,41.9490603]},"n2199093514":{"id":"n2199093514","loc":[-85.6236228,41.9496059]},"n2199093513":{"id":"n2199093513","loc":[-85.6236231,41.9496997]},"n2199093512":{"id":"n2199093512","loc":[-85.623357,41.9497002]},"n2199093511":{"id":"n2199093511","loc":[-85.6233567,41.9496136]},"n2199093508":{"id":"n2199093508","loc":[-85.6239735,41.9494287]},"n2199093507":{"id":"n2199093507","loc":[-85.6239741,41.9496052]},"n2199093488":{"id":"n2199093488","loc":[-85.624497,41.9512286]},"n2199093487":{"id":"n2199093487","loc":[-85.6244966,41.9511259]},"n2199093486":{"id":"n2199093486","loc":[-85.6243151,41.9511263]},"n2199093485":{"id":"n2199093485","loc":[-85.6243154,41.951229]},"n2199093484":{"id":"n2199093484","loc":[-85.6241205,41.9508665]},"n2199093483":{"id":"n2199093483","loc":[-85.624115,41.9505249]},"n2199093482":{"id":"n2199093482","loc":[-85.6243149,41.9505231]},"n2199093481":{"id":"n2199093481","loc":[-85.6243203,41.9508648]},"n2199093480":{"id":"n2199093480","loc":[-85.624393,41.9508668]},"n2199093479":{"id":"n2199093479","loc":[-85.6243904,41.9505956]},"n2199093478":{"id":"n2199093478","loc":[-85.6246727,41.950594]},"n2199093477":{"id":"n2199093477","loc":[-85.624675,41.9508203]},"n2199093476":{"id":"n2199093476","loc":[-85.6245097,41.9508212]},"n2199093475":{"id":"n2199093475","loc":[-85.6245101,41.9508662]},"n2199093474":{"id":"n2199093474","loc":[-85.6241008,41.9493459]},"n2199093473":{"id":"n2199093473","loc":[-85.6242442,41.9493459]},"n2199093472":{"id":"n2199093472","loc":[-85.6242442,41.9493681]},"n2199093471":{"id":"n2199093471","loc":[-85.6243397,41.9493681]},"n2199093470":{"id":"n2199093470","loc":[-85.6243417,41.9493511]},"n2199093469":{"id":"n2199093469","loc":[-85.6247251,41.9493485]},"n2199093468":{"id":"n2199093468","loc":[-85.6247548,41.9504949]},"n2199093467":{"id":"n2199093467","loc":[-85.6241214,41.9505017]},"n2199093466":{"id":"n2199093466","loc":[-85.6254398,41.950174]},"n2199093465":{"id":"n2199093465","loc":[-85.6254412,41.9499872]},"n2199093464":{"id":"n2199093464","loc":[-85.6255363,41.9499876]},"n2199093463":{"id":"n2199093463","loc":[-85.6255374,41.9498439]},"n2199093462":{"id":"n2199093462","loc":[-85.6255638,41.949844]},"n2199093461":{"id":"n2199093461","loc":[-85.6255652,41.9496672]},"n2199093460":{"id":"n2199093460","loc":[-85.6251823,41.9496656]},"n2199093459":{"id":"n2199093459","loc":[-85.6251785,41.9501729]},"n2199093510":{"id":"n2199093510","loc":[-85.6229922,41.9496143]},"n2199093509":{"id":"n2199093509","loc":[-85.6229915,41.9494306]},"n185948903":{"id":"n185948903","loc":[-85.616514,41.947449]},"n185955120":{"id":"n185955120","loc":[-85.620103,41.951]},"n185955143":{"id":"n185955143","loc":[-85.619784,41.94746]},"n185960124":{"id":"n185960124","loc":[-85.615238,41.947468]},"n185961362":{"id":"n185961362","loc":[-85.617437,41.947451]},"n185961364":{"id":"n185961364","loc":[-85.61861,41.947456]},"n185961367":{"id":"n185961367","loc":[-85.620088,41.947458]},"n185965105":{"id":"n185965105","loc":[-85.620087,41.94924]},"n185970220":{"id":"n185970220","loc":[-85.62156,41.948333]},"n185974697":{"id":"n185974697","loc":[-85.6201059,41.950132]},"n2138420778":{"id":"n2138420778","loc":[-85.616948,41.9474499]},"w17967535":{"id":"w17967535","tags":{"highway":"residential","name":"10th Ave"},"nodes":["n185955120","n185986812","n185983141"]},"w209716130":{"id":"w209716130","tags":{"area":"yes","building":"yes"},"nodes":["n2199093485","n2199093486","n2199093487","n2199093488","n2199093485"]},"w17964788":{"id":"w17964788","tags":{"highway":"residential","name":"6th Ave"},"nodes":["n185960124","n185948903","n2138420778","n185961362","n185961364","n185955143","n185961367","n185961369","n185961371"]},"w17965159":{"id":"w17965159","tags":{"highway":"residential","name":"8th Ave"},"nodes":["n185965105","n185965108","n185965110"]},"w209716125":{"id":"w209716125","tags":{"area":"yes","building":"yes"},"nodes":["n2199093459","n2199093460","n2199093461","n2199093462","n2199093463","n2199093464","n2199093465","n2199093466","n2199093459"]},"w17965699":{"id":"w17965699","tags":{"highway":"residential","name":"7th Ave"},"nodes":["n185970220","n185970222","n185970224"]},"w209716132":{"id":"w209716132","tags":{"area":"yes","building":"yes"},"nodes":["n2199093507","n2199093508","n2199093509","n2199093510","n2199093511","n2199093512","n2199093513","n2199093514","n2199093507"]},"w17966129":{"id":"w17966129","tags":{"highway":"residential","name":"9th Ave"},"nodes":["n185974697","n185974699"]},"w209716127":{"id":"w209716127","tags":{"area":"yes","building":"yes"},"nodes":["n2199093475","n2199093476","n2199093477","n2199093478","n2199093479","n2199093480","n2199093475"]},"w209716131":{"id":"w209716131","tags":{"area":"yes","natural":"water","water":"pond"},"nodes":["n2199093489","n2199093490","n2199093491","n2199093492","n2199093493","n2199093494","n2199093495","n2199093496","n2199093497","n2199093498","n2199093499","n2199093500","n2199093501","n2199093502","n2199093503","n2199093504","n2199093505","n2199093506","n2199093489"]},"w209716126":{"id":"w209716126","tags":{"area":"yes","building":"yes"},"nodes":["n2199093467","n2199093468","n2199093469","n2199093470","n2199093471","n2199093472","n2199093473","n2199093474","n2199093467"]},"w209716124":{"id":"w209716124","tags":{"area":"yes","building":"yes"},"nodes":["n2199093455","n2199093456","n2199093457","n2199093458","n2199093455"]},"w209716128":{"id":"w209716128","tags":{"area":"yes","building":"yes"},"nodes":["n2199093481","n2199093482","n2199093483","n2199093484","n2199093481"]},"n185949872":{"id":"n185949872","loc":[-85.643009,41.949264]},"n185949875":{"id":"n185949875","loc":[-85.642598,41.94929]},"n185949877":{"id":"n185949877","loc":[-85.642127,41.949382]},"n185949881":{"id":"n185949881","loc":[-85.64169,41.949936]},"n185988165":{"id":"n185988165","loc":[-85.642167,41.947657]},"n185988167":{"id":"n185988167","loc":[-85.642347,41.947662]},"n185988169":{"id":"n185988169","loc":[-85.642621,41.947659]},"n185965019":{"id":"n185965019","loc":[-85.6385084,41.951127]},"n1475293248":{"id":"n1475293248","loc":[-85.6386095,41.9512214]},"n185962639":{"id":"n185962639","loc":[-85.649669,41.949161]},"n185962810":{"id":"n185962810","loc":[-85.649907,41.949157]},"n185964355":{"id":"n185964355","loc":[-85.637412,41.9511359]},"n185965021":{"id":"n185965021","loc":[-85.638661,41.952386]},"n185965023":{"id":"n185965023","loc":[-85.638654,41.953665]},"n185965025":{"id":"n185965025","loc":[-85.638694,41.954649]},"n185965027":{"id":"n185965027","loc":[-85.638724,41.955913]},"n185971415":{"id":"n185971415","loc":[-85.644466,41.949246]},"n185971417":{"id":"n185971417","loc":[-85.647021,41.949193]},"n185971420":{"id":"n185971420","loc":[-85.648476,41.949169]},"n185979975":{"id":"n185979975","loc":[-85.644429,41.947633]},"n185988171":{"id":"n185988171","loc":[-85.645377,41.947622]},"w17963211":{"id":"w17963211","tags":{"highway":"residential"},"nodes":["n185949870","n185949872","n185949875","n185949877","n185949881"]},"w17965839":{"id":"w17965839","tags":{"highway":"residential","name":"Arnold St"},"nodes":["n185949870","n185971415","n185971417","n185971420","n185962639","n185962810"]},"w17967618":{"id":"w17967618","tags":{"highway":"residential","name":"Pierson St"},"nodes":["n185967777","n185988165","n185988167","n185988169","n185985824","n185979975","n185988171"]},"w17965149":{"id":"w17965149","tags":{"highway":"residential","name":"Oak St"},"nodes":["n185965019","n1475293248","n185965021","n185965023","n185965025","n185965027"]},"w17966118":{"id":"w17966118","tags":{"highway":"residential","name":"West Armitage Street"},"nodes":["n185974583","n185974585","n185964355","n185965019"]},"n2208608800":{"id":"n2208608800","loc":[-85.6354294,41.9486201]},"n2199109806":{"id":"n2199109806","loc":[-85.6350474,41.9477884]},"n2199109804":{"id":"n2199109804","loc":[-85.6350476,41.9477962]},"n2199109802":{"id":"n2199109802","loc":[-85.635002,41.9477969]},"n2199109799":{"id":"n2199109799","loc":[-85.6350018,41.9477883]},"n2199109797":{"id":"n2199109797","loc":[-85.6349141,41.9477897]},"n2199109795":{"id":"n2199109795","loc":[-85.6349131,41.9477535]},"n2199109793":{"id":"n2199109793","loc":[-85.6349395,41.9477531]},"n2199109791":{"id":"n2199109791","loc":[-85.6349382,41.9477077]},"n2199109789":{"id":"n2199109789","loc":[-85.6351236,41.9477049]},"n2199109787":{"id":"n2199109787","loc":[-85.6351259,41.9477872]},"n2199109785":{"id":"n2199109785","loc":[-85.634972,41.9475992]},"n2199109783":{"id":"n2199109783","loc":[-85.6349206,41.9475997]},"n2199109770":{"id":"n2199109770","loc":[-85.6348499,41.9475461]},"n2199109768":{"id":"n2199109768","loc":[-85.6348499,41.9475084]},"n2199109765":{"id":"n2199109765","loc":[-85.6349241,41.9474569]},"n2199109763":{"id":"n2199109763","loc":[-85.634967,41.9474564]},"n2199109762":{"id":"n2199109762","loc":[-85.6350405,41.9475121]},"n2199109761":{"id":"n2199109761","loc":[-85.6350405,41.9475419]},"n2199109753":{"id":"n2199109753","loc":[-85.6342443,41.9478391]},"n2199109751":{"id":"n2199109751","loc":[-85.6342427,41.9477927]},"n2199109745":{"id":"n2199109745","loc":[-85.6342439,41.9476859]},"n2199109743":{"id":"n2199109743","loc":[-85.6342429,41.9476575]},"n2199109741":{"id":"n2199109741","loc":[-85.6344615,41.9476533]},"n2199109739":{"id":"n2199109739","loc":[-85.6344678,41.9478348]},"n2199109737":{"id":"n2199109737","loc":[-85.634416,41.9480059]},"n2199109735":{"id":"n2199109735","loc":[-85.6344145,41.9478983]},"n2199109733":{"id":"n2199109733","loc":[-85.6342749,41.9478993]},"n2199109731":{"id":"n2199109731","loc":[-85.6342753,41.9479272]},"n2199109729":{"id":"n2199109729","loc":[-85.6342498,41.9479274]},"n2199109727":{"id":"n2199109727","loc":[-85.6342505,41.9479762]},"n2199109725":{"id":"n2199109725","loc":[-85.6342743,41.947976]},"n2199109723":{"id":"n2199109723","loc":[-85.6342747,41.948007]},"n2199109721":{"id":"n2199109721","loc":[-85.6343415,41.9476355]},"n2199109719":{"id":"n2199109719","loc":[-85.6343391,41.9474973]},"n2199109717":{"id":"n2199109717","loc":[-85.6343133,41.9474798]},"n2199109715":{"id":"n2199109715","loc":[-85.6342874,41.9474737]},"n2199109709":{"id":"n2199109709","loc":[-85.6349804,41.94815]},"n2199109707":{"id":"n2199109707","loc":[-85.6348915,41.9481505]},"n2199109705":{"id":"n2199109705","loc":[-85.6348917,41.9481692]},"n2199109702":{"id":"n2199109702","loc":[-85.6348522,41.9481694]},"n2199109700":{"id":"n2199109700","loc":[-85.6348532,41.9482679]},"n2199109698":{"id":"n2199109698","loc":[-85.6348315,41.948268]},"n2199109696":{"id":"n2199109696","loc":[-85.6348318,41.9482955]},"n2199109694":{"id":"n2199109694","loc":[-85.6349653,41.9482946]},"n2199109692":{"id":"n2199109692","loc":[-85.6349656,41.9483211]},"n2199109690":{"id":"n2199109690","loc":[-85.634999,41.9483209]},"n2199109688":{"id":"n2199109688","loc":[-85.6349987,41.9482947]},"n2199109686":{"id":"n2199109686","loc":[-85.6351753,41.9482935]},"n2199109684":{"id":"n2199109684","loc":[-85.6351749,41.9482617]},"n2199109682":{"id":"n2199109682","loc":[-85.6351588,41.9482618]},"n2199109680":{"id":"n2199109680","loc":[-85.6351575,41.9481518]},"n2199109678":{"id":"n2199109678","loc":[-85.6350671,41.9481524]},"n2199109676":{"id":"n2199109676","loc":[-85.6350649,41.9479659]},"n2199109674":{"id":"n2199109674","loc":[-85.6349785,41.9479665]},"n2199109671":{"id":"n2199109671","loc":[-85.6343069,41.9483263]},"n2199109669":{"id":"n2199109669","loc":[-85.6343052,41.9482981]},"n2199109658":{"id":"n2199109658","loc":[-85.6343314,41.9480549]},"n2199109656":{"id":"n2199109656","loc":[-85.6343305,41.9480461]},"n2199109654":{"id":"n2199109654","loc":[-85.634435,41.9480468]},"n2199109652":{"id":"n2199109652","loc":[-85.6344342,41.9483746]},"n2199109650":{"id":"n2199109650","loc":[-85.6344629,41.9483727]},"n2199109648":{"id":"n2199109648","loc":[-85.6344637,41.9484561]},"n2199109645":{"id":"n2199109645","loc":[-85.63443,41.9484567]},"n2199109642":{"id":"n2199109642","loc":[-85.6344317,41.948505]},"n185964352":{"id":"n185964352","loc":[-85.6373958,41.9489943]},"n185964351":{"id":"n185964351","loc":[-85.637113,41.9486]},"n2208608825":{"id":"n2208608825","loc":[-85.6354483,41.9494241]},"n2208608823":{"id":"n2208608823","loc":[-85.6360418,41.949416]},"n2208608821":{"id":"n2208608821","loc":[-85.6360458,41.9495802]},"n2208608811":{"id":"n2208608811","loc":[-85.6357458,41.9495843]},"n2208608808":{"id":"n2208608808","loc":[-85.6357508,41.9497835]},"n2208608806":{"id":"n2208608806","loc":[-85.6354573,41.9497875]},"n2208608795":{"id":"n2208608795","loc":[-85.6354595,41.9498778]},"n2199109638":{"id":"n2199109638","loc":[-85.6349605,41.949749]},"n2199109636":{"id":"n2199109636","loc":[-85.6349605,41.9497639]},"n2199109634":{"id":"n2199109634","loc":[-85.6349061,41.94971]},"n2199109632":{"id":"n2199109632","loc":[-85.6349048,41.9496569]},"n2199109630":{"id":"n2199109630","loc":[-85.6348835,41.9496571]},"n2199109628":{"id":"n2199109628","loc":[-85.6348829,41.9497103]},"n2199109626":{"id":"n2199109626","loc":[-85.635227,41.9497738]},"n2199109624":{"id":"n2199109624","loc":[-85.6352184,41.9497787]},"n2199109622":{"id":"n2199109622","loc":[-85.6351181,41.9497806]},"n2199109620":{"id":"n2199109620","loc":[-85.6351181,41.9497456]},"n2199109618":{"id":"n2199109618","loc":[-85.6348842,41.9497651]},"n2199109616":{"id":"n2199109616","loc":[-85.6348827,41.9496238]},"n2199109615":{"id":"n2199109615","loc":[-85.6351268,41.9496206]},"n2199109614":{"id":"n2199109614","loc":[-85.6351261,41.9495891]},"n2199109613":{"id":"n2199109613","loc":[-85.6351957,41.9495881]},"n2199109612":{"id":"n2199109612","loc":[-85.6351924,41.9494515]},"n2199109611":{"id":"n2199109611","loc":[-85.6353997,41.9494488]},"n2199109610":{"id":"n2199109610","loc":[-85.6354074,41.9497715]},"n2189015681":{"id":"n2189015681","loc":[-85.6344229,41.9509639]},"n2189015677":{"id":"n2189015677","loc":[-85.634424,41.9507396]},"n2138493843":{"id":"n2138493843","loc":[-85.6343935,41.9502836]},"n2138493840":{"id":"n2138493840","loc":[-85.634398,41.9506264]},"n354002838":{"id":"n354002838","loc":[-85.6345197,41.9510631]},"n2114807590":{"id":"n2114807590","loc":[-85.634511,41.9499767]},"n185964353":{"id":"n185964353","loc":[-85.6374092,41.9498755]},"n1819849180":{"id":"n1819849180","loc":[-85.6348236,41.94996]},"n1819849115":{"id":"n1819849115","loc":[-85.6354372,41.9499538]},"n1819848921":{"id":"n1819848921","loc":[-85.6348439,41.951064]},"n1819848885":{"id":"n1819848885","loc":[-85.6354575,41.9510578]},"n185984281":{"id":"n185984281","loc":[-85.638075,41.949872]},"n2208608827":{"id":"n2208608827","loc":[-85.6339169,41.9473191]},"n2199109749":{"id":"n2199109749","loc":[-85.6342082,41.9477934]},"n2199109747":{"id":"n2199109747","loc":[-85.6342045,41.9476867]},"n2199109713":{"id":"n2199109713","loc":[-85.6342404,41.9474746]},"n2199109711":{"id":"n2199109711","loc":[-85.6342404,41.9476355]},"n2199109673":{"id":"n2199109673","loc":[-85.6340886,41.9483282]},"n2199109667":{"id":"n2199109667","loc":[-85.6342403,41.9482988]},"n2199109665":{"id":"n2199109665","loc":[-85.6342386,41.9482116]},"n2199109662":{"id":"n2199109662","loc":[-85.6340861,41.9482135]},"n2199109660":{"id":"n2199109660","loc":[-85.6340802,41.9480562]},"n2199109640":{"id":"n2199109640","loc":[-85.6340928,41.9485063]},"n354031366":{"id":"n354031366","loc":[-85.6341667,41.9477778],"tags":{"amenity":"place_of_worship","name":"Faith Tabernacle Church","religion":"christian"}},"n2189015686":{"id":"n2189015686","loc":[-85.6337798,41.95099]},"n2189015684":{"id":"n2189015684","loc":[-85.6337794,41.9509674]},"n2189015673":{"id":"n2189015673","loc":[-85.6337501,41.9507457]},"n2189015669":{"id":"n2189015669","loc":[-85.6337501,41.9506974]},"n2189015665":{"id":"n2189015665","loc":[-85.6339034,41.9506959]},"n2189015662":{"id":"n2189015662","loc":[-85.6339015,41.950436]},"n2189015658":{"id":"n2189015658","loc":[-85.6334916,41.9504376]},"n2189015655":{"id":"n2189015655","loc":[-85.6334939,41.9507558]},"n2189015650":{"id":"n2189015650","loc":[-85.6334543,41.950756]},"n2189015649":{"id":"n2189015649","loc":[-85.633456,41.9509915]},"n2138493842":{"id":"n2138493842","loc":[-85.6339937,41.9502836]},"n2138493841":{"id":"n2138493841","loc":[-85.6339983,41.9506281]},"n2114807579":{"id":"n2114807579","loc":[-85.6333644,41.9510682]},"n2114807573":{"id":"n2114807573","loc":[-85.6333557,41.9499819]},"n354031330":{"id":"n354031330","loc":[-85.6341667,41.9497222],"tags":{"amenity":"place_of_worship","name":"Trinity Episcopal Church","religion":"christian"}},"n185960794":{"id":"n185960794","loc":[-85.633307,41.9537]},"n185964357":{"id":"n185964357","loc":[-85.637432,41.952399]},"n185964358":{"id":"n185964358","loc":[-85.637452,41.953665]},"n185964359":{"id":"n185964359","loc":[-85.63746,41.954658]},"n185964360":{"id":"n185964360","loc":[-85.637473,41.95592]},"n185964361":{"id":"n185964361","loc":[-85.637468,41.956906]},"n185964362":{"id":"n185964362","loc":[-85.637483,41.958313]},"n185966957":{"id":"n185966957","loc":[-85.633361,41.957422]},"n185975351":{"id":"n185975351","loc":[-85.63334,41.9559]},"n185978784":{"id":"n185978784","loc":[-85.633311,41.954679]},"n185986157":{"id":"n185986157","loc":[-85.633287,41.952426]},"n185986158":{"id":"n185986158","loc":[-85.6333607,41.9582301],"tags":{"highway":"turning_circle"}},"w17965182":{"id":"w17965182","tags":{"highway":"residential","name":"W Prutzman St"},"nodes":["n185965289","n2189153241","n185965291"]},"w208627205":{"id":"w208627205","tags":{"area":"yes","building":"yes"},"nodes":["n2189015649","n2189015650","n2189015655","n2189015658","n2189015662","n2189015665","n2189015669","n2189015673","n2189015677","n2189015681","n2189015684","n2189015686","n2189015649"]},"w209717042":{"id":"w209717042","tags":{"amenity":"place_of_worship","area":"yes","building":"yes","denomination":"presbyterian","name":"First Presbyterian Church","religion":"christian"},"nodes":["n2199109610","n2199109611","n2199109612","n2199109613","n2199109614","n2199109615","n2199109616","n2199109630","n2199109632","n2199109634","n2199109628","n2199109618","n2199109636","n2199109638","n2199109620","n2199109622","n2199109624","n2199109626","n2199109610"]},"w209717045":{"id":"w209717045","tags":{"area":"yes","building":"yes"},"nodes":["n2199109711","n2199109713","n2199109715","n2199109717","n2199109719","n2199109721","n2199109711"]},"w209717047":{"id":"w209717047","tags":{"area":"yes","building":"yes"},"nodes":["n2199109739","n2199109741","n2199109743","n2199109745","n2199109747","n2199109749","n2199109751","n2199109753","n2199109739"]},"w209717044":{"id":"w209717044","tags":{"area":"yes","building":"yes"},"nodes":["n2199109674","n2199109676","n2199109678","n2199109680","n2199109682","n2199109684","n2199109686","n2199109688","n2199109690","n2199109692","n2199109694","n2199109696","n2199109698","n2199109700","n2199109702","n2199109705","n2199109707","n2199109709","n2199109674"]},"w210822776":{"id":"w210822776","tags":{"highway":"service","service":"alley","surface":"unpaved"},"nodes":["n2208608795","n2208608806","n2208608825","n2208608800","n2189153241"]},"w210822778":{"id":"w210822778","tags":{"highway":"service","service":"alley"},"nodes":["n2208608826","n2208608827"]},"w209717050":{"id":"w209717050","tags":{"area":"yes","building":"yes"},"nodes":["n2199109787","n2199109789","n2199109791","n2199109793","n2199109795","n2199109797","n2199109799","n2199109802","n2199109804","n2199109806","n2199109787"]},"w17965097":{"id":"w17965097","tags":{"highway":"residential","name":"Maple Street"},"nodes":["n185964351","n185964352","n185964353","n185964355","n185964357","n185964358","n185964359","n185964360","n185964361","n185964362"]},"w17965856":{"id":"w17965856","tags":{"highway":"residential","name":"W Kelsey St"},"nodes":["n185971578","n2208608800","n185971580","n185964351"]},"w17967444":{"id":"w17967444","tags":{"highway":"residential","name":"East Street"},"nodes":["n185966937","n185978969","n185986155","n185985812","n185986157","n185960794","n185978784","n185975351","n185966957","n185986158"]},"w17967764":{"id":"w17967764","tags":{"highway":"residential","name":"Rock River Ave"},"nodes":["n185984017","n185964351"]},"w170848329":{"id":"w170848329","tags":{"leisure":"park","name":"LaFayette Park"},"nodes":["n1819849180","n1819849115","n1819848885","n1819848921","n1819849180"]},"w17967208":{"id":"w17967208","tags":{"highway":"residential","name":"West Bennett Street"},"nodes":["n185978390","n2208608795","n185984020","n185964353","n185984281"]},"w17965349":{"id":"w17965349","tags":{"highway":"residential","name":"E Prutzman St"},"nodes":["n185966937","n2208608827","n185965289"]},"w209717049":{"id":"w209717049","tags":{"area":"yes","building":"yes"},"nodes":["n2199109761","n2199109762","n2199109763","n2199109765","n2199109768","n2199109770","n2199109783","n2199109785","n2199109761"]},"w203841840":{"id":"w203841840","tags":{"area":"yes","leisure":"playground"},"nodes":["n2138493840","n2138493841","n2138493842","n2138493843","n2138493840"]},"w209717043":{"id":"w209717043","tags":{"amenity":"place_of_worship","area":"yes","building":"church","denomination":"methodist","name":"First United Methodist Church","religion":"christian"},"nodes":["n2199109640","n2199109642","n2199109645","n2199109648","n2199109650","n2199109652","n2199109654","n2199109656","n2199109658","n2199109660","n2199109662","n2199109665","n2199109667","n2199109669","n2199109671","n2199109673","n2199109640"]},"w201484341":{"id":"w201484341","tags":{"amenity":"school","name":"Hoppin School"},"nodes":["n354002838","n2114807579","n2114807573","n2114807590","n354002838"]},"w209717046":{"id":"w209717046","tags":{"area":"yes","building":"yes"},"nodes":["n2199109723","n2199109725","n2199109727","n2199109729","n2199109731","n2199109733","n2199109735","n2199109737","n2199109723"]},"w210822777":{"id":"w210822777","tags":{"amenity":"parking","area":"yes"},"nodes":["n2208608806","n2208608808","n2208608811","n2208608821","n2208608823","n2208608825","n2208608806"]},"n185954965":{"id":"n185954965","loc":[-85.6191189,41.9441922]},"n185954968":{"id":"n185954968","loc":[-85.6194384,41.9442405]},"n185954970":{"id":"n185954970","loc":[-85.6196543,41.9443252]},"n185954972":{"id":"n185954972","loc":[-85.6197862,41.9444539]},"n354002931":{"id":"n354002931","loc":[-85.6198991,41.9455269]},"n354030853":{"id":"n354030853","loc":[-85.6219444,41.9455556],"tags":{"amenity":"place_of_worship","name":"Grant Chapel","religion":"christian"}},"n367815963":{"id":"n367815963","loc":[-85.6202778,41.9461111],"tags":{"building":"yes","name":"George Washington Carver Community Center"}},"n185947331":{"id":"n185947331","loc":[-85.618779,41.943269]},"n185947333":{"id":"n185947333","loc":[-85.618795,41.943511]},"n185947336":{"id":"n185947336","loc":[-85.618711,41.94413]},"n185947338":{"id":"n185947338","loc":[-85.618704,41.944189]},"n185947339":{"id":"n185947339","loc":[-85.618597,41.944337]},"n185947340":{"id":"n185947340","loc":[-85.618485,41.944528]},"n185947343":{"id":"n185947343","loc":[-85.618442,41.944716]},"n185947345":{"id":"n185947345","loc":[-85.618457,41.945107]},"n185947347":{"id":"n185947347","loc":[-85.618296,41.945338]},"n185947374":{"id":"n185947374","loc":[-85.616748,41.944453]},"n185947375":{"id":"n185947375","loc":[-85.616813,41.944646]},"n185947376":{"id":"n185947376","loc":[-85.616859,41.945196]},"n185947377":{"id":"n185947377","loc":[-85.616941,41.945352]},"n185947406":{"id":"n185947406","loc":[-85.618184,41.944227]},"n185947409":{"id":"n185947409","loc":[-85.617911,41.943875]},"n185947410":{"id":"n185947410","loc":[-85.617579,41.943682]},"n185947411":{"id":"n185947411","loc":[-85.61713,41.943589]},"n185947412":{"id":"n185947412","loc":[-85.616549,41.943559]},"n185947414":{"id":"n185947414","loc":[-85.616482,41.943556]},"n185947464":{"id":"n185947464","loc":[-85.616526,41.943788]},"n185947466":{"id":"n185947466","loc":[-85.616504,41.944002]},"n185948863":{"id":"n185948863","loc":[-85.619017,41.943391]},"n185948865":{"id":"n185948865","loc":[-85.619059,41.943368]},"n185955022":{"id":"n185955022","loc":[-85.620088,41.945571]},"n185955025":{"id":"n185955025","loc":[-85.620051,41.945505]},"n185955028":{"id":"n185955028","loc":[-85.62001,41.94541]},"n185980371":{"id":"n185980371","loc":[-85.620982,41.944742]},"n185980398":{"id":"n185980398","loc":[-85.621305,41.944782]},"n185980401":{"id":"n185980401","loc":[-85.621174,41.944819]},"n185980403":{"id":"n185980403","loc":[-85.621029,41.944871]},"n185980405":{"id":"n185980405","loc":[-85.620741,41.945011]},"n185980407":{"id":"n185980407","loc":[-85.620616,41.945085]},"n185980409":{"id":"n185980409","loc":[-85.620506,41.945172]},"n185980411":{"id":"n185980411","loc":[-85.620394,41.945273]},"n185980413":{"id":"n185980413","loc":[-85.620316,41.94536]},"n185980415":{"id":"n185980415","loc":[-85.620257,41.945452]},"n185980417":{"id":"n185980417","loc":[-85.620212,41.945535]},"n185985910":{"id":"n185985910","loc":[-85.620101,41.945811]},"n185985912":{"id":"n185985912","loc":[-85.620081,41.945937]},"n1475283972":{"id":"n1475283972","loc":[-85.6198991,41.9437179]},"n1475283982":{"id":"n1475283982","loc":[-85.6195022,41.9433463]},"n1475284007":{"id":"n1475284007","loc":[-85.6193037,41.9433383]},"n1475284040":{"id":"n1475284040","loc":[-85.6197329,41.9434121]},"n1475284044":{"id":"n1475284044","loc":[-85.6198756,41.9435363]},"n1475284050":{"id":"n1475284050","loc":[-85.6199689,41.9432106]},"n1475284053":{"id":"n1475284053","loc":[-85.6198943,41.9432921]},"n185954974":{"id":"n185954974","loc":[-85.6198296,41.94473]},"n185954977":{"id":"n185954977","loc":[-85.6200474,41.9447384]},"n2196831365":{"id":"n2196831365","loc":[-85.6202259,41.9460883]},"n2196831366":{"id":"n2196831366","loc":[-85.6202245,41.9458642]},"n2196831367":{"id":"n2196831367","loc":[-85.6205184,41.9458631]},"n2196831368":{"id":"n2196831368","loc":[-85.6205189,41.9459437]},"n2196831369":{"id":"n2196831369","loc":[-85.6203879,41.9459441]},"n2196831370":{"id":"n2196831370","loc":[-85.6203888,41.9460878]},"n2196831371":{"id":"n2196831371","loc":[-85.6184046,41.9465663]},"n2196831372":{"id":"n2196831372","loc":[-85.6191563,41.9465618]},"n2196831373":{"id":"n2196831373","loc":[-85.6191536,41.946319]},"n2196831374":{"id":"n2196831374","loc":[-85.6187356,41.9463216]},"n2196831375":{"id":"n2196831375","loc":[-85.6187334,41.9461197]},"n2196831376":{"id":"n2196831376","loc":[-85.6193167,41.9461162]},"n2196831377":{"id":"n2196831377","loc":[-85.6193156,41.9460229]},"n2196831378":{"id":"n2196831378","loc":[-85.619622,41.946021]},"n2196831379":{"id":"n2196831379","loc":[-85.6196237,41.9461712]},"n2196831380":{"id":"n2196831380","loc":[-85.6197702,41.9461703]},"n2196831381":{"id":"n2196831381","loc":[-85.6197685,41.9460202]},"n2196831382":{"id":"n2196831382","loc":[-85.6197323,41.9460204]},"n2196831383":{"id":"n2196831383","loc":[-85.6197305,41.9458563]},"n2196831384":{"id":"n2196831384","loc":[-85.6196165,41.945857]},"n2196831385":{"id":"n2196831385","loc":[-85.6196156,41.9457764]},"n2196831386":{"id":"n2196831386","loc":[-85.6194472,41.9457775]},"n2196831387":{"id":"n2196831387","loc":[-85.6194151,41.9457777]},"n2196831388":{"id":"n2196831388","loc":[-85.6183779,41.9457883]},"n2196831389":{"id":"n2196831389","loc":[-85.6183842,41.9461317]},"n2196831390":{"id":"n2196831390","loc":[-85.6185026,41.9461304]},"n2196831391":{"id":"n2196831391","loc":[-85.6185061,41.9463194]},"n2196831392":{"id":"n2196831392","loc":[-85.6184001,41.9463205]},"n2196831393":{"id":"n2196831393","loc":[-85.6182482,41.9464163]},"n2196831394":{"id":"n2196831394","loc":[-85.6182467,41.9463193]},"n2196831395":{"id":"n2196831395","loc":[-85.6180389,41.946321]},"n2196831397":{"id":"n2196831397","loc":[-85.6180404,41.946418]},"n185947303":{"id":"n185947303","loc":[-85.611074,41.943389]},"n185947304":{"id":"n185947304","loc":[-85.611332,41.943267]},"n185947305":{"id":"n185947305","loc":[-85.611635,41.943218]},"n185947306":{"id":"n185947306","loc":[-85.612762,41.943311]},"n185947308":{"id":"n185947308","loc":[-85.613027,41.943327]},"n185947310":{"id":"n185947310","loc":[-85.615377,41.942996]},"n185947312":{"id":"n185947312","loc":[-85.615701,41.943007]},"n185947314":{"id":"n185947314","loc":[-85.61604,41.943067]},"n185947315":{"id":"n185947315","loc":[-85.61626,41.943083]},"n185947316":{"id":"n185947316","loc":[-85.616507,41.943048]},"n185947319":{"id":"n185947319","loc":[-85.616702,41.94299]},"n185947321":{"id":"n185947321","loc":[-85.617078,41.942918]},"n185947322":{"id":"n185947322","loc":[-85.617366,41.942973]},"n185947323":{"id":"n185947323","loc":[-85.617601,41.943033]},"n185947325":{"id":"n185947325","loc":[-85.617799,41.943027]},"n185947327":{"id":"n185947327","loc":[-85.618264,41.942961]},"n185947328":{"id":"n185947328","loc":[-85.618508,41.942972]},"n185947329":{"id":"n185947329","loc":[-85.618707,41.943076]},"n185947361":{"id":"n185947361","loc":[-85.615356,41.944922]},"n185947363":{"id":"n185947363","loc":[-85.61536,41.944893]},"n185947365":{"id":"n185947365","loc":[-85.615406,41.944547]},"n185947367":{"id":"n185947367","loc":[-85.61548,41.944351]},"n185947369":{"id":"n185947369","loc":[-85.615805,41.94419]},"n185947371":{"id":"n185947371","loc":[-85.616166,41.944156]},"n185947373":{"id":"n185947373","loc":[-85.616411,41.944197]},"n185947416":{"id":"n185947416","loc":[-85.616335,41.94343]},"n185947417":{"id":"n185947417","loc":[-85.616069,41.943293]},"n185947419":{"id":"n185947419","loc":[-85.615803,41.943249]},"n185947420":{"id":"n185947420","loc":[-85.615524,41.943342]},"n185947421":{"id":"n185947421","loc":[-85.615311,41.94353]},"n185947422":{"id":"n185947422","loc":[-85.614338,41.943558]},"n185947423":{"id":"n185947423","loc":[-85.61422,41.94369]},"n185947425":{"id":"n185947425","loc":[-85.614221,41.944224]},"n185947427":{"id":"n185947427","loc":[-85.614198,41.944888]},"n185947429":{"id":"n185947429","loc":[-85.614221,41.945439]},"n185947468":{"id":"n185947468","loc":[-85.615908,41.944756]},"n185947470":{"id":"n185947470","loc":[-85.615871,41.944888]},"n185947472":{"id":"n185947472","loc":[-85.615878,41.94507]},"n185955153":{"id":"n185955153","loc":[-85.620087,41.947701]},"n185960690":{"id":"n185960690","loc":[-85.620141,41.951901]},"n185978817":{"id":"n185978817","loc":[-85.617193,41.954706]},"n185985916":{"id":"n185985916","loc":[-85.620088,41.94758]},"n185985918":{"id":"n185985918","loc":[-85.620133,41.951538]},"n185985919":{"id":"n185985919","loc":[-85.62013,41.952104]},"n185985920":{"id":"n185985920","loc":[-85.620104,41.952305]},"n185985921":{"id":"n185985921","loc":[-85.620062,41.952499]},"n185985922":{"id":"n185985922","loc":[-85.619993,41.952702]},"n185985925":{"id":"n185985925","loc":[-85.619879,41.952986]},"n185985927":{"id":"n185985927","loc":[-85.619689,41.95329]},"n185985928":{"id":"n185985928","loc":[-85.619508,41.953521]},"n185985929":{"id":"n185985929","loc":[-85.619286,41.953728]},"n185985930":{"id":"n185985930","loc":[-85.618925,41.954007]},"n185985931":{"id":"n185985931","loc":[-85.618638,41.954189]},"n185985932":{"id":"n185985932","loc":[-85.61831,41.954358]},"n185985934":{"id":"n185985934","loc":[-85.618015,41.954485]},"n185985936":{"id":"n185985936","loc":[-85.617606,41.954611]},"n1475283975":{"id":"n1475283975","loc":[-85.6150935,41.9434118]},"n1475283979":{"id":"n1475283979","loc":[-85.6193367,41.9430252]},"n1475283989":{"id":"n1475283989","loc":[-85.6104771,41.9455269]},"n1475283990":{"id":"n1475283990","loc":[-85.6104771,41.9437179]},"n1475283994":{"id":"n1475283994","loc":[-85.6198042,41.9429763]},"n1475283998":{"id":"n1475283998","loc":[-85.6192101,41.9426716]},"n1475284000":{"id":"n1475284000","loc":[-85.6198622,41.942836]},"n1475284002":{"id":"n1475284002","loc":[-85.6163262,41.9427688]},"n1475284006":{"id":"n1475284006","loc":[-85.6179527,41.9429168]},"n1475284029":{"id":"n1475284029","loc":[-85.6197195,41.9427278]},"n1475284038":{"id":"n1475284038","loc":[-85.6194405,41.9427837]},"n1475284052":{"id":"n1475284052","loc":[-85.6153225,41.942841]},"n1475284055":{"id":"n1475284055","loc":[-85.6129233,41.9437179]},"n2139966627":{"id":"n2139966627","loc":[-85.61958,41.9427558]},"w17966773":{"id":"w17966773","tags":{"highway":"secondary","name":"E Michigan Ave","ref":"M 60"},"nodes":["n185980372","n185980398","n185980401","n185980403","n185980405","n185980407","n185980409","n185980411","n185980413","n185980415","n185980417","n185955019"]},"w17964043":{"id":"w17964043","tags":{"highway":"residential"},"nodes":["n185955019","n185955022","n185955025","n185955028","n185954977","n185971477","n1475284050","n1475284000","n1475284029","n2139966627","n1475284038"]},"w17962834":{"id":"w17962834","tags":{"highway":"service"},"nodes":["n185947316","n185947414","n185947464","n185947466","n185947373","n185947468","n185947470","n185947472","n185947474"]},"w209470310":{"id":"w209470310","tags":{"area":"yes","building":"yes"},"nodes":["n2196831393","n2196831394","n2196831395","n2196831397","n2196831393"]},"w17963058":{"id":"w17963058","tags":{"highway":"service"},"nodes":["n185947333","n185948863","n185948865","n1475284007","n1475283982","n1475284040","n1475284044"]},"w17962823":{"id":"w17962823","tags":{"highway":"service"},"nodes":["n185947359","n185947361","n185947363","n185947365","n185947367","n185947369","n185947371","n185947373","n185947374","n185947375","n185947376","n185947377","n185947378"]},"w17962821":{"id":"w17962821","tags":{"highway":"service"},"nodes":["n185947303","n185947304","n185947305","n185947306","n185947308","n185947310","n185947312","n185947314","n185947315","n185947316","n185947319","n185947321","n185947322","n185947323","n185947325","n185947327","n185947328","n185947329","n185947331","n185947333","n185947336","n185947338","n185947339","n185947340","n185947343","n185947345","n185947347","n185947349"]},"w134150798":{"id":"w134150798","tags":{"amenity":"grave_yard","name":"Riverside Cemetery"},"nodes":["n354002931","n1475283972","n1475284053","n1475283994","n1475283979","n1475283998","n1475284006","n1475284002","n1475284052","n1475283975","n1475284055","n1475283990","n1475283989","n354002931"]},"w17964040":{"id":"w17964040","tags":{"highway":"service"},"nodes":["n185947336","n185954965","n185954968","n185954970","n185954972","n185954974","n185954977"]},"w209470308":{"id":"w209470308","tags":{"area":"yes","building":"yes"},"nodes":["n2196831365","n2196831366","n2196831367","n2196831368","n2196831369","n2196831370","n2196831365"]},"w17962828":{"id":"w17962828","tags":{"highway":"service"},"nodes":["n185947340","n185947406","n185947409","n185947410","n185947411","n185947412","n185947414","n185947416","n185947417","n185947419","n185947420","n185947421","n185947422","n185947423","n185947425","n185947427","n185947429"]},"w209470309":{"id":"w209470309","tags":{"area":"yes","building":"yes"},"nodes":["n2196831371","n2196831372","n2196831373","n2196831374","n2196831375","n2196831376","n2196831377","n2196831378","n2196831379","n2196831380","n2196831381","n2196831382","n2196831383","n2196831384","n2196831385","n2196831386","n2196831387","n2196831388","n2196831389","n2196831390","n2196831391","n2196831392","n2196831371"]},"w17967415":{"id":"w17967415","tags":{"highway":"secondary","name":"Jefferson St","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185955019","n185985910","n185985912","n185985914","n185961367","n185985916","n185955153","n185965105","n185974697","n185955120","n185985918","n185960690","n185985919","n185985920","n185985921","n185985922","n185985925","n185985927","n185985928","n185985929","n185985930","n185985931","n185985932","n185985934","n185985936","n185978817"]},"w17966772":{"id":"w17966772","tags":{"highway":"unclassified","name":"E Michigan Ave","name_1":"State Highway 60"},"nodes":["n185954977","n185980371","n185980372"]},"n185958500":{"id":"n185958500","loc":[-85.621591,41.941075]},"n185963110":{"id":"n185963110","loc":[-85.6204416,41.9408882]},"n2139966628":{"id":"n2139966628","loc":[-85.6196431,41.9426467],"tags":{"leisure":"fishing"}},"n2139966630":{"id":"n2139966630","loc":[-85.6199354,41.9429616]},"n2199127051":{"id":"n2199127051","loc":[-85.6170556,41.939696]},"n2199127052":{"id":"n2199127052","loc":[-85.6170536,41.9392909]},"n2199127053":{"id":"n2199127053","loc":[-85.6172067,41.9392905]},"n2199127054":{"id":"n2199127054","loc":[-85.6172061,41.9391853]},"n2199127055":{"id":"n2199127055","loc":[-85.6171481,41.9391854]},"n2199127060":{"id":"n2199127060","loc":[-85.6167389,41.9392896]},"n2199127061":{"id":"n2199127061","loc":[-85.6168728,41.9392892]},"n2199127062":{"id":"n2199127062","loc":[-85.6168747,41.9396965]},"n2199127071":{"id":"n2199127071","loc":[-85.620196,41.9399446]},"n2199127072":{"id":"n2199127072","loc":[-85.620193,41.9397316]},"n2199127073":{"id":"n2199127073","loc":[-85.6200381,41.9397328]},"n2199127074":{"id":"n2199127074","loc":[-85.6200412,41.9399458]},"n2199127075":{"id":"n2199127075","loc":[-85.6203606,41.9399939]},"n2199127076":{"id":"n2199127076","loc":[-85.6205527,41.9399922]},"n2199127077":{"id":"n2199127077","loc":[-85.6205482,41.9397115]},"n2199127078":{"id":"n2199127078","loc":[-85.6204132,41.9397124]},"n2199127079":{"id":"n2199127079","loc":[-85.6204144,41.9396341]},"n2199127080":{"id":"n2199127080","loc":[-85.6205699,41.9396324]},"n2199127081":{"id":"n2199127081","loc":[-85.6205722,41.939498]},"n2199127082":{"id":"n2199127082","loc":[-85.6204064,41.9394997]},"n2199127083":{"id":"n2199127083","loc":[-85.6204087,41.939561]},"n2199127084":{"id":"n2199127084","loc":[-85.6203103,41.9395618]},"n2199127085":{"id":"n2199127085","loc":[-85.620308,41.9396069]},"n2199127086":{"id":"n2199127086","loc":[-85.6200347,41.9396086]},"n2199127087":{"id":"n2199127087","loc":[-85.6200382,41.9397141]},"n2199127088":{"id":"n2199127088","loc":[-85.6202257,41.9397149]},"n2199127089":{"id":"n2199127089","loc":[-85.6202269,41.9399182]},"n2199127090":{"id":"n2199127090","loc":[-85.6203595,41.9399199]},"n2199127091":{"id":"n2199127091","loc":[-85.6212335,41.939688]},"n2199127092":{"id":"n2199127092","loc":[-85.6212328,41.939595]},"n2199127093":{"id":"n2199127093","loc":[-85.6208807,41.9395966]},"n2199127094":{"id":"n2199127094","loc":[-85.6208815,41.9396896]},"n2199127095":{"id":"n2199127095","loc":[-85.6208676,41.9396872]},"n2199127096":{"id":"n2199127096","loc":[-85.6208583,41.9393539]},"n2199127097":{"id":"n2199127097","loc":[-85.6207006,41.9393563]},"n2199127098":{"id":"n2199127098","loc":[-85.6207099,41.9396896]},"n185967054":{"id":"n185967054","loc":[-85.6173384,41.9356126]},"n185967063":{"id":"n185967063","loc":[-85.617371,41.936243]},"n185967065":{"id":"n185967065","loc":[-85.617337,41.936299]},"n185967068":{"id":"n185967068","loc":[-85.617321,41.936373]},"n185967070":{"id":"n185967070","loc":[-85.6173562,41.9366969]},"n185967074":{"id":"n185967074","loc":[-85.6173635,41.9377414]},"n185967075":{"id":"n185967075","loc":[-85.6173696,41.9381886]},"n185967076":{"id":"n185967076","loc":[-85.617372,41.938535]},"n2199127056":{"id":"n2199127056","loc":[-85.617147,41.9389616]},"n2199127057":{"id":"n2199127057","loc":[-85.6172136,41.9389614]},"n2199127058":{"id":"n2199127058","loc":[-85.6172123,41.9386909]},"n2199127059":{"id":"n2199127059","loc":[-85.616736,41.9386922]},"n2203921041":{"id":"n2203921041","loc":[-85.6173018,41.9346369]},"w203983952":{"id":"w203983952","tags":{"highway":"service"},"nodes":["n2139966627","n1819800319"]},"w209718301":{"id":"w209718301","tags":{"area":"yes","building":"yes"},"nodes":["n2199127051","n2199127052","n2199127053","n2199127054","n2199127055","n2199127056","n2199127057","n2199127058","n2199127059","n2199127060","n2199127061","n2199127062","n2199127051"]},"w209718304":{"id":"w209718304","tags":{"area":"yes","building":"yes"},"nodes":["n2199127071","n2199127072","n2199127073","n2199127074","n2199127071"]},"w17964961":{"id":"w17964961","tags":{"highway":"residential","name":"Whipple St"},"nodes":["n185963099","n185963110"]},"w17964489":{"id":"w17964489","tags":{"highway":"residential","name":"Jackson St"},"nodes":["n185958498","n185958500"]},"w203983953":{"id":"w203983953","tags":{"area":"yes","leisure":"park","name":"Marina Park"},"nodes":["n1475283994","n1475283979","n1475283998","n2139966629","n2139966625","n1819800319","n2139966623","n2139966622","n2139966621","n2139966630","n1475283994"]},"w17965366":{"id":"w17965366","tags":{"highway":"residential","name":"14th St"},"nodes":["n2203921041","n185967054","n185967063","n185967065","n185967068","n185967070","n185967074","n185967075","n185967076","n185967077"]},"w209718306":{"id":"w209718306","tags":{"area":"yes","building":"yes"},"nodes":["n2199127091","n2199127092","n2199127093","n2199127094","n2199127091"]},"w209718307":{"id":"w209718307","tags":{"area":"yes","building":"yes"},"nodes":["n2199127095","n2199127096","n2199127097","n2199127098","n2199127095"]},"w209718305":{"id":"w209718305","tags":{"area":"yes","building":"yes"},"nodes":["n2199127075","n2199127076","n2199127077","n2199127078","n2199127079","n2199127080","n2199127081","n2199127082","n2199127083","n2199127084","n2199127085","n2199127086","n2199127087","n2199127088","n2199127089","n2199127090","n2199127075"]},"n185960199":{"id":"n185960199","loc":[-85.62965,41.95469]},"n185980737":{"id":"n185980737","loc":[-85.629083,41.953725]},"n2114807561":{"id":"n2114807561","loc":[-85.6297681,41.9524688]},"n2114807597":{"id":"n2114807597","loc":[-85.6296517,41.952563]},"n185960197":{"id":"n185960197","loc":[-85.629676,41.9537314]},"n185978791":{"id":"n185978791","loc":[-85.6244542,41.9547066]},"w17967573":{"id":"w17967573","tags":{"highway":"residential","name":"E Wheeler St"},"nodes":["n185960195","n2114807561","n185968102","n185967430","n185986157","n185978392"]},"w17966553":{"id":"w17966553","tags":{"highway":"residential","name":"East Hoffman Street"},"nodes":["n185971631","n185978784","n185967432","n185968106","n185960199","n185978787","n185978790","n185978791"]},"w17966787":{"id":"w17966787","tags":{"highway":"residential","name":"East Cushman Street"},"nodes":["n185980735","n185980737","n185960197","n185968104","n185960792"]},"w17964723":{"id":"w17964723","tags":{"highway":"residential","name":"Cushman Street"},"nodes":["n185960792","n185960794","n185960796"]},"w17964654":{"id":"w17964654","tags":{"highway":"residential","name":"Pine Street"},"nodes":["n185960195","n2114807597","n185960197","n185960199"]},"n1819848862":{"id":"n1819848862","loc":[-85.6346087,41.9545845]},"n1819848935":{"id":"n1819848935","loc":[-85.6345948,41.9537717]},"n1819848973":{"id":"n1819848973","loc":[-85.6334247,41.9537827]},"n1819848997":{"id":"n1819848997","loc":[-85.6334386,41.9545956]},"n2189015861":{"id":"n2189015861","loc":[-85.6375906,41.954836]},"n2189015865":{"id":"n2189015865","loc":[-85.6383307,41.9548291]},"n2189015867":{"id":"n2189015867","loc":[-85.6383337,41.9550072]},"n2189015868":{"id":"n2189015868","loc":[-85.6380986,41.9550094]},"n2189015869":{"id":"n2189015869","loc":[-85.6381005,41.9551226]},"n2199109808":{"id":"n2199109808","loc":[-85.6372702,41.9522894]},"n2199109810":{"id":"n2199109810","loc":[-85.6372677,41.9521583]},"n2199109812":{"id":"n2199109812","loc":[-85.6369505,41.9521617]},"n2199109814":{"id":"n2199109814","loc":[-85.636953,41.9522927]},"n185952156":{"id":"n185952156","loc":[-85.640983,41.9546557]},"n185953423":{"id":"n185953423","loc":[-85.641871,41.954652]},"n185971637":{"id":"n185971637","loc":[-85.641583,41.95465]},"n185971639":{"id":"n185971639","loc":[-85.6421344,41.9546444]},"n185971642":{"id":"n185971642","loc":[-85.6428264,41.9545612]},"n185971648":{"id":"n185971648","loc":[-85.6436023,41.9544262]},"n185975066":{"id":"n185975066","loc":[-85.640532,41.953638]},"n185975067":{"id":"n185975067","loc":[-85.64079,41.953638]},"n185982166":{"id":"n185982166","loc":[-85.6399012,41.9523817]},"n2189015858":{"id":"n2189015858","loc":[-85.6376104,41.9560138]},"n2189015870":{"id":"n2189015870","loc":[-85.6386794,41.9551172]},"n2189015871":{"id":"n2189015871","loc":[-85.6386817,41.955256]},"n2189015873":{"id":"n2189015873","loc":[-85.6385437,41.9552573]},"n2189015876":{"id":"n2189015876","loc":[-85.638555,41.9559278]},"n2189015879":{"id":"n2189015879","loc":[-85.6384954,41.9559283]},"n2189015882":{"id":"n2189015882","loc":[-85.6384965,41.9559935]},"n2189015885":{"id":"n2189015885","loc":[-85.6383533,41.9559949]},"n2189015888":{"id":"n2189015888","loc":[-85.638351,41.9558607]},"n2189015891":{"id":"n2189015891","loc":[-85.6382178,41.9558619]},"n2189015894":{"id":"n2189015894","loc":[-85.6382203,41.956008]},"w208627223":{"id":"w208627223","tags":{"area":"yes","building":"yes"},"nodes":["n2189015858","n2189015861","n2189015865","n2189015867","n2189015868","n2189015869","n2189015870","n2189015871","n2189015873","n2189015876","n2189015879","n2189015882","n2189015885","n2189015888","n2189015891","n2189015894","n2189015858"]},"w170848328":{"id":"w170848328","tags":{"leisure":"park","name":"Bowman Park"},"nodes":["n1819848935","n1819848973","n1819848997","n1819848862","n1819848935"]},"w17965866":{"id":"w17965866","tags":{"highway":"residential","name":"West Hoffman Street"},"nodes":["n185971631","n185971632","n185964359","n185965025","n1475293264","n185952156","n185971637","n185953423","n185971639","n185971642","n185971648"]},"w209717051":{"id":"w209717051","tags":{"amenity":"place_of_worship","area":"yes","building":"yes","denomination":"baptist","name":"Calvary Missionary Baptist Church","religion":"christian"},"nodes":["n2199109808","n2199109810","n2199109812","n2199109814","n2199109808"]},"w17966172":{"id":"w17966172","tags":{"highway":"residential","name":"West Cushman Street"},"nodes":["n185960796","n185975064","n185964358","n185965023","n1475293222","n185975066","n185975067"]},"w17966975":{"id":"w17966975","tags":{"highway":"residential","name":"W Wheeler St"},"nodes":["n185978392","n185982163","n185964357","n185965021","n1475293261","n185982166"]},"n185960684":{"id":"n185960684","loc":[-85.622687,41.951885]},"n185960686":{"id":"n185960686","loc":[-85.622492,41.951901]},"n185978795":{"id":"n185978795","loc":[-85.6240991,41.954708]},"n185978803":{"id":"n185978803","loc":[-85.623348,41.954547]},"n185978806":{"id":"n185978806","loc":[-85.623123,41.954502]},"n185978808":{"id":"n185978808","loc":[-85.622923,41.954469]},"n185978810":{"id":"n185978810","loc":[-85.622787,41.954457]},"n185978811":{"id":"n185978811","loc":[-85.622612,41.954458]},"n185978813":{"id":"n185978813","loc":[-85.622368,41.954472]},"n1819790545":{"id":"n1819790545","loc":[-85.6240295,41.9548949]},"n1819790621":{"id":"n1819790621","loc":[-85.6235789,41.954855]},"n1819790664":{"id":"n1819790664","loc":[-85.6238363,41.9549507]},"n1819790683":{"id":"n1819790683","loc":[-85.6224727,41.9545921]},"n1819790730":{"id":"n1819790730","loc":[-85.6227527,41.9545795]},"n1819790740":{"id":"n1819790740","loc":[-85.6240402,41.9550784]},"n1819790831":{"id":"n1819790831","loc":[-85.624126,41.9549986]},"n1819790861":{"id":"n1819790861","loc":[-85.6231712,41.9546872]},"n1819790887":{"id":"n1819790887","loc":[-85.6242762,41.955206]},"n2168544739":{"id":"n2168544739","loc":[-85.6249102,41.952801]},"n2168544740":{"id":"n2168544740","loc":[-85.6251859,41.9527564]},"n2168544741":{"id":"n2168544741","loc":[-85.6255515,41.9527921]},"n2168544742":{"id":"n2168544742","loc":[-85.626001,41.9529481]},"n2168544743":{"id":"n2168544743","loc":[-85.6265284,41.9529838]},"n2168544744":{"id":"n2168544744","loc":[-85.626942,41.9528857]},"n2168544745":{"id":"n2168544745","loc":[-85.6270918,41.9526851]},"n2168544746":{"id":"n2168544746","loc":[-85.6272117,41.95244]},"n2168544747":{"id":"n2168544747","loc":[-85.6271578,41.952226]},"n2168544748":{"id":"n2168544748","loc":[-85.6270019,41.9519719]},"n2168544749":{"id":"n2168544749","loc":[-85.6268221,41.9518382]},"n2168544750":{"id":"n2168544750","loc":[-85.6265284,41.951807]},"n2168544751":{"id":"n2168544751","loc":[-85.6256534,41.9518516]},"n2168544752":{"id":"n2168544752","loc":[-85.6253477,41.9518338]},"n2168544753":{"id":"n2168544753","loc":[-85.6251139,41.9517669]},"n185955747":{"id":"n185955747","loc":[-85.620674,41.954709]},"n185960688":{"id":"n185960688","loc":[-85.621032,41.951913]},"n185972054":{"id":"n185972054","loc":[-85.6186728,41.9547335]},"n185978814":{"id":"n185978814","loc":[-85.6201708,41.9547403]},"n1819790532":{"id":"n1819790532","loc":[-85.6244908,41.9555731]},"n1819790536":{"id":"n1819790536","loc":[-85.6217925,41.9583135]},"n1819790538":{"id":"n1819790538","loc":[-85.6233954,41.9600014]},"n1819790539":{"id":"n1819790539","loc":[-85.6204611,41.9562117]},"n1819790546":{"id":"n1819790546","loc":[-85.6210898,41.9567657]},"n1819790548":{"id":"n1819790548","loc":[-85.6202465,41.9562237]},"n1819790550":{"id":"n1819790550","loc":[-85.6250165,41.9560677]},"n1819790551":{"id":"n1819790551","loc":[-85.6227946,41.9597023]},"n1819790553":{"id":"n1819790553","loc":[-85.6215726,41.9584571]},"n1819790556":{"id":"n1819790556","loc":[-85.6196306,41.9573002]},"n1819790557":{"id":"n1819790557","loc":[-85.6209503,41.9563109]},"n1819790558":{"id":"n1819790558","loc":[-85.6196939,41.9574085]},"n1819790561":{"id":"n1819790561","loc":[-85.621079,41.957751]},"n1819790562":{"id":"n1819790562","loc":[-85.6224255,41.9611417]},"n1819790565":{"id":"n1819790565","loc":[-85.6232506,41.9604841]},"n1819790566":{"id":"n1819790566","loc":[-85.6190835,41.9562909]},"n1819790567":{"id":"n1819790567","loc":[-85.622227,41.9593028]},"n1819790569":{"id":"n1819790569","loc":[-85.620976,41.9591039]},"n1819790571":{"id":"n1819790571","loc":[-85.6212078,41.9565303]},"n1819790572":{"id":"n1819790572","loc":[-85.6235306,41.9595102]},"n1819790581":{"id":"n1819790581","loc":[-85.6235563,41.9579351]},"n1819790584":{"id":"n1819790584","loc":[-85.6230371,41.9574598]},"n1819790586":{"id":"n1819790586","loc":[-85.6211748,41.9564272]},"n1819790588":{"id":"n1819790588","loc":[-85.6226508,41.9601086]},"n1819790591":{"id":"n1819790591","loc":[-85.6218032,41.9607468]},"n1819790593":{"id":"n1819790593","loc":[-85.6207915,41.9618735]},"n1819790596":{"id":"n1819790596","loc":[-85.6252955,41.9567858]},"n1819790598":{"id":"n1819790598","loc":[-85.6196618,41.9568939]},"n1819790600":{"id":"n1819790600","loc":[-85.6224416,41.9587084]},"n1819790602":{"id":"n1819790602","loc":[-85.6217442,41.9558641]},"n1819790603":{"id":"n1819790603","loc":[-85.6213355,41.9592116]},"n1819790604":{"id":"n1819790604","loc":[-85.622801,41.9573042]},"n1819790608":{"id":"n1819790608","loc":[-85.6199729,41.9574325]},"n1819790610":{"id":"n1819790610","loc":[-85.6195555,41.9557165]},"n1819790611":{"id":"n1819790611","loc":[-85.622978,41.9586007]},"n1819790613":{"id":"n1819790613","loc":[-85.6253963,41.9562636]},"n1819790614":{"id":"n1819790614","loc":[-85.6235252,41.9580342]},"n1819790616":{"id":"n1819790616","loc":[-85.6232988,41.9596305]},"n1819790617":{"id":"n1819790617","loc":[-85.6226776,41.9598732]},"n1819790619":{"id":"n1819790619","loc":[-85.625553,41.9561794]},"n1819790620":{"id":"n1819790620","loc":[-85.6235574,41.959231]},"n1819790624":{"id":"n1819790624","loc":[-85.6228429,41.9573726]},"n1819790626":{"id":"n1819790626","loc":[-85.6193785,41.9556766]},"n1819790628":{"id":"n1819790628","loc":[-85.620092,41.9554253]},"n1819790630":{"id":"n1819790630","loc":[-85.6226658,41.9604402]},"n1819790638":{"id":"n1819790638","loc":[-85.6219964,41.9602561]},"n1819790640":{"id":"n1819790640","loc":[-85.6232731,41.9599969]},"n1819790643":{"id":"n1819790643","loc":[-85.6247698,41.9568895]},"n1819790650":{"id":"n1819790650","loc":[-85.6216412,41.9550149]},"n1819790652":{"id":"n1819790652","loc":[-85.6224952,41.9603918]},"n1819790656":{"id":"n1819790656","loc":[-85.61918,41.9555649]},"n1819790661":{"id":"n1819790661","loc":[-85.6200169,41.955505]},"n1819790662":{"id":"n1819790662","loc":[-85.6217389,41.9563149]},"n1819790666":{"id":"n1819790666","loc":[-85.6229566,41.9598373]},"n1819790667":{"id":"n1819790667","loc":[-85.6209117,41.9609189]},"n1819790669":{"id":"n1819790669","loc":[-85.6252311,41.9562353]},"n1819790670":{"id":"n1819790670","loc":[-85.6209758,41.961868]},"n1819790672":{"id":"n1819790672","loc":[-85.6209557,41.9589078]},"n1819790673":{"id":"n1819790673","loc":[-85.6190352,41.9561393]},"n1819790675":{"id":"n1819790675","loc":[-85.6236432,41.9586685]},"n1819790676":{"id":"n1819790676","loc":[-85.6194901,41.9565389]},"n1819790678":{"id":"n1819790678","loc":[-85.6219266,41.9582417]},"n1819790680":{"id":"n1819790680","loc":[-85.6208258,41.9557211]},"n1819790681":{"id":"n1819790681","loc":[-85.6212024,41.9613212]},"n1819790682":{"id":"n1819790682","loc":[-85.624877,41.9559401]},"n1819790684":{"id":"n1819790684","loc":[-85.6206499,41.9583693]},"n1819790699":{"id":"n1819790699","loc":[-85.6215243,41.956279]},"n1819790701":{"id":"n1819790701","loc":[-85.6246625,41.9559321]},"n1819790703":{"id":"n1819790703","loc":[-85.6230478,41.9585089]},"n1819790708":{"id":"n1819790708","loc":[-85.6211102,41.9575402]},"n1819790710":{"id":"n1819790710","loc":[-85.6215082,41.9548468]},"n1819790711":{"id":"n1819790711","loc":[-85.6206552,41.9586007]},"n1819790713":{"id":"n1819790713","loc":[-85.6215404,41.9549705]},"n1819790715":{"id":"n1819790715","loc":[-85.6216906,41.955521]},"n1819790717":{"id":"n1819790717","loc":[-85.6215404,41.9547391]},"n1819790722":{"id":"n1819790722","loc":[-85.6219964,41.9599131]},"n1819790723":{"id":"n1819790723","loc":[-85.622286,41.9606989]},"n1819790725":{"id":"n1819790725","loc":[-85.6228439,41.9572005]},"n1819790727":{"id":"n1819790727","loc":[-85.6202518,41.9554458]},"n1819790728":{"id":"n1819790728","loc":[-85.623434,41.9575276]},"n1819790729":{"id":"n1819790729","loc":[-85.6234287,41.9568576]},"n1819790732":{"id":"n1819790732","loc":[-85.6229566,41.9571369]},"n1819790733":{"id":"n1819790733","loc":[-85.6225543,41.9590275]},"n1819790734":{"id":"n1819790734","loc":[-85.6232892,41.9583135]},"n1819790736":{"id":"n1819790736","loc":[-85.622977,41.9608551]},"n1819790737":{"id":"n1819790737","loc":[-85.624008,41.9569533]},"n1819790741":{"id":"n1819790741","loc":[-85.6212775,41.9608545]},"n1819790742":{"id":"n1819790742","loc":[-85.6231282,41.9569932]},"n1819790743":{"id":"n1819790743","loc":[-85.6224523,41.9591831]},"n1819790744":{"id":"n1819790744","loc":[-85.6210951,41.9610819]},"n1819790745":{"id":"n1819790745","loc":[-85.6220114,41.960544]},"n1819790755":{"id":"n1819790755","loc":[-85.6216369,41.9553854]},"n1819790757":{"id":"n1819790757","loc":[-85.6209986,41.9592709]},"n1819790758":{"id":"n1819790758","loc":[-85.6200437,41.9563468]},"n1819790764":{"id":"n1819790764","loc":[-85.6219363,41.9596823]},"n1819790765":{"id":"n1819790765","loc":[-85.6237612,41.9568496]},"n1819790769":{"id":"n1819790769","loc":[-85.6212389,41.9593433]},"n1819790771":{"id":"n1819790771","loc":[-85.6210726,41.9560123]},"n1819790772":{"id":"n1819790772","loc":[-85.6212711,41.9561838]},"n1819790776":{"id":"n1819790776","loc":[-85.6234437,41.9577795]},"n1819790777":{"id":"n1819790777","loc":[-85.6212502,41.9618599]},"n1819790783":{"id":"n1819790783","loc":[-85.6216895,41.9610585]},"n1819790784":{"id":"n1819790784","loc":[-85.6200115,41.9556367]},"n1819790785":{"id":"n1819790785","loc":[-85.6210576,41.9573002]},"n1819790786":{"id":"n1819790786","loc":[-85.621138,41.9576632]},"n1819790788":{"id":"n1819790788","loc":[-85.6207733,41.9578946]},"n1819790789":{"id":"n1819790789","loc":[-85.6200705,41.9571566]},"n1819790790":{"id":"n1819790790","loc":[-85.6245337,41.9558443]},"n1819790792":{"id":"n1819790792","loc":[-85.621932,41.9608066]},"n1819790793":{"id":"n1819790793","loc":[-85.6233578,41.9581385]},"n1819790794":{"id":"n1819790794","loc":[-85.6204557,41.9555136]},"n1819790797":{"id":"n1819790797","loc":[-85.6235038,41.9576074]},"n1819790800":{"id":"n1819790800","loc":[-85.6214438,41.9607508]},"n1819790801":{"id":"n1819790801","loc":[-85.623492,41.9602129]},"n1819790802":{"id":"n1819790802","loc":[-85.6216691,41.9546553]},"n1819790803":{"id":"n1819790803","loc":[-85.6231057,41.9586851]},"n1819790804":{"id":"n1819790804","loc":[-85.6209224,41.9578673]},"n1819790813":{"id":"n1819790813","loc":[-85.620092,41.9572962]},"n1819790814":{"id":"n1819790814","loc":[-85.6216691,41.9552218]},"n1819790816":{"id":"n1819790816","loc":[-85.6216144,41.9609668]},"n1819790818":{"id":"n1819790818","loc":[-85.6216906,41.9557324]},"n1819790820":{"id":"n1819790820","loc":[-85.6192069,41.9564186]},"n1819790823":{"id":"n1819790823","loc":[-85.6211155,41.9566027]},"n1819790825":{"id":"n1819790825","loc":[-85.6233106,41.9569294]},"n1819790839":{"id":"n1819790839","loc":[-85.625671,41.9564986]},"n1819790842":{"id":"n1819790842","loc":[-85.6235252,41.9567379]},"n1819790844":{"id":"n1819790844","loc":[-85.6253813,41.9566342]},"n1819790847":{"id":"n1819790847","loc":[-85.6200963,41.9567702]},"n1819790849":{"id":"n1819790849","loc":[-85.6238031,41.9587449]},"n1819790851":{"id":"n1819790851","loc":[-85.6234984,41.9584571]},"n1819790856":{"id":"n1819790856","loc":[-85.6242226,41.9570092]},"n1819790865":{"id":"n1819790865","loc":[-85.6200265,41.9569458]},"n1819790869":{"id":"n1819790869","loc":[-85.6230049,41.9601245]},"n1819790871":{"id":"n1819790871","loc":[-85.6190727,41.9558322]},"n1819790873":{"id":"n1819790873","loc":[-85.6217442,41.9550104]},"n1819790875":{"id":"n1819790875","loc":[-85.6208044,41.9587808]},"n1819790879":{"id":"n1819790879","loc":[-85.6198444,41.9574484]},"n1819790883":{"id":"n1819790883","loc":[-85.623713,41.9588719]},"n1819790885":{"id":"n1819790885","loc":[-85.6223289,41.9605075]},"n1819790889":{"id":"n1819790889","loc":[-85.6208044,41.9562437]},"n1819790893":{"id":"n1819790893","loc":[-85.6218183,41.9559684]},"n1819790906":{"id":"n1819790906","loc":[-85.6214052,41.958697]},"n1819790913":{"id":"n1819790913","loc":[-85.6209981,41.9609957]},"n1819790917":{"id":"n1819790917","loc":[-85.6216208,41.9604436]},"n1819790919":{"id":"n1819790919","loc":[-85.6209406,41.9616373]},"n1819790920":{"id":"n1819790920","loc":[-85.6221948,41.9583334]},"n1819790922":{"id":"n1819790922","loc":[-85.6216681,41.9615292]},"n1819790924":{"id":"n1819790924","loc":[-85.6210147,41.9570489]},"n1819790929":{"id":"n1819790929","loc":[-85.6193678,41.955521]},"w17964707":{"id":"w17964707","tags":{"highway":"residential","name":"11th Ave"},"nodes":["n185960682","n185960684","n185960686","n185960688","n185960690"]},"w201484345":{"id":"w201484345","tags":{"bridge":"yes","highway":"residential","name":"E Hoffman St"},"nodes":["n185978791","n185978795"]},"w201484348":{"id":"w201484348","tags":{"highway":"residential","name":"E Hoffman St"},"nodes":["n185978795","n185978800","n185978803","n185978806","n185978808","n185978810","n185978811","n185978813","n185955747","n185978814","n185972054","n185978817"]},"w170843845":{"id":"w170843845","tags":{"landuse":"reservoir","name":"Hoffman Pond","natural":"water"},"nodes":["n1819790732","n1819790742","n1819790825","n1819790729","n1819790842","n1819790765","n1819790737","n1819790856","n1819790643","n1819790596","n1819790844","n1819790839","n1819849190","n1819790619","n1819790613","n1819790669","n1819790550","n1819790682","n1819790701","n1819790790","n1819790532","n1819790887","n1819790740","n1819790831","n1819790545","n1819790664","n1819790621","n1819790861","n1819790730","n1819790683","n1819790802","n1819790717","n1819790710","n1819790713","n1819790650","n1819790873","n1819790814","n1819790755","n1819790715","n1819790818","n1819790602","n1819790893","n1819790662","n1819790699","n1819790772","n1819790771","n1819790680","n1819790794","n1819790727","n1819790628","n1819790661","n1819790784","n1819790610","n1819790626","n1819790929","n1819790656","n1819790871","n1819790673","n1819790566","n1819790820","n1819790676","n1819790598","n1819790556","n1819790558","n1819790879","n1819790608","n1819790813","n1819790789","n1819790865","n1819790847","n1819790758","n1819790548","n1819790539","n1819790889","n1819790557","n1819790586","n1819790571","n1819790823","n1819790546","n1819790924","n1819790785","n1819790708","n1819790786","n1819790561","n1819790804","n1819790788","n1819790684","n1819790711","n1819790875","n1819790672","n1819790569","n1819790757","n1819790769","n1819790603","n1819790906","n1819790553","n1819790536","n1819790678","n1819790920","n1819790600","n1819790733","n1819790743","n1819790567","n1819790764","n1819790722","n1819790638","n1819790917","n1819790800","n1819790741","n1819790667","n1819790913","n1819790744","n1819790816","n1819790591","n1819790745","n1819790885","n1819790652","n1819790588","n1819790617","n1819790551","n1819790666","n1819790869","n1819790630","n1819790723","n1819790792","n1819790783","n1819790681","n1819790919","n1819790593","n1819790670","n1819790777","n1819790922","n1819790562","n1819790736","n1819790565","n1819790801","n1819790538","n1819790640","n1819790616","n1819790572","n1819790620","n1819790883","n1819790849","n1819790675","n1819790851","n1819790803","n1819790611","n1819790703","n1819790734","n1819790793","n1819790614","n1819790581","n1819790776","n1819790797","n1819790728","n1819790584","n1819790624","n1819790604","n1819790725","n1819790732"]},"w206805240":{"id":"w206805240","tags":{"waterway":"river"},"nodes":["n2168544738","n2168544739","n2168544740","n2168544741","n2168544742","n2168544743","n2168544744","n2168544745","n2168544746","n2168544747","n2168544748","n2168544749","n2168544750","n2168544751","n2168544752","n2168544753","n1819848944"]},"n394490429":{"id":"n394490429","loc":[-85.643883,41.954365]},"n185953421":{"id":"n185953421","loc":[-85.641876,41.954946]},"n185953417":{"id":"n185953417","loc":[-85.6418306,41.9551597]},"n185977233":{"id":"n185977233","loc":[-85.642987,41.95486]},"n185977232":{"id":"n185977232","loc":[-85.642894,41.9547842]},"n1475293244":{"id":"n1475293244","loc":[-85.63974,41.9521543]},"n1819848890":{"id":"n1819848890","loc":[-85.6410004,41.9552822]},"n1819848965":{"id":"n1819848965","loc":[-85.6409795,41.9553892]},"n2189015846":{"id":"n2189015846","loc":[-85.6420457,41.9549528]},"n2189015849":{"id":"n2189015849","loc":[-85.6425867,41.9551392]},"n2189015852":{"id":"n2189015852","loc":[-85.6426877,41.9549771]},"n2199109816":{"id":"n2199109816","loc":[-85.6399215,41.9540925]},"n2199109818":{"id":"n2199109818","loc":[-85.6399182,41.9538236]},"n2199109820":{"id":"n2199109820","loc":[-85.6402201,41.9538216]},"n2199109822":{"id":"n2199109822","loc":[-85.640222,41.9539771]},"n2199109825":{"id":"n2199109825","loc":[-85.6402904,41.9539766]},"n2199109827":{"id":"n2199109827","loc":[-85.6402918,41.95409]},"n2199109829":{"id":"n2199109829","loc":[-85.6395845,41.9544626]},"n2199109831":{"id":"n2199109831","loc":[-85.6395792,41.9540671]},"n2199109833":{"id":"n2199109833","loc":[-85.6397173,41.9540661]},"n2199109835":{"id":"n2199109835","loc":[-85.6397226,41.9544616]},"n2199109837":{"id":"n2199109837","loc":[-85.6399641,41.9545058]},"n2199109839":{"id":"n2199109839","loc":[-85.6399637,41.9541859]},"n2199109841":{"id":"n2199109841","loc":[-85.6401098,41.9541858]},"n2199109843":{"id":"n2199109843","loc":[-85.64011,41.9543272]},"n2199109845":{"id":"n2199109845","loc":[-85.6400783,41.9543273]},"n2199109847":{"id":"n2199109847","loc":[-85.6400785,41.9545058]},"n2199109853":{"id":"n2199109853","loc":[-85.6396184,41.9554049]},"n2199109855":{"id":"n2199109855","loc":[-85.6396825,41.9553713]},"n185949745":{"id":"n185949745","loc":[-85.6442727,41.9553112]},"n185949748":{"id":"n185949748","loc":[-85.6448804,41.9555238]},"n185949755":{"id":"n185949755","loc":[-85.6420011,41.9603536]},"n185949763":{"id":"n185949763","loc":[-85.6408843,41.9555822]},"n185949765":{"id":"n185949765","loc":[-85.6414548,41.9557751]},"n185952158":{"id":"n185952158","loc":[-85.640066,41.956854]},"n185952160":{"id":"n185952160","loc":[-85.639848,41.957229]},"n185952161":{"id":"n185952161","loc":[-85.6396089,41.9576192]},"n185952163":{"id":"n185952163","loc":[-85.63892,41.957957]},"n185953413":{"id":"n185953413","loc":[-85.64162,41.955475]},"n185971651":{"id":"n185971651","loc":[-85.6440766,41.9543462]},"n185977234":{"id":"n185977234","loc":[-85.645044,41.955581]},"n394490395":{"id":"n394490395","loc":[-85.657336,41.936762]},"n394490396":{"id":"n394490396","loc":[-85.653896,41.936978]},"n394490397":{"id":"n394490397","loc":[-85.653732,41.937386]},"n394490398":{"id":"n394490398","loc":[-85.65182,41.937378]},"n394490399":{"id":"n394490399","loc":[-85.651843,41.938445]},"n394490400":{"id":"n394490400","loc":[-85.652536,41.938447]},"n394490401":{"id":"n394490401","loc":[-85.652533,41.938901]},"n394490402":{"id":"n394490402","loc":[-85.652084,41.9389]},"n394490403":{"id":"n394490403","loc":[-85.6521,41.939627]},"n394490404":{"id":"n394490404","loc":[-85.652301,41.939628]},"n394490405":{"id":"n394490405","loc":[-85.652302,41.939755]},"n394490406":{"id":"n394490406","loc":[-85.652783,41.939747]},"n394490407":{"id":"n394490407","loc":[-85.652835,41.94112]},"n394490408":{"id":"n394490408","loc":[-85.651968,41.941123]},"n394490409":{"id":"n394490409","loc":[-85.651983,41.941969]},"n394490410":{"id":"n394490410","loc":[-85.652908,41.941961]},"n394490411":{"id":"n394490411","loc":[-85.65292,41.94278]},"n394490412":{"id":"n394490412","loc":[-85.651698,41.942816]},"n394490413":{"id":"n394490413","loc":[-85.651509,41.942823]},"n394490414":{"id":"n394490414","loc":[-85.651272,41.942837]},"n394490415":{"id":"n394490415","loc":[-85.651272,41.943325]},"n394490416":{"id":"n394490416","loc":[-85.65122,41.944053]},"n394490417":{"id":"n394490417","loc":[-85.651193,41.944449]},"n394490418":{"id":"n394490418","loc":[-85.651088,41.944969]},"n394490419":{"id":"n394490419","loc":[-85.650949,41.945554]},"n394490420":{"id":"n394490420","loc":[-85.650907,41.945719]},"n394490421":{"id":"n394490421","loc":[-85.650808,41.946016]},"n394490422":{"id":"n394490422","loc":[-85.650712,41.946516]},"n394490423":{"id":"n394490423","loc":[-85.650493,41.947166]},"n394490424":{"id":"n394490424","loc":[-85.650626,41.947213]},"n394490425":{"id":"n394490425","loc":[-85.650201,41.948109]},"n394490426":{"id":"n394490426","loc":[-85.649868,41.948797]},"n394490427":{"id":"n394490427","loc":[-85.649669,41.949161]},"n394490428":{"id":"n394490428","loc":[-85.64659,41.954067]},"n394490430":{"id":"n394490430","loc":[-85.644034,41.95444]},"n394490431":{"id":"n394490431","loc":[-85.644248,41.954507]},"n394490432":{"id":"n394490432","loc":[-85.64491,41.954481]},"n394490433":{"id":"n394490433","loc":[-85.645213,41.954433]},"n394490434":{"id":"n394490434","loc":[-85.645426,41.954477]},"n394490435":{"id":"n394490435","loc":[-85.6458,41.954704]},"n394490436":{"id":"n394490436","loc":[-85.64605,41.954804]},"n394490437":{"id":"n394490437","loc":[-85.646125,41.954817]},"n394490438":{"id":"n394490438","loc":[-85.646002,41.954997]},"n394490439":{"id":"n394490439","loc":[-85.645764,41.955366]},"n394490440":{"id":"n394490440","loc":[-85.645525,41.955734]},"n394490441":{"id":"n394490441","loc":[-85.64443,41.957424]},"n394490442":{"id":"n394490442","loc":[-85.641712,41.961723]},"n394490443":{"id":"n394490443","loc":[-85.640747,41.963246]},"n394490444":{"id":"n394490444","loc":[-85.637803,41.967894]},"n394490445":{"id":"n394490445","loc":[-85.637673,41.967861]},"n394490446":{"id":"n394490446","loc":[-85.636637,41.969275]},"n394490447":{"id":"n394490447","loc":[-85.634923,41.969269]},"n394490448":{"id":"n394490448","loc":[-85.634893,41.968537]},"n394490449":{"id":"n394490449","loc":[-85.634544,41.96927]},"n394490450":{"id":"n394490450","loc":[-85.630835,41.969274]},"n394490451":{"id":"n394490451","loc":[-85.630834,41.968348]},"n394490452":{"id":"n394490452","loc":[-85.630857,41.968179]},"n394490453":{"id":"n394490453","loc":[-85.630924,41.968044]},"n394490454":{"id":"n394490454","loc":[-85.631004,41.967925]},"n394490455":{"id":"n394490455","loc":[-85.631143,41.967811]},"n394490456":{"id":"n394490456","loc":[-85.631311,41.967736]},"n394490457":{"id":"n394490457","loc":[-85.631595,41.967693]},"n394490458":{"id":"n394490458","loc":[-85.63325,41.967702]},"n394490459":{"id":"n394490459","loc":[-85.633247,41.967021]},"n394490460":{"id":"n394490460","loc":[-85.634858,41.967021]},"n394490461":{"id":"n394490461","loc":[-85.634865,41.967711]},"n394490462":{"id":"n394490462","loc":[-85.634884,41.968231]},"n394490463":{"id":"n394490463","loc":[-85.636559,41.963867]},"n394490464":{"id":"n394490464","loc":[-85.634832,41.963866]},"n394490465":{"id":"n394490465","loc":[-85.63481,41.961899]},"n394490466":{"id":"n394490466","loc":[-85.637219,41.961842]},"n394490467":{"id":"n394490467","loc":[-85.637837,41.960019]},"n394490468":{"id":"n394490468","loc":[-85.637459,41.960022]},"n394490469":{"id":"n394490469","loc":[-85.635295,41.959987]},"n394490470":{"id":"n394490470","loc":[-85.634783,41.959979]},"n394490471":{"id":"n394490471","loc":[-85.634776,41.959834]},"n394490472":{"id":"n394490472","loc":[-85.634767,41.959009]},"n394490473":{"id":"n394490473","loc":[-85.634763,41.958292]},"n394490474":{"id":"n394490474","loc":[-85.633346,41.958287]},"n394490475":{"id":"n394490475","loc":[-85.632128,41.9583]},"n394490476":{"id":"n394490476","loc":[-85.631414,41.958318]},"n394490477":{"id":"n394490477","loc":[-85.63137,41.959033]},"n394490478":{"id":"n394490478","loc":[-85.631325,41.959753]},"n394490479":{"id":"n394490479","loc":[-85.631494,41.95977]},"n394490480":{"id":"n394490480","loc":[-85.631456,41.960673]},"n394490481":{"id":"n394490481","loc":[-85.631421,41.961494]},"n394490482":{"id":"n394490482","loc":[-85.631404,41.961887]},"n394490483":{"id":"n394490483","loc":[-85.631401,41.961968]},"n394490484":{"id":"n394490484","loc":[-85.630962,41.961967]},"n394490485":{"id":"n394490485","loc":[-85.6299,41.961973]},"n394490486":{"id":"n394490486","loc":[-85.624929,41.962002]},"n394490487":{"id":"n394490487","loc":[-85.623333,41.961987]},"n394490488":{"id":"n394490488","loc":[-85.621894,41.963956]},"n394490489":{"id":"n394490489","loc":[-85.62131,41.963727]},"n394490490":{"id":"n394490490","loc":[-85.621216,41.963868]},"n394490491":{"id":"n394490491","loc":[-85.620356,41.965119]},"n394490492":{"id":"n394490492","loc":[-85.620848,41.965341]},"n394490493":{"id":"n394490493","loc":[-85.620684,41.965558]},"n394490494":{"id":"n394490494","loc":[-85.620621,41.965658]},"n394490495":{"id":"n394490495","loc":[-85.618165,41.965759]},"n394490496":{"id":"n394490496","loc":[-85.618071,41.965759]},"n394490497":{"id":"n394490497","loc":[-85.617986,41.965759]},"n394490498":{"id":"n394490498","loc":[-85.605673,41.965764]},"n394490499":{"id":"n394490499","loc":[-85.605668,41.963548]},"n394490500":{"id":"n394490500","loc":[-85.605664,41.962094]},"n394490501":{"id":"n394490501","loc":[-85.595828,41.962159]},"n394490502":{"id":"n394490502","loc":[-85.587869,41.962169]},"n394490503":{"id":"n394490503","loc":[-85.586289,41.962179]},"n394490504":{"id":"n394490504","loc":[-85.583774,41.962178]},"n394490505":{"id":"n394490505","loc":[-85.583774,41.961789]},"n394490506":{"id":"n394490506","loc":[-85.581303,41.961783]},"n394490507":{"id":"n394490507","loc":[-85.581304,41.961616]},"n394490508":{"id":"n394490508","loc":[-85.581292,41.961616]},"n394490509":{"id":"n394490509","loc":[-85.581247,41.959244]},"n394490510":{"id":"n394490510","loc":[-85.581245,41.958394]},"n394490511":{"id":"n394490511","loc":[-85.581276,41.958372]},"n394490512":{"id":"n394490512","loc":[-85.581302,41.958353]},"n394490513":{"id":"n394490513","loc":[-85.581376,41.9583]},"n394490514":{"id":"n394490514","loc":[-85.582256,41.957663]},"n394490515":{"id":"n394490515","loc":[-85.585299,41.955483]},"n394490516":{"id":"n394490516","loc":[-85.585588,41.955331]},"n394490517":{"id":"n394490517","loc":[-85.586053,41.955163]},"n394490518":{"id":"n394490518","loc":[-85.58632,41.955076]},"n394490519":{"id":"n394490519","loc":[-85.586478,41.955025]},"n394490520":{"id":"n394490520","loc":[-85.58692,41.954947]},"n394490521":{"id":"n394490521","loc":[-85.587327,41.954914]},"n394490522":{"id":"n394490522","loc":[-85.587345,41.954913]},"n394490523":{"id":"n394490523","loc":[-85.587358,41.954913]},"n394490524":{"id":"n394490524","loc":[-85.58963,41.954877]},"n394490525":{"id":"n394490525","loc":[-85.591077,41.954865]},"n394490526":{"id":"n394490526","loc":[-85.594824,41.954843]},"n394490527":{"id":"n394490527","loc":[-85.594804,41.95331]},"n394490528":{"id":"n394490528","loc":[-85.599336,41.95331]},"n394490529":{"id":"n394490529","loc":[-85.599336,41.954825]},"n394490530":{"id":"n394490530","loc":[-85.597828,41.954839]},"n394490531":{"id":"n394490531","loc":[-85.597833,41.95614]},"n394490532":{"id":"n394490532","loc":[-85.596586,41.956151]},"n394490533":{"id":"n394490533","loc":[-85.596586,41.956394]},"n394490534":{"id":"n394490534","loc":[-85.595933,41.956394]},"n394490535":{"id":"n394490535","loc":[-85.595933,41.958176]},"n394490536":{"id":"n394490536","loc":[-85.597635,41.958179]},"n394490537":{"id":"n394490537","loc":[-85.597717,41.958177]},"n394490538":{"id":"n394490538","loc":[-85.601671,41.958194]},"n394490539":{"id":"n394490539","loc":[-85.605619,41.958194]},"n394490540":{"id":"n394490540","loc":[-85.608054,41.958187]},"n394490542":{"id":"n394490542","loc":[-85.6080762,41.9547864]},"n394490545":{"id":"n394490545","loc":[-85.6104354,41.9548263]},"n394490546":{"id":"n394490546","loc":[-85.610274,41.951106]},"n394490547":{"id":"n394490547","loc":[-85.610278,41.950829]},"n394490548":{"id":"n394490548","loc":[-85.610309,41.948377]},"n394490549":{"id":"n394490549","loc":[-85.610314,41.947986]},"n394490550":{"id":"n394490550","loc":[-85.610464,41.947985]},"n394490551":{"id":"n394490551","loc":[-85.610447,41.947468]},"n394490552":{"id":"n394490552","loc":[-85.612469,41.947471]},"n394490553":{"id":"n394490553","loc":[-85.612494,41.945576]},"n394490554":{"id":"n394490554","loc":[-85.610292,41.94558]},"n394490555":{"id":"n394490555","loc":[-85.608412,41.945625]},"n394490556":{"id":"n394490556","loc":[-85.608412,41.943036]},"n394490557":{"id":"n394490557","loc":[-85.608702,41.943087]},"n394490558":{"id":"n394490558","loc":[-85.609196,41.943224]},"n394490559":{"id":"n394490559","loc":[-85.609571,41.943263]},"n394490560":{"id":"n394490560","loc":[-85.610116,41.943295]},"n394490561":{"id":"n394490561","loc":[-85.610273,41.943275]},"n394490562":{"id":"n394490562","loc":[-85.611339,41.943075]},"n394490563":{"id":"n394490563","loc":[-85.611575,41.942997]},"n394490564":{"id":"n394490564","loc":[-85.611847,41.942849]},"n394490565":{"id":"n394490565","loc":[-85.612164,41.942568]},"n394490566":{"id":"n394490566","loc":[-85.612341,41.942529]},"n394490567":{"id":"n394490567","loc":[-85.612562,41.942524]},"n394490568":{"id":"n394490568","loc":[-85.612768,41.942546]},"n394490569":{"id":"n394490569","loc":[-85.612938,41.942633]},"n394490570":{"id":"n394490570","loc":[-85.6131,41.942782]},"n394490571":{"id":"n394490571","loc":[-85.613299,41.942919]},"n394490572":{"id":"n394490572","loc":[-85.613498,41.942996]},"n394490573":{"id":"n394490573","loc":[-85.614698,41.942842]},"n394490574":{"id":"n394490574","loc":[-85.615288,41.942698]},"n394490575":{"id":"n394490575","loc":[-85.616054,41.942693]},"n394490576":{"id":"n394490576","loc":[-85.61603,41.942175]},"n394490577":{"id":"n394490577","loc":[-85.616004,41.941741]},"n394490578":{"id":"n394490578","loc":[-85.615994,41.940156]},"n394490579":{"id":"n394490579","loc":[-85.615144,41.940159]},"n394490580":{"id":"n394490580","loc":[-85.614915,41.940161]},"n394490582":{"id":"n394490582","loc":[-85.614875,41.938532]},"n394490583":{"id":"n394490583","loc":[-85.616167,41.938787]},"n394490585":{"id":"n394490585","loc":[-85.616176,41.938589]},"n394490586":{"id":"n394490586","loc":[-85.614537,41.938282]},"n394490588":{"id":"n394490588","loc":[-85.610141,41.937459]},"n394490589":{"id":"n394490589","loc":[-85.610172,41.937298]},"n394490590":{"id":"n394490590","loc":[-85.609918,41.935495]},"n394490592":{"id":"n394490592","loc":[-85.610092,41.935451]},"n394490594":{"id":"n394490594","loc":[-85.610681,41.935247]},"n394490595":{"id":"n394490595","loc":[-85.611446,41.934955]},"n394490596":{"id":"n394490596","loc":[-85.612057,41.934696]},"n394490598":{"id":"n394490598","loc":[-85.613256,41.934084]},"n394490599":{"id":"n394490599","loc":[-85.613948,41.933682]},"n394490601":{"id":"n394490601","loc":[-85.61436,41.933417]},"n394490602":{"id":"n394490602","loc":[-85.614638,41.933212]},"n394490604":{"id":"n394490604","loc":[-85.615249,41.9332]},"n394490605":{"id":"n394490605","loc":[-85.618218,41.933223]},"n394490607":{"id":"n394490607","loc":[-85.618241,41.933479]},"n394490608":{"id":"n394490608","loc":[-85.618257,41.93365]},"n394490609":{"id":"n394490609","loc":[-85.618298,41.935067]},"n394490611":{"id":"n394490611","loc":[-85.619791,41.935067]},"n394490612":{"id":"n394490612","loc":[-85.619794,41.933301]},"n394490613":{"id":"n394490613","loc":[-85.619795,41.932692]},"n394490614":{"id":"n394490614","loc":[-85.619729,41.929517]},"n394490615":{"id":"n394490615","loc":[-85.619801,41.929305]},"n394490616":{"id":"n394490616","loc":[-85.619809,41.927391]},"n394490617":{"id":"n394490617","loc":[-85.620883,41.927378]},"n394490618":{"id":"n394490618","loc":[-85.620988,41.927368]},"n394490619":{"id":"n394490619","loc":[-85.621076,41.927368]},"n394490620":{"id":"n394490620","loc":[-85.621156,41.927376]},"n394490621":{"id":"n394490621","loc":[-85.621685,41.92737]},"n394490622":{"id":"n394490622","loc":[-85.624716,41.927359]},"n394490623":{"id":"n394490623","loc":[-85.625308,41.92737]},"n394490624":{"id":"n394490624","loc":[-85.625655,41.927377]},"n394490625":{"id":"n394490625","loc":[-85.625093,41.925591]},"n394490626":{"id":"n394490626","loc":[-85.625174,41.92559]},"n394490627":{"id":"n394490627","loc":[-85.625249,41.925597]},"n394490628":{"id":"n394490628","loc":[-85.625532,41.925604]},"n394490629":{"id":"n394490629","loc":[-85.625761,41.925597]},"n394490630":{"id":"n394490630","loc":[-85.625955,41.926153]},"n394490631":{"id":"n394490631","loc":[-85.626209,41.926155]},"n394490632":{"id":"n394490632","loc":[-85.627757,41.926151]},"n394490633":{"id":"n394490633","loc":[-85.627825,41.926298]},"n394490634":{"id":"n394490634","loc":[-85.627994,41.926315]},"n394490635":{"id":"n394490635","loc":[-85.628049,41.927196]},"n394490636":{"id":"n394490636","loc":[-85.62949,41.927221]},"n394490637":{"id":"n394490637","loc":[-85.629602,41.927277]},"n394490638":{"id":"n394490638","loc":[-85.6297102,41.9273279]},"n394490639":{"id":"n394490639","loc":[-85.630958,41.927398]},"n394490699":{"id":"n394490699","loc":[-85.632741,41.927388]},"n394490700":{"id":"n394490700","loc":[-85.632997,41.927391]},"n394490701":{"id":"n394490701","loc":[-85.633149,41.927393]},"n394490702":{"id":"n394490702","loc":[-85.633334,41.927393]},"n394490703":{"id":"n394490703","loc":[-85.633468,41.927561]},"n394490704":{"id":"n394490704","loc":[-85.633563,41.927755]},"n394490705":{"id":"n394490705","loc":[-85.633662,41.928192]},"n394490706":{"id":"n394490706","loc":[-85.633679,41.928807]},"n394490707":{"id":"n394490707","loc":[-85.633687,41.929107]},"n394490708":{"id":"n394490708","loc":[-85.633927,41.929109]},"n394490709":{"id":"n394490709","loc":[-85.634126,41.929111]},"n394490710":{"id":"n394490710","loc":[-85.634207,41.92911]},"n394490711":{"id":"n394490711","loc":[-85.634323,41.929111]},"n394490712":{"id":"n394490712","loc":[-85.636712,41.929128]},"n394490713":{"id":"n394490713","loc":[-85.63808,41.9291]},"n394490714":{"id":"n394490714","loc":[-85.639213,41.929088]},"n394490715":{"id":"n394490715","loc":[-85.639189,41.92852]},"n394490716":{"id":"n394490716","loc":[-85.639204,41.925488]},"n394490717":{"id":"n394490717","loc":[-85.644204,41.925452]},"n394490718":{"id":"n394490718","loc":[-85.651425,41.925406]},"n394490719":{"id":"n394490719","loc":[-85.651449,41.926321]},"n394490720":{"id":"n394490720","loc":[-85.651451,41.926969]},"n394490721":{"id":"n394490721","loc":[-85.651458,41.928052]},"n394490722":{"id":"n394490722","loc":[-85.651446,41.928892]},"n394490723":{"id":"n394490723","loc":[-85.651456,41.929447]},"n394490724":{"id":"n394490724","loc":[-85.651707,41.929454]},"n394490725":{"id":"n394490725","loc":[-85.652369,41.929473]},"n394490726":{"id":"n394490726","loc":[-85.6525,41.929452]},"n394490727":{"id":"n394490727","loc":[-85.654066,41.92946]},"n394490728":{"id":"n394490728","loc":[-85.654816,41.92946]},"n394490729":{"id":"n394490729","loc":[-85.654816,41.930337]},"n394490730":{"id":"n394490730","loc":[-85.654587,41.930337]},"n394490731":{"id":"n394490731","loc":[-85.654548,41.931072]},"n394490732":{"id":"n394490732","loc":[-85.654538,41.931701]},"n394490733":{"id":"n394490733","loc":[-85.654898,41.931689]},"n394490734":{"id":"n394490734","loc":[-85.654898,41.932505]},"n394490735":{"id":"n394490735","loc":[-85.654854,41.932514]},"n394490736":{"id":"n394490736","loc":[-85.655497,41.932499]},"n394490737":{"id":"n394490737","loc":[-85.656405,41.932493]},"n394490738":{"id":"n394490738","loc":[-85.656422,41.933416]},"n394490739":{"id":"n394490739","loc":[-85.657322,41.933438]},"n1475293233":{"id":"n1475293233","loc":[-85.6385522,41.9585167]},"n1475293242":{"id":"n1475293242","loc":[-85.64609,41.9540815]},"n1475293249":{"id":"n1475293249","loc":[-85.6358079,41.9692721]},"n1475293256":{"id":"n1475293256","loc":[-85.6387369,41.9581583]},"n1475293259":{"id":"n1475293259","loc":[-85.6455882,41.9541138]},"n1475293266":{"id":"n1475293266","loc":[-85.6451008,41.9541821]},"n1819800253":{"id":"n1819800253","loc":[-85.6134286,41.9429692]},"n2114807558":{"id":"n2114807558","loc":[-85.6365609,41.963866],"tags":{"railway":"level_crossing"}},"n2189015728":{"id":"n2189015728","loc":[-85.6383956,41.9590576]},"n2189015838":{"id":"n2189015838","loc":[-85.6435144,41.9563705]},"n2189015842":{"id":"n2189015842","loc":[-85.6415782,41.9557035]},"n2189015855":{"id":"n2189015855","loc":[-85.6440829,41.9554577]},"n2199109849":{"id":"n2199109849","loc":[-85.6393434,41.9565591]},"n2199109851":{"id":"n2199109851","loc":[-85.6393208,41.9565002]},"n2199109857":{"id":"n2199109857","loc":[-85.6401986,41.955545]},"n2199109859":{"id":"n2199109859","loc":[-85.6402362,41.955587]},"n2199109861":{"id":"n2199109861","loc":[-85.6395958,41.9565675]},"n2199109863":{"id":"n2199109863","loc":[-85.639528,41.9566011]},"w209717053":{"id":"w209717053","tags":{"area":"yes","building":"yes"},"nodes":["n2199109829","n2199109831","n2199109833","n2199109835","n2199109829"]},"w17966415":{"id":"w17966415","tags":{"access":"private","highway":"service","name":"Manufacturing Way"},"nodes":["n185971642","n185977232","n185977233","n185949745","n185949748","n185977234"]},"w209717054":{"id":"w209717054","tags":{"area":"yes","building":"yes"},"nodes":["n2199109837","n2199109839","n2199109841","n2199109843","n2199109845","n2199109847","n2199109837"]},"w208627214":{"id":"w208627214","tags":{"highway":"service"},"nodes":["n185949755","n2189015728","n1475293233","n1475293256","n185952163","n185952161","n185952160","n185952158","n185949763","n1819848965","n1819848890","n185952156"]},"w17963817":{"id":"w17963817","tags":{"access":"private","highway":"service"},"nodes":["n185949765","n185953413","n185953417","n185953421","n185953423"]},"w34369809":{"id":"w34369809","tags":{"admin_level":"8","boundary":"administrative","landuse":"residential"},"nodes":["n394490395","n394490396","n394490397","n394490398","n394490399","n394490400","n394490401","n394490402","n394490403","n394490404","n394490405","n394490406","n394490407","n394490408","n394490409","n394490410","n394490411","n394490412","n394490413","n394490414","n394490415","n394490416","n394490417","n394490418","n394490419","n394490420","n394490421","n394490422","n394490423","n394490424","n394490425","n394490426","n394490427","n394490428","n1475293242","n1475293259","n1475293266","n394490429","n394490430","n394490431","n394490432","n394490433","n394490434","n394490435","n394490436","n394490437","n394490438","n394490439","n394490440","n394490441","n394490442","n394490443","n394490444","n394490445","n394490446","n1475293249","n394490447","n394490448","n394490449","n394490450","n394490451","n394490452","n394490453","n394490454","n394490455","n394490456","n394490457","n394490458","n394490459","n394490460","n394490461","n394490462","n2114807558","n394490463","n1475293226","n394490464","n394490465","n394490466","n394490467","n394490468","n394490469","n394490470","n394490471","n394490472","n394490473","n394490474","n394490475","n394490476","n394490477","n394490478","n394490479","n394490480","n394490481","n394490482","n394490483","n394490484","n394490485","n394490486","n394490487","n394490488","n394490489","n394490490","n394490491","n394490492","n394490493","n394490494","n394490495","n394490496","n394490497","n394490498","n394490499","n394490500","n394490501","n394490502","n394490503","n394490504","n394490505","n394490506","n394490507","n394490508","n394490509","n394490510","n394490511","n394490512","n394490513","n394490514","n394490515","n394490516","n394490517","n394490518","n394490519","n394490520","n394490521","n394490522","n394490523","n394490524","n394490525","n394490526","n394490527","n394490528","n394490529","n394490530","n394490531","n394490532","n394490533","n394490534","n394490535","n394490536","n394490537","n394490538","n394490539","n394490540","n394490542","n394490545","n394490546","n394490547","n394490548","n394490549","n394490550","n394490551","n394490552","n394490553","n394490554","n394490555","n394490556","n394490557","n394490558","n394490559","n394490560","n394490561","n394490562","n394490563","n394490564","n394490565","n394490566","n394490567","n394490568","n394490569","n394490570","n394490571","n1819800253","n394490572","n394490573","n394490574","n394490575","n394490576","n394490577","n394490578","n394490579","n394490580","n394490582","n394490583","n394490585","n394490586","n394490588","n394490589","n394490590","n394490592","n394490594","n394490595","n394490596","n394490598","n394490599","n394490601","n394490602","n394490604","n394490605","n394490607","n394490608","n394490609","n394490611","n394490612","n394490613","n394490614","n394490615","n394490616","n394490617","n394490618","n394490619","n394490620","n394490621","n394490622","n394490623","n394490624","n394490625","n394490626","n394490627","n394490628","n394490629","n394490630","n394490631","n394490632","n394490633","n394490634","n394490635","n394490636","n394490637","n394490638","n394490639","n394490699","n394490700","n394490701","n394490702","n394490703","n394490704","n394490705","n394490706","n394490707","n394490708","n394490709","n394490710","n394490711","n394490712","n394490713","n394490714","n394490715","n394490716","n394490717","n394490718","n394490719","n394490720","n394490721","n394490722","n394490723","n394490724","n394490725","n394490726","n394490727","n394490728","n394490729","n394490730","n394490731","n394490732","n394490733","n394490734","n394490735","n394490736","n394490737","n394490738","n394490739","n394490395"]},"w208627221":{"id":"w208627221","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189015838","n2189015842","n2189015846","n2189015849","n2189015852","n2189015855","n2189015838"]},"w209717052":{"id":"w209717052","tags":{"area":"yes","building":"yes"},"nodes":["n2199109816","n2199109818","n2199109820","n2199109822","n2199109825","n2199109827","n2199109816"]},"w134151784":{"id":"w134151784","tags":{"bridge":"yes","highway":"residential","name":"W Hoffman St"},"nodes":["n185971648","n185971651"]},"w209717055":{"id":"w209717055","tags":{"area":"yes","landuse":"basin"},"nodes":["n2199109849","n2199109851","n2199109853","n2199109855","n2199109857","n2199109859","n2199109861","n2199109863","n2199109849"]},"w17967763":{"id":"w17967763","tags":{"highway":"residential","name":"Rock River Ave"},"nodes":["n1475293244","n185982166","n185975067","n185971637"]},"r134949":{"id":"r134949","tags":{"admin_level":"8","border_type":"city","boundary":"administrative","name":"Three Rivers","place":"city","type":"boundary"},"members":[{"id":"w34369809","type":"way","role":"outer"},{"id":"w34369821","type":"way","role":"outer"},{"id":"w34369822","type":"way","role":"outer"},{"id":"w34369823","type":"way","role":"outer"},{"id":"w34369824","type":"way","role":"outer"},{"id":"w34369825","type":"way","role":"outer"},{"id":"w34369826","type":"way","role":"outer"},{"id":"w34369810","type":"way","role":"inner"},{"id":"w34369811","type":"way","role":"inner"},{"id":"w34369812","type":"way","role":"inner"},{"id":"w34367079","type":"way","role":"inner"},{"id":"w34369814","type":"way","role":"inner"},{"id":"w34367080","type":"way","role":"inner"},{"id":"w34369815","type":"way","role":"inner"},{"id":"w34369820","type":"way","role":"inner"}]},"n1819848881":{"id":"n1819848881","loc":[-85.638562,41.9569965]},"n1819848947":{"id":"n1819848947","loc":[-85.6384348,41.9576565]},"n1819849044":{"id":"n1819849044","loc":[-85.6385749,41.9573345]},"n2114807547":{"id":"n2114807547","loc":[-85.6384626,41.9583756]},"n2114807564":{"id":"n2114807564","loc":[-85.638535,41.9581283]},"n2189015691":{"id":"n2189015691","loc":[-85.6435584,41.9565243]},"n2189015696":{"id":"n2189015696","loc":[-85.6435805,41.9566049]},"n2189015722":{"id":"n2189015722","loc":[-85.6435035,41.9567438]},"n2189015744":{"id":"n2189015744","loc":[-85.6437991,41.9569582]},"n2189015747":{"id":"n2189015747","loc":[-85.6433042,41.9567742]},"n2189015750":{"id":"n2189015750","loc":[-85.6433827,41.9566844]},"n2189015753":{"id":"n2189015753","loc":[-85.6430447,41.9565588]},"n2189015756":{"id":"n2189015756","loc":[-85.6431111,41.956451]},"n2189015759":{"id":"n2189015759","loc":[-85.6420247,41.956083]},"n2189015760":{"id":"n2189015760","loc":[-85.6419945,41.9561369]},"n2189015764":{"id":"n2189015764","loc":[-85.6413729,41.9558945]},"n2189015766":{"id":"n2189015766","loc":[-85.6412884,41.9560606]},"n2189015770":{"id":"n2189015770","loc":[-85.6411798,41.9560112]},"n2189015771":{"id":"n2189015771","loc":[-85.6410651,41.9562132]},"n2189015774":{"id":"n2189015774","loc":[-85.6409504,41.9561728]},"n2189015778":{"id":"n2189015778","loc":[-85.6407996,41.9564241]},"n2189015781":{"id":"n2189015781","loc":[-85.6406889,41.9563892]},"n2189015785":{"id":"n2189015785","loc":[-85.6404857,41.9567024]},"n2189015789":{"id":"n2189015789","loc":[-85.6406909,41.9567877]},"n2189015793":{"id":"n2189015793","loc":[-85.6405642,41.9570165]},"n2189015796":{"id":"n2189015796","loc":[-85.6415359,41.9573711]},"n2189015800":{"id":"n2189015800","loc":[-85.6411738,41.9579501]},"n2189015804":{"id":"n2189015804","loc":[-85.6411119,41.957921]},"n2189015808":{"id":"n2189015808","loc":[-85.6403186,41.9591751]},"n2189015909":{"id":"n2189015909","loc":[-85.6389293,41.9564636]},"n2189015926":{"id":"n2189015926","loc":[-85.6385431,41.9564617]},"n2189015929":{"id":"n2189015929","loc":[-85.6385457,41.9561823]},"n2189015932":{"id":"n2189015932","loc":[-85.6389319,41.9561843]},"n2199109865":{"id":"n2199109865","loc":[-85.6400768,41.956776]},"n2199109867":{"id":"n2199109867","loc":[-85.639902,41.9567153]},"n2199109869":{"id":"n2199109869","loc":[-85.640004,41.956553]},"n2199109871":{"id":"n2199109871","loc":[-85.6401788,41.9566137]},"n2199109873":{"id":"n2199109873","loc":[-85.6399316,41.9564506],"tags":{"man_made":"water_tower"}},"n2199109876":{"id":"n2199109876","loc":[-85.6397689,41.9572354]},"n2199109878":{"id":"n2199109878","loc":[-85.6399229,41.9569826]},"n2199109880":{"id":"n2199109880","loc":[-85.639706,41.9569095]},"n2199109882":{"id":"n2199109882","loc":[-85.639552,41.9571623]},"n2199109884":{"id":"n2199109884","loc":[-85.6391028,41.9569517]},"n2199109886":{"id":"n2199109886","loc":[-85.6392876,41.956646]},"n2199109888":{"id":"n2199109888","loc":[-85.639484,41.9567117]},"n2199109889":{"id":"n2199109889","loc":[-85.6394322,41.9567973]},"n2199109890":{"id":"n2199109890","loc":[-85.6393718,41.9567771]},"n2199109891":{"id":"n2199109891","loc":[-85.6392387,41.9569972]},"n1819848900":{"id":"n1819848900","loc":[-85.638281,41.9576578]},"n1819848978":{"id":"n1819848978","loc":[-85.6377186,41.9580867]},"n1819849039":{"id":"n1819849039","loc":[-85.6384217,41.9573405]},"n1819849050":{"id":"n1819849050","loc":[-85.6377011,41.9570042]},"n1819849088":{"id":"n1819849088","loc":[-85.6382879,41.9580817]},"n2114807549":{"id":"n2114807549","loc":[-85.6362551,41.96473]},"n2114807587":{"id":"n2114807587","loc":[-85.6368694,41.9629829]},"n2189015725":{"id":"n2189015725","loc":[-85.644156,41.9569753]},"n2189015741":{"id":"n2189015741","loc":[-85.6419825,41.9597632]},"w208627217":{"id":"w208627217","tags":{"area":"yes","building":"yes"},"nodes":["n2189015741","n2189015744","n2189015747","n2189015750","n2189015753","n2189015756","n2189015759","n2189015760","n2189015764","n2189015766","n2189015770","n2189015771","n2189015774","n2189015778","n2189015781","n2189015785","n2189015789","n2189015793","n2189015796","n2189015800","n2189015804","n2189015808","n2189015741"]},"w208627212":{"id":"w208627212","tags":{"highway":"service"},"nodes":["n2189015691","n2189015696","n2189015722","n2189015725"]},"w209717057":{"id":"w209717057","tags":{"area":"yes","building":"yes"},"nodes":["n2199109876","n2199109878","n2199109880","n2199109882","n2199109876"]},"w209717056":{"id":"w209717056","tags":{"area":"yes","building":"yes"},"nodes":["n2199109865","n2199109867","n2199109869","n2199109871","n2199109865"]},"w208627231":{"id":"w208627231","tags":{"area":"yes","building":"yes"},"nodes":["n2189015909","n2189015926","n2189015929","n2189015932","n2189015909"]},"w170848326":{"id":"w170848326","tags":{"building":"yes"},"nodes":["n1819848881","n1819849050","n1819848978","n1819849088","n1819848900","n1819848947","n1819849039","n1819849044","n1819848881"]},"w17963182":{"id":"w17963182","tags":{"highway":"service"},"nodes":["n185949763","n185949765","n2189015691","n185949745"]},"w201484340":{"id":"w201484340","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807565","n2114807564","n2114807547","n2114807587","n2114807558","n2114807549","n2114807593"]},"w209717058":{"id":"w209717058","tags":{"area":"yes","building":"yes"},"nodes":["n2199109884","n2199109886","n2199109888","n2199109889","n2199109890","n2199109891","n2199109884"]},"n185954650":{"id":"n185954650","loc":[-85.627331,41.957439]},"n185966949":{"id":"n185966949","loc":[-85.626868,41.957314]},"n185989335":{"id":"n185989335","loc":[-85.62529,41.958568]},"n185989337":{"id":"n185989337","loc":[-85.624962,41.958453]},"n185989339":{"id":"n185989339","loc":[-85.624832,41.958399]},"n185989340":{"id":"n185989340","loc":[-85.624707,41.958325]},"n185989342":{"id":"n185989342","loc":[-85.624636,41.958251]},"n185989345":{"id":"n185989345","loc":[-85.624578,41.95818]},"n185989347":{"id":"n185989347","loc":[-85.624533,41.958099]},"n185989349":{"id":"n185989349","loc":[-85.624507,41.957985]},"n185989351":{"id":"n185989351","loc":[-85.624495,41.957807]},"n185989353":{"id":"n185989353","loc":[-85.624514,41.957663]},"n185989354":{"id":"n185989354","loc":[-85.624577,41.957593]},"n185989356":{"id":"n185989356","loc":[-85.624685,41.95754]},"n185989357":{"id":"n185989357","loc":[-85.624802,41.957523]},"n185989359":{"id":"n185989359","loc":[-85.624996,41.957524]},"n185989361":{"id":"n185989361","loc":[-85.625409,41.957515]},"n185989364":{"id":"n185989364","loc":[-85.625634,41.957496]},"n185989367":{"id":"n185989367","loc":[-85.625832,41.957453]},"n185989368":{"id":"n185989368","loc":[-85.626044,41.957394]},"n354031352":{"id":"n354031352","loc":[-85.6252778,41.9586111],"tags":{"amenity":"place_of_worship","denomination":"baptist","name":"First Baptist Church","religion":"christian"}},"n2199109892":{"id":"n2199109892","loc":[-85.6261578,41.9589963]},"n2199109893":{"id":"n2199109893","loc":[-85.6263191,41.9586865]},"n2199109894":{"id":"n2199109894","loc":[-85.6261186,41.9586288]},"n2199109895":{"id":"n2199109895","loc":[-85.6260644,41.9587329]},"n2199109896":{"id":"n2199109896","loc":[-85.6261547,41.9587589]},"n2199109898":{"id":"n2199109898","loc":[-85.6260476,41.9589646]},"n185966951":{"id":"n185966951","loc":[-85.628404,41.957438]},"w17965351":{"id":"w17965351","tags":{"highway":"residential","name":"Flower Street"},"nodes":["n185966948","n185966949","n185954650","n185966951","n185966953","n185966955","n185966957"]},"w17967809":{"id":"w17967809","tags":{"highway":"residential","name":"Azaleamum Drive"},"nodes":["n185982197","n185989335","n185989337","n185989339","n185989340","n185989342","n185989345","n185989347","n185989349","n185989351","n185989353","n185989354","n185989356","n185989357","n185989359","n185989361","n185989364","n185989367","n185989368","n185982196"]},"w209717059":{"id":"w209717059","tags":{"area":"yes","building":"yes"},"nodes":["n2199109892","n2199109893","n2199109894","n2199109895","n2199109896","n2199109898","n2199109892"]},"n185961390":{"id":"n185961390","loc":[-85.63137,41.959033]},"n185961393":{"id":"n185961393","loc":[-85.634315,41.959017]},"w17966214":{"id":"w17966214","tags":{"highway":"residential","name":"East Adams Street"},"nodes":["n185975351","n185967434","n185968108"]},"w17964793":{"id":"w17964793","tags":{"highway":"residential","name":"Morris Ave"},"nodes":["n185961389","n185961390","n185961391","n185961393","n185961396"]},"n185952166":{"id":"n185952166","loc":[-85.638174,41.95831]},"n2114807552":{"id":"n2114807552","loc":[-85.6383526,41.9593788]},"n2114807591":{"id":"n2114807591","loc":[-85.6383741,41.9593968]},"n2189015731":{"id":"n2189015731","loc":[-85.6368404,41.9592785]},"n2189015734":{"id":"n2189015734","loc":[-85.6368404,41.9585918]},"n2189015737":{"id":"n2189015737","loc":[-85.6376009,41.9585918]},"n2189015738":{"id":"n2189015738","loc":[-85.6376009,41.9592785]},"n2189015897":{"id":"n2189015897","loc":[-85.6376839,41.9566137]},"n2189015900":{"id":"n2189015900","loc":[-85.6376831,41.9564865]},"n2189015903":{"id":"n2189015903","loc":[-85.6381161,41.9564851]},"n2189015906":{"id":"n2189015906","loc":[-85.6381168,41.9566122]},"n2189015937":{"id":"n2189015937","loc":[-85.6364789,41.9590634]},"n2189015940":{"id":"n2189015940","loc":[-85.6361137,41.9590672]},"n2189015943":{"id":"n2189015943","loc":[-85.6361169,41.9594033]},"n2189015945":{"id":"n2189015945","loc":[-85.6363456,41.9594021]},"n2189015952":{"id":"n2189015952","loc":[-85.636112,41.958892]},"n2189015955":{"id":"n2189015955","loc":[-85.6364757,41.9588894]},"n2189015957":{"id":"n2189015957","loc":[-85.6364729,41.9586747]},"n2189015958":{"id":"n2189015958","loc":[-85.6361103,41.9586765]},"n2189015959":{"id":"n2189015959","loc":[-85.6364719,41.9585562]},"n2189015960":{"id":"n2189015960","loc":[-85.6361093,41.958558]},"n2189015961":{"id":"n2189015961","loc":[-85.6355494,41.9586403]},"n2189015962":{"id":"n2189015962","loc":[-85.635549,41.9584711]},"n2189015963":{"id":"n2189015963","loc":[-85.6351831,41.9584715]},"n2189015964":{"id":"n2189015964","loc":[-85.6351834,41.9586408]},"n2189015966":{"id":"n2189015966","loc":[-85.6359579,41.9586359]},"n2189015968":{"id":"n2189015968","loc":[-85.6359561,41.9585465]},"n2189015971":{"id":"n2189015971","loc":[-85.6355476,41.9585509]},"n2189015974":{"id":"n2189015974","loc":[-85.6359516,41.9592934]},"n2189015977":{"id":"n2189015977","loc":[-85.635949,41.9586697]},"n2189015980":{"id":"n2189015980","loc":[-85.6351329,41.9586716]},"n2189015983":{"id":"n2189015983","loc":[-85.6351318,41.9583949]},"n2189015986":{"id":"n2189015986","loc":[-85.6349148,41.9583954]},"n2189015989":{"id":"n2189015989","loc":[-85.6349186,41.9592958]},"n2189015995":{"id":"n2189015995","loc":[-85.6360173,41.9593286]},"n2189015998":{"id":"n2189015998","loc":[-85.6360278,41.9583079]},"n2114807550":{"id":"n2114807550","loc":[-85.6383392,41.9595404]},"n2114807551":{"id":"n2114807551","loc":[-85.6375855,41.9616107]},"n2114807559":{"id":"n2114807559","loc":[-85.6373978,41.9621273]},"n2114807562":{"id":"n2114807562","loc":[-85.6373361,41.9622609]},"n2114807563":{"id":"n2114807563","loc":[-85.6376472,41.9613953]},"n2114807574":{"id":"n2114807574","loc":[-85.636974,41.9627695]},"n2114807589":{"id":"n2114807589","loc":[-85.6383017,41.9595005]},"n2114807592":{"id":"n2114807592","loc":[-85.6377169,41.9613494]},"n2114807595":{"id":"n2114807595","loc":[-85.6371081,41.962574]},"n2189015934":{"id":"n2189015934","loc":[-85.6364855,41.9595098]},"n2189015949":{"id":"n2189015949","loc":[-85.6363466,41.9595105]},"w208627244":{"id":"w208627244","tags":{"highway":"service"},"nodes":["n2189015992","n2189015995","n2189015998"]},"w208627240":{"id":"w208627240","tags":{"area":"yes","building":"yes"},"nodes":["n2189015961","n2189015971","n2189015962","n2189015963","n2189015964","n2189015961"]},"w17967437":{"id":"w17967437","tags":{"highway":"residential","name":"Lyman St"},"nodes":["n185964361","n185984024"]},"w208627237":{"id":"w208627237","tags":{"area":"yes","building":"yes"},"nodes":["n2189015955","n2189015957","n2189015958","n2189015952","n2189015955"]},"w17967465":{"id":"w17967465","tags":{"highway":"residential","name":"W Adams St"},"nodes":["n185978394","n185984022","n185964360"]},"w208627228":{"id":"w208627228","tags":{"area":"yes","building":"yes"},"nodes":["n2189015897","n2189015900","n2189015903","n2189015906","n2189015897"]},"w201484351":{"id":"w201484351","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807587","n2114807574","n2114807595","n2114807562","n2114807559","n2114807551","n2114807563","n2114807589","n2114807552"]},"w208627239":{"id":"w208627239","tags":{"area":"yes","building":"yes"},"nodes":["n2189015957","n2189015959","n2189015960","n2189015958","n2189015957"]},"w208627233":{"id":"w208627233","tags":{"area":"yes","building":"yes"},"nodes":["n2189015934","n2189015937","n2189015940","n2189015943","n2189015945","n2189015949","n2189015934"]},"w208627241":{"id":"w208627241","tags":{"area":"yes","building":"yes"},"nodes":["n2189015961","n2189015966","n2189015968","n2189015971","n2189015961"]},"w17967970":{"id":"w17967970","tags":{"highway":"residential","name":"Adams St"},"nodes":["n185975351","n185978394"]},"w208627235":{"id":"w208627235","tags":{"area":"yes","building":"yes"},"nodes":["n2189015940","n2189015952","n2189015955","n2189015937","n2189015940"]},"w17965468":{"id":"w17965468","tags":{"highway":"residential","name":"Armstrong Blvd"},"nodes":["n185967917","n2189015998","n185967918","n185964362","n185952166"]},"w201484346":{"id":"w201484346","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807551","n2114807592","n2114807550","n2114807591"]},"w208627242":{"id":"w208627242","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189015974","n2189015977","n2189015980","n2189015983","n2189015986","n2189015989","n2189015974"]},"w208627216":{"id":"w208627216","tags":{"area":"yes","building":"yes"},"nodes":["n2189015731","n2189015734","n2189015737","n2189015738","n2189015731"]},"n185984309":{"id":"n185984309","loc":[-85.631421,41.961494]},"n185987987":{"id":"n185987987","loc":[-85.631456,41.960673]},"n185965397":{"id":"n185965397","loc":[-85.634603,41.959838]},"w17965196":{"id":"w17965196","tags":{"highway":"residential","name":"Burke Ave"},"nodes":["n185965395","n185965397","n185965399"]},"w17967215":{"id":"w17967215","tags":{"highway":"residential","name":"Kellogg Ave"},"nodes":["n185968114","n185984309","n185967440","n185978402"]},"w17967597":{"id":"w17967597","tags":{"highway":"residential","name":"Barnard Ave"},"nodes":["n185968112","n185987987","n185967438","n185978399"]},"n394490857":{"id":"n394490857","loc":[-85.633952,41.960664]},"n394490858":{"id":"n394490858","loc":[-85.633938,41.960227]},"n394490859":{"id":"n394490859","loc":[-85.634794,41.960212]},"n394490860":{"id":"n394490860","loc":[-85.634815,41.960662]},"n394490861":{"id":"n394490861","loc":[-85.634103,41.961268]},"n394490862":{"id":"n394490862","loc":[-85.634103,41.961001]},"n394490863":{"id":"n394490863","loc":[-85.634504,41.961003]},"n394490864":{"id":"n394490864","loc":[-85.634561,41.961269]},"n1057629869":{"id":"n1057629869","loc":[-85.6382599,41.9612134]},"n1057629937":{"id":"n1057629937","loc":[-85.6380035,41.9616137]},"n2189016014":{"id":"n2189016014","loc":[-85.6360365,41.9626496]},"n2189016017":{"id":"n2189016017","loc":[-85.6360374,41.9623228]},"n2189016020":{"id":"n2189016020","loc":[-85.6367557,41.9623239]},"n2189016022":{"id":"n2189016022","loc":[-85.6367566,41.9619919]},"n2189016025":{"id":"n2189016025","loc":[-85.6351794,41.9619893]},"n2189016028":{"id":"n2189016028","loc":[-85.6351788,41.9622011]},"n2189016031":{"id":"n2189016031","loc":[-85.6350855,41.9622009]},"n2189016034":{"id":"n2189016034","loc":[-85.6350845,41.962527]},"n2189016037":{"id":"n2189016037","loc":[-85.6352732,41.9625273]},"n2189016039":{"id":"n2189016039","loc":[-85.6352738,41.9623178]},"n2189016042":{"id":"n2189016042","loc":[-85.6357712,41.9623186]},"n2189016044":{"id":"n2189016044","loc":[-85.6357702,41.9626492]},"n1057629880":{"id":"n1057629880","loc":[-85.638817,41.9619017]},"n1057629923":{"id":"n1057629923","loc":[-85.6390733,41.9615014]},"w91092312":{"id":"w91092312","tags":{"power":"station"},"nodes":["n1057629923","n1057629869","n1057629937","n1057629880","n1057629923"]},"w34369826":{"id":"w34369826","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490861","n394490862","n394490863","n394490864","n394490861"]},"w34369825":{"id":"w34369825","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490857","n394490858","n394490859","n394490860","n394490857"]},"w208627248":{"id":"w208627248","tags":{"area":"yes","building":"yes"},"nodes":["n2189016014","n2189016017","n2189016020","n2189016022","n2189016025","n2189016028","n2189016031","n2189016034","n2189016037","n2189016039","n2189016042","n2189016044","n2189016014"]},"n394490766":{"id":"n394490766","loc":[-85.616777,41.955642]},"n394490768":{"id":"n394490768","loc":[-85.617239,41.955644]},"n394490792":{"id":"n394490792","loc":[-85.619034,41.95543]},"n185972055":{"id":"n185972055","loc":[-85.6185905,41.9568211]},"n185972057":{"id":"n185972057","loc":[-85.6186688,41.9570086]},"n185972059":{"id":"n185972059","loc":[-85.6186924,41.9581453]},"n185972060":{"id":"n185972060","loc":[-85.6187082,41.9588211],"tags":{"highway":"turning_circle"}},"n1819790724":{"id":"n1819790724","loc":[-85.6182155,41.9555703]},"n1819790735":{"id":"n1819790735","loc":[-85.6184059,41.9566188]},"n1819790799":{"id":"n1819790799","loc":[-85.6182372,41.9563771]},"n1819790896":{"id":"n1819790896","loc":[-85.6181431,41.9557227]},"n185971405":{"id":"n185971405","loc":[-85.6186766,41.9577468]},"n185971565":{"id":"n185971565","loc":[-85.6181613,41.9560879]},"n185967985":{"id":"n185967985","loc":[-85.6186798,41.9585791]},"n185955753":{"id":"n185955753","loc":[-85.620773,41.9555854]},"n185955755":{"id":"n185955755","loc":[-85.6212652,41.9559891]},"n185955748":{"id":"n185955748","loc":[-85.620722,41.954858]},"n185955751":{"id":"n185955751","loc":[-85.6206912,41.955367]},"n185967987":{"id":"n185967987","loc":[-85.6159351,41.9585809]},"n185971407":{"id":"n185971407","loc":[-85.6159142,41.9577578]},"n185971570":{"id":"n185971570","loc":[-85.6162248,41.95603]},"n185971572":{"id":"n185971572","loc":[-85.6160402,41.9560749]},"n185971574":{"id":"n185971574","loc":[-85.61593,41.956201]},"n185981301":{"id":"n185981301","loc":[-85.6158973,41.9581601]},"n394490762":{"id":"n394490762","loc":[-85.617193,41.954706]},"n394490764":{"id":"n394490764","loc":[-85.616773,41.954737]},"n394490787":{"id":"n394490787","loc":[-85.618972,41.954737]},"n394490790":{"id":"n394490790","loc":[-85.619046,41.954929]},"n394490794":{"id":"n394490794","loc":[-85.619922,41.955296]},"n394490796":{"id":"n394490796","loc":[-85.61991,41.95501]},"n394490798":{"id":"n394490798","loc":[-85.619974,41.954751]},"n1819790677":{"id":"n1819790677","loc":[-85.6187031,41.9550522]},"n1819790787":{"id":"n1819790787","loc":[-85.6186436,41.9552022]},"n1819790828":{"id":"n1819790828","loc":[-85.6185127,41.9553393]},"w17966857":{"id":"w17966857","tags":{"access":"private","highway":"service","name":"Sable River Rd"},"nodes":["n185972059","n185981301"]},"w34369814":{"id":"w34369814","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490787","n394490790","n394490792","n394490794","n394490796","n394490798","n394490787"]},"w17964176":{"id":"w17964176","tags":{"highway":"residential"},"nodes":["n185955747","n185955748","n185955751","n185955753","n185955755"]},"w17965838":{"id":"w17965838","tags":{"access":"private","highway":"service","name":"Pine River Rd"},"nodes":["n185971405","n185971407"]},"w17965476":{"id":"w17965476","tags":{"access":"private","highway":"service","name":"Raisin River Rd"},"nodes":["n185967985","n185967987"]},"w17965913":{"id":"w17965913","tags":{"access":"private","highway":"service","name":"Shiawassee River Rd"},"nodes":["n185972054","n1819790677","n1819790787","n1819790828","n1819790724","n1819790896","n185971565","n1819790799","n1819790735","n185972055","n185972057","n185971405","n185972059","n185967985","n185972060"]},"w34369811":{"id":"w34369811","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490762","n394490764","n394490766","n394490768","n394490762"]},"w17965854":{"id":"w17965854","tags":{"access":"private","highway":"service","name":"Sturgeon River Rd"},"nodes":["n185971565","n185971570","n185971572","n185971574"]},"n2139795769":{"id":"n2139795769","loc":[-85.6250804,41.9608796]},"n2139795770":{"id":"n2139795770","loc":[-85.6250315,41.9613684]},"n2139795771":{"id":"n2139795771","loc":[-85.6249671,41.9614362]},"n2139795772":{"id":"n2139795772","loc":[-85.6249698,41.961522]},"n2139795773":{"id":"n2139795773","loc":[-85.6250798,41.9615838]},"n2139795774":{"id":"n2139795774","loc":[-85.6252273,41.9615639]},"n2139795775":{"id":"n2139795775","loc":[-85.6252863,41.9614622]},"n2139795776":{"id":"n2139795776","loc":[-85.6252273,41.9613764]},"n2139795777":{"id":"n2139795777","loc":[-85.6251227,41.9613525]},"n2139795778":{"id":"n2139795778","loc":[-85.6249564,41.9612527]},"n2139795779":{"id":"n2139795779","loc":[-85.6249846,41.9610254]},"n2139795780":{"id":"n2139795780","loc":[-85.6266725,41.9599647]},"n2139795781":{"id":"n2139795781","loc":[-85.6259162,41.9599711]},"n2139795782":{"id":"n2139795782","loc":[-85.6257185,41.960019]},"n2139795783":{"id":"n2139795783","loc":[-85.6255509,41.9601213]},"n185963539":{"id":"n185963539","loc":[-85.615718,41.983893]},"n185964418":{"id":"n185964418","loc":[-85.616626,42.049512]},"n185966614":{"id":"n185966614","loc":[-85.615514,41.976603]},"n185966635":{"id":"n185966635","loc":[-85.616118,42.013017]},"n185969040":{"id":"n185969040","loc":[-85.615632,41.972357]},"n185969070":{"id":"n185969070","loc":[-85.619145,41.967648]},"n185972156":{"id":"n185972156","loc":[-85.621894,41.963956]},"n185972157":{"id":"n185972157","loc":[-85.621806,41.964077]},"n185972158":{"id":"n185972158","loc":[-85.620848,41.965341]},"n185972159":{"id":"n185972159","loc":[-85.620684,41.965558]},"n185972160":{"id":"n185972160","loc":[-85.620621,41.965658]},"n185972161":{"id":"n185972161","loc":[-85.617844,41.969359]},"n185972162":{"id":"n185972162","loc":[-85.616843,41.97068]},"n185972164":{"id":"n185972164","loc":[-85.616714,41.970839]},"n185972166":{"id":"n185972166","loc":[-85.615879,41.971969]},"n185972168":{"id":"n185972168","loc":[-85.615748,41.972159]},"n185972170":{"id":"n185972170","loc":[-85.615589,41.972502]},"n185972172":{"id":"n185972172","loc":[-85.615542,41.972733]},"n185972175":{"id":"n185972175","loc":[-85.615524,41.972947]},"n185972177":{"id":"n185972177","loc":[-85.615512,41.973715]},"n185972179":{"id":"n185972179","loc":[-85.615513,41.976496]},"n185972180":{"id":"n185972180","loc":[-85.615538,41.977246]},"n185972181":{"id":"n185972181","loc":[-85.61558,41.982139]},"n185972184":{"id":"n185972184","loc":[-85.61557,41.983317]},"n185972186":{"id":"n185972186","loc":[-85.615591,41.983463]},"n185972188":{"id":"n185972188","loc":[-85.615763,41.984146]},"n185972190":{"id":"n185972190","loc":[-85.615814,41.98435]},"n185972192":{"id":"n185972192","loc":[-85.615965,41.998453]},"n185972194":{"id":"n185972194","loc":[-85.615982,42.001237]},"n185972195":{"id":"n185972195","loc":[-85.616055,42.00555]},"n185972197":{"id":"n185972197","loc":[-85.616134,42.014887]},"n185972199":{"id":"n185972199","loc":[-85.616177,42.018465]},"n185972201":{"id":"n185972201","loc":[-85.616298,42.027627]},"n185972203":{"id":"n185972203","loc":[-85.616513,42.042212]},"w203968015":{"id":"w203968015","tags":{"highway":"residential"},"nodes":["n2139795768","n2139795769"]},"w17965932":{"id":"w17965932","tags":{"highway":"residential","name":"Buckhorn Road","name_1":"County Highway 122"},"nodes":["n185972155","n185972156","n185972157","n185972158","n185972159","n185972160","n185969070","n185972161","n185972162","n185972164","n185972166","n185972168","n185969040","n185972170","n185972172","n185972175","n185972177","n185972179","n185966614","n185972180","n185972181","n185972184","n185972186","n185963539","n185972188","n185972190","n185972192","n185972194","n185972195","n185966635","n185972197","n185972199","n185972201","n185972203","n185964418"]},"w203968016":{"id":"w203968016","tags":{"highway":"residential","name":"New Jersey Court"},"nodes":["n2139795770","n2139795771","n2139795772","n2139795773","n2139795774","n2139795775","n2139795776","n2139795777","n2139795770","n2139795778","n2139795779","n2139795769"]},"w203968017":{"id":"w203968017","tags":{"highway":"residential","name":"Oklahoma Drive"},"nodes":["n2139795780","n2139795781","n2139795782","n2139795783","n2139795769"]},"n1819790528":{"id":"n1819790528","loc":[-85.6184827,41.960025]},"n1819790530":{"id":"n1819790530","loc":[-85.6168626,41.9605834]},"n1819790534":{"id":"n1819790534","loc":[-85.6197379,41.9617163]},"n1819790541":{"id":"n1819790541","loc":[-85.6198881,41.9620833]},"n1819790543":{"id":"n1819790543","loc":[-85.619695,41.9619397]},"n1819790547":{"id":"n1819790547","loc":[-85.6190298,41.9609504]},"n1819790555":{"id":"n1819790555","loc":[-85.6180471,41.9609788]},"n1819790559":{"id":"n1819790559","loc":[-85.6203817,41.9605436]},"n1819790583":{"id":"n1819790583","loc":[-85.6201564,41.9603282]},"n1819790590":{"id":"n1819790590","loc":[-85.617045,41.9598894]},"n1819790609":{"id":"n1819790609","loc":[-85.6177638,41.9598495]},"n1819790618":{"id":"n1819790618","loc":[-85.6195234,41.9610143]},"n1819790642":{"id":"n1819790642","loc":[-85.6181179,41.9627933]},"n1819790659":{"id":"n1819790659","loc":[-85.6174634,41.962897]},"n1819790665":{"id":"n1819790665","loc":[-85.6170343,41.9630885]},"n1819790674":{"id":"n1819790674","loc":[-85.6194697,41.9601925]},"n1819790685":{"id":"n1819790685","loc":[-85.6207722,41.9610665]},"n1819790687":{"id":"n1819790687","loc":[-85.6202315,41.9622109]},"n1819790697":{"id":"n1819790697","loc":[-85.6184505,41.9624662]},"n1819790726":{"id":"n1819790726","loc":[-85.6178926,41.9628492]},"n1819790738":{"id":"n1819790738","loc":[-85.6173347,41.9598016]},"n1819790762":{"id":"n1819790762","loc":[-85.6186221,41.9609105]},"n1819790774":{"id":"n1819790774","loc":[-85.6175922,41.9608308]},"n1819790781":{"id":"n1819790781","loc":[-85.6167768,41.9633198]},"n1819790796":{"id":"n1819790796","loc":[-85.619856,41.961461]},"n1819790811":{"id":"n1819790811","loc":[-85.6208215,41.9620195]},"n1819790833":{"id":"n1819790833","loc":[-85.618311,41.9612536]},"n1819790854":{"id":"n1819790854","loc":[-85.6183646,41.9626417]},"n1819790863":{"id":"n1819790863","loc":[-85.6204997,41.9608547]},"n1819790867":{"id":"n1819790867","loc":[-85.6184934,41.9621391]},"n1819790877":{"id":"n1819790877","loc":[-85.6206928,41.9621152]},"n1819790881":{"id":"n1819790881","loc":[-85.6170879,41.960735]},"n1819790891":{"id":"n1819790891","loc":[-85.6168304,41.9601207]},"n1819790898":{"id":"n1819790898","loc":[-85.619813,41.9612297]},"n1819790909":{"id":"n1819790909","loc":[-85.6167982,41.960376]},"n1819790912":{"id":"n1819790912","loc":[-85.6205855,41.9610462]},"n1819790544":{"id":"n1819790544","loc":[-85.612968,41.9707781]},"n1819790549":{"id":"n1819790549","loc":[-85.614395,41.9697172]},"n1819790552":{"id":"n1819790552","loc":[-85.6180535,41.9655536]},"n1819790554":{"id":"n1819790554","loc":[-85.6111227,41.9703713]},"n1819790560":{"id":"n1819790560","loc":[-85.6112729,41.9701958]},"n1819790563":{"id":"n1819790563","loc":[-85.6137512,41.9689917]},"n1819790564":{"id":"n1819790564","loc":[-85.6181072,41.9659205]},"n1819790595":{"id":"n1819790595","loc":[-85.6170021,41.9666863]},"n1819790605":{"id":"n1819790605","loc":[-85.6168948,41.9644527]},"n1819790606":{"id":"n1819790606","loc":[-85.6128071,41.9701081]},"n1819790607":{"id":"n1819790607","loc":[-85.6129251,41.9704032]},"n1819790612":{"id":"n1819790612","loc":[-85.6177638,41.9663912]},"n1819790615":{"id":"n1819790615","loc":[-85.6152533,41.9670373]},"n1819790622":{"id":"n1819790622","loc":[-85.6146739,41.9673804]},"n1819790623":{"id":"n1819790623","loc":[-85.6180428,41.9661838]},"n1819790625":{"id":"n1819790625","loc":[-85.6172918,41.9646202]},"n1819790645":{"id":"n1819790645","loc":[-85.6178067,41.965043]},"n1819790647":{"id":"n1819790647","loc":[-85.6143306,41.9712488]},"n1819790649":{"id":"n1819790649","loc":[-85.6147383,41.9707702]},"n1819790654":{"id":"n1819790654","loc":[-85.6157361,41.9668459]},"n1819790657":{"id":"n1819790657","loc":[-85.6145666,41.9710733]},"n1819790668":{"id":"n1819790668","loc":[-85.6166909,41.9642692]},"n1819790671":{"id":"n1819790671","loc":[-85.6141482,41.9696538]},"n1819790679":{"id":"n1819790679","loc":[-85.6148349,41.9705388]},"n1819790686":{"id":"n1819790686","loc":[-85.6139551,41.9695501]},"n1819790696":{"id":"n1819790696","loc":[-85.6119703,41.9699087]},"n1819790704":{"id":"n1819790704","loc":[-85.6140731,41.9684174]},"n1819790706":{"id":"n1819790706","loc":[-85.6124745,41.9699246]},"n1819790718":{"id":"n1819790718","loc":[-85.6165407,41.9636868]},"n1819790720":{"id":"n1819790720","loc":[-85.61388,41.9687365]},"n1819790731":{"id":"n1819790731","loc":[-85.6165193,41.9639421]},"n1819790739":{"id":"n1819790739","loc":[-85.6146739,41.9699964]},"n1819790753":{"id":"n1819790753","loc":[-85.6173883,41.9665747]},"n1819790760":{"id":"n1819790760","loc":[-85.6133221,41.9712089]},"n1819790767":{"id":"n1819790767","loc":[-85.6116698,41.9699246]},"n1819790779":{"id":"n1819790779","loc":[-85.6130753,41.9710573]},"n1819790791":{"id":"n1819790791","loc":[-85.6137083,41.9692869]},"n1819790795":{"id":"n1819790795","loc":[-85.6141482,41.9679627]},"n1819790798":{"id":"n1819790798","loc":[-85.6137727,41.9694305]},"n1819790836":{"id":"n1819790836","loc":[-85.6143842,41.9676037]},"n1819790915":{"id":"n1819790915","loc":[-85.6148456,41.9702756]},"n1819790926":{"id":"n1819790926","loc":[-85.6138371,41.9713525]},"n1819790927":{"id":"n1819790927","loc":[-85.6141053,41.9713525]},"n1819790931":{"id":"n1819790931","loc":[-85.6162832,41.966814]},"n1821014625":{"id":"n1821014625","loc":[-85.5960611,41.9808498]},"n1821014627":{"id":"n1821014627","loc":[-85.5565843,42.010982]},"n1821014629":{"id":"n1821014629","loc":[-85.5971541,41.9805808]},"n1821014632":{"id":"n1821014632","loc":[-85.6061837,41.9725907]},"n1821014633":{"id":"n1821014633","loc":[-85.5247773,42.025766]},"n1821014635":{"id":"n1821014635","loc":[-85.5908938,41.9902384]},"n1821014636":{"id":"n1821014636","loc":[-85.5917682,41.9860637]},"n1821014637":{"id":"n1821014637","loc":[-85.5456556,42.0166797]},"n1821014638":{"id":"n1821014638","loc":[-85.5795749,42.0032352]},"n1821014639":{"id":"n1821014639","loc":[-85.6103988,41.9723456]},"n1821014642":{"id":"n1821014642","loc":[-85.5818816,42.0022466]},"n1821014643":{"id":"n1821014643","loc":[-85.5570604,42.0091586]},"n1821014644":{"id":"n1821014644","loc":[-85.5952886,41.9803792]},"n1821014645":{"id":"n1821014645","loc":[-85.5780366,42.0040343]},"n1821014646":{"id":"n1821014646","loc":[-85.6050505,41.9751971]},"n1821014647":{"id":"n1821014647","loc":[-85.5854435,41.9946162]},"n1821014648":{"id":"n1821014648","loc":[-85.5452278,42.0168768]},"n1821014649":{"id":"n1821014649","loc":[-85.6023254,41.9780166]},"n1821014651":{"id":"n1821014651","loc":[-85.5761899,42.0046783]},"n1821014653":{"id":"n1821014653","loc":[-85.5897351,41.9876707]},"n1821014657":{"id":"n1821014657","loc":[-85.5963601,41.9808998]},"n1821014658":{"id":"n1821014658","loc":[-85.5892952,41.9951983]},"n1821014660":{"id":"n1821014660","loc":[-85.5778328,42.0037194]},"n1821014661":{"id":"n1821014661","loc":[-85.5541475,42.0125705]},"n1821014663":{"id":"n1821014663","loc":[-85.5914047,41.9856469]},"n1821014664":{"id":"n1821014664","loc":[-85.6101681,41.9727723]},"n1821014665":{"id":"n1821014665","loc":[-85.5910172,41.9854696]},"n1821014666":{"id":"n1821014666","loc":[-85.5398688,42.0187699]},"n1821014667":{"id":"n1821014667","loc":[-85.5218752,42.0282884]},"n1821014668":{"id":"n1821014668","loc":[-85.5159582,42.0329384]},"n1821014669":{"id":"n1821014669","loc":[-85.5898102,41.9847319]},"n1821014670":{"id":"n1821014670","loc":[-85.5734809,42.0066235]},"n1821014671":{"id":"n1821014671","loc":[-85.5922939,41.980852]},"n1821014672":{"id":"n1821014672","loc":[-85.6023629,41.9781163]},"n1821014674":{"id":"n1821014674","loc":[-85.5409953,42.0191724]},"n1821014676":{"id":"n1821014676","loc":[-85.584435,41.9949909]},"n1821014677":{"id":"n1821014677","loc":[-85.5972399,41.9783835]},"n1821014678":{"id":"n1821014678","loc":[-85.5616738,42.0071337]},"n1821014681":{"id":"n1821014681","loc":[-85.5202994,42.0310755]},"n1821014682":{"id":"n1821014682","loc":[-85.5915912,41.9857767]},"n1821014684":{"id":"n1821014684","loc":[-85.6022288,41.977897]},"n1821014687":{"id":"n1821014687","loc":[-85.5933024,41.9846362]},"n1821014688":{"id":"n1821014688","loc":[-85.5846871,41.9956169]},"n1821014689":{"id":"n1821014689","loc":[-85.5898209,41.99037]},"n1821014691":{"id":"n1821014691","loc":[-85.5448939,42.0149261]},"n1821014692":{"id":"n1821014692","loc":[-85.5977763,41.9786348]},"n1821014694":{"id":"n1821014694","loc":[-85.5767706,42.0034523]},"n1821014695":{"id":"n1821014695","loc":[-85.6103559,41.9726766]},"n1821014697":{"id":"n1821014697","loc":[-85.5922134,41.9809876]},"n1821014698":{"id":"n1821014698","loc":[-85.5935277,41.9831728]},"n1821014700":{"id":"n1821014700","loc":[-85.5674674,42.0078273]},"n1821014703":{"id":"n1821014703","loc":[-85.6021,41.9778053]},"n1821014704":{"id":"n1821014704","loc":[-85.5756763,42.0053737]},"n1821014705":{"id":"n1821014705","loc":[-85.5887695,41.9895207]},"n1821014707":{"id":"n1821014707","loc":[-85.6061073,41.9746866]},"n1821014708":{"id":"n1821014708","loc":[-85.6033446,41.9751692]},"n1821014710":{"id":"n1821014710","loc":[-85.5180986,42.0322332]},"n1821014711":{"id":"n1821014711","loc":[-85.543365,42.0163569]},"n1821014712":{"id":"n1821014712","loc":[-85.6030656,41.9753646]},"n1821014713":{"id":"n1821014713","loc":[-85.6104417,41.9704792]},"n1821014714":{"id":"n1821014714","loc":[-85.5205716,42.030998]},"n1821014716":{"id":"n1821014716","loc":[-85.516382,42.032536]},"n1821014717":{"id":"n1821014717","loc":[-85.5932863,41.9820882]},"n1821014718":{"id":"n1821014718","loc":[-85.5361928,42.0194974]},"n1821014720":{"id":"n1821014720","loc":[-85.6011613,41.9773586]},"n1821014721":{"id":"n1821014721","loc":[-85.554287,42.0109124]},"n1821014722":{"id":"n1821014722","loc":[-85.5577524,42.0103425]},"n1821014725":{"id":"n1821014725","loc":[-85.5867256,41.9921004]},"n1821014726":{"id":"n1821014726","loc":[-85.5856045,41.9968807]},"n1821014727":{"id":"n1821014727","loc":[-85.5545445,42.0106454]},"n1821014728":{"id":"n1821014728","loc":[-85.5923797,41.9842534]},"n1821014729":{"id":"n1821014729","loc":[-85.5696346,42.0081462]},"n1821014730":{"id":"n1821014730","loc":[-85.5998322,41.9786884]},"n1821014735":{"id":"n1821014735","loc":[-85.5337426,42.0218266]},"n1821014736":{"id":"n1821014736","loc":[-85.5847944,41.994672]},"n1821014740":{"id":"n1821014740","loc":[-85.5315271,42.0238669]},"n1821014741":{"id":"n1821014741","loc":[-85.5248846,42.027085]},"n1821014742":{"id":"n1821014742","loc":[-85.5853376,41.997018]},"n1821014743":{"id":"n1821014743","loc":[-85.5894883,41.988811]},"n1821014745":{"id":"n1821014745","loc":[-85.6095311,41.9726226]},"n1821014746":{"id":"n1821014746","loc":[-85.5531511,42.0133416]},"n1821014747":{"id":"n1821014747","loc":[-85.5735882,42.007058]},"n1821014749":{"id":"n1821014749","loc":[-85.5428554,42.0164366]},"n1821014751":{"id":"n1821014751","loc":[-85.5395255,42.0186304]},"n1821014752":{"id":"n1821014752","loc":[-85.571378,42.0083176]},"n1821014754":{"id":"n1821014754","loc":[-85.5541918,42.0113925]},"n1821014755":{"id":"n1821014755","loc":[-85.5278029,42.0250806]},"n1821014756":{"id":"n1821014756","loc":[-85.5936725,41.9827102]},"n1821014757":{"id":"n1821014757","loc":[-85.5176266,42.0346677]},"n1821014758":{"id":"n1821014758","loc":[-85.6096692,41.9714245]},"n1821014759":{"id":"n1821014759","loc":[-85.5770321,42.0034266]},"n1821014761":{"id":"n1821014761","loc":[-85.5988921,41.9779369]},"n1821014762":{"id":"n1821014762","loc":[-85.5811788,42.0024499]},"n1821014763":{"id":"n1821014763","loc":[-85.5154003,42.0381101]},"n1821014764":{"id":"n1821014764","loc":[-85.5155827,42.0374089]},"n1821014765":{"id":"n1821014765","loc":[-85.5891249,41.9884978]},"n1821014766":{"id":"n1821014766","loc":[-85.5313863,42.0238293]},"n1821014768":{"id":"n1821014768","loc":[-85.593297,41.9833363]},"n1821014769":{"id":"n1821014769","loc":[-85.5849446,41.9957245]},"n1821014770":{"id":"n1821014770","loc":[-85.5537774,42.0130847]},"n1821014771":{"id":"n1821014771","loc":[-85.6111766,41.9706069]},"n1821014772":{"id":"n1821014772","loc":[-85.5585477,42.008989]},"n1821014774":{"id":"n1821014774","loc":[-85.5928142,41.9852623]},"n1821014777":{"id":"n1821014777","loc":[-85.5891933,41.9882608]},"n1821014778":{"id":"n1821014778","loc":[-85.5926909,41.9817532]},"n1821014779":{"id":"n1821014779","loc":[-85.5260272,42.0252201]},"n1821014781":{"id":"n1821014781","loc":[-85.5894615,41.9950468]},"n1821014782":{"id":"n1821014782","loc":[-85.5461063,42.0143242]},"n1821014783":{"id":"n1821014783","loc":[-85.5711527,42.0085886]},"n1821014784":{"id":"n1821014784","loc":[-85.5329379,42.0218624]},"n1821014786":{"id":"n1821014786","loc":[-85.583047,42.0020252]},"n1821014787":{"id":"n1821014787","loc":[-85.5758962,42.0054095]},"n1821014788":{"id":"n1821014788","loc":[-85.5626354,42.0077733]},"n1821014789":{"id":"n1821014789","loc":[-85.6029852,41.9755999]},"n1821014790":{"id":"n1821014790","loc":[-85.5892362,41.9886755]},"n1821014791":{"id":"n1821014791","loc":[-85.5157597,42.0372017]},"n1821014793":{"id":"n1821014793","loc":[-85.6054582,41.9751094]},"n1821014794":{"id":"n1821014794","loc":[-85.5986936,41.9778412]},"n1821014795":{"id":"n1821014795","loc":[-85.5880775,41.98976]},"n1821014796":{"id":"n1821014796","loc":[-85.5858727,41.9963624]},"n1821014798":{"id":"n1821014798","loc":[-85.5792543,42.0035958]},"n1821014799":{"id":"n1821014799","loc":[-85.5921665,41.9838326]},"n1821014801":{"id":"n1821014801","loc":[-85.599214,41.9782599]},"n1821014802":{"id":"n1821014802","loc":[-85.5571905,42.0090967]},"n1821014803":{"id":"n1821014803","loc":[-85.5426891,42.0173612]},"n1821014804":{"id":"n1821014804","loc":[-85.5889626,41.9896404]},"n1821014805":{"id":"n1821014805","loc":[-85.5491264,42.0141648]},"n1821014806":{"id":"n1821014806","loc":[-85.5618897,42.0072631]},"n1821014808":{"id":"n1821014808","loc":[-85.5573501,42.0109802]},"n1821014809":{"id":"n1821014809","loc":[-85.5983463,41.9778031]},"n1821014810":{"id":"n1821014810","loc":[-85.5885173,41.9895128]},"n1821014811":{"id":"n1821014811","loc":[-85.6084998,41.9721143]},"n1821014812":{"id":"n1821014812","loc":[-85.5737598,42.0056389]},"n1821014814":{"id":"n1821014814","loc":[-85.5542173,42.0118132]},"n1821014816":{"id":"n1821014816","loc":[-85.5277868,42.024451]},"n1821014817":{"id":"n1821014817","loc":[-85.5403999,42.0191724]},"n1821014819":{"id":"n1821014819","loc":[-85.5983879,41.9791452]},"n1821014820":{"id":"n1821014820","loc":[-85.5891302,41.9897578]},"n1821014822":{"id":"n1821014822","loc":[-85.5930731,41.9805108]},"n1821014824":{"id":"n1821014824","loc":[-85.515395,42.0378471]},"n1821014825":{"id":"n1821014825","loc":[-85.5352755,42.0205136]},"n1821014826":{"id":"n1821014826","loc":[-85.5502744,42.0133398]},"n1821014828":{"id":"n1821014828","loc":[-85.5701295,42.0088256]},"n1821014830":{"id":"n1821014830","loc":[-85.5888929,41.9953099]},"n1821014832":{"id":"n1821014832","loc":[-85.5880077,41.9901547]},"n1821014833":{"id":"n1821014833","loc":[-85.5451192,42.0157072]},"n1821014834":{"id":"n1821014834","loc":[-85.6096478,41.9711932]},"n1821014835":{"id":"n1821014835","loc":[-85.5806424,42.0026532]},"n1821014836":{"id":"n1821014836","loc":[-85.5911674,41.9868732]},"n1821014838":{"id":"n1821014838","loc":[-85.5930302,41.9836571]},"n1821014839":{"id":"n1821014839","loc":[-85.588925,41.9938148]},"n1821014840":{"id":"n1821014840","loc":[-85.6111874,41.9705311]},"n1821014841":{"id":"n1821014841","loc":[-85.5680843,42.0075842]},"n1821014842":{"id":"n1821014842","loc":[-85.6012793,41.9775062]},"n1821014843":{"id":"n1821014843","loc":[-85.5855562,41.9989777]},"n1821014844":{"id":"n1821014844","loc":[-85.5506137,42.0131662]},"n1821014845":{"id":"n1821014845","loc":[-85.5270049,42.025457]},"n1821014846":{"id":"n1821014846","loc":[-85.5257054,42.025244]},"n1821014847":{"id":"n1821014847","loc":[-85.6011184,41.9771832]},"n1821014848":{"id":"n1821014848","loc":[-85.515534,42.0389234]},"n1821014850":{"id":"n1821014850","loc":[-85.5847032,42.0010347]},"n1821014853":{"id":"n1821014853","loc":[-85.5361499,42.019063]},"n1821014854":{"id":"n1821014854","loc":[-85.5439176,42.0165721]},"n1821014855":{"id":"n1821014855","loc":[-85.5838825,42.0017284]},"n1821014857":{"id":"n1821014857","loc":[-85.5542173,42.0122317]},"n1821014859":{"id":"n1821014859","loc":[-85.5708201,42.0089195]},"n1821014860":{"id":"n1821014860","loc":[-85.5844833,41.9954415]},"n1821014862":{"id":"n1821014862","loc":[-85.5223204,42.0295396]},"n1821014863":{"id":"n1821014863","loc":[-85.5777898,42.0035918]},"n1821014864":{"id":"n1821014864","loc":[-85.591044,41.9898078]},"n1821014865":{"id":"n1821014865","loc":[-85.5973204,41.980182]},"n1821014866":{"id":"n1821014866","loc":[-85.5699578,42.0085825]},"n1821014867":{"id":"n1821014867","loc":[-85.5210598,42.0305278]},"n1821014868":{"id":"n1821014868","loc":[-85.5929108,41.9819008]},"n1821014869":{"id":"n1821014869","loc":[-85.5279799,42.0242995]},"n1821014870":{"id":"n1821014870","loc":[-85.5196114,42.0320539]},"n1821014871":{"id":"n1821014871","loc":[-85.5785449,42.0040883]},"n1821014872":{"id":"n1821014872","loc":[-85.588292,41.9895766]},"n1821014873":{"id":"n1821014873","loc":[-85.5160172,42.0331775]},"n1821014874":{"id":"n1821014874","loc":[-85.5688849,42.0077016]},"n1821014876":{"id":"n1821014876","loc":[-85.5857976,41.9996036]},"n1821014879":{"id":"n1821014879","loc":[-85.5990906,41.9780765]},"n1821014881":{"id":"n1821014881","loc":[-85.5483647,42.0144279]},"n1821014883":{"id":"n1821014883","loc":[-85.5691209,42.0077972]},"n1821014885":{"id":"n1821014885","loc":[-85.6076844,41.9721103]},"n1821014886":{"id":"n1821014886","loc":[-85.6015489,41.9766147]},"n1821014887":{"id":"n1821014887","loc":[-85.574822,42.0052802]},"n1821014888":{"id":"n1821014888","loc":[-85.5880024,41.9899593]},"n1821014890":{"id":"n1821014890","loc":[-85.5909421,41.9893772]},"n1821014892":{"id":"n1821014892","loc":[-85.5497326,42.0138141]},"n1821014893":{"id":"n1821014893","loc":[-85.5167106,42.0357811]},"n1821014895":{"id":"n1821014895","loc":[-85.5844404,41.9952501]},"n1821014896":{"id":"n1821014896","loc":[-85.5362465,42.0192662]},"n1821014898":{"id":"n1821014898","loc":[-85.5906095,41.9889147]},"n1821014899":{"id":"n1821014899","loc":[-85.5590667,42.0089354]},"n1821014900":{"id":"n1821014900","loc":[-85.5921598,41.9844209]},"n1821014902":{"id":"n1821014902","loc":[-85.5778971,42.0039266]},"n1821014903":{"id":"n1821014903","loc":[-85.603012,41.9761981]},"n1821014904":{"id":"n1821014904","loc":[-85.6108977,41.9706787]},"n1821014905":{"id":"n1821014905","loc":[-85.5685738,42.0076139]},"n1821014906":{"id":"n1821014906","loc":[-85.5392787,42.0186304]},"n1821014907":{"id":"n1821014907","loc":[-85.5227885,42.0274972]},"n1821014908":{"id":"n1821014908","loc":[-85.5857547,41.9961431]},"n1821014910":{"id":"n1821014910","loc":[-85.5610354,42.0072812]},"n1821014911":{"id":"n1821014911","loc":[-85.5209632,42.0308705]},"n1821014912":{"id":"n1821014912","loc":[-85.5709757,42.0087959]},"n1821014913":{"id":"n1821014913","loc":[-85.59231,41.9839344]},"n1821014914":{"id":"n1821014914","loc":[-85.5375245,42.0185865]},"n1821014916":{"id":"n1821014916","loc":[-85.5901548,41.9839841]},"n1821014917":{"id":"n1821014917","loc":[-85.5611213,42.0086405]},"n1821014918":{"id":"n1821014918","loc":[-85.5360426,42.0198122]},"n1821014919":{"id":"n1821014919","loc":[-85.5862817,41.9948691]},"n1821014921":{"id":"n1821014921","loc":[-85.5469807,42.0144438]},"n1821014922":{"id":"n1821014922","loc":[-85.5761309,42.0053838]},"n1821014924":{"id":"n1821014924","loc":[-85.516264,42.0332971]},"n1821014925":{"id":"n1821014925","loc":[-85.5277224,42.0246661]},"n1821014926":{"id":"n1821014926","loc":[-85.5980016,41.9798231]},"n1821014928":{"id":"n1821014928","loc":[-85.5924548,41.9806965]},"n1821014930":{"id":"n1821014930","loc":[-85.5899121,41.985023]},"n1821014931":{"id":"n1821014931","loc":[-85.5706015,42.0089492]},"n1821014932":{"id":"n1821014932","loc":[-85.515926,42.033046]},"n1821014933":{"id":"n1821014933","loc":[-85.5982377,41.9796796]},"n1821014936":{"id":"n1821014936","loc":[-85.5475721,42.0145253]},"n1821014938":{"id":"n1821014938","loc":[-85.5895701,41.9902323]},"n1821014939":{"id":"n1821014939","loc":[-85.6030495,41.9759947]},"n1821014942":{"id":"n1821014942","loc":[-85.6094721,41.9724989]},"n1821014944":{"id":"n1821014944","loc":[-85.5921973,41.9811112]},"n1821014945":{"id":"n1821014945","loc":[-85.5223526,42.0291332]},"n1821014946":{"id":"n1821014946","loc":[-85.5965103,41.9808998]},"n1821014948":{"id":"n1821014948","loc":[-85.517766,42.0349227]},"n1821014950":{"id":"n1821014950","loc":[-85.5889894,41.990996]},"n1821014951":{"id":"n1821014951","loc":[-85.5601932,42.0092902]},"n1821014954":{"id":"n1821014954","loc":[-85.6028135,41.9764055]},"n1821014955":{"id":"n1821014955","loc":[-85.5520621,42.0130666]},"n1821014956":{"id":"n1821014956","loc":[-85.593002,41.9839344]},"n1821014957":{"id":"n1821014957","loc":[-85.515926,42.0369666]},"n1821014960":{"id":"n1821014960","loc":[-85.5761255,42.003877]},"n1821014961":{"id":"n1821014961","loc":[-85.5716355,42.007911]},"n1821014962":{"id":"n1821014962","loc":[-85.5575378,42.0109045]},"n1821014963":{"id":"n1821014963","loc":[-85.5735667,42.0068188]},"n1821014964":{"id":"n1821014964","loc":[-85.5915214,41.9865861]},"n1821014965":{"id":"n1821014965","loc":[-85.5866344,41.9923157]},"n1821014967":{"id":"n1821014967","loc":[-85.5283138,42.0242256]},"n1821014968":{"id":"n1821014968","loc":[-85.5177875,42.0355801]},"n1821014969":{"id":"n1821014969","loc":[-85.548071,42.0144934]},"n1821014972":{"id":"n1821014972","loc":[-85.5611159,42.0088557]},"n1821014973":{"id":"n1821014973","loc":[-85.541686,42.0188757]},"n1821014974":{"id":"n1821014974","loc":[-85.5917628,41.9862631]},"n1821014975":{"id":"n1821014975","loc":[-85.5854864,41.9959478]},"n1821014977":{"id":"n1821014977","loc":[-85.609102,41.9722317]},"n1821014980":{"id":"n1821014980","loc":[-85.5761202,42.0042438]},"n1821014982":{"id":"n1821014982","loc":[-85.5465944,42.0143601]},"n1821014983":{"id":"n1821014983","loc":[-85.5173261,42.0342732]},"n1821014984":{"id":"n1821014984","loc":[-85.5897297,41.9888509]},"n1821014985":{"id":"n1821014985","loc":[-85.5856688,41.999181]},"n1821014986":{"id":"n1821014986","loc":[-85.5344011,42.0217251]},"n1821014987":{"id":"n1821014987","loc":[-85.601467,41.9768203]},"n1821014988":{"id":"n1821014988","loc":[-85.5457254,42.0165123]},"n1821014989":{"id":"n1821014989","loc":[-85.6023482,41.9784332]},"n1821014991":{"id":"n1821014991","loc":[-85.5361606,42.01823]},"n1821014992":{"id":"n1821014992","loc":[-85.5178465,42.0351139]},"n1821014995":{"id":"n1821014995","loc":[-85.5634293,42.0078092]},"n1821014996":{"id":"n1821014996","loc":[-85.573497,42.0072015]},"n1821014997":{"id":"n1821014997","loc":[-85.5976328,41.9799725]},"n1821014998":{"id":"n1821014998","loc":[-85.5210651,42.0303166]},"n1821015003":{"id":"n1821015003","loc":[-85.5222131,42.0288064]},"n1821015004":{"id":"n1821015004","loc":[-85.5897941,41.984405]},"n1821015005":{"id":"n1821015005","loc":[-85.5975725,41.9776099]},"n1821015006":{"id":"n1821015006","loc":[-85.5765708,42.0034903]},"n1821015007":{"id":"n1821015007","loc":[-85.5250187,42.026559]},"n1821015009":{"id":"n1821015009","loc":[-85.5426998,42.0166279]},"n1821015010":{"id":"n1821015010","loc":[-85.5957606,41.9806584]},"n1821015011":{"id":"n1821015011","loc":[-85.5262753,42.0252497]},"n1821015012":{"id":"n1821015012","loc":[-85.5266455,42.0253374]},"n1821015014":{"id":"n1821015014","loc":[-85.5515632,42.0130187]},"n1821015015":{"id":"n1821015015","loc":[-85.6024058,41.9765212]},"n1821015017":{"id":"n1821015017","loc":[-85.5175032,42.0357156]},"n1821015018":{"id":"n1821015018","loc":[-85.5302718,42.0236039]},"n1821015019":{"id":"n1821015019","loc":[-85.6024005,41.9782759]},"n1821015020":{"id":"n1821015020","loc":[-85.5907758,41.9890821]},"n1821015021":{"id":"n1821015021","loc":[-85.6019445,41.9777215]},"n1821015022":{"id":"n1821015022","loc":[-85.5942854,41.9800881]},"n1821015024":{"id":"n1821015024","loc":[-85.5325826,42.0222711]},"n1821015029":{"id":"n1821015029","loc":[-85.555093,42.0105316]},"n1821015033":{"id":"n1821015033","loc":[-85.5249704,42.0270372]},"n1821015034":{"id":"n1821015034","loc":[-85.5243965,42.0272205]},"n1821015038":{"id":"n1821015038","loc":[-85.5413426,42.0190749]},"n1821015039":{"id":"n1821015039","loc":[-85.5920431,41.9848175]},"n1821015041":{"id":"n1821015041","loc":[-85.5577685,42.0106015]},"n1821015042":{"id":"n1821015042","loc":[-85.5453606,42.0158866]},"n1821015045":{"id":"n1821015045","loc":[-85.5333228,42.0217889]},"n1821015046":{"id":"n1821015046","loc":[-85.5426891,42.0175924]},"n1821015048":{"id":"n1821015048","loc":[-85.5886836,41.9936474]},"n1821015050":{"id":"n1821015050","loc":[-85.6001152,41.9786467]},"n1821015051":{"id":"n1821015051","loc":[-85.6094064,41.9723655]},"n1821015053":{"id":"n1821015053","loc":[-85.605721,41.9749738]},"n1821015055":{"id":"n1821015055","loc":[-85.6106791,41.9705048]},"n1821015057":{"id":"n1821015057","loc":[-85.5210437,42.0307071]},"n1821015059":{"id":"n1821015059","loc":[-85.5995694,41.9786725]},"n1821015060":{"id":"n1821015060","loc":[-85.5371638,42.0182938]},"n1821015062":{"id":"n1821015062","loc":[-85.6111766,41.9704593]},"n1821015065":{"id":"n1821015065","loc":[-85.577704,42.0034921]},"n1821015067":{"id":"n1821015067","loc":[-85.5570067,42.0093699]},"n1821015068":{"id":"n1821015068","loc":[-85.5920364,41.9845525]},"n1821015069":{"id":"n1821015069","loc":[-85.5252065,42.0253954]},"n1821015072":{"id":"n1821015072","loc":[-85.5664159,42.0088517]},"n1821015073":{"id":"n1821015073","loc":[-85.5880399,41.991905]},"n1821015075":{"id":"n1821015075","loc":[-85.6099871,41.9727861]},"n1821015076":{"id":"n1821015076","loc":[-85.5319603,42.0231478]},"n1821015078":{"id":"n1821015078","loc":[-85.6036088,41.9751112]},"n1821015080":{"id":"n1821015080","loc":[-85.5983128,41.9789179]},"n1821015082":{"id":"n1821015082","loc":[-85.5614069,42.0071395]},"n1821015083":{"id":"n1821015083","loc":[-85.60968,41.9709738]},"n1821015086":{"id":"n1821015086","loc":[-85.5914195,41.9837351]},"n1821015087":{"id":"n1821015087","loc":[-85.5895473,41.9948036]},"n1821015090":{"id":"n1821015090","loc":[-85.5929913,41.9851905]},"n1821015093":{"id":"n1821015093","loc":[-85.5907396,41.9838485]},"n1821015095":{"id":"n1821015095","loc":[-85.5893864,41.9880176]},"n1821015096":{"id":"n1821015096","loc":[-85.5788024,42.0039807]},"n1821015097":{"id":"n1821015097","loc":[-85.5630592,42.0078411]},"n1821015098":{"id":"n1821015098","loc":[-85.5350609,42.0211274]},"n1821015099":{"id":"n1821015099","loc":[-85.5967195,41.9808679]},"n1821015100":{"id":"n1821015100","loc":[-85.5666734,42.0088119]},"n1821015101":{"id":"n1821015101","loc":[-85.564694,42.0077675]},"n1821015103":{"id":"n1821015103","loc":[-85.6066544,41.9726527]},"n1821015104":{"id":"n1821015104","loc":[-85.6011827,41.9769838]},"n1821015105":{"id":"n1821015105","loc":[-85.5972131,41.9776697]},"n1821015106":{"id":"n1821015106","loc":[-85.5880828,41.9903341]},"n1821015107":{"id":"n1821015107","loc":[-85.5510268,42.0130626]},"n1821015108":{"id":"n1821015108","loc":[-85.6102164,41.970543]},"n1821015109":{"id":"n1821015109","loc":[-85.5905344,41.9853899]},"n1821015111":{"id":"n1821015111","loc":[-85.5888821,41.9913429]},"n1821015112":{"id":"n1821015112","loc":[-85.606295,41.9741921]},"n1821015114":{"id":"n1821015114","loc":[-85.5969556,41.9807443]},"n1821015115":{"id":"n1821015115","loc":[-85.5882223,41.9934081]},"n1821015116":{"id":"n1821015116","loc":[-85.6104471,41.9724971]},"n1821015118":{"id":"n1821015118","loc":[-85.5406091,42.0192162]},"n1821015120":{"id":"n1821015120","loc":[-85.589955,41.9888429]},"n1821015121":{"id":"n1821015121","loc":[-85.5598821,42.0092304]},"n1821015122":{"id":"n1821015122","loc":[-85.545598,42.0144097]},"n1821015123":{"id":"n1821015123","loc":[-85.5649528,42.0079965]},"n1821015125":{"id":"n1821015125","loc":[-85.5883993,41.9917814]},"n1821015126":{"id":"n1821015126","loc":[-85.5295785,42.0239967]},"n1821015129":{"id":"n1821015129","loc":[-85.5648723,42.0078809]},"n1821015132":{"id":"n1821015132","loc":[-85.564989,42.0081103]},"n1821015133":{"id":"n1821015133","loc":[-85.5946127,41.9800841]},"n1821015134":{"id":"n1821015134","loc":[-85.583448,42.0019078]},"n1821015135":{"id":"n1821015135","loc":[-85.5905934,41.9871842]},"n1821015137":{"id":"n1821015137","loc":[-85.610608,41.9704752]},"n1821015138":{"id":"n1821015138","loc":[-85.5752257,42.0052939]},"n1821015139":{"id":"n1821015139","loc":[-85.5893864,41.9943491]},"n1821015140":{"id":"n1821015140","loc":[-85.5426247,42.0169866]},"n1821015141":{"id":"n1821015141","loc":[-85.562001,42.0074526]},"n1821015142":{"id":"n1821015142","loc":[-85.5212046,42.0301094]},"n1821015143":{"id":"n1821015143","loc":[-85.602214,41.9784531]},"n1821015144":{"id":"n1821015144","loc":[-85.5858687,41.9948293]},"n1821015145":{"id":"n1821015145","loc":[-85.5608477,42.0074805]},"n1821015146":{"id":"n1821015146","loc":[-85.5651607,42.0083614]},"n1821015147":{"id":"n1821015147","loc":[-85.5288288,42.0242495]},"n1821015149":{"id":"n1821015149","loc":[-85.5450334,42.0146989]},"n1821015151":{"id":"n1821015151","loc":[-85.5578275,42.0092304]},"n1821015154":{"id":"n1821015154","loc":[-85.6056634,41.9724511]},"n1821015155":{"id":"n1821015155","loc":[-85.5902179,41.9852742]},"n1821015156":{"id":"n1821015156","loc":[-85.5156256,42.0387157]},"n1821015157":{"id":"n1821015157","loc":[-85.5734433,42.0059459]},"n1821015158":{"id":"n1821015158","loc":[-85.6050773,41.9731273]},"n1821015160":{"id":"n1821015160","loc":[-85.5223419,42.0275233]},"n1821015163":{"id":"n1821015163","loc":[-85.6053562,41.972525]},"n1821015164":{"id":"n1821015164","loc":[-85.5850412,41.9946082]},"n1821015165":{"id":"n1821015165","loc":[-85.5359031,42.0186326]},"n1821015166":{"id":"n1821015166","loc":[-85.5608745,42.0077635]},"n1821015169":{"id":"n1821015169","loc":[-85.572876,42.0073189]},"n1821015171":{"id":"n1821015171","loc":[-85.5875424,41.9919188]},"n1821015172":{"id":"n1821015172","loc":[-85.5240116,42.0272581]},"n1821015173":{"id":"n1821015173","loc":[-85.5318369,42.0236818]},"n1821015174":{"id":"n1821015174","loc":[-85.566888,42.0086923]},"n1821015175":{"id":"n1821015175","loc":[-85.5931522,41.9850669]},"n1821015176":{"id":"n1821015176","loc":[-85.5604842,42.0093199]},"n1821015177":{"id":"n1821015177","loc":[-85.5868168,41.9927543]},"n1821015178":{"id":"n1821015178","loc":[-85.6052275,41.9732549]},"n1821015179":{"id":"n1821015179","loc":[-85.5910118,41.9900431]},"n1821015182":{"id":"n1821015182","loc":[-85.5610032,42.0082897]},"n1821015183":{"id":"n1821015183","loc":[-85.5425443,42.0179431]},"n1821015184":{"id":"n1821015184","loc":[-85.5843277,42.0014055]},"n1821015186":{"id":"n1821015186","loc":[-85.5733307,42.0063564]},"n1821015188":{"id":"n1821015188","loc":[-85.5277385,42.0248694]},"n1821015189":{"id":"n1821015189","loc":[-85.5558427,42.0108168]},"n1821015190":{"id":"n1821015190","loc":[-85.5650587,42.0082618]},"n1821015191":{"id":"n1821015191","loc":[-85.5660351,42.0088278]},"n1821015192":{"id":"n1821015192","loc":[-85.5849768,41.9980049]},"n1821015194":{"id":"n1821015194","loc":[-85.5359139,42.0188199]},"n1821015195":{"id":"n1821015195","loc":[-85.593238,41.9849194]},"n1821015197":{"id":"n1821015197","loc":[-85.5850841,41.9983239]},"n1821015199":{"id":"n1821015199","loc":[-85.5983396,41.9794283]},"n1821015204":{"id":"n1821015204","loc":[-85.5452801,42.0145355]},"n1821015205":{"id":"n1821015205","loc":[-85.5340685,42.0218407]},"n1821015207":{"id":"n1821015207","loc":[-85.5773272,42.0034186]},"n1821015209":{"id":"n1821015209","loc":[-85.5535212,42.0132419]},"n1821015211":{"id":"n1821015211","loc":[-85.6107703,41.9706045]},"n1821015212":{"id":"n1821015212","loc":[-85.6030066,41.9758193]},"n1821015213":{"id":"n1821015213","loc":[-85.5359943,42.0184213]},"n1821015214":{"id":"n1821015214","loc":[-85.5922993,41.9813305]},"n1821015215":{"id":"n1821015215","loc":[-85.5672689,42.0080465]},"n1821015217":{"id":"n1821015217","loc":[-85.5160494,42.0365682]},"n1821015218":{"id":"n1821015218","loc":[-85.5401142,42.0190351]},"n1821015219":{"id":"n1821015219","loc":[-85.5607632,42.0092282]},"n1821015220":{"id":"n1821015220","loc":[-85.5866197,41.9947894]},"n1821015221":{"id":"n1821015221","loc":[-85.6017889,41.9765132]},"n1821015222":{"id":"n1821015222","loc":[-85.5595978,42.009059]},"n1821015226":{"id":"n1821015226","loc":[-85.5871494,41.9929018]},"n1821015227":{"id":"n1821015227","loc":[-85.5857708,41.9998866]},"n1821015228":{"id":"n1821015228","loc":[-85.5317135,42.0238094]},"n1821015231":{"id":"n1821015231","loc":[-85.5733521,42.0061372]},"n1821015233":{"id":"n1821015233","loc":[-85.5855991,42.0001936]},"n1821015234":{"id":"n1821015234","loc":[-85.5213924,42.029962]},"n1821015235":{"id":"n1821015235","loc":[-85.6052221,41.9726567]},"n1821015236":{"id":"n1821015236","loc":[-85.5763723,42.0035422]},"n1821015237":{"id":"n1821015237","loc":[-85.5858512,41.9966215]},"n1821015238":{"id":"n1821015238","loc":[-85.567061,42.008439]},"n1821015239":{"id":"n1821015239","loc":[-85.5250563,42.0269057]},"n1821015240":{"id":"n1821015240","loc":[-85.5347551,42.0214263]},"n1821015241":{"id":"n1821015241","loc":[-85.6098463,41.9707066]},"n1821015242":{"id":"n1821015242","loc":[-85.5676927,42.0076519]},"n1821015243":{"id":"n1821015243","loc":[-85.516775,42.0322669]},"n1821015244":{"id":"n1821015244","loc":[-85.5762275,42.0036538]},"n1821015245":{"id":"n1821015245","loc":[-85.5583639,42.0090949]},"n1821015246":{"id":"n1821015246","loc":[-85.5554041,42.0106432]},"n1821015247":{"id":"n1821015247","loc":[-85.5973364,41.9776099]},"n1821015248":{"id":"n1821015248","loc":[-85.6098945,41.9717513]},"n1821015249":{"id":"n1821015249","loc":[-85.6045315,41.9751511]},"n1821015250":{"id":"n1821015250","loc":[-85.5579938,42.0092264]},"n1821015253":{"id":"n1821015253","loc":[-85.6058873,41.9724652]},"n1821015254":{"id":"n1821015254","loc":[-85.5869456,41.9947517]},"n1821015255":{"id":"n1821015255","loc":[-85.5936565,41.9823713]},"n1821015256":{"id":"n1821015256","loc":[-85.5218269,42.0278102]},"n1821015258":{"id":"n1821015258","loc":[-85.5887802,41.9905534]},"n1821015259":{"id":"n1821015259","loc":[-85.5901924,41.9904515]},"n1821015263":{"id":"n1821015263","loc":[-85.5249222,42.0255787]},"n1821015265":{"id":"n1821015265","loc":[-85.5175206,42.0321672]},"n1821015266":{"id":"n1821015266","loc":[-85.5275722,42.0254034]},"n1821015267":{"id":"n1821015267","loc":[-85.6016226,41.9765451]},"n1821015269":{"id":"n1821015269","loc":[-85.5569316,42.011032]},"n1821015271":{"id":"n1821015271","loc":[-85.6010714,41.9785209]},"n1821015272":{"id":"n1821015272","loc":[-85.6050666,41.9729917]},"n1821015273":{"id":"n1821015273","loc":[-85.5891235,41.99529]},"n1821015274":{"id":"n1821015274","loc":[-85.515454,42.0376439]},"n1821015276":{"id":"n1821015276","loc":[-85.5776021,42.0034443]},"n1821015277":{"id":"n1821015277","loc":[-85.6041707,41.9751453]},"n1821015278":{"id":"n1821015278","loc":[-85.5444701,42.0167435]},"n1821015280":{"id":"n1821015280","loc":[-85.5923274,41.9852202]},"n1821015283":{"id":"n1821015283","loc":[-85.5893649,41.9900271]},"n1821015284":{"id":"n1821015284","loc":[-85.5933453,41.9804412]},"n1821015285":{"id":"n1821015285","loc":[-85.5247237,42.026017]},"n1821015286":{"id":"n1821015286","loc":[-85.5286182,42.0242477]},"n1821015287":{"id":"n1821015287","loc":[-85.5904003,41.9888549]},"n1821015288":{"id":"n1821015288","loc":[-85.6062146,41.9739369]},"n1821015290":{"id":"n1821015290","loc":[-85.5762596,42.0052602]},"n1821015292":{"id":"n1821015292","loc":[-85.5849715,41.9975465]},"n1821015293":{"id":"n1821015293","loc":[-85.585229,42.0006241]},"n1821015294":{"id":"n1821015294","loc":[-85.5926922,41.9805946]},"n1821015295":{"id":"n1821015295","loc":[-85.5703387,42.0089133]},"n1821015299":{"id":"n1821015299","loc":[-85.5789955,42.0038611]},"n1821015301":{"id":"n1821015301","loc":[-85.6072888,41.9721918]},"n1821015302":{"id":"n1821015302","loc":[-85.5356349,42.0200992]},"n1821015304":{"id":"n1821015304","loc":[-85.5891772,41.994066]},"n1821015306":{"id":"n1821015306","loc":[-85.606295,41.9744952]},"n1821015307":{"id":"n1821015307","loc":[-85.538871,42.0186583]},"n1821015308":{"id":"n1821015308","loc":[-85.587997,41.994971]},"n1821015311":{"id":"n1821015311","loc":[-85.606869,41.9725809]},"n1821015312":{"id":"n1821015312","loc":[-85.5171974,42.0339943]},"n1821015314":{"id":"n1821015314","loc":[-85.5327435,42.0220479]},"n1821015315":{"id":"n1821015315","loc":[-85.5383439,42.0187282]},"n1821015316":{"id":"n1821015316","loc":[-85.5248095,42.0263119]},"n1821015318":{"id":"n1821015318","loc":[-85.5732502,42.0073051]},"n1821015319":{"id":"n1821015319","loc":[-85.5924226,41.9852663]},"n1821015321":{"id":"n1821015321","loc":[-85.5179001,42.0353052]},"n1821015322":{"id":"n1821015322","loc":[-85.5456771,42.0162413]},"n1821015323":{"id":"n1821015323","loc":[-85.5936618,41.9829096]},"n1821015325":{"id":"n1821015325","loc":[-85.5656931,42.0086582]},"n1821015326":{"id":"n1821015326","loc":[-85.5448456,42.0150975]},"n1821015327":{"id":"n1821015327","loc":[-85.5220039,42.027615]},"n1821015329":{"id":"n1821015329","loc":[-85.517884,42.0354885]},"n1821015330":{"id":"n1821015330","loc":[-85.5576666,42.0101671]},"n1821015332":{"id":"n1821015332","loc":[-85.5368754,42.0181402]},"n1821015333":{"id":"n1821015333","loc":[-85.5367078,42.0181145]},"n1821015334":{"id":"n1821015334","loc":[-85.5903909,41.9904316]},"n1821015335":{"id":"n1821015335","loc":[-85.5430767,42.0163587]},"n1821015336":{"id":"n1821015336","loc":[-85.5277492,42.0252878]},"n1821015337":{"id":"n1821015337","loc":[-85.5312146,42.0236898]},"n1821015338":{"id":"n1821015338","loc":[-85.5886568,41.991614]},"n1821015339":{"id":"n1821015339","loc":[-85.5782498,42.0040883]},"n1821015341":{"id":"n1821015341","loc":[-85.562233,42.0076457]},"n1821015342":{"id":"n1821015342","loc":[-85.588626,41.9952479]},"n1821015343":{"id":"n1821015343","loc":[-85.5762865,42.005033]},"n1821015344":{"id":"n1821015344","loc":[-85.5850841,41.9971478]},"n1821015346":{"id":"n1821015346","loc":[-85.5643144,42.0076936]},"n1821015347":{"id":"n1821015347","loc":[-85.5164893,42.0359467]},"n1821015348":{"id":"n1821015348","loc":[-85.5906846,41.9903541]},"n1821015349":{"id":"n1821015349","loc":[-85.557688,42.0107769]},"n1821015350":{"id":"n1821015350","loc":[-85.5363698,42.0181424]},"n1821015351":{"id":"n1821015351","loc":[-85.5939636,41.9801918]},"n1821015352":{"id":"n1821015352","loc":[-85.5524041,42.0131644]},"n1821015354":{"id":"n1821015354","loc":[-85.5308606,42.0236221]},"n1821015355":{"id":"n1821015355","loc":[-85.5877449,41.9932367]},"n1821015356":{"id":"n1821015356","loc":[-85.519885,42.0318586]},"n1821015357":{"id":"n1821015357","loc":[-85.5454035,42.0168431]},"n1821015358":{"id":"n1821015358","loc":[-85.5970629,41.9781881]},"n1821015359":{"id":"n1821015359","loc":[-85.5932541,41.9844767]},"n1821015360":{"id":"n1821015360","loc":[-85.5970736,41.9778252]},"n1821015361":{"id":"n1821015361","loc":[-85.537031,42.0181601]},"n1821015362":{"id":"n1821015362","loc":[-85.5548355,42.0105156]},"n1821015363":{"id":"n1821015363","loc":[-85.5168648,42.0336158]},"n1821015365":{"id":"n1821015365","loc":[-85.5870435,41.9919507]},"n1821015366":{"id":"n1821015366","loc":[-85.5719681,42.0075443]},"n1821015367":{"id":"n1821015367","loc":[-85.5969985,41.9780446]},"n1821015368":{"id":"n1821015368","loc":[-85.5926761,41.98528]},"n1821015369":{"id":"n1821015369","loc":[-85.5224009,42.0293444]},"n1821015371":{"id":"n1821015371","loc":[-85.518737,42.0322651]},"n1821015372":{"id":"n1821015372","loc":[-85.6064573,41.9726465]},"n1821015373":{"id":"n1821015373","loc":[-85.5201103,42.0313088]},"n1821015375":{"id":"n1821015375","loc":[-85.5378182,42.0186844]},"n1821015376":{"id":"n1821015376","loc":[-85.6109741,41.9706882]},"n1821015377":{"id":"n1821015377","loc":[-85.5993333,41.9785488]},"n1821015378":{"id":"n1821015378","loc":[-85.5889787,41.9907368]},"n1821015380":{"id":"n1821015380","loc":[-85.6060161,41.9737375]},"n1821015381":{"id":"n1821015381","loc":[-85.5743016,42.0053679]},"n1821015382":{"id":"n1821015382","loc":[-85.6014724,41.9776099]},"n1821015383":{"id":"n1821015383","loc":[-85.5574426,42.0091644]},"n1821015385":{"id":"n1821015385","loc":[-85.5208613,42.0309302]},"n1821015386":{"id":"n1821015386","loc":[-85.5919023,41.9837789]},"n1821015387":{"id":"n1821015387","loc":[-85.5455484,42.0160221]},"n1821015392":{"id":"n1821015392","loc":[-85.5801757,42.0028964]},"n1821015395":{"id":"n1821015395","loc":[-85.5493785,42.0139974]},"n1821015396":{"id":"n1821015396","loc":[-85.5449475,42.015488]},"n1821015398":{"id":"n1821015398","loc":[-85.611123,41.9706627]},"n1821015400":{"id":"n1821015400","loc":[-85.5935706,41.9822477]},"n1821015401":{"id":"n1821015401","loc":[-85.5724254,42.0073508]},"n1821015403":{"id":"n1821015403","loc":[-85.5486812,42.0143442]},"n1821015404":{"id":"n1821015404","loc":[-85.5161835,42.0327711]},"n1821015406":{"id":"n1821015406","loc":[-85.5921705,41.9851107]},"n1821015407":{"id":"n1821015407","loc":[-85.531912,42.0234069]},"n1821015410":{"id":"n1821015410","loc":[-85.5292566,42.024176]},"n1821015411":{"id":"n1821015411","loc":[-85.5845316,41.9948315]},"n1821015413":{"id":"n1821015413","loc":[-85.5217947,42.0280413]},"n1821015414":{"id":"n1821015414","loc":[-85.5527367,42.013272]},"n1821015415":{"id":"n1821015415","loc":[-85.5191179,42.0321973]},"n1821015416":{"id":"n1821015416","loc":[-85.5540241,42.0128655]},"n1821015418":{"id":"n1821015418","loc":[-85.5272892,42.0254849]},"n1821015419":{"id":"n1821015419","loc":[-85.5449744,42.016867]},"n1821015420":{"id":"n1821015420","loc":[-85.5852665,41.9986787]},"n1821015421":{"id":"n1821015421","loc":[-85.6102701,41.972186]},"n1821015423":{"id":"n1821015423","loc":[-85.6026365,41.9764972]},"n1821015427":{"id":"n1821015427","loc":[-85.5898692,41.9841498]},"n1821015429":{"id":"n1821015429","loc":[-85.5422546,42.0183855]},"n1821015430":{"id":"n1821015430","loc":[-85.5866505,41.9925549]},"n1821015431":{"id":"n1821015431","loc":[-85.5234376,42.0273577]},"n1821015432":{"id":"n1821015432","loc":[-85.6096746,41.9727284]},"n1821015433":{"id":"n1821015433","loc":[-85.5824891,42.0021567]},"n1821015434":{"id":"n1821015434","loc":[-85.5923905,41.9841139]},"n1821015435":{"id":"n1821015435","loc":[-85.5874565,41.9948014]},"n1821015437":{"id":"n1821015437","loc":[-85.6055279,41.9734423]},"n1821015438":{"id":"n1821015438","loc":[-85.5299379,42.0237376]},"n1821015439":{"id":"n1821015439","loc":[-85.5155022,42.0383651]},"n1821015442":{"id":"n1821015442","loc":[-85.527422,42.0254711]},"n1821015443":{"id":"n1821015443","loc":[-85.5920699,41.9849291]},"n1821015444":{"id":"n1821015444","loc":[-85.5639711,42.0077494]},"n1821015445":{"id":"n1821015445","loc":[-85.5162586,42.0361777]},"n1821015446":{"id":"n1821015446","loc":[-85.5220039,42.029695]},"n1821015448":{"id":"n1821015448","loc":[-85.5176641,42.0356956]},"n1821015449":{"id":"n1821015449","loc":[-85.5930556,41.9841577]},"n1821015451":{"id":"n1821015451","loc":[-85.5320783,42.0228848]},"n1821015452":{"id":"n1821015452","loc":[-85.5170096,42.0357235]},"n1821015453":{"id":"n1821015453","loc":[-85.5571355,42.009613]},"n1821015454":{"id":"n1821015454","loc":[-85.5609979,42.009059]},"n1821015455":{"id":"n1821015455","loc":[-85.6097336,41.9708342]},"n1821015456":{"id":"n1821015456","loc":[-85.5884476,41.9904218]},"w170843846":{"id":"w170843846","tags":{"waterway":"river"},"nodes":["n1819790555","n1819790762","n1819790547","n1819790618","n1819790898","n1819790796","n1819790534","n1819790543","n1819790541","n1819790687","n1819790877","n1819790811","n1819790670"]},"w209083541":{"id":"w209083541","tags":{"name":"Portage River","waterway":"river"},"nodes":["n1821014848","n1821015156","n1821015439","n1821014763","n1821014824","n1821015274","n1821014764","n1821014791","n1821014957","n1821015217","n1821015445","n1821015347","n1821014893","n1821015452","n1821015017","n1821015448","n1821014968","n1821015329","n1821015321","n1821014992","n1821014948","n1821014757","n1821014983","n1821015312","n1821015363","n1821014924","n1821014873","n1821014932","n1821014668","n1821015404","n1821014716","n1821015243","n1821015265","n1821014710","n1821015371","n1821015415","n1821014870","n1821015356","n1821015373","n1821014681","n1821014714","n1821015385","n1821014911","n1821015057","n1821014867","n1821014998","n1821015142","n1821015234","n1821015446","n1821014862","n1821015369","n1821014945","n1821015003","n1821014667","n1821015413","n1821015256","n1821015327","n1821015160","n1821014907","n1821015431","n1821015172","n1821015034","n1821014741","n1821015033","n1821015239","n1821015007","n1821015316","n1821015285","n1821014633","n1821015263","n1821015069","n1821014846","n1821014779","n1821015011","n1821015012","n1821014845","n1821015418","n1821015442","n1821015266","n1821015336","n1821014755","n1821015188","n1821014925","n1821014816","n1821014869","n1821014967","n1821015286","n1821015147","n1821015410","n1821015126","n1821015438","n1821015018","n1821015354","n1821015337","n1821014766","n1821014740","n1821015228","n1821015173","n1821015407","n1821015076","n1821015451","n1821015024","n1821015314","n1821014784","n1821015045","n1821014735","n1821015205","n1821014986","n1821015240","n1821015098","n1821014825","n1821015302","n1821014918","n1821014718","n1821014896","n1821014853","n1821015194","n1821015165","n1821015213","n1821014991","n1821015350","n1821015333","n1821015332","n1821015361","n1821015060","n1821014914","n1821015375","n1821015315","n1821015307","n1821014906","n1821014751","n1821014666","n1821015218","n1821014817","n1821015118","n1821014674","n1821015038","n1821014973","n1821015429","n1821015183","n1821015046","n1821014803","n1821015140","n1821015009","n1821014749","n1821015335","n1821014711","n1821014854","n1821015278","n1821015419","n1821014648","n1821015357","n1821014637","n1821014988","n1821015322","n1821015387","n1821015042","n1821014833","n1821015396","n1821015326","n1821014691","n1821015149","n1821015204","n1821015122","n1821014782","n1821014982","n1821014921","n1821014936","n1821014969","n1821014881","n1821015403","n1821014805","n1821015395","n1821014892","n1821014826","n1821014844","n1821015107","n1821015014","n1821014955","n1821015352","n1821015414","n1821014746","n1821015209","n1821014770","n1821015416","n1821014661","n1821014857","n1821014814","n1821014754","n1821014721","n1821014727","n1821015362","n1821015029","n1821015246","n1821015189","n1821014627","n1821015269","n1821014808","n1821014962","n1821015349","n1821015041","n1821014722","n1821015330","n1821015453","n1821015067","n1821014643","n1821014802","n1821015383","n1821015151","n1821015250","n1821015245","n1821014772","n1821014899","n1821015222","n1821015121","n1821014951","n1821015176","n1821015219","n1821015454","n1821014972","n1821014917","n1821015182","n1821015166","n1821015145","n1821014910","n1821015082","n1821014678","n1821014806","n1821015141","n1821015341","n1821014788","n1821015097","n1821014995","n1821015444","n1821015346","n1821015101","n1821015129","n1821015123","n1821015132","n1821015190","n1821015146","n1821015325","n1821015191","n1821015072","n1821015100","n1821015174","n1821015238","n1821015215","n1821014700","n1821015242","n1821014841","n1821014905","n1821014874","n1821014883","n1821014729","n1821014866","n1821014828","n1821015295","n1821014931","n1821014859","n1821014912","n1821014783","n1821014752","n1821014961","n1821015366","n1821015401","n1821015169","n1821015318","n1821014996","n1821014747","n1821014963","n1821014670","n1821015186","n1821015231","n1821015157","n1821014812","n1821015381","n1821014887","n1821015138","n1821014704","n1821014787","n1821014922","n1821015290","n1821015343","n1821014651","n1821014980","n1821014960","n1821015244","n1821015236","n1821015006","n1821014694","n1821014759","n1821015207","n1821015276","n1821015065","n1821014863","n1821014660","n1821014902","n1821014645","n1821015339","n1821014871","n1821015096","n1821015299","n1821014798","n1821014638","n1821015392","n1821014835","n1821014762","n1821014642","n1821015433","n1821014786","n1821015134","n1821014855","n1821015184","n1821014850","n1821015293","n1821015233","n1821015227","n1821014876","n1821014985","n1821014843","n1821015420","n1821015197","n1821015192","n1821015292","n1821015344","n1821014742","n1821014726","n1821015237","n1821014796","n1821014908","n1821014975","n1821014769","n1821014688","n1821014860","n1821014895","n1821014676","n1821015411","n1821014736","n1821015164","n1821014647","n1821015144","n1821014919","n1821015220","n1821015254","n1821015435","n1821015308","n1821015342","n1821014830","n1821015273","n1821014658","n1821014781","n1821015087","n1821015139","n1821015304","n1821014839","n1821015048","n1821015115","n1821015355","n1821015226","n1821015177","n1821015430","n1821014965","n1821014725","n1821015365","n1821015171","n1821015073","n1821015125","n1821015338","n1821015111","n1821014950","n1821015378","n1821015258","n1821015456","n1821015106","n1821014832","n1821014888","n1821014795","n1821014872","n1821014810","n1821014705","n1821014804","n1821014820","n1821015283","n1821014938","n1821014689","n1821015259","n1821015334","n1821015348","n1821014635","n1821015179","n1821014864","n1821014890","n1821015020","n1821014898","n1821015287","n1821015120","n1821014984","n1821014743","n1821014790","n1821014765","n1821014777","n1821015095","n1821014653","n1821015135","n1821014836","n1821014964","n1821014974","n1821014636","n1821014682","n1821014663","n1821014665","n1821015109","n1821015155","n1821014930","n1821014669","n1821015004","n1821015427","n1821014916","n1821015093","n1821015086","n1821015386","n1821014799","n1821014913","n1821015434","n1821014728","n1821014900","n1821015068","n1821015039","n1821015443","n1821015406","n1821015280","n1821015319","n1821015368","n1821014774","n1821015090","n1821015175","n1821015195","n1821014687","n1821015359","n1821015449","n1821014956","n1821014838","n1821014768","n1821014698","n1821015323","n1821014756","n1821015255","n1821015400","n1821014717","n1821014868","n1821014778","n1821015214","n1821014944","n1821014697","n1821014671","n1821014928","n1821015294","n1821014822","n1821015284","n1821015351","n1821015022","n1821015133","n1821014644","n1821015010","n1821014625","n1821014657","n1821014946","n1821015099","n1821015114","n1821014629","n1821014865","n1821014997","n1821014926","n1821014933","n1821015199","n1821014819","n1821015080","n1821014692","n1821014677","n1821015358","n1821015367","n1821015360","n1821015105","n1821015247","n1821015005","n1821014809","n1821014794","n1821014761","n1821014879","n1821014801","n1821015377","n1821015059","n1821014730","n1821015050","n1821015271","n1821015143","n1821014989","n1821015019","n1821014672","n1821014649","n1821014684","n1821014703","n1821015021","n1821015382","n1821014842","n1821014720","n1821014847","n1821015104","n1821014987","n1821014886","n1821015267","n1821015221","n1821015015","n1821015423","n1821014954","n1821014903","n1821014939","n1821015212","n1821014789","n1821014712","n1821014708","n1821015078","n1821015277","n1821015249","n1821014646","n1821014793","n1821015053","n1821014707","n1821015306","n1821015112","n1821015288","n1821015380","n1821015437","n1821015178","n1821015158","n1821015272","n1821015235","n1821015163","n1821015154","n1821015253","n1821014632","n1821015372","n1821015103","n1821015311","n1821015301","n1821014885","n1821014811","n1821014977","n1821015051","n1821014942","n1821014745","n1821015432","n1821015075","n1821014664","n1821014695","n1821015116","n1821014639","n1821015421","n1821015248","n1821014758","n1821014834","n1821015083","n1821015455","n1821015241","n1821015108","n1821014713","n1821015137","n1821015055","n1821015211","n1821014904","n1821015376","n1821015398","n1821014771","n1821014840","n1821015062","n1819790554","n1819790560","n1819790767","n1819790696","n1819790706","n1819790606","n1819790607","n1819790544","n1819790779","n1819790760","n1819790926","n1819790927","n1819790647","n1819790657","n1819790649","n1819790679","n1819790915","n1819790739","n1819790549","n1819790671","n1819790686","n1819790798","n1819790791","n1819790563","n1819790720","n1819790704","n1819790795","n1819790836","n1819790622","n1819790615","n1819790654","n1819790931","n1819790595","n1819790753","n1819790612","n1819790623","n1819790564","n1819790552","n1819790645","n1819790625","n1819790605","n1819790668","n1819790731","n1819790718","n1819790781","n1819790665","n1819790659","n1819790726","n1819790642","n1819790854","n1819790697","n1819790867","n1819790833","n1819790555","n1819790774","n1819790881","n1819790530","n1819790909","n1819790891","n1819790590","n1819790738","n1819790609","n1819790528","n1819790674","n1819790583","n1819790559","n1819790863","n1819790912","n1819790685","n1819790913"]},"n185955128":{"id":"n185955128","loc":[-85.6189367,41.9519432]},"n185948818":{"id":"n185948818","loc":[-85.616755,41.952231]},"n185978819":{"id":"n185978819","loc":[-85.616773,41.954737]},"n185978821":{"id":"n185978821","loc":[-85.616699,41.954742]},"n2138420714":{"id":"n2138420714","loc":[-85.6176304,41.9515154]},"n2138420715":{"id":"n2138420715","loc":[-85.6177355,41.9515717]},"n2138420716":{"id":"n2138420716","loc":[-85.6192901,41.951573]},"n2138420718":{"id":"n2138420718","loc":[-85.6171481,41.9513579]},"n2138420719":{"id":"n2138420719","loc":[-85.6165981,41.9519199]},"n2138420720":{"id":"n2138420720","loc":[-85.6165719,41.9519922]},"n2138420721":{"id":"n2138420721","loc":[-85.6165832,41.9520757]},"n2138420722":{"id":"n2138420722","loc":[-85.6166355,41.9521453]},"n2138420723":{"id":"n2138420723","loc":[-85.6169161,41.9522788]},"n2138420724":{"id":"n2138420724","loc":[-85.6170882,41.9522538]},"n2138420725":{"id":"n2138420725","loc":[-85.6189204,41.9514674]},"n2138420726":{"id":"n2138420726","loc":[-85.6180346,41.9514735]},"n2138420727":{"id":"n2138420727","loc":[-85.6180362,41.9515719]},"n2138420728":{"id":"n2138420728","loc":[-85.6189204,41.9515727]},"n2138420744":{"id":"n2138420744","loc":[-85.618919,41.9519571]},"n2138420745":{"id":"n2138420745","loc":[-85.6194575,41.9522374]},"n2138420746":{"id":"n2138420746","loc":[-85.6181777,41.9536179]},"n2138420747":{"id":"n2138420747","loc":[-85.6176582,41.9533658]},"n2138420748":{"id":"n2138420748","loc":[-85.6179871,41.9530242]},"n2138420749":{"id":"n2138420749","loc":[-85.618429,41.9532476]},"n2138420750":{"id":"n2138420750","loc":[-85.6185538,41.9531194]},"n2138420751":{"id":"n2138420751","loc":[-85.6180765,41.9528677]},"n2138420752":{"id":"n2138420752","loc":[-85.6180394,41.9528855]},"n2138420753":{"id":"n2138420753","loc":[-85.6193752,41.9521695]},"n2138420754":{"id":"n2138420754","loc":[-85.6181374,41.9535376]},"n2138420755":{"id":"n2138420755","loc":[-85.6179898,41.9535545]},"n2138420756":{"id":"n2138420756","loc":[-85.6177286,41.9534228]},"n2138420757":{"id":"n2138420757","loc":[-85.6181011,41.9530292]},"n2138420759":{"id":"n2138420759","loc":[-85.6185158,41.9531194]},"n2138420760":{"id":"n2138420760","loc":[-85.6191318,41.9520425]},"n2138420761":{"id":"n2138420761","loc":[-85.6182348,41.9529815]},"n2138420762":{"id":"n2138420762","loc":[-85.6184853,41.9524248]},"n2138420763":{"id":"n2138420763","loc":[-85.6186764,41.9525193]},"n2138420764":{"id":"n2138420764","loc":[-85.6189421,41.9526483]},"n2138420765":{"id":"n2138420765","loc":[-85.6182875,41.9531222]},"n2138420766":{"id":"n2138420766","loc":[-85.6179141,41.9535163]},"n2138420767":{"id":"n2138420767","loc":[-85.6178363,41.9535735]},"n185948824":{"id":"n185948824","loc":[-85.6165667,41.9529715]},"n2138420758":{"id":"n2138420758","loc":[-85.6184408,41.953201]},"n2138422349":{"id":"n2138422349","loc":[-85.6175136,41.9533346]},"n2138422350":{"id":"n2138422350","loc":[-85.6171867,41.9531679]},"n2138422351":{"id":"n2138422351","loc":[-85.61722,41.9531305]},"n2138422352":{"id":"n2138422352","loc":[-85.6171889,41.9531158]},"n2138422353":{"id":"n2138422353","loc":[-85.6171733,41.9531284]},"n2138422354":{"id":"n2138422354","loc":[-85.616765,41.9529207]},"n2138422355":{"id":"n2138422355","loc":[-85.6167565,41.9529355]},"n2138422356":{"id":"n2138422356","loc":[-85.6164772,41.9527911]},"n2138422357":{"id":"n2138422357","loc":[-85.6168227,41.9524261]},"n2138422358":{"id":"n2138422358","loc":[-85.6171913,41.9526158]},"n2138422359":{"id":"n2138422359","loc":[-85.6172403,41.9525589]},"n2138422360":{"id":"n2138422360","loc":[-85.6172097,41.952542]},"n2138422361":{"id":"n2138422361","loc":[-85.6173948,41.9523512]},"n2138422362":{"id":"n2138422362","loc":[-85.6174256,41.9523678]},"n2138422363":{"id":"n2138422363","loc":[-85.6174831,41.9523086]},"n2138422364":{"id":"n2138422364","loc":[-85.6173316,41.9522289]},"n2138422365":{"id":"n2138422365","loc":[-85.6174507,41.9521024]},"n2138422366":{"id":"n2138422366","loc":[-85.6174773,41.9521155]},"n2138422367":{"id":"n2138422367","loc":[-85.6176577,41.9519232]},"n2138422368":{"id":"n2138422368","loc":[-85.6176336,41.9519105]},"n2138422369":{"id":"n2138422369","loc":[-85.617747,41.9517861]},"n2138422370":{"id":"n2138422370","loc":[-85.6182675,41.9520559]},"n2138422371":{"id":"n2138422371","loc":[-85.6182105,41.9521219]},"n2138422372":{"id":"n2138422372","loc":[-85.6183863,41.9522203]},"n2138422373":{"id":"n2138422373","loc":[-85.6180984,41.9525266]},"n2138422374":{"id":"n2138422374","loc":[-85.6179159,41.9524295]},"n2138422375":{"id":"n2138422375","loc":[-85.617854,41.9524979]},"n2138422376":{"id":"n2138422376","loc":[-85.6177686,41.9524531]},"n2138422377":{"id":"n2138422377","loc":[-85.6174716,41.9527765]},"n2138422378":{"id":"n2138422378","loc":[-85.6178545,41.9529756]},"n2138425424":{"id":"n2138425424","loc":[-85.6171736,41.9536385]},"n2138425425":{"id":"n2138425425","loc":[-85.6180159,41.9535782]},"n2138425426":{"id":"n2138425426","loc":[-85.6181068,41.9536282]},"n2138425427":{"id":"n2138425427","loc":[-85.6180673,41.9542678]},"n2138425428":{"id":"n2138425428","loc":[-85.6178636,41.9542634]},"n2138425429":{"id":"n2138425429","loc":[-85.6176204,41.9542046]},"n2138425430":{"id":"n2138425430","loc":[-85.6174366,41.9541031]},"n2138425431":{"id":"n2138425431","loc":[-85.6172942,41.9539781]},"n2138425432":{"id":"n2138425432","loc":[-85.6172171,41.9538399]},"n2138425433":{"id":"n2138425433","loc":[-85.6168138,41.9543266]},"n2138425434":{"id":"n2138425434","loc":[-85.6167779,41.9538098]},"n2138425435":{"id":"n2138425435","loc":[-85.6165849,41.9537073]},"n2138425441":{"id":"n2138425441","loc":[-85.616458,41.9543184]},"n2138425442":{"id":"n2138425442","loc":[-85.6166428,41.954345]},"n2138425445":{"id":"n2138425445","loc":[-85.6181332,41.9514117]},"n2138425446":{"id":"n2138425446","loc":[-85.6183263,41.9514111]},"n2138425447":{"id":"n2138425447","loc":[-85.6185033,41.9514102]},"n2138425449":{"id":"n2138425449","loc":[-85.6186809,41.9514093]},"n2138425451":{"id":"n2138425451","loc":[-85.6188681,41.9514082]},"n2138436008":{"id":"n2138436008","loc":[-85.6170474,41.9513604]},"n2138436009":{"id":"n2138436009","loc":[-85.6164937,41.9519586]},"n2138436010":{"id":"n2138436010","loc":[-85.616497,41.9520725]},"n2138436011":{"id":"n2138436011","loc":[-85.6165654,41.9521645]},"n2138436012":{"id":"n2138436012","loc":[-85.6166631,41.9522178]},"n2138436013":{"id":"n2138436013","loc":[-85.6167327,41.9522554]},"n2138436014":{"id":"n2138436014","loc":[-85.6172383,41.9525125]},"n2138439319":{"id":"n2138439319","loc":[-85.6170432,41.9524057]},"n2138439320":{"id":"n2138439320","loc":[-85.617691,41.9517107]},"n2138439321":{"id":"n2138439321","loc":[-85.6177727,41.9516794]},"n2138439322":{"id":"n2138439322","loc":[-85.619085,41.9516811]},"n2138439323":{"id":"n2138439323","loc":[-85.6179432,41.952895]},"n2138439324":{"id":"n2138439324","loc":[-85.6180389,41.9529384]},"n2138439325":{"id":"n2138439325","loc":[-85.6176303,41.9533604]},"n2138439326":{"id":"n2138439326","loc":[-85.6175538,41.9534396]},"n2138439327":{"id":"n2138439327","loc":[-85.6173806,41.9523658]},"n2138439328":{"id":"n2138439328","loc":[-85.6171841,41.9522542]},"n2138439329":{"id":"n2138439329","loc":[-85.6172077,41.9524958]},"n2138439330":{"id":"n2138439330","loc":[-85.6171235,41.9525809]},"n2138439331":{"id":"n2138439331","loc":[-85.6180938,41.9527349]},"n2138439332":{"id":"n2138439332","loc":[-85.6177023,41.9525253]},"n2138439333":{"id":"n2138439333","loc":[-85.6175543,41.9526865]},"n2138439334":{"id":"n2138439334","loc":[-85.6179589,41.9528783]},"n185948820":{"id":"n185948820","loc":[-85.6163249,41.952701]},"n185948822":{"id":"n185948822","loc":[-85.6163757,41.952855]},"n185955123":{"id":"n185955123","loc":[-85.6198103,41.9510408]},"n185958839":{"id":"n185958839","loc":[-85.611651,41.954761]},"n185965033":{"id":"n185965033","loc":[-85.614195,41.954754]},"n185976502":{"id":"n185976502","loc":[-85.617375,41.947559]},"n185976504":{"id":"n185976504","loc":[-85.6174164,41.9510804]},"n185978828":{"id":"n185978828","loc":[-85.613542,41.954756]},"n185978830":{"id":"n185978830","loc":[-85.610373,41.954774]},"n2138420713":{"id":"n2138420713","loc":[-85.6174641,41.9506942]},"n2138420717":{"id":"n2138420717","loc":[-85.6173027,41.9512895]},"n2138420768":{"id":"n2138420768","loc":[-85.61745,41.9501974]},"n2138420773":{"id":"n2138420773","loc":[-85.6174135,41.9489136]},"n2138425436":{"id":"n2138425436","loc":[-85.6159148,41.9538036]},"n2138425437":{"id":"n2138425437","loc":[-85.6159534,41.9539677]},"n2138425438":{"id":"n2138425438","loc":[-85.6160306,41.9540846]},"n2138425439":{"id":"n2138425439","loc":[-85.6161354,41.954181]},"n2138425440":{"id":"n2138425440","loc":[-85.6162733,41.954263]},"n2138425443":{"id":"n2138425443","loc":[-85.6183273,41.9510826]},"n2138425444":{"id":"n2138425444","loc":[-85.6181354,41.9510835]},"n2138425448":{"id":"n2138425448","loc":[-85.6185033,41.9510816]},"n2138425450":{"id":"n2138425450","loc":[-85.6186816,41.9510808]},"n2138425452":{"id":"n2138425452","loc":[-85.6188641,41.9510818]},"n2138435984":{"id":"n2138435984","loc":[-85.6167607,41.9501009]},"n2138436000":{"id":"n2138436000","loc":[-85.6173169,41.947558]},"n2138436001":{"id":"n2138436001","loc":[-85.6173362,41.948883]},"n2138436002":{"id":"n2138436002","loc":[-85.6167791,41.9492952]},"n2138436003":{"id":"n2138436003","loc":[-85.6167543,41.949349]},"n2138436004":{"id":"n2138436004","loc":[-85.6167648,41.9509125]},"n2138436005":{"id":"n2138436005","loc":[-85.6168832,41.9510412]},"n2138436006":{"id":"n2138436006","loc":[-85.6170045,41.9511417]},"n2138436007":{"id":"n2138436007","loc":[-85.6170624,41.9512483]},"n2138436017":{"id":"n2138436017","loc":[-85.6168094,41.9492729]},"n2138436021":{"id":"n2138436021","loc":[-85.6167553,41.9494886]},"n2138436023":{"id":"n2138436023","loc":[-85.6167585,41.9499707]},"n2138436025":{"id":"n2138436025","loc":[-85.6167567,41.9497018]},"w203838284":{"id":"w203838284","tags":{"area":"yes","leisure":"pitch","sport":"baseball"},"nodes":["n2138425424","n2138425425","n2138425426","n2138425427","n2138425428","n2138425429","n2138425430","n2138425431","n2138425432","n2138425424"]},"w203837928":{"id":"w203837928","tags":{"highway":"service"},"nodes":["n2138420717","n2138420718","n2138420719","n2138420720","n2138420721","n2138420722","n185948818","n2138420723","n2138420724","n2138420715"]},"w203839364":{"id":"w203839364","tags":{"highway":"footway"},"nodes":["n2138439331","n2138439332"]},"w203837932":{"id":"w203837932","tags":{"amenity":"parking","area":"yes"},"nodes":["n2138420744","n2138420745","n2138420746","n2138420747","n2138420748","n2138420749","n2138420750","n2138420751","n2138420744"]},"w203839362":{"id":"w203839362","tags":{"highway":"footway"},"nodes":["n2138439327","n2138439328"]},"w203839363":{"id":"w203839363","tags":{"highway":"footway"},"nodes":["n2138439329","n2138439330"]},"w203837933":{"id":"w203837933","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n185955128","n2138420760","n2138420753","n2138420764","n2138420759","n2138420758","n2138420754","n2138420755","n2138420766","n2138420756"]},"w203837936":{"id":"w203837936","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420765","n2138420766"]},"w17966364":{"id":"w17966364","tags":{"access":"private","highway":"service","name":"Collins Dr"},"nodes":["n185961362","n185976502","n2138420773","n2138420768","n2138420713","n185976504","n2138420717","n2138420714","n2138420715","n2138420727","n2138420728","n2138420716"]},"w203838040":{"id":"w203838040","tags":{"amenity":"school","area":"yes","building":"yes","name":"Three Rivers Middle School"},"nodes":["n2138422349","n2138422350","n2138422351","n2138422352","n2138422353","n2138422354","n2138422355","n2138422356","n2138422357","n2138439330","n2138422358","n2138422359","n2138422360","n2138436014","n2138439327","n2138422361","n2138422362","n2138422363","n2138422364","n2138422365","n2138422366","n2138422367","n2138422368","n2138422369","n2138422370","n2138422371","n2138422372","n2138422373","n2138422374","n2138422375","n2138422376","n2138439332","n2138439333","n2138422377","n2138422378","n2138422349"]},"w17964049":{"id":"w17964049","tags":{"highway":"service"},"nodes":["n185955120","n185955123","n2138420716","n185955128","n2138420762","n2138420752","n2138420761","n2138420759"]},"w41074899":{"id":"w41074899","tags":{"highway":"secondary","name":"E Hoffman St","ref":"M 60"},"nodes":["n185978817","n185978819","n185978821","n185965033","n185978828","n185958839","n185978830"]},"w203839365":{"id":"w203839365","tags":{"highway":"footway"},"nodes":["n2138439333","n2138439334"]},"w203837935":{"id":"w203837935","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420762","n2138420763","n2138420764"]},"w203838287":{"id":"w203838287","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425446","n2138425447","n2138425448","n2138425443","n2138425446"]},"w203837934":{"id":"w203837934","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420760","n2138420763","n2138420761"]},"w203838289":{"id":"w203838289","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425449","n2138425451","n2138425452","n2138425450","n2138425449"]},"w17963047":{"id":"w17963047","tags":{"highway":"service"},"nodes":["n185948818","n2138436013","n185948820","n185948822","n185948824","n2138439326","n2138420767","n2138420766"]},"w203839091":{"id":"w203839091","tags":{"highway":"footway"},"nodes":["n185976502","n2138436000","n2138436001","n2138436017","n2138436002","n2138436003","n2138436021","n2138436025","n2138436023","n2138435984","n2138436004","n2138436005","n2138436006","n2138436007","n2138436008","n2138436009","n2138436010","n2138436011","n2138436012","n2138436013","n2138439319","n2138439329","n2138436014"]},"w204830797":{"id":"w204830797","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420756","n2138420757","n2138420765","n2138420758"]},"w203838288":{"id":"w203838288","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425447","n2138425449","n2138425450","n2138425448","n2138425447"]},"w203838285":{"id":"w203838285","tags":{"area":"yes","leisure":"pitch","sport":"baseball"},"nodes":["n2138425433","n2138425434","n2138425435","n2138425436","n2138425437","n2138425438","n2138425439","n2138425440","n2138425441","n2138425442","n2138425433"]},"w203838286":{"id":"w203838286","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425443","n2138425444","n2138425445","n2138425446","n2138425443"]},"w203837929":{"id":"w203837929","tags":{"amenity":"parking","area":"yes"},"nodes":["n2138420725","n2138420726","n2138420727","n2138420728","n2138420725"]},"w203839361":{"id":"w203839361","tags":{"highway":"footway"},"nodes":["n2138439319","n2138439328","n2138439320","n2138439321","n2138439322","n2138439331","n2138439334","n2138439323","n2138439324","n2138439325","n2138439326"]},"n394381698":{"id":"n394381698","loc":[-85.614471,41.954755]},"n394381699":{"id":"n394381699","loc":[-85.6152,41.954744]},"n394381700":{"id":"n394381700","loc":[-85.615201,41.954081]},"n394381701":{"id":"n394381701","loc":[-85.614426,41.954042]},"n394381702":{"id":"n394381702","loc":[-85.616319,41.954749]},"n394381704":{"id":"n394381704","loc":[-85.616152,41.954752]},"n394381706":{"id":"n394381706","loc":[-85.615201,41.95483]},"n394490775":{"id":"n394490775","loc":[-85.613971,41.954839]},"n394490782":{"id":"n394490782","loc":[-85.614372,41.954841]},"n185958835":{"id":"n185958835","loc":[-85.611615,41.953704]},"n185958837":{"id":"n185958837","loc":[-85.611636,41.953938]},"n185958842":{"id":"n185958842","loc":[-85.611187,41.951686]},"n185958844":{"id":"n185958844","loc":[-85.611087,41.951741]},"n185958845":{"id":"n185958845","loc":[-85.611034,41.951852]},"n185958847":{"id":"n185958847","loc":[-85.611016,41.95196]},"n185958849":{"id":"n185958849","loc":[-85.610989,41.95328]},"n185958851":{"id":"n185958851","loc":[-85.611021,41.953484]},"n185958852":{"id":"n185958852","loc":[-85.611091,41.953603]},"n185958853":{"id":"n185958853","loc":[-85.6112,41.953661]},"n185958855":{"id":"n185958855","loc":[-85.611364,41.953686]},"n185965031":{"id":"n185965031","loc":[-85.614204,41.953696]},"n185965032":{"id":"n185965032","loc":[-85.6142,41.953978]},"n185965062":{"id":"n185965062","loc":[-85.614617,41.951639]},"n185965064":{"id":"n185965064","loc":[-85.61463,41.951852]},"n185965066":{"id":"n185965066","loc":[-85.614642,41.953436]},"n185965068":{"id":"n185965068","loc":[-85.6146,41.953551]},"n185965071":{"id":"n185965071","loc":[-85.614487,41.95363]},"n185965073":{"id":"n185965073","loc":[-85.614354,41.953672]},"n185966288":{"id":"n185966288","loc":[-85.61179,41.953695]},"n185966290":{"id":"n185966290","loc":[-85.612232,41.953685]},"n185966293":{"id":"n185966293","loc":[-85.613438,41.953677]},"n185966349":{"id":"n185966349","loc":[-85.611323,41.951653]},"n185966351":{"id":"n185966351","loc":[-85.611892,41.951642]},"n185966352":{"id":"n185966352","loc":[-85.612216,41.951641]},"n185966353":{"id":"n185966353","loc":[-85.613111,41.951639]},"n185966354":{"id":"n185966354","loc":[-85.613396,41.95164]},"n185966355":{"id":"n185966355","loc":[-85.614221,41.95164]},"n185973839":{"id":"n185973839","loc":[-85.61341,41.951919]},"n185973840":{"id":"n185973840","loc":[-85.613438,41.953308]},"n185980222":{"id":"n185980222","loc":[-85.613781,41.955164]},"n185980223":{"id":"n185980223","loc":[-85.613815,41.955237]},"n185980225":{"id":"n185980225","loc":[-85.613837,41.955316]},"n185990345":{"id":"n185990345","loc":[-85.612211,41.951977]},"n185955743":{"id":"n185955743","loc":[-85.613873,41.95635]},"n185980227":{"id":"n185980227","loc":[-85.613851,41.955415]},"n185980229":{"id":"n185980229","loc":[-85.613918,41.957134]},"n394381703":{"id":"n394381703","loc":[-85.616287,41.955674]},"n394381705":{"id":"n394381705","loc":[-85.615164,41.955676]},"n394490777":{"id":"n394490777","loc":[-85.613973,41.955979]},"n394490780":{"id":"n394490780","loc":[-85.614364,41.955987]},"w17965307":{"id":"w17965307","tags":{"highway":"residential","name":"Bates Ave"},"nodes":["n185958842","n185966349","n185966351","n185966352","n185966353","n185966354","n185966355","n185965062"]},"w17967957":{"id":"w17967957","tags":{"highway":"residential","name":"Krum Ave"},"nodes":["n185966352","n185990345","n185966290"]},"w17964508":{"id":"w17964508","tags":{"highway":"residential","name":"Blossom Dr"},"nodes":["n185958842","n185958844","n185958845","n185958847","n185958849","n185958851","n185958852","n185958853","n185958855","n185958835"]},"w17964507":{"id":"w17964507","tags":{"highway":"residential","name":"Blossom Dr"},"nodes":["n185958835","n185958837","n185958839"]},"w34367080":{"id":"w34367080","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394381699","n394381706","n394381705","n394381703","n394381702","n394381704","n394381699"]},"w17965302":{"id":"w17965302","tags":{"highway":"residential","name":"Clausen Ave"},"nodes":["n185958835","n185966288","n185966290","n185966293","n185965031"]},"w17965156":{"id":"w17965156","tags":{"highway":"residential","name":"Orchard Dr"},"nodes":["n185965062","n185965064","n185965066","n185965068","n185965071","n185965073","n185965031"]},"w34369812":{"id":"w34369812","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490775","n394490777","n394490780","n394490782","n394490775"]},"w17965151":{"id":"w17965151","tags":{"highway":"residential","name":"Orchard Dr"},"nodes":["n185965031","n185965032","n185965033"]},"w17966756":{"id":"w17966756","tags":{"access":"private","highway":"service","name":"Lockport Dr"},"nodes":["n185978828","n185980222","n185980223","n185980225","n185980227","n185955743","n185980229"]},"w17966056":{"id":"w17966056","tags":{"highway":"residential","name":"Angell Ave"},"nodes":["n185966354","n185973839","n185973840","n185966293"]},"w34367079":{"id":"w34367079","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394381700","n394381701","n394381698","n394381699","n394381700"]},"n185955744":{"id":"n185955744","loc":[-85.611753,41.956208]},"n185988932":{"id":"n185988932","loc":[-85.6159,41.956336]},"n185988934":{"id":"n185988934","loc":[-85.6159158,41.9590646]},"n185988935":{"id":"n185988935","loc":[-85.6157358,41.959364],"tags":{"highway":"turning_circle"}},"n2138447007":{"id":"n2138447007","loc":[-85.6130784,41.9590689]},"n2138447008":{"id":"n2138447008","loc":[-85.6133328,41.9593805]},"n2138447003":{"id":"n2138447003","loc":[-85.610238,41.9547745]},"n2138447004":{"id":"n2138447004","loc":[-85.6102652,41.9566041]},"n2138447005":{"id":"n2138447005","loc":[-85.610325,41.9568823]},"n2138447006":{"id":"n2138447006","loc":[-85.6105644,41.9571383]},"n2138447009":{"id":"n2138447009","loc":[-85.6135946,41.959948]},"n2138447010":{"id":"n2138447010","loc":[-85.6136071,41.9629372]},"n2138447011":{"id":"n2138447011","loc":[-85.6134392,41.9633182]},"n2138447012":{"id":"n2138447012","loc":[-85.6130151,41.9636073]},"n2138447013":{"id":"n2138447013","loc":[-85.6122729,41.9637125]},"n2138447014":{"id":"n2138447014","loc":[-85.6056682,41.963752]},"w17964174":{"id":"w17964174","tags":{"access":"private","highway":"service"},"nodes":["n185955743","n185955744"]},"w17967743":{"id":"w17967743","tags":{"access":"private","highway":"service","name":"Manistee River Rd"},"nodes":["n185971574","n185988932","n185971407","n185981301","n185967987","n185988934","n185988935"]},"w203839666":{"id":"w203839666","tags":{"highway":"residential","name":"Hov Aire Drive"},"nodes":["n2138447003","n2138447004","n2138447005","n2138447006","n2138447007","n2138447008","n2138447009","n2138447010","n2138447011","n2138447012","n2138447013","n2138447014"]}}';iD.data = {"deprecated":[{"old":{"amenity":"firepit"},"replace":{"leisure":"firepit"}},{"old":{"barrier":"wire_fence"},"replace":{"barrier":"fence","fence_type":"chain"}},{"old":{"barrier":"wood_fence"},"replace":{"barrier":"fence","fence_type":"wood"}},{"old":{"highway":"ford"},"replace":{"ford":"yes"}},{"old":{"highway":"stile"},"replace":{"barrier":"stile"}},{"old":{"highway":"incline"},"replace":{"highway":"road","incline":"up"}},{"old":{"highway":"incline_steep"},"replace":{"highway":"road","incline":"up"}},{"old":{"highway":"unsurfaced"},"replace":{"highway":"road","incline":"unpaved"}},{"old":{"landuse":"wood"},"replace":{"landuse":"forest","natural":"wood"}},{"old":{"natural":"marsh"},"replace":{"natural":"wetland","wetland":"marsh"}},{"old":{"power_source":"*"},"replace":{"generator:source":"$1"}},{"old":{"power_rating":"*"},"replace":{"generator:output":"$1"}},{"old":{"shop":"organic"},"replace":{"shop":"supermarket","organic":"only"}}],"discarded":["created_by","odbl","odbl:note","tiger:upload_uuid","tiger:tlid","tiger:source","tiger:separated","geobase:datasetName","geobase:uuid","sub_sea:type","KSJ2:ADS","KSJ2:ARE","KSJ2:AdminArea","KSJ2:COP_label","KSJ2:DFD","KSJ2:INT","KSJ2:INT_label","KSJ2:LOC","KSJ2:LPN","KSJ2:OPC","KSJ2:PubFacAdmin","KSJ2:RAC","KSJ2:RAC_label","KSJ2:RIC","KSJ2:RIN","KSJ2:WSC","KSJ2:coordinate","KSJ2:curve_id","KSJ2:curve_type","KSJ2:filename","KSJ2:lake_id","KSJ2:lat","KSJ2:long","KSJ2:river_id","yh:LINE_NAME","yh:LINE_NUM","yh:STRUCTURE","yh:TOTYUMONO","yh:TYPE","yh:WIDTH","yh:WIDTH_RANK","SK53_bulk:load"],"wikipedia":[["Abkhazian","Аҧсшәа","ab"],["Achinese","Acèh","ace"],["Adyghe","адыгабзэ","ady"],["Afrikaans","Afrikaans","af"],["Akan","Akan","ak"],["Alemannisch","Alemannisch","als"],["Amharic","አማርኛ","am"],["Aragonese","aragonés","an"],["Old English","Ænglisc","ang"],["Arabic","العربية","ar"],["Aramaic","ܐܪܡܝܐ","arc"],["Egyptian Arabic","مصرى","arz"],["Assamese","অসমীয়া","as"],["Asturian","asturianu","ast"],["Avaric","авар","av"],["Aymara","Aymar aru","ay"],["Azerbaijani","azərbaycanca","az"],["تۆرکجه","تۆرکجه","azb"],["Bashkir","башҡортса","ba"],["Bavarian","Boarisch","bar"],["Samogitian","žemaitėška","bat-smg"],["Bikol Central","Bikol Central","bcl"],["Belarusian","беларуская","be"],["беларуская (тарашкевіца)‎","беларуская (тарашкевіца)‎","be-x-old"],["Bulgarian","български","bg"],["भोजपुरी","भोजपुरी","bh"],["Bislama","Bislama","bi"],["Banjar","Bahasa Banjar","bjn"],["Bambara","bamanankan","bm"],["Bengali","বাংলা","bn"],["Tibetan","བོད་ཡིག","bo"],["Bishnupriya","বিষ্ণুপ্রিয়া মণিপুরী","bpy"],["Breton","brezhoneg","br"],["Bosnian","bosanski","bs"],["Buginese","ᨅᨔ ᨕᨘᨁᨗ","bug"],["буряад","буряад","bxr"],["Catalan","català","ca"],["Chavacano de Zamboanga","Chavacano de Zamboanga","cbk-zam"],["Min Dong Chinese","Mìng-dĕ̤ng-ngṳ̄","cdo"],["Chechen","нохчийн","ce"],["Cebuano","Cebuano","ceb"],["Chamorro","Chamoru","ch"],["Cherokee","ᏣᎳᎩ","chr"],["Cheyenne","Tsetsêhestâhese","chy"],["Central Kurdish","کوردیی ناوەندی","ckb"],["Corsican","corsu","co"],["Cree","Nēhiyawēwin / ᓀᐦᐃᔭᐍᐏᐣ","cr"],["Crimean Turkish","qırımtatarca","crh"],["Czech","čeština","cs"],["Kashubian","kaszëbsczi","csb"],["Church Slavic","словѣньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ","cu"],["Chuvash","Чӑвашла","cv"],["Welsh","Cymraeg","cy"],["Danish","dansk","da"],["German","Deutsch","de"],["Zazaki","Zazaki","diq"],["Lower Sorbian","dolnoserbski","dsb"],["Divehi","ދިވެހިބަސް","dv"],["Dzongkha","ཇོང་ཁ","dz"],["Ewe","eʋegbe","ee"],["Greek","Ελληνικά","el"],["Emiliano-Romagnolo","emiliàn e rumagnòl","eml"],["English","English","en"],["Esperanto","Esperanto","eo"],["Spanish","español","es"],["Estonian","eesti","et"],["Basque","euskara","eu"],["Extremaduran","estremeñu","ext"],["Persian","فارسی","fa"],["Fulah","Fulfulde","ff"],["Finnish","suomi","fi"],["Võro","Võro","fiu-vro"],["Fijian","Na Vosa Vakaviti","fj"],["Faroese","føroyskt","fo"],["French","français","fr"],["Arpitan","arpetan","frp"],["Northern Frisian","Nordfriisk","frr"],["Friulian","furlan","fur"],["Western Frisian","Frysk","fy"],["Irish","Gaeilge","ga"],["Gagauz","Gagauz","gag"],["Gan Chinese","贛語","gan"],["Scottish Gaelic","Gàidhlig","gd"],["Galician","galego","gl"],["Gilaki","گیلکی","glk"],["Guarani","Avañe'ẽ","gn"],["Goan Konkani","गोंयची कोंकणी / Gõychi Konknni","gom"],["Gothic","𐌲𐌿𐍄𐌹𐍃𐌺","got"],["Gujarati","ગુજરાતી","gu"],["Manx","Gaelg","gv"],["Hausa","Hausa","ha"],["Hakka Chinese","客家語/Hak-kâ-ngî","hak"],["Hawaiian","Hawaiʻi","haw"],["Hebrew","עברית","he"],["Hindi","हिन्दी","hi"],["Fiji Hindi","Fiji Hindi","hif"],["Croatian","hrvatski","hr"],["Upper Sorbian","hornjoserbsce","hsb"],["Haitian Creole","Kreyòl ayisyen","ht"],["Hungarian","magyar","hu"],["Armenian","Հայերեն","hy"],["Interlingua","interlingua","ia"],["Indonesian","Bahasa Indonesia","id"],["Interlingue","Interlingue","ie"],["Igbo","Igbo","ig"],["Inupiaq","Iñupiak","ik"],["Iloko","Ilokano","ilo"],["Ido","Ido","io"],["Icelandic","íslenska","is"],["Italian","italiano","it"],["Inuktitut","ᐃᓄᒃᑎᑐᑦ/inuktitut","iu"],["Japanese","日本語","ja"],["Jamaican Creole English","Patois","jam"],["Lojban","la .lojban.","jbo"],["Javanese","Basa Jawa","jv"],["Georgian","ქართული","ka"],["Kara-Kalpak","Qaraqalpaqsha","kaa"],["Kabyle","Taqbaylit","kab"],["Kabardian","Адыгэбзэ","kbd"],["Kongo","Kongo","kg"],["Kikuyu","Gĩkũyũ","ki"],["Kazakh","қазақша","kk"],["Kalaallisut","kalaallisut","kl"],["Khmer","ភាសាខ្មែរ","km"],["Kannada","ಕನ್ನಡ","kn"],["Korean","한국어","ko"],["Komi-Permyak","Перем Коми","koi"],["Karachay-Balkar","къарачай-малкъар","krc"],["Kashmiri","कॉशुर / کٲشُر","ks"],["Colognian","Ripoarisch","ksh"],["Kurdish","Kurdî","ku"],["Komi","коми","kv"],["Cornish","kernowek","kw"],["Kyrgyz","Кыргызча","ky"],["Latin","Latina","la"],["Ladino","Ladino","lad"],["Luxembourgish","Lëtzebuergesch","lb"],["лакку","лакку","lbe"],["Lezghian","лезги","lez"],["Ganda","Luganda","lg"],["Limburgish","Limburgs","li"],["Ligurian","Ligure","lij"],["Lombard","lumbaart","lmo"],["Lingala","lingála","ln"],["Lao","ລາວ","lo"],["Northern Luri","لۊری شومالی","lrc"],["Lithuanian","lietuvių","lt"],["Latgalian","latgaļu","ltg"],["Latvian","latviešu","lv"],["Maithili","मैथिली","mai"],["Basa Banyumasan","Basa Banyumasan","map-bms"],["Moksha","мокшень","mdf"],["Malagasy","Malagasy","mg"],["Eastern Mari","олык марий","mhr"],["Maori","Māori","mi"],["Minangkabau","Baso Minangkabau","min"],["Macedonian","македонски","mk"],["Malayalam","മലയാളം","ml"],["Mongolian","монгол","mn"],["Marathi","मराठी","mr"],["Western Mari","кырык мары","mrj"],["Malay","Bahasa Melayu","ms"],["Maltese","Malti","mt"],["Mirandese","Mirandés","mwl"],["Burmese","မြန်မာဘာသာ","my"],["Erzya","эрзянь","myv"],["Mazanderani","مازِرونی","mzn"],["Nauru","Dorerin Naoero","na"],["Nāhuatl","Nāhuatl","nah"],["Neapolitan","Napulitano","nap"],["Low German","Plattdüütsch","nds"],["Low Saxon","Nedersaksies","nds-nl"],["Nepali","नेपाली","ne"],["Newari","नेपाल भाषा","new"],["Dutch","Nederlands","nl"],["Norwegian Nynorsk","norsk nynorsk","nn"],["Norwegian","norsk bokmål","no"],["Novial","Novial","nov"],["Nouormand","Nouormand","nrm"],["Northern Sotho","Sesotho sa Leboa","nso"],["Navajo","Diné bizaad","nv"],["Nyanja","Chi-Chewa","ny"],["Occitan","occitan","oc"],["Oromo","Oromoo","om"],["Oriya","ଓଡ଼ିଆ","or"],["Ossetic","Ирон","os"],["Punjabi","ਪੰਜਾਬੀ","pa"],["Pangasinan","Pangasinan","pag"],["Pampanga","Kapampangan","pam"],["Papiamento","Papiamentu","pap"],["Picard","Picard","pcd"],["Pennsylvania German","Deitsch","pdc"],["Palatine German","Pälzisch","pfl"],["Pali","पालि","pi"],["Norfuk / Pitkern","Norfuk / Pitkern","pih"],["Polish","polski","pl"],["Piedmontese","Piemontèis","pms"],["Western Punjabi","پنجابی","pnb"],["Pontic","Ποντιακά","pnt"],["Pashto","پښتو","ps"],["Portuguese","português","pt"],["Quechua","Runa Simi","qu"],["Romansh","rumantsch","rm"],["Romani","Romani","rmy"],["Rundi","Kirundi","rn"],["Romanian","română","ro"],["Aromanian","armãneashti","roa-rup"],["tarandíne","tarandíne","roa-tara"],["Russian","русский","ru"],["Rusyn","русиньскый","rue"],["Kinyarwanda","Kinyarwanda","rw"],["Sanskrit","संस्कृतम्","sa"],["Sakha","саха тыла","sah"],["Sardinian","sardu","sc"],["Sicilian","sicilianu","scn"],["Scots","Scots","sco"],["Sindhi","سنڌي","sd"],["Northern Sami","sámegiella","se"],["Sango","Sängö","sg"],["Serbo-Croatian","srpskohrvatski / српскохрватски","sh"],["Sinhala","සිංහල","si"],["Simple English","Simple English","simple"],["Slovak","slovenčina","sk"],["Slovenian","slovenščina","sl"],["Samoan","Gagana Samoa","sm"],["Shona","chiShona","sn"],["Somali","Soomaaliga","so"],["Albanian","shqip","sq"],["Serbian","српски / srpski","sr"],["Sranan Tongo","Sranantongo","srn"],["Swati","SiSwati","ss"],["Southern Sotho","Sesotho","st"],["Saterland Frisian","Seeltersk","stq"],["Sundanese","Basa Sunda","su"],["Swedish","svenska","sv"],["Swahili","Kiswahili","sw"],["Silesian","ślůnski","szl"],["Tamil","தமிழ்","ta"],["Telugu","తెలుగు","te"],["Tetum","tetun","tet"],["Tajik","тоҷикӣ","tg"],["Thai","ไทย","th"],["Tigrinya","ትግርኛ","ti"],["Turkmen","Türkmençe","tk"],["Tagalog","Tagalog","tl"],["Tswana","Setswana","tn"],["Tongan","lea faka-Tonga","to"],["Tok Pisin","Tok Pisin","tpi"],["Turkish","Türkçe","tr"],["Tsonga","Xitsonga","ts"],["Tatar","татарча/tatarça","tt"],["Tumbuka","chiTumbuka","tum"],["Twi","Twi","tw"],["Tahitian","reo tahiti","ty"],["Tuvinian","тыва дыл","tyv"],["Udmurt","удмурт","udm"],["Uyghur","ئۇيغۇرچە / Uyghurche","ug"],["Ukrainian","українська","uk"],["Urdu","اردو","ur"],["Uzbek","oʻzbekcha/ўзбекча","uz"],["Venda","Tshivenda","ve"],["Venetian","vèneto","vec"],["Veps","vepsän kel’","vep"],["Vietnamese","Tiếng Việt","vi"],["West Flemish","West-Vlams","vls"],["Volapük","Volapük","vo"],["Walloon","walon","wa"],["Waray","Winaray","war"],["Wolof","Wolof","wo"],["Wu Chinese","吴语","wuu"],["Kalmyk","хальмг","xal"],["Xhosa","isiXhosa","xh"],["Mingrelian","მარგალური","xmf"],["Yiddish","ייִדיש","yi"],["Yoruba","Yorùbá","yo"],["Zhuang","Vahcuengh","za"],["Zeelandic","Zeêuws","zea"],["Chinese","中文","zh"],["Classical Chinese","文言","zh-classical"],["Chinese (Min Nan)","Bân-lâm-gú","zh-min-nan"],["Cantonese","粵語","zh-yue"],["Zulu","isiZulu","zu"]],"imperial":{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[1.97754,51.13111],[1.8457,63.45051],[-10.45898,57.01681],[-6.82251,55.26503],[-7.25583,55.06525],[-7.26546,55.0665],[-7.26992,55.06419],[-7.2725,55.06346],[-7.27818,55.05362],[-7.2893,55.04735],[-7.29939,55.0561],[-7.31835,55.04475],[-7.3447,55.05079],[-7.38831,55.02389],[-7.40547,55.00292],[-7.39157,54.99507],[-7.40075,54.98892],[-7.40706,54.98291],[-7.40363,54.97963],[-7.40633,54.97813],[-7.39835,54.97013],[-7.40745,54.96357],[-7.40178,54.95961],[-7.40727,54.95515],[-7.3944,54.94376],[-7.44444,54.93858],[-7.45216,54.89793],[-7.44204,54.87532],[-7.4713,54.83431],[-7.48092,54.83093],[-7.49216,54.82185],[-7.55121,54.79054],[-7.5443,54.78609],[-7.54958,54.75653],[-7.5349,54.74917],[-7.54881,54.74068],[-7.55941,54.74556],[-7.57894,54.74221],[-7.57507,54.7494],[-7.58606,54.75039],[-7.58872,54.74377],[-7.60031,54.74603],[-7.60632,54.74405],[-7.61662,54.74459],[-7.63593,54.75108],[-7.68854,54.72968],[-7.72064,54.72155],[-7.75094,54.70469],[-7.79094,54.71942],[-7.8051,54.71932],[-7.83497,54.73632],[-7.85419,54.72745],[-7.91496,54.67582],[-7.90174,54.66182],[-7.83832,54.63401],[-7.7433,54.6188],[-7.70863,54.63485],[-7.70682,54.6189],[-7.69386,54.6188],[-7.69631,54.61125],[-7.75845,54.59509],[-7.78708,54.58],[-7.79446,54.58141],[-7.79969,54.57704],[-7.79673,54.56915],[-7.8184,54.56315],[-7.83334,54.55227],[-7.82737,54.54299],[-7.85007,54.53363],[-7.90741,54.53722],[-7.93213,54.53388],[-8.00487,54.54568],[-8.03727,54.51162],[-8.04285,54.48759],[-8.08027,54.48829],[-8.09988,54.48395],[-8.09126,54.4765],[-8.111,54.47807],[-8.11512,54.46904],[-8.16542,54.46914],[-8.1776,54.46485],[-8.14293,54.45003],[-8.16284,54.4413],[-8.08731,54.4002],[-8.06062,54.37051],[-8.03289,54.35711],[-8.00054,54.34835],[-7.93333,54.30561],[-7.85849,54.29151],[-7.87067,54.28794],[-7.87265,54.26648],[-7.86123,54.25931],[-7.85917,54.21256],[-7.71043,54.20307],[-7.70193,54.20776],[-7.68828,54.202],[-7.67644,54.18906],[-7.66082,54.1871],[-7.62554,54.16545],[-7.62541,54.15319],[-7.61026,54.14353],[-7.57421,54.14142],[-7.57181,54.13287],[-7.56228,54.12704],[-7.51379,54.12998],[-7.47944,54.122],[-7.47169,54.12665],[-7.47075,54.13318],[-7.44684,54.15168],[-7.40792,54.156],[-7.42579,54.14092],[-7.41903,54.13629],[-7.3744,54.14172],[-7.37234,54.13881],[-7.39509,54.12624],[-7.39182,54.12017],[-7.36341,54.13157],[-7.34518,54.11577],[-7.32471,54.12123],[-7.32003,54.11379],[-7.3078,54.11718],[-7.30548,54.12347],[-7.31591,54.12697],[-7.31213,54.13162],[-7.3187,54.13411],[-7.31857,54.13745],[-7.32222,54.13836],[-7.32737,54.13544],[-7.3399,54.14585],[-7.30827,54.16716],[-7.30024,54.16625],[-7.29029,54.1715],[-7.28158,54.16839],[-7.2863,54.14919],[-7.29874,54.14904],[-7.30162,54.14411],[-7.28411,54.13971],[-7.29192,54.13071],[-7.29737,54.133],[-7.30883,54.13242],[-7.30333,54.12251],[-7.29218,54.11929],[-7.27844,54.12282],[-7.27707,54.12986],[-7.26613,54.13624],[-7.2566,54.16354],[-7.24015,54.17125],[-7.2575,54.17678],[-7.2581,54.19257],[-7.25179,54.19403],[-7.23608,54.1935],[-7.23338,54.19792],[-7.24317,54.20076],[-7.24892,54.1977],[-7.25183,54.20201],[-7.24119,54.20623],[-7.23094,54.20578],[-7.23269,54.20912],[-7.22188,54.21607],[-7.20643,54.2117],[-7.18506,54.22485],[-7.17055,54.21742],[-7.14721,54.22488],[-7.14633,54.23008],[-7.15051,54.23165],[-7.14613,54.23983],[-7.15802,54.24434],[-7.13985,54.25298],[-7.15255,54.26235],[-7.16064,54.27405],[-7.17991,54.27144],[-7.17201,54.28627],[-7.21252,54.2985],[-7.19888,54.31117],[-7.17918,54.30946],[-7.1812,54.3397],[-7.15339,54.33514],[-7.10253,54.35811],[-7.10811,54.36677],[-7.06927,54.3899],[-7.05593,54.41056],[-7.02898,54.42135],[-7.00198,54.40832],[-6.98683,54.40829],[-6.97562,54.40014],[-6.96774,54.40145],[-6.90682,54.36966],[-6.89772,54.35075],[-6.87527,54.33853],[-6.86512,54.32568],[-6.85163,54.29137],[-6.87452,54.28677],[-6.87791,54.27918],[-6.86673,54.27522],[-6.85177,54.26489],[-6.83693,54.26658],[-6.82165,54.24346],[-6.81633,54.22299],[-6.80045,54.22108],[-6.80122,54.21338],[-6.77599,54.19965],[-6.75573,54.1987],[-6.74316,54.18258],[-6.73406,54.18566],[-6.72445,54.18127],[-6.70295,54.20036],[-6.69166,54.20018],[-6.68673,54.19398],[-6.669,54.19584],[-6.65248,54.18102],[-6.6433,54.17801],[-6.63467,54.16449],[-6.63179,54.14766],[-6.64081,54.14238],[-6.63935,54.13599],[-6.66149,54.1205],[-6.6481,54.10153],[-6.66119,54.0934],[-6.66458,54.06629],[-6.64681,54.05873],[-6.62501,54.03737],[-6.59291,54.04755],[-6.58905,54.05808],[-6.5597,54.0481],[-6.52897,54.05888],[-6.50442,54.05566],[-6.47824,54.07004],[-6.47919,54.07762],[-6.43601,54.05959],[-6.36314,54.07057],[-6.36589,54.09338],[-6.36293,54.09758],[-6.37104,54.11497],[-6.3522,54.11084],[-6.34242,54.1114],[-6.33589,54.10833],[-6.33636,54.09469],[-6.31808,54.09096],[-6.30903,54.10463],[-6.29165,54.11235],[-6.28246,54.11145],[-6.26272,54.09786],[-5.35583,53.72597],[-7.0752,49.23912],[-1.83472,49.02346],[-2.12036,49.94415],[1.97754,51.13111]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-65.2,18.7],[-65,16.3],[-63.7,19.2],[-65.2,18.7]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-63,-50.5],[-55,-51],[-60,-54],[-63,-50.5]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-139.19952,60.08402],[-141,60.30621],[-141,76],[-169,68.63655],[-169,65.20147],[-180,61],[-180,-4],[-154,9],[-133.76404,54.54021],[-130.73868,54.71986],[-129.96277,55.29163],[-130.15228,55.7758],[-130.01787,55.90688],[-130.00362,56.00798],[-130.10284,56.12336],[-130.24498,56.09656],[-130.42625,56.14249],[-131.87439,56.79787],[-135.02884,59.56285],[-135.11759,59.62306],[-135.15827,59.6261],[-135.47928,59.79822],[-136.28677,59.57955],[-136.30531,59.46462],[-136.36836,59.44898],[-136.47697,59.46558],[-137.19727,59.01935],[-139.19952,60.08402]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-111.96064,48.99841],[-121.22623,49.00049],[-122.26513,49.00246],[-122.7565,49.00208],[-123.32218,49.00218],[-122.97821,48.76524],[-123.2666,48.69821],[-123.21991,48.21186],[-125.80444,48.60749],[-124.32129,31.54109],[-117.125,32.53429],[-116.82417,32.55996],[-115.88036,32.63735],[-115.49738,32.66486],[-114.71984,32.71877],[-114.7649,32.64602],[-114.80885,32.60959],[-114.81481,32.49451],[-112.81743,31.88004],[-111.07481,31.33224],[-109.56051,31.33402],[-108.20847,31.33384],[-108.20838,31.78363],[-106.52847,31.78391],[-106.52781,31.78086],[-106.52249,31.77501],[-106.51249,31.76933],[-106.50988,31.7612],[-106.50709,31.76123],[-106.48896,31.74806],[-106.48473,31.74769],[-106.4719,31.75101],[-106.46816,31.75897],[-106.45434,31.76466],[-106.45035,31.76426],[-106.43516,31.75492],[-106.41484,31.75101],[-106.37864,31.73021],[-106.37225,31.71174],[-106.34924,31.69633],[-106.33289,31.66178],[-106.3068,31.62459],[-106.28079,31.56179],[-106.24775,31.54226],[-106.2329,31.49982],[-106.2105,31.46857],[-106.08201,31.39863],[-106.00554,31.39233],[-105.76401,31.17051],[-105.58548,31.06117],[-105.56419,30.98526],[-104.99153,30.6639],[-104.97162,30.60896],[-104.90639,30.57822],[-104.83772,30.38117],[-104.70177,30.20567],[-104.68048,29.92399],[-104.57611,29.77838],[-104.51157,29.63674],[-104.39758,29.57047],[-104.39278,29.55293],[-104.05769,29.32173],[-103.79883,29.2581],[-103.78196,29.26555],[-103.76759,29.22799],[-103.14102,28.93666],[-102.86087,29.2217],[-102.65076,29.79418],[-101.41068,29.73457],[-101.26511,29.51372],[-101.05997,29.452],[-101.04083,29.38038],[-100.96303,29.34735],[-100.94406,29.34369],[-100.94071,29.33351],[-100.92775,29.32663],[-100.89814,29.30957],[-100.87818,29.28086],[-100.80076,29.2238],[-100.76437,29.15981],[-100.67047,29.08663],[-100.6412,28.91299],[-100.63236,28.90255],[-100.61296,28.89939],[-100.534,28.75622],[-100.51495,28.74531],[-100.50705,28.7143],[-100.51203,28.70666],[-100.51014,28.69127],[-100.50048,28.66186],[-100.45547,28.6381],[-100.44697,28.60743],[-100.35599,28.45239],[-100.34946,28.39653],[-100.29488,28.31315],[-100.29591,28.27324],[-100.17197,28.17493],[-99.93645,27.9568],[-99.87722,27.80173],[-99.79671,27.73338],[-99.772,27.72532],[-99.74556,27.69979],[-99.71947,27.65981],[-99.5957,27.59974],[-99.54094,27.60537],[-99.53055,27.57973],[-99.52034,27.55782],[-99.52802,27.49773],[-99.50141,27.49986],[-99.48755,27.49518],[-99.47897,27.48421],[-99.48661,27.46453],[-99.49534,27.44861],[-99.48927,27.40941],[-99.53957,27.31565],[-99.43588,27.19678],[-99.46404,27.01968],[-99.16698,26.56039],[-99.17474,26.53939],[-99.12698,26.51958],[-99.1135,26.42954],[-99.08355,26.39625],[-99.06007,26.39737],[-99.03634,26.41255],[-99.02042,26.40598],[-99.01291,26.39364],[-98.95686,26.38641],[-98.9566,26.37365],[-98.94523,26.36949],[-98.90013,26.36419],[-98.89905,26.35454],[-98.80305,26.36626],[-98.78254,26.30511],[-98.66667,26.23457],[-98.58496,26.24647],[-98.57951,26.23434],[-98.56519,26.23987],[-98.56294,26.22464],[-98.50599,26.20858],[-98.44806,26.21236],[-98.38617,26.15721],[-98.34176,26.15278],[-98.33579,26.1388],[-98.30626,26.10003],[-98.28841,26.10512],[-98.26524,26.0914],[-98.19898,26.06411],[-98.09577,26.05698],[-98.07568,26.06667],[-98.08302,26.03396],[-97.9771,26.04136],[-97.9532,26.06179],[-97.81643,26.04475],[-97.77017,26.02439],[-97.73884,26.02902],[-97.5289,25.90648],[-97.52151,25.88625],[-97.50615,25.89031],[-97.49851,25.89903],[-97.49637,25.89641],[-97.49748,25.88008],[-97.49422,25.87981],[-97.48847,25.88564],[-97.46409,25.88174],[-97.42607,25.842],[-97.36856,25.8396],[-97.26231,25.94724],[-80.81543,24.00633],[-66.87378,44.77794],[-67.16148,45.16715],[-67.2286,45.16739],[-67.26246,45.18797],[-67.28311,45.19175],[-67.28959,45.18784],[-67.29332,45.17568],[-67.29049,45.17317],[-67.3001,45.16776],[-67.3025,45.16122],[-67.29761,45.14766],[-67.33975,45.1255],[-67.40524,45.16122],[-67.40387,45.17139],[-67.4818,45.27682],[-67.42172,45.38543],[-67.45262,45.41008],[-67.50498,45.4889],[-67.41623,45.50105],[-67.42219,45.55661],[-67.42902,45.56833],[-67.42331,45.57154],[-67.42498,45.57836],[-67.45193,45.60323],[-67.77981,45.6738],[-67.79019,47.06776],[-67.88006,47.1067],[-67.91319,47.14793],[-67.92598,47.15418],[-67.95181,47.1875],[-68.02374,47.23915],[-68.13017,47.29309],[-68.17669,47.32893],[-68.24046,47.35354],[-68.32809,47.36005],[-68.36363,47.35476],[-68.38054,47.34167],[-68.38509,47.30321],[-68.37367,47.28796],[-68.4377,47.28232],[-68.47916,47.29623],[-68.51074,47.29885],[-68.54593,47.28441],[-68.58408,47.28482],[-68.59777,47.27134],[-68.59271,47.25762],[-68.61889,47.24148],[-68.68936,47.24125],[-68.71768,47.23676],[-68.80128,47.21423],[-68.89629,47.17676],[-69.05354,47.24847],[-69.04924,47.41798],[-69.22425,47.45961],[-69.99729,46.69558],[-70.0569,46.4149],[-70.25551,46.10871],[-70.29001,46.09431],[-70.39919,45.80667],[-70.83229,45.40062],[-70.80794,45.37878],[-70.82663,45.2367],[-70.87538,45.23453],[-70.92138,45.28099],[-70.90645,45.30918],[-71.0109,45.34798],[-71.08429,45.30556],[-71.1454,45.24226],[-71.20525,45.25278],[-71.28925,45.30097],[-71.41405,45.23513],[-71.43044,45.12381],[-71.49692,45.06991],[-71.50623,45.04878],[-71.49284,45.03629],[-71.50027,45.01372],[-71.79359,45.01075],[-72.08774,45.00581],[-72.14155,45.00568],[-72.15282,45.00609],[-72.17142,45.00584],[-72.25847,45.00436],[-72.38795,45.00626],[-72.4496,45.00863],[-72.5356,45.00936],[-72.66257,45.01523],[-72.82537,45.01642],[-73.08466,45.01561],[-73.45219,45.00875],[-74.14699,44.99145],[-74.33753,44.9923],[-74.50786,44.99798],[-74.66158,44.99949],[-74.71244,44.99734],[-74.75887,44.98708],[-74.76368,45.00632],[-74.78977,45.00365],[-74.82376,45.01773],[-74.94186,44.98229],[-75.30098,44.83883],[-75.30304,44.82836],[-75.59418,44.6457],[-75.97269,44.33502],[-75.97295,44.34595],[-76.00059,44.34797],[-76.17645,44.2865],[-76.18744,44.22158],[-76.88782,43.82759],[-79.16851,43.32168],[-79.05487,43.25371],[-79.05092,43.169],[-79.04603,43.16093],[-79.04208,43.13942],[-79.07002,43.12038],[-79.06015,43.114],[-79.0568,43.10474],[-79.0774,43.07861],[-78.9996,43.05484],[-79.02311,43.02071],[-79.02552,42.99473],[-78.96235,42.9573],[-78.91188,42.9426],[-78.90398,42.89181],[-82.42767,41.47978],[-83.14316,42.03807],[-83.12805,42.23843],[-83.09715,42.29052],[-83.07252,42.31515],[-82.94575,42.34332],[-82.59676,42.5479],[-82.51368,42.61785],[-82.5108,42.66464],[-82.4675,42.76415],[-82.48055,42.80573],[-82.45497,42.9284],[-82.41334,42.97099],[-82.42596,42.99536],[-82.15851,43.39507],[-83.53729,46.098],[-83.96301,46.05036],[-84.11021,46.23851],[-84.09794,46.25656],[-84.11613,46.26878],[-84.11905,46.31516],[-84.10721,46.3218],[-84.14394,46.41076],[-84.11682,46.51576],[-84.13536,46.53218],[-84.16162,46.5284],[-84.21621,46.53891],[-84.26994,46.49189],[-84.36092,46.50997],[-84.55284,46.4407],[-84.95178,46.77185],[-89.59179,48.00307],[-89.67547,48.00371],[-90.87204,48.25943],[-91.41312,48.06753],[-92.99377,48.62474],[-93.34877,48.62604],[-93.35529,48.61124],[-93.37074,48.60584],[-93.39812,48.60369],[-93.40542,48.61089],[-93.43846,48.59478],[-93.46859,48.59205],[-93.45735,48.56667],[-93.46533,48.54593],[-93.64763,48.51751],[-93.80625,48.51888],[-93.80642,48.58047],[-93.83328,48.62582],[-93.84865,48.63064],[-93.93388,48.6326],[-94.01327,48.64471],[-94.16176,48.64697],[-94.25025,48.65463],[-94.24931,48.67827],[-94.26046,48.69816],[-94.30578,48.71073],[-94.32758,48.70433],[-94.36123,48.70478],[-94.38406,48.71135],[-94.41629,48.71067],[-94.44294,48.69266],[-94.53615,48.7024],[-94.55031,48.71419],[-94.58894,48.71928],[-94.69425,48.77938],[-94.70129,48.83376],[-94.68996,48.83953],[-94.68395,48.99914],[-111.96064,48.99841]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[180,55],[170,53],[180,49],[180,55]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[143,22],[147,22],[147,12],[143,12],[143,22]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-171.5,-10],[-171,-15],[-167,-15],[-171.5,-10]]]}}]},"featureIcons":{"circle-stroked-24":{"x":0,"y":0,"width":24,"height":24},"circle-stroked-18":{"x":24,"y":0,"width":18,"height":18},"circle-stroked-12":{"x":42,"y":0,"width":12,"height":12},"circle-24":{"x":54,"y":0,"width":24,"height":24},"circle-18":{"x":78,"y":0,"width":18,"height":18},"circle-12":{"x":96,"y":0,"width":12,"height":12},"square-stroked-24":{"x":108,"y":0,"width":24,"height":24},"square-stroked-18":{"x":132,"y":0,"width":18,"height":18},"square-stroked-12":{"x":150,"y":0,"width":12,"height":12},"square-24":{"x":162,"y":0,"width":24,"height":24},"square-18":{"x":186,"y":0,"width":18,"height":18},"square-12":{"x":204,"y":0,"width":12,"height":12},"triangle-stroked-24":{"x":216,"y":0,"width":24,"height":24},"triangle-stroked-18":{"x":240,"y":0,"width":18,"height":18},"triangle-stroked-12":{"x":258,"y":0,"width":12,"height":12},"triangle-24":{"x":0,"y":24,"width":24,"height":24},"triangle-18":{"x":24,"y":24,"width":18,"height":18},"triangle-12":{"x":42,"y":24,"width":12,"height":12},"star-stroked-24":{"x":54,"y":24,"width":24,"height":24},"star-stroked-18":{"x":78,"y":24,"width":18,"height":18},"star-stroked-12":{"x":96,"y":24,"width":12,"height":12},"star-24":{"x":108,"y":24,"width":24,"height":24},"star-18":{"x":132,"y":24,"width":18,"height":18},"star-12":{"x":150,"y":24,"width":12,"height":12},"cross-24":{"x":162,"y":24,"width":24,"height":24},"cross-18":{"x":186,"y":24,"width":18,"height":18},"cross-12":{"x":204,"y":24,"width":12,"height":12},"marker-stroked-24":{"x":216,"y":24,"width":24,"height":24},"marker-stroked-18":{"x":240,"y":24,"width":18,"height":18},"marker-stroked-12":{"x":258,"y":24,"width":12,"height":12},"marker-24":{"x":0,"y":48,"width":24,"height":24},"marker-18":{"x":24,"y":48,"width":18,"height":18},"marker-12":{"x":42,"y":48,"width":12,"height":12},"religious-jewish-24":{"x":54,"y":48,"width":24,"height":24},"religious-jewish-18":{"x":78,"y":48,"width":18,"height":18},"religious-jewish-12":{"x":96,"y":48,"width":12,"height":12},"religious-christian-24":{"x":108,"y":48,"width":24,"height":24},"religious-christian-18":{"x":132,"y":48,"width":18,"height":18},"religious-christian-12":{"x":150,"y":48,"width":12,"height":12},"religious-muslim-24":{"x":162,"y":48,"width":24,"height":24},"religious-muslim-18":{"x":186,"y":48,"width":18,"height":18},"religious-muslim-12":{"x":204,"y":48,"width":12,"height":12},"cemetery-24":{"x":216,"y":48,"width":24,"height":24},"cemetery-18":{"x":240,"y":48,"width":18,"height":18},"cemetery-12":{"x":258,"y":48,"width":12,"height":12},"rocket-24":{"x":0,"y":72,"width":24,"height":24},"rocket-18":{"x":24,"y":72,"width":18,"height":18},"rocket-12":{"x":42,"y":72,"width":12,"height":12},"airport-24":{"x":54,"y":72,"width":24,"height":24},"airport-18":{"x":78,"y":72,"width":18,"height":18},"airport-12":{"x":96,"y":72,"width":12,"height":12},"heliport-24":{"x":108,"y":72,"width":24,"height":24},"heliport-18":{"x":132,"y":72,"width":18,"height":18},"heliport-12":{"x":150,"y":72,"width":12,"height":12},"rail-24":{"x":162,"y":72,"width":24,"height":24},"rail-18":{"x":186,"y":72,"width":18,"height":18},"rail-12":{"x":204,"y":72,"width":12,"height":12},"rail-metro-24":{"x":216,"y":72,"width":24,"height":24},"rail-metro-18":{"x":240,"y":72,"width":18,"height":18},"rail-metro-12":{"x":258,"y":72,"width":12,"height":12},"rail-light-24":{"x":0,"y":96,"width":24,"height":24},"rail-light-18":{"x":24,"y":96,"width":18,"height":18},"rail-light-12":{"x":42,"y":96,"width":12,"height":12},"bus-24":{"x":54,"y":96,"width":24,"height":24},"bus-18":{"x":78,"y":96,"width":18,"height":18},"bus-12":{"x":96,"y":96,"width":12,"height":12},"fuel-24":{"x":108,"y":96,"width":24,"height":24},"fuel-18":{"x":132,"y":96,"width":18,"height":18},"fuel-12":{"x":150,"y":96,"width":12,"height":12},"parking-24":{"x":162,"y":96,"width":24,"height":24},"parking-18":{"x":186,"y":96,"width":18,"height":18},"parking-12":{"x":204,"y":96,"width":12,"height":12},"parking-garage-24":{"x":216,"y":96,"width":24,"height":24},"parking-garage-18":{"x":240,"y":96,"width":18,"height":18},"parking-garage-12":{"x":258,"y":96,"width":12,"height":12},"airfield-24":{"x":0,"y":120,"width":24,"height":24},"airfield-18":{"x":24,"y":120,"width":18,"height":18},"airfield-12":{"x":42,"y":120,"width":12,"height":12},"roadblock-24":{"x":54,"y":120,"width":24,"height":24},"roadblock-18":{"x":78,"y":120,"width":18,"height":18},"roadblock-12":{"x":96,"y":120,"width":12,"height":12},"ferry-24":{"x":108,"y":120,"width":24,"height":24},"ferry-18":{"x":132,"y":120,"width":18,"height":18},"ferry-12":{"x":150,"y":120,"width":12,"height":12},"harbor-24":{"x":162,"y":120,"width":24,"height":24},"harbor-18":{"x":186,"y":120,"width":18,"height":18},"harbor-12":{"x":204,"y":120,"width":12,"height":12},"bicycle-24":{"x":216,"y":120,"width":24,"height":24},"bicycle-18":{"x":240,"y":120,"width":18,"height":18},"bicycle-12":{"x":258,"y":120,"width":12,"height":12},"park-24":{"x":0,"y":144,"width":24,"height":24},"park-18":{"x":24,"y":144,"width":18,"height":18},"park-12":{"x":42,"y":144,"width":12,"height":12},"park2-24":{"x":54,"y":144,"width":24,"height":24},"park2-18":{"x":78,"y":144,"width":18,"height":18},"park2-12":{"x":96,"y":144,"width":12,"height":12},"museum-24":{"x":108,"y":144,"width":24,"height":24},"museum-18":{"x":132,"y":144,"width":18,"height":18},"museum-12":{"x":150,"y":144,"width":12,"height":12},"lodging-24":{"x":162,"y":144,"width":24,"height":24},"lodging-18":{"x":186,"y":144,"width":18,"height":18},"lodging-12":{"x":204,"y":144,"width":12,"height":12},"monument-24":{"x":216,"y":144,"width":24,"height":24},"monument-18":{"x":240,"y":144,"width":18,"height":18},"monument-12":{"x":258,"y":144,"width":12,"height":12},"zoo-24":{"x":0,"y":168,"width":24,"height":24},"zoo-18":{"x":24,"y":168,"width":18,"height":18},"zoo-12":{"x":42,"y":168,"width":12,"height":12},"garden-24":{"x":54,"y":168,"width":24,"height":24},"garden-18":{"x":78,"y":168,"width":18,"height":18},"garden-12":{"x":96,"y":168,"width":12,"height":12},"campsite-24":{"x":108,"y":168,"width":24,"height":24},"campsite-18":{"x":132,"y":168,"width":18,"height":18},"campsite-12":{"x":150,"y":168,"width":12,"height":12},"theatre-24":{"x":162,"y":168,"width":24,"height":24},"theatre-18":{"x":186,"y":168,"width":18,"height":18},"theatre-12":{"x":204,"y":168,"width":12,"height":12},"art-gallery-24":{"x":216,"y":168,"width":24,"height":24},"art-gallery-18":{"x":240,"y":168,"width":18,"height":18},"art-gallery-12":{"x":258,"y":168,"width":12,"height":12},"pitch-24":{"x":0,"y":192,"width":24,"height":24},"pitch-18":{"x":24,"y":192,"width":18,"height":18},"pitch-12":{"x":42,"y":192,"width":12,"height":12},"soccer-24":{"x":54,"y":192,"width":24,"height":24},"soccer-18":{"x":78,"y":192,"width":18,"height":18},"soccer-12":{"x":96,"y":192,"width":12,"height":12},"america-football-24":{"x":108,"y":192,"width":24,"height":24},"america-football-18":{"x":132,"y":192,"width":18,"height":18},"america-football-12":{"x":150,"y":192,"width":12,"height":12},"tennis-24":{"x":162,"y":192,"width":24,"height":24},"tennis-18":{"x":186,"y":192,"width":18,"height":18},"tennis-12":{"x":204,"y":192,"width":12,"height":12},"basketball-24":{"x":216,"y":192,"width":24,"height":24},"basketball-18":{"x":240,"y":192,"width":18,"height":18},"basketball-12":{"x":258,"y":192,"width":12,"height":12},"baseball-24":{"x":0,"y":216,"width":24,"height":24},"baseball-18":{"x":24,"y":216,"width":18,"height":18},"baseball-12":{"x":42,"y":216,"width":12,"height":12},"golf-24":{"x":54,"y":216,"width":24,"height":24},"golf-18":{"x":78,"y":216,"width":18,"height":18},"golf-12":{"x":96,"y":216,"width":12,"height":12},"swimming-24":{"x":108,"y":216,"width":24,"height":24},"swimming-18":{"x":132,"y":216,"width":18,"height":18},"swimming-12":{"x":150,"y":216,"width":12,"height":12},"cricket-24":{"x":162,"y":216,"width":24,"height":24},"cricket-18":{"x":186,"y":216,"width":18,"height":18},"cricket-12":{"x":204,"y":216,"width":12,"height":12},"skiing-24":{"x":216,"y":216,"width":24,"height":24},"skiing-18":{"x":240,"y":216,"width":18,"height":18},"skiing-12":{"x":258,"y":216,"width":12,"height":12},"school-24":{"x":0,"y":240,"width":24,"height":24},"school-18":{"x":24,"y":240,"width":18,"height":18},"school-12":{"x":42,"y":240,"width":12,"height":12},"college-24":{"x":54,"y":240,"width":24,"height":24},"college-18":{"x":78,"y":240,"width":18,"height":18},"college-12":{"x":96,"y":240,"width":12,"height":12},"library-24":{"x":108,"y":240,"width":24,"height":24},"library-18":{"x":132,"y":240,"width":18,"height":18},"library-12":{"x":150,"y":240,"width":12,"height":12},"post-24":{"x":162,"y":240,"width":24,"height":24},"post-18":{"x":186,"y":240,"width":18,"height":18},"post-12":{"x":204,"y":240,"width":12,"height":12},"fire-station-24":{"x":216,"y":240,"width":24,"height":24},"fire-station-18":{"x":240,"y":240,"width":18,"height":18},"fire-station-12":{"x":258,"y":240,"width":12,"height":12},"town-hall-24":{"x":0,"y":264,"width":24,"height":24},"town-hall-18":{"x":24,"y":264,"width":18,"height":18},"town-hall-12":{"x":42,"y":264,"width":12,"height":12},"police-24":{"x":54,"y":264,"width":24,"height":24},"police-18":{"x":78,"y":264,"width":18,"height":18},"police-12":{"x":96,"y":264,"width":12,"height":12},"prison-24":{"x":108,"y":264,"width":24,"height":24},"prison-18":{"x":132,"y":264,"width":18,"height":18},"prison-12":{"x":150,"y":264,"width":12,"height":12},"embassy-24":{"x":162,"y":264,"width":24,"height":24},"embassy-18":{"x":186,"y":264,"width":18,"height":18},"embassy-12":{"x":204,"y":264,"width":12,"height":12},"beer-24":{"x":216,"y":264,"width":24,"height":24},"beer-18":{"x":240,"y":264,"width":18,"height":18},"beer-12":{"x":258,"y":264,"width":12,"height":12},"restaurant-24":{"x":0,"y":288,"width":24,"height":24},"restaurant-18":{"x":24,"y":288,"width":18,"height":18},"restaurant-12":{"x":42,"y":288,"width":12,"height":12},"cafe-24":{"x":54,"y":288,"width":24,"height":24},"cafe-18":{"x":78,"y":288,"width":18,"height":18},"cafe-12":{"x":96,"y":288,"width":12,"height":12},"shop-24":{"x":108,"y":288,"width":24,"height":24},"shop-18":{"x":132,"y":288,"width":18,"height":18},"shop-12":{"x":150,"y":288,"width":12,"height":12},"fast-food-24":{"x":162,"y":288,"width":24,"height":24},"fast-food-18":{"x":186,"y":288,"width":18,"height":18},"fast-food-12":{"x":204,"y":288,"width":12,"height":12},"bar-24":{"x":216,"y":288,"width":24,"height":24},"bar-18":{"x":240,"y":288,"width":18,"height":18},"bar-12":{"x":258,"y":288,"width":12,"height":12},"bank-24":{"x":0,"y":312,"width":24,"height":24},"bank-18":{"x":24,"y":312,"width":18,"height":18},"bank-12":{"x":42,"y":312,"width":12,"height":12},"grocery-24":{"x":54,"y":312,"width":24,"height":24},"grocery-18":{"x":78,"y":312,"width":18,"height":18},"grocery-12":{"x":96,"y":312,"width":12,"height":12},"cinema-24":{"x":108,"y":312,"width":24,"height":24},"cinema-18":{"x":132,"y":312,"width":18,"height":18},"cinema-12":{"x":150,"y":312,"width":12,"height":12},"pharmacy-24":{"x":162,"y":312,"width":24,"height":24},"pharmacy-18":{"x":186,"y":312,"width":18,"height":18},"pharmacy-12":{"x":204,"y":312,"width":12,"height":12},"hospital-24":{"x":216,"y":312,"width":24,"height":24},"hospital-18":{"x":240,"y":312,"width":18,"height":18},"hospital-12":{"x":258,"y":312,"width":12,"height":12},"danger-24":{"x":0,"y":336,"width":24,"height":24},"danger-18":{"x":24,"y":336,"width":18,"height":18},"danger-12":{"x":42,"y":336,"width":12,"height":12},"industrial-24":{"x":54,"y":336,"width":24,"height":24},"industrial-18":{"x":78,"y":336,"width":18,"height":18},"industrial-12":{"x":96,"y":336,"width":12,"height":12},"warehouse-24":{"x":108,"y":336,"width":24,"height":24},"warehouse-18":{"x":132,"y":336,"width":18,"height":18},"warehouse-12":{"x":150,"y":336,"width":12,"height":12},"commercial-24":{"x":162,"y":336,"width":24,"height":24},"commercial-18":{"x":186,"y":336,"width":18,"height":18},"commercial-12":{"x":204,"y":336,"width":12,"height":12},"building-24":{"x":216,"y":336,"width":24,"height":24},"building-18":{"x":240,"y":336,"width":18,"height":18},"building-12":{"x":258,"y":336,"width":12,"height":12},"place-of-worship-24":{"x":0,"y":360,"width":24,"height":24},"place-of-worship-18":{"x":24,"y":360,"width":18,"height":18},"place-of-worship-12":{"x":42,"y":360,"width":12,"height":12},"alcohol-shop-24":{"x":54,"y":360,"width":24,"height":24},"alcohol-shop-18":{"x":78,"y":360,"width":18,"height":18},"alcohol-shop-12":{"x":96,"y":360,"width":12,"height":12},"logging-24":{"x":108,"y":360,"width":24,"height":24},"logging-18":{"x":132,"y":360,"width":18,"height":18},"logging-12":{"x":150,"y":360,"width":12,"height":12},"oil-well-24":{"x":162,"y":360,"width":24,"height":24},"oil-well-18":{"x":186,"y":360,"width":18,"height":18},"oil-well-12":{"x":204,"y":360,"width":12,"height":12},"slaughterhouse-24":{"x":216,"y":360,"width":24,"height":24},"slaughterhouse-18":{"x":240,"y":360,"width":18,"height":18},"slaughterhouse-12":{"x":258,"y":360,"width":12,"height":12},"dam-24":{"x":0,"y":384,"width":24,"height":24},"dam-18":{"x":24,"y":384,"width":18,"height":18},"dam-12":{"x":42,"y":384,"width":12,"height":12},"water-24":{"x":54,"y":384,"width":24,"height":24},"water-18":{"x":78,"y":384,"width":18,"height":18},"water-12":{"x":96,"y":384,"width":12,"height":12},"wetland-24":{"x":108,"y":384,"width":24,"height":24},"wetland-18":{"x":132,"y":384,"width":18,"height":18},"wetland-12":{"x":150,"y":384,"width":12,"height":12},"disability-24":{"x":162,"y":384,"width":24,"height":24},"disability-18":{"x":186,"y":384,"width":18,"height":18},"disability-12":{"x":204,"y":384,"width":12,"height":12},"telephone-24":{"x":216,"y":384,"width":24,"height":24},"telephone-18":{"x":240,"y":384,"width":18,"height":18},"telephone-12":{"x":258,"y":384,"width":12,"height":12},"emergency-telephone-24":{"x":0,"y":408,"width":24,"height":24},"emergency-telephone-18":{"x":24,"y":408,"width":18,"height":18},"emergency-telephone-12":{"x":42,"y":408,"width":12,"height":12},"toilets-24":{"x":54,"y":408,"width":24,"height":24},"toilets-18":{"x":78,"y":408,"width":18,"height":18},"toilets-12":{"x":96,"y":408,"width":12,"height":12},"waste-basket-24":{"x":108,"y":408,"width":24,"height":24},"waste-basket-18":{"x":132,"y":408,"width":18,"height":18},"waste-basket-12":{"x":150,"y":408,"width":12,"height":12},"music-24":{"x":162,"y":408,"width":24,"height":24},"music-18":{"x":186,"y":408,"width":18,"height":18},"music-12":{"x":204,"y":408,"width":12,"height":12},"land-use-24":{"x":216,"y":408,"width":24,"height":24},"land-use-18":{"x":240,"y":408,"width":18,"height":18},"land-use-12":{"x":258,"y":408,"width":12,"height":12},"city-24":{"x":0,"y":432,"width":24,"height":24},"city-18":{"x":24,"y":432,"width":18,"height":18},"city-12":{"x":42,"y":432,"width":12,"height":12},"town-24":{"x":54,"y":432,"width":24,"height":24},"town-18":{"x":78,"y":432,"width":18,"height":18},"town-12":{"x":96,"y":432,"width":12,"height":12},"village-24":{"x":108,"y":432,"width":24,"height":24},"village-18":{"x":132,"y":432,"width":18,"height":18},"village-12":{"x":150,"y":432,"width":12,"height":12},"farm-24":{"x":162,"y":432,"width":24,"height":24},"farm-18":{"x":186,"y":432,"width":18,"height":18},"farm-12":{"x":204,"y":432,"width":12,"height":12},"bakery-24":{"x":216,"y":432,"width":24,"height":24},"bakery-18":{"x":240,"y":432,"width":18,"height":18},"bakery-12":{"x":258,"y":432,"width":12,"height":12},"dog-park-24":{"x":0,"y":456,"width":24,"height":24},"dog-park-18":{"x":24,"y":456,"width":18,"height":18},"dog-park-12":{"x":42,"y":456,"width":12,"height":12},"lighthouse-24":{"x":54,"y":456,"width":24,"height":24},"lighthouse-18":{"x":78,"y":456,"width":18,"height":18},"lighthouse-12":{"x":96,"y":456,"width":12,"height":12},"clothing-store-24":{"x":108,"y":456,"width":24,"height":24},"clothing-store-18":{"x":132,"y":456,"width":18,"height":18},"clothing-store-12":{"x":150,"y":456,"width":12,"height":12},"polling-place-24":{"x":162,"y":456,"width":24,"height":24},"polling-place-18":{"x":186,"y":456,"width":18,"height":18},"polling-place-12":{"x":204,"y":456,"width":12,"height":12},"playground-24":{"x":216,"y":456,"width":24,"height":24},"playground-18":{"x":240,"y":456,"width":18,"height":18},"playground-12":{"x":258,"y":456,"width":12,"height":12},"entrance-24":{"x":0,"y":480,"width":24,"height":24},"entrance-18":{"x":24,"y":480,"width":18,"height":18},"entrance-12":{"x":42,"y":480,"width":12,"height":12},"heart-24":{"x":54,"y":480,"width":24,"height":24},"heart-18":{"x":78,"y":480,"width":18,"height":18},"heart-12":{"x":96,"y":480,"width":12,"height":12},"london-underground-24":{"x":108,"y":480,"width":24,"height":24},"london-underground-18":{"x":132,"y":480,"width":18,"height":18},"london-underground-12":{"x":150,"y":480,"width":12,"height":12},"minefield-24":{"x":162,"y":480,"width":24,"height":24},"minefield-18":{"x":186,"y":480,"width":18,"height":18},"minefield-12":{"x":204,"y":480,"width":12,"height":12},"rail-underground-24":{"x":216,"y":480,"width":24,"height":24},"rail-underground-18":{"x":240,"y":480,"width":18,"height":18},"rail-underground-12":{"x":258,"y":480,"width":12,"height":12},"rail-above-24":{"x":0,"y":504,"width":24,"height":24},"rail-above-18":{"x":24,"y":504,"width":18,"height":18},"rail-above-12":{"x":42,"y":504,"width":12,"height":12},"camera-24":{"x":54,"y":504,"width":24,"height":24},"camera-18":{"x":78,"y":504,"width":18,"height":18},"camera-12":{"x":96,"y":504,"width":12,"height":12},"laundry-24":{"x":108,"y":504,"width":24,"height":24},"laundry-18":{"x":132,"y":504,"width":18,"height":18},"laundry-12":{"x":150,"y":504,"width":12,"height":12},"car-24":{"x":162,"y":504,"width":24,"height":24},"car-18":{"x":186,"y":504,"width":18,"height":18},"car-12":{"x":204,"y":504,"width":12,"height":12},"suitcase-24":{"x":216,"y":504,"width":24,"height":24},"suitcase-18":{"x":240,"y":504,"width":18,"height":18},"suitcase-12":{"x":258,"y":504,"width":12,"height":12},"hairdresser-24":{"x":0,"y":528,"width":24,"height":24},"hairdresser-18":{"x":24,"y":528,"width":18,"height":18},"hairdresser-12":{"x":42,"y":528,"width":12,"height":12},"chemist-24":{"x":54,"y":528,"width":24,"height":24},"chemist-18":{"x":78,"y":528,"width":18,"height":18},"chemist-12":{"x":96,"y":528,"width":12,"height":12},"mobilephone-24":{"x":108,"y":528,"width":24,"height":24},"mobilephone-18":{"x":132,"y":528,"width":18,"height":18},"mobilephone-12":{"x":150,"y":528,"width":12,"height":12},"scooter-24":{"x":162,"y":528,"width":24,"height":24},"scooter-18":{"x":186,"y":528,"width":18,"height":18},"scooter-12":{"x":204,"y":528,"width":12,"height":12},"gift-24":{"x":216,"y":528,"width":24,"height":24},"gift-18":{"x":240,"y":528,"width":18,"height":18},"gift-12":{"x":258,"y":528,"width":12,"height":12},"ice-cream-24":{"x":0,"y":552,"width":24,"height":24},"ice-cream-18":{"x":24,"y":552,"width":18,"height":18},"ice-cream-12":{"x":42,"y":552,"width":12,"height":12}},"locales":["af","sq","ar","ar-AA","hy","ast","bn","bs","bg-BG","ca","zh","zh-CN","zh-HK","zh-TW","yue","hr","cs","da","nl","en-GB","eo","et","fi","fr","gl","de","el","hi","hu","is","id","it","ja","kn","ko","lv","lij","lt","no","fa","pl","pt","pt-BR","ro","ru","sc","sr","si","sk","sl","es","sv","tl","ta","te","th","tr","uk","vi"],"en":{"modes":{"add_area":{"title":"Area","description":"Add parks, buildings, lakes or other areas to the map.","tail":"Click on the map to start drawing an area, like a park, lake, or building."},"add_line":{"title":"Line","description":"Add highways, streets, pedestrian paths, canals or other lines to the map.","tail":"Click on the map to start drawing a road, path, or route."},"add_point":{"title":"Point","description":"Add restaurants, monuments, postal boxes or other points to the map.","tail":"Click on the map to add a point."},"browse":{"title":"Browse","description":"Pan and zoom the map."},"draw_area":{"tail":"Click to add nodes to your area. Click the first node to finish the area."},"draw_line":{"tail":"Click to add more nodes to the line. Click on other lines to connect to them, and double-click to end the line."}},"operations":{"add":{"annotation":{"point":"Added a point.","vertex":"Added a node to a way.","relation":"Added a relation."}},"start":{"annotation":{"line":"Started a line.","area":"Started an area."}},"continue":{"key":"A","title":"Continue","description":"Continue this line.","not_eligible":"No line can be continued here.","multiple":"Several lines can be continued here. To choose a line, press the Shift key and click on it to select it.","annotation":{"line":"Continued a line.","area":"Continued an area."}},"cancel_draw":{"annotation":"Canceled drawing."},"change_role":{"annotation":"Changed the role of a relation member."},"change_tags":{"annotation":"Changed tags."},"circularize":{"title":"Circularize","description":{"line":"Make this line circular.","area":"Make this area circular."},"key":"O","annotation":{"line":"Made a line circular.","area":"Made an area circular."},"not_closed":"This can't be made circular because it's not a loop.","too_large":"This can't be made circular because not enough of it is currently visible.","connected_to_hidden":"This can't be made circular because it is connected to a hidden feature."},"orthogonalize":{"title":"Square","description":{"line":"Square the corners of this line.","area":"Square the corners of this area."},"key":"S","annotation":{"line":"Squared the corners of a line.","area":"Squared the corners of an area."},"not_squarish":"This can't be made square because it is not squarish.","too_large":"This can't be made square because not enough of it is currently visible.","connected_to_hidden":"This can't be made square because it is connected to a hidden feature."},"straighten":{"title":"Straighten","description":"Straighten this line.","key":"S","annotation":"Straightened a line.","too_bendy":"This can't be straightened because it bends too much.","connected_to_hidden":"This line can't be straightened because it is connected to a hidden feature."},"delete":{"title":"Delete","description":"Delete object permanently.","annotation":{"point":"Deleted a point.","vertex":"Deleted a node from a way.","line":"Deleted a line.","area":"Deleted an area.","relation":"Deleted a relation.","multiple":"Deleted {n} objects."},"incomplete_relation":"This feature can't be deleted because it hasn't been fully downloaded.","part_of_relation":"This feature can't be deleted because it's part of a larger relation. You must remove it from the relation first.","connected_to_hidden":"This can't be deleted because it is connected to a hidden feature."},"add_member":{"annotation":"Added a member to a relation."},"delete_member":{"annotation":"Removed a member from a relation."},"connect":{"annotation":{"point":"Connected a way to a point.","vertex":"Connected a way to another.","line":"Connected a way to a line.","area":"Connected a way to an area."}},"disconnect":{"title":"Disconnect","description":"Disconnect these lines/areas from each other.","key":"D","annotation":"Disconnected lines/areas.","not_connected":"There aren't enough lines/areas here to disconnect.","connected_to_hidden":"This can't be disconnected because it is connected to a hidden feature.","relation":"This can't be disconnected because it connects members of a relation."},"merge":{"title":"Merge","description":"Merge these features.","key":"C","annotation":"Merged {n} features.","not_eligible":"These features can't be merged.","not_adjacent":"These features can't be merged because they aren't connected.","restriction":"These features can't be merged because at least one is a member of a \"{relation}\" relation.","incomplete_relation":"These features can't be merged because at least one hasn't been fully downloaded.","conflicting_tags":"These features can't be merged because some of their tags have conflicting values."},"move":{"title":"Move","description":"Move this to a different location.","key":"M","annotation":{"point":"Moved a point.","vertex":"Moved a node in a way.","line":"Moved a line.","area":"Moved an area.","multiple":"Moved multiple objects."},"incomplete_relation":"This feature can't be moved because it hasn't been fully downloaded.","too_large":"This can't be moved because not enough of it is currently visible.","connected_to_hidden":"This can't be moved because it is connected to a hidden feature."},"rotate":{"title":"Rotate","description":"Rotate this object around its center point.","key":"R","annotation":{"line":"Rotated a line.","area":"Rotated an area."},"too_large":"This can't be rotated because not enough of it is currently visible.","connected_to_hidden":"This can't be rotated because it is connected to a hidden feature."},"reverse":{"title":"Reverse","description":"Make this line go in the opposite direction.","key":"V","annotation":"Reversed a line."},"split":{"title":"Split","description":{"line":"Split this line into two at this node.","area":"Split the boundary of this area into two.","multiple":"Split the lines/area boundaries at this node into two."},"key":"X","annotation":{"line":"Split a line.","area":"Split an area boundary.","multiple":"Split {n} lines/area boundaries."},"not_eligible":"Lines can't be split at their beginning or end.","multiple_ways":"There are too many lines here to split.","connected_to_hidden":"This can't be split because it is connected to a hidden feature."},"restriction":{"help":{"select":"Click to select a road segment.","toggle":"Click to toggle turn restrictions.","toggle_on":"Click to add a \"{restriction}\" restriction.","toggle_off":"Click to remove the \"{restriction}\" restriction."},"annotation":{"create":"Added a turn restriction","delete":"Deleted a turn restriction"}}},"undo":{"tooltip":"Undo: {action}","nothing":"Nothing to undo."},"redo":{"tooltip":"Redo: {action}","nothing":"Nothing to redo."},"tooltip_keyhint":"Shortcut:","browser_notice":"This editor is supported in Firefox, Chrome, Safari, Opera, and Internet Explorer 11 and above. Please upgrade your browser or use Potlatch 2 to edit the map.","translate":{"translate":"Translate","localized_translation_label":"Multilingual name","localized_translation_language":"Choose language","localized_translation_name":"Name"},"zoom_in_edit":"Zoom in to Edit","logout":"logout","loading_auth":"Connecting to OpenStreetMap...","report_a_bug":"Report a bug","help_translate":"Help translate","feature_info":{"hidden_warning":"{count} hidden features","hidden_details":"These features are currently hidden: {details}"},"status":{"error":"Unable to connect to API.","offline":"The API is offline. Please try editing later.","readonly":"The API is read-only. You will need to wait to save your changes."},"commit":{"title":"Save Changes","description_placeholder":"Brief description of your contributions (required)","message_label":"Changeset comment","upload_explanation":"The changes you upload will be visible on all maps that use OpenStreetMap data.","upload_explanation_with_user":"The changes you upload as {user} will be visible on all maps that use OpenStreetMap data.","save":"Save","cancel":"Cancel","changes":"{count} Changes","warnings":"Warnings","modified":"Modified","deleted":"Deleted","created":"Created","about_changeset_comments":"About changeset comments","about_changeset_comments_link":"//wiki.openstreetmap.org/wiki/Good_changeset_comments","google_warning":"You mentioned Google in this comment: remember that copying from Google Maps is strictly forbidden.","google_warning_link":"http://www.openstreetmap.org/copyright"},"contributors":{"list":"Edits by {users}","truncated_list":"Edits by {users} and {count} others"},"infobox":{"selected":"{n} selected","geometry":"Geometry","closed":"closed","center":"Center","perimeter":"Perimeter","length":"Length","area":"Area","centroid":"Centroid","location":"Location","metric":"Metric","imperial":"Imperial"},"geometry":{"point":"point","vertex":"vertex","line":"line","area":"area","relation":"relation"},"geocoder":{"search":"Search worldwide...","no_results_visible":"No results in visible map area","no_results_worldwide":"No results found"},"geolocate":{"title":"Show My Location","locating":"Locating, please wait..."},"inspector":{"no_documentation_combination":"There is no documentation available for this tag combination","no_documentation_key":"There is no documentation available for this key","show_more":"Show More","view_on_osm":"View on openstreetmap.org","all_fields":"All fields","all_tags":"All tags","all_members":"All members","all_relations":"All relations","new_relation":"New relation...","role":"Role","choose":"Select feature type","results":"{n} results for {search}","reference":"View on OpenStreetMap Wiki","back_tooltip":"Change feature","remove":"Remove","search":"Search","multiselect":"Selected items","unknown":"Unknown","incomplete":"<not downloaded>","feature_list":"Search features","edit":"Edit feature","check":{"yes":"Yes","no":"No"},"add":"Add","none":"None","node":"Node","way":"Way","relation":"Relation","location":"Location","add_fields":"Add field:"},"background":{"title":"Background","description":"Background settings","percent_brightness":"{opacity}% brightness","none":"None","best_imagery":"Best known imagery source for this location","switch":"Switch back to this background","custom":"Custom","custom_button":"Edit custom background","custom_prompt":"Enter a tile URL template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme.","fix_misalignment":"Adjust imagery offset","imagery_source_faq":"Where does this imagery come from?","reset":"reset","offset":"Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters.","minimap":{"description":"Minimap","tooltip":"Show a zoomed out map to help locate the area currently displayed."}},"map_data":{"title":"Map Data","description":"Map Data","data_layers":"Data Layers","fill_area":"Fill Areas","map_features":"Map Features","autohidden":"These features have been automatically hidden because too many would be shown on the screen.  You can zoom in to edit them."},"feature":{"points":{"description":"Points","tooltip":"Points of Interest"},"traffic_roads":{"description":"Traffic Roads","tooltip":"Highways, Streets, etc."},"service_roads":{"description":"Service Roads","tooltip":"Service Roads, Parking Aisles, Tracks, etc."},"paths":{"description":"Paths","tooltip":"Sidewalks, Foot Paths, Cycle Paths, etc."},"buildings":{"description":"Buildings","tooltip":"Buildings, Shelters, Garages, etc."},"landuse":{"description":"Landuse Features","tooltip":"Forests, Farmland, Parks, Residential, Commercial, etc."},"boundaries":{"description":"Boundaries","tooltip":"Administrative Boundaries"},"water":{"description":"Water Features","tooltip":"Rivers, Lakes, Ponds, Basins, etc."},"rail":{"description":"Rail Features","tooltip":"Railways"},"power":{"description":"Power Features","tooltip":"Power Lines, Power Plants, Substations, etc."},"past_future":{"description":"Past/Future","tooltip":"Proposed, Construction, Abandoned, Demolished, etc."},"others":{"description":"Others","tooltip":"Everything Else"}},"area_fill":{"wireframe":{"description":"No Fill (Wireframe)","tooltip":"Enabling wireframe mode makes it easy to see the background imagery."},"partial":{"description":"Partial Fill","tooltip":"Areas are drawn with fill only around their inner edges. (Recommended for beginner mappers)"},"full":{"description":"Full Fill","tooltip":"Areas are drawn fully filled."}},"restore":{"heading":"You have unsaved changes","description":"Do you wish to restore unsaved changes from a previous editing session?","restore":"Restore","reset":"Reset"},"save":{"title":"Save","help":"Save changes to OpenStreetMap, making them visible to other users.","no_changes":"No changes to save.","error":"Errors occurred while trying to save","status_code":"Server returned status code {code}","unknown_error_details":"Please ensure you are connected to the internet.","uploading":"Uploading changes to OpenStreetMap.","unsaved_changes":"You have unsaved changes","conflict":{"header":"Resolve conflicting edits","count":"Conflict {num} of {total}","previous":"< Previous","next":"Next >","keep_local":"Keep mine","keep_remote":"Use theirs","restore":"Restore","delete":"Leave Deleted","download_changes":"Or download your changes.","done":"All conflicts resolved!","help":"Another user changed some of the same map features you changed.\nClick on each item below for more details about the conflict, and choose whether to keep\nyour changes or the other user's changes.\n"}},"merge_remote_changes":{"conflict":{"deleted":"This object has been deleted by {user}.","location":"This object was moved by both you and {user}.","nodelist":"Nodes were changed by both you and {user}.","memberlist":"Relation members were changed by both you and {user}.","tags":"You changed the <b>{tag}</b> tag to \"{local}\" and {user} changed it to \"{remote}\"."}},"success":{"edited_osm":"Edited OSM!","just_edited":"You just edited OpenStreetMap!","view_on_osm":"View on OSM","facebook":"Share on Facebook","twitter":"Share on Twitter","google":"Share on Google+","help_html":"Your changes should appear in the \"Standard\" layer in a few minutes. Other layers, and certain features, may take longer.","help_link_text":"Details","help_link_url":"https://wiki.openstreetmap.org/wiki/FAQ#I_have_just_made_some_changes_to_the_map._How_do_I_get_to_see_my_changes.3F"},"confirm":{"okay":"Okay","cancel":"Cancel"},"splash":{"welcome":"Welcome to the iD OpenStreetMap editor","text":"iD is a friendly but powerful tool for contributing to the world's best free world map. This is version {version}. For more information see {website} and report bugs at {github}.","walkthrough":"Start the Walkthrough","start":"Edit Now"},"source_switch":{"live":"live","lose_changes":"You have unsaved changes. Switching the map server will discard them. Are you sure you want to switch servers?","dev":"dev"},"tag_reference":{"description":"Description","on_wiki":"{tag} on wiki.osm.org","used_with":"used with {type}"},"validations":{"untagged_point":"Untagged point","untagged_line":"Untagged line","untagged_area":"Untagged area","many_deletions":"You're deleting {n} objects. Are you sure you want to do this? This will delete them from the map that everyone else sees on openstreetmap.org.","tag_suggests_area":"The tag {tag} suggests line should be area, but it is not an area","untagged_point_tooltip":"Select a feature type that describes what this point is.","untagged_line_tooltip":"Select a feature type that describes what this line is.","untagged_area_tooltip":"Select a feature type that describes what this area is.","deprecated_tags":"Deprecated tags: {tags}"},"zoom":{"in":"Zoom In","out":"Zoom Out"},"cannot_zoom":"Cannot zoom out further in current mode.","full_screen":"Toggle Full Screen","gpx":{"local_layer":"Local GPX file","drag_drop":"Drag and drop a .gpx file on the page, or click the button to the right to browse","zoom":"Zoom to GPX track","browse":"Browse for a .gpx file"},"mapillary_images":{"tooltip":"Street-level photos from Mapillary","title":"Photo Overlay (Mapillary)"},"mapillary_signs":{"tooltip":"Traffic signs from Mapillary (must enable Photo Overlay)","title":"Traffic Sign Overlay (Mapillary)"},"mapillary":{"view_on_mapillary":"View this image on Mapillary"},"help":{"title":"Help","help":"# Help\n\nThis is an editor for [OpenStreetMap](http://www.openstreetmap.org/), the\nfree and editable map of the world. You can use it to add and update\ndata in your area, making an open-source and open-data map of the world\nbetter for everyone.\n\nEdits that you make on this map will be visible to everyone who uses\nOpenStreetMap. In order to make an edit, you'll need to\n[log in](https://www.openstreetmap.org/login).\n\nThe [iD editor](http://ideditor.com/) is a collaborative project with [source\ncode available on GitHub](https://github.com/openstreetmap/iD).\n","editing_saving":"# Editing & Saving\n\nThis editor is designed to work primarily online, and you're accessing\nit through a website right now.\n\n### Selecting Features\n\nTo select a map feature, like a road or point of interest, click\non it on the map. This will highlight the selected feature, open a panel with\ndetails about it, and show a menu of things you can do with the feature.\n\nTo select multiple features, hold down the 'Shift' key. Then either click\non the features you want to select, or drag on the map to draw a rectangle.\nThis will draw a box and select all the points within it.\n\n### Saving Edits\n\nWhen you make changes like editing roads, buildings, and places, these are\nstored locally until you save them to the server. Don't worry if you make\na mistake - you can undo changes by clicking the undo button, and redo\nchanges by clicking the redo button.\n\nClick 'Save' to finish a group of edits - for instance, if you've completed\nan area of town and would like to start on a new area. You'll have a chance\nto review what you've done, and the editor supplies helpful suggestions\nand warnings if something doesn't seem right about the changes.\n\nIf everything looks good, you can enter a short comment explaining the change\nyou made, and click 'Save' again to post the changes\nto [OpenStreetMap.org](http://www.openstreetmap.org/), where they are visible\nto all other users and available for others to build and improve upon.\n\nIf you can't finish your edits in one sitting, you can leave the editor\nwindow and come back (on the same browser and computer), and the\neditor application will offer to restore your work.\n\n### Using the editor\n\nA list of available keyboard shortcuts can be found [here](http://wiki.openstreetmap.org/wiki/ID/Shortcuts).\n","roads":"# Roads\n\nYou can create, fix, and delete roads with this editor. Roads can be all\nkinds: paths, highways, trails, cycleways, and more - any often-crossed\nsegment should be mappable.\n\n### Selecting\n\nClick on a road to select it. An outline should become visible, along\nwith a small tools menu on the map and a sidebar showing more information\nabout the road.\n\n### Modifying\n\nOften you'll see roads that aren't aligned to the imagery behind them\nor to a GPS track. You can adjust these roads so they are in the correct\nplace.\n\nFirst click on the road you want to change. This will highlight it and show\ncontrol points along it that you can drag to better locations. If\nyou want to add new control points for more detail, double-click a part\nof the road without a node, and one will be added.\n\nIf the road connects to another road, but doesn't properly connect on\nthe map, you can drag one of its control points onto the other road in\norder to join them. Having roads connect is important for the map\nand essential for providing driving directions.\n\nYou can also click the 'Move' tool or press the `M` shortcut key to move the entire road at\none time, and then click again to save that movement.\n\n### Deleting\n\nIf a road is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the road could simply be newly built.\n\nYou can delete a road by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n\n### Creating\n\nFound somewhere there should be a road but there isn't? Click the 'Line'\nicon in the top-left of the editor or press the shortcut key `2` to start drawing\na line.\n\nClick on the start of the road on the map to start drawing. If the road\nbranches off from an existing road, start by clicking on the place where they connect.\n\nThen click on points along the road so that it follows the right path, according\nto satellite imagery or GPS. If the road you are drawing crosses another road, connect\nit by clicking on the intersection point. When you're done drawing, double-click\nor press 'Return' or 'Enter' on your keyboard.\n","gps":"# GPS\n\nCollected GPS traces are one valuable source of data for OpenStreetMap. This editor\nsupports local traces - `.gpx` files on your local computer. You can collect\nthis kind of GPS trace with a number of smartphone applications as well as\npersonal GPS hardware.\n\nFor information on how to perform a GPS survey, read\n[Mapping with a smartphone, GPS, or paper](http://learnosm.org/en/mobile-mapping/).\n\nTo use a GPX track for mapping, drag and drop the GPX file onto the map\neditor. If it's recognized, it will be added to the map as a bright purple\nline. Click on the 'Map Data' menu on the right side to enable,\ndisable, or zoom to this new GPX-powered layer.\n\nThe GPX track isn't directly uploaded to OpenStreetMap - the best way to\nuse it is to draw on the map, using it as a guide for the new features that\nyou add, and also to [upload it to OpenStreetMap](http://www.openstreetmap.org/trace/create)\nfor other users to use.\n","imagery":"# Imagery\n\nAerial imagery is an important resource for mapping. A combination of\nairplane flyovers, satellite views, and freely-compiled sources are available\nin the editor under the 'Background Settings' menu on the right.\n\nBy default a [Bing Maps](http://www.bing.com/maps/) satellite layer is\npresented in the editor, but as you pan and zoom the map to new geographical\nareas, new sources will become available. Some countries, like the United\nStates, France, and Denmark have very high-quality imagery available for some areas.\n\nImagery is sometimes offset from the map data because of a mistake on the\nimagery provider's side. If you see a lot of roads shifted from the background,\ndon't immediately move them all to match the background. Instead you can adjust\nthe imagery so that it matches the existing data by clicking 'Fix alignment' at\nthe bottom of the Background Settings UI.\n","addresses":"# Addresses\n\nAddresses are some of the most useful information for the map.\n\nAlthough addresses are often represented as parts of streets, in OpenStreetMap\nthey're recorded as attributes of buildings and places along streets.\n\nYou can add address information to places mapped as building outlines\nas well as those mapped as single points. The optimal source of address\ndata is from an on-the-ground survey or personal knowledge - as with any\nother feature, copying from commercial sources like Google Maps is strictly\nforbidden.\n","inspector":"# Using the Inspector\n\nThe inspector is the section on the left side of the page that allows you to\nedit the details of the selected feature.\n\n### Selecting a Feature Type\n\nAfter you add a point, line, or area, you can choose what type of feature it\nis, like whether it's a highway or residential road, supermarket or cafe.\nThe inspector will display buttons for common feature types, and you can\nfind others by typing what you're looking for in the search box.\n\nClick the 'i' in the bottom-right-hand corner of a feature type button to\nlearn more about it. Click a button to choose that type.\n\n### Using Forms and Editing Tags\n\nAfter you choose a feature type, or when you select a feature that already\nhas a type assigned, the inspector will display fields with details about\nthe feature like its name and address.\n\nBelow the fields you see, you can click the 'Add field' dropdown to add\nother details, like a Wikipedia link, wheelchair access, and more.\n\nAt the bottom of the inspector, click 'Additional tags' to add arbitrary\nother tags to the element. [Taginfo](http://taginfo.openstreetmap.org/) is a\ngreat resource for learn more about popular tag combinations.\n\nChanges you make in the inspector are automatically applied to the map.\nYou can undo them at any time by clicking the 'Undo' button.\n","buildings":"# Buildings\n\nOpenStreetMap is the world's largest database of buildings. You can create\nand improve this database.\n\n### Selecting\n\nYou can select a building by clicking on its border. This will highlight the\nbuilding and open a small tools menu and a sidebar showing more information\nabout the building.\n\n### Modifying\n\nSometimes buildings are incorrectly placed or have incorrect tags.\n\nTo move an entire building, select it, then click the 'Move' tool. Move your\nmouse to shift the building, and click when it's correctly placed.\n\nTo fix the specific shape of a building, click and drag the nodes that form\nits border into better places.\n\n### Creating\n\nOne of the main questions around adding buildings to the map is that\nOpenStreetMap records buildings both as shapes and points. The rule of thumb\nis to _map a building as a shape whenever possible_, and map companies, homes,\namenities, and other things that operate out of buildings as points placed\nwithin the building shape.\n\nStart drawing a building as a shape by clicking the 'Area' button in the top\nleft of the interface, and end it either by pressing 'Return' on your keyboard\nor clicking on the first node drawn to close the shape.\n\n### Deleting\n\nIf a building is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the building could simply be newly built.\n\nYou can delete a building by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n","relations":"# Relations\n\nA relation is a special type of feature in OpenStreetMap that groups together\nother features. For example, two common types of relations are *route relations*,\nwhich group together sections of road that belong to a specific freeway or\nhighway, and *multipolygons*, which group together several lines that define\na complex area (one with several pieces or holes in it like a donut).\n\nThe group of features in a relation are called *members*. In the sidebar, you can\nsee which relations a feature is a member of, and click on a relation there\nto select the it. When the relation is selected, you can see all of its\nmembers listed in the sidebar and highlighted on the map.\n\nFor the most part, iD will take care of maintaining relations automatically\nwhile you edit. The main thing you should be aware of is that if you delete a\nsection of road to redraw it more accurately, you should make sure that the\nnew section is a member of the same relations as the original.\n\n## Editing Relations\n\nIf you want to edit relations, here are the basics.\n\nTo add a feature to a relation, select the feature, click the \"+\" button in the\n\"All relations\" section of the sidebar, and select or type the name of the relation.\n\nTo create a new relation, select the first feature that should be a member,\nclick the \"+\" button in the \"All relations\" section, and select \"New relation...\".\n\nTo remove a feature from a relation, select the feature and click the trash\nbutton next to the relation you want to remove it from.\n\nYou can create multipolygons with holes using the \"Merge\" tool. Draw two areas (inner\nand outer), hold the Shift key and click on each of them to select them both, and then\nclick the \"Merge\" (+) button.\n"},"intro":{"graph":{"city_hall":"Three Rivers City Hall","fire_department":"Three Rivers Fire Department","memory_isle_park":"Memory Isle Park","riverwalk_trail":"Riverwalk Trail","w_michigan_ave":"West Michigan Avenue","e_michigan_ave":"East Michigan Avenue","spring_st":"Spring Street","scidmore_park":"Scidmore Park","petting_zoo":"Scidmore Park Petting Zoo","n_andrews_st":"North Andrews Street","s_andrews_st":"South Andrews Street","n_constantine_st":"North Constantine Street","s_constantine_st":"South Constantine Street","rocky_river":"Rocky River","railroad_dr":"Railroad Drive","conrail_rr":"Conrail Railroad","st_joseph_river":"Saint Joseph River","n_main_st":"North Main Street","s_main_st":"South Main Street","water_st":"Water Street","foster_st":"Foster Street","portage_river":"Portage River","flower_st":"Flower Street","elm_st":"Elm Street","walnut_st":"Walnut Street","morris_ave":"Morris Avenue","east_st":"East Street","portage_ave":"Portage Avenue"},"navigation":{"title":"Navigation","drag":"The main map area shows OpenStreetMap data on top of a background. You can navigate by dragging and scrolling, just like any web map. **Drag the map!**","select":"Map features are represented three ways: using points, lines or areas. All features can be selected by clicking on them. **Click on the point to select it.**","pane":"When a feature is selected, the feature editor is displayed. The header shows us the feature type and the main pane shows the feature's attributes, such as its name and address. **Close the feature editor by pressing the {button} button in the top right.**","search":"You can also search for features in the current view, or worldwide. **Search for '{name}'**","choose":"**Choose {name} from the list to select it.**","chosen":"Great! {name} is now selected. **Close the feature editor by pressing the {button} button.**"},"points":{"title":"Points","add":"Points can be used to represent features such as shops, restaurants, and monuments. They mark a specific location, and describe what's there. **Click the {button} Point button to add a new point.**","place":"The point can be placed by clicking on the map. **Click the map to place the new point on top of the building.**","search":"There are many different features that can be represented by points. The point you just added is a Cafe. **Search for '{name}'**","choose":"**Choose Cafe from the list.**","describe":"The point is now marked as a cafe. Using the feature editor, we can add more information about the feature. **Add a name**","close":"The feature editor will remember all of your changes automatically.  When you change a feature, the close button will change to a checkmark. **Click the {button} button to close the feature editor**","reselect":"Often points will already exist, but have mistakes or be incomplete. We can edit existing points. **Click to select the point you just created.**","fixname":"**Change the name, then click the {button} button to close the feature editor.**","reselect_delete":"All features on the map can be deleted. **Click to select the point you created.**","delete":"The menu around the point contains operations that can be performed on it, including delete. **Click on the {button} button to delete the point.**"},"areas":{"title":"Areas","add":"Areas are used to show the boundaries of features like lakes, buildings, and residential areas. They can be also be used for more detailed mapping of many features you might normally map as points. **Click the {button} Area button to add a new area.**","corner":"Areas are drawn by placing nodes that mark the boundary of the area. **Click to place a starting node on one of the corners of the playground.**","place":"Draw the area by placing more nodes. Finish the area by clicking on the starting node. **Draw an area for the playground.**","search":"**Search for '{name}'.**","choose":"**Choose Playground from the list.**","describe":"**Add a name, then click the {button} button to close the feature editor**"},"lines":{"title":"Lines","add":"Lines are used to represent features such as roads, railroads, and rivers. **Click the {button} Line button to add a new line.**","start":"**Start the line by clicking on the end of the road.**","intersect":"Click to add more nodes to the line. You can drag the map while drawing if necessary. Roads, and many other types of lines, are part of a larger network. It is important for these lines to be connected properly in order for routing applications to work. **Click on {name} to create an intersection connecting the two lines.**","finish":"Lines can be finished by clicking on the last node again. **Finish drawing the road.**","road":"**Select Road from the list**","residential":"There are different types of roads, the most common of which is Residential. **Choose the Residential road type**","describe":"**Name the road, then click the {button} button to close the feature editor.**","restart":"The road needs to intersect {name}.","wrong_preset":"You didn't select the Residential road type. **Click here to choose again**"},"startediting":{"title":"Start Editing","help":"You can replay this walkthrough or view more documentation by clicking the {button} Help button.","save":"Don't forget to regularly save your changes!","start":"Start mapping!"}},"presets":{"categories":{"category-barrier":{"name":"Barrier Features"},"category-building":{"name":"Building Features"},"category-golf":{"name":"Golf Features"},"category-landuse":{"name":"Land Use Features"},"category-path":{"name":"Path Features"},"category-rail":{"name":"Rail Features"},"category-restriction":{"name":"Restriction Features"},"category-road":{"name":"Road Features"},"category-route":{"name":"Route Features"},"category-water-area":{"name":"Water Features"},"category-water-line":{"name":"Water Features"}},"fields":{"access":{"label":"Allowed Access","placeholder":"Not Specified","types":{"access":"All","foot":"Foot","motor_vehicle":"Motor Vehicles","bicycle":"Bicycles","horse":"Horses"},"options":{"yes":{"title":"Allowed","description":"Access permitted by law; a right of way"},"no":{"title":"Prohibited","description":"Access not permitted to the general public"},"permissive":{"title":"Permissive","description":"Access permitted until such time as the owner revokes the permission"},"private":{"title":"Private","description":"Access permitted only with permission of the owner on an individual basis"},"designated":{"title":"Designated","description":"Access permitted according to signs or specific local laws"},"destination":{"title":"Destination","description":"Access permitted only to reach a destination"},"dismount":{"title":"Dismount","description":"Access permitted but rider must dismount"}}},"access_simple":{"label":"Allowed Access","placeholder":"yes"},"access_toilets":{"label":"Access"},"address":{"label":"Address","placeholders":{"housename":"Housename","housenumber":"123","conscriptionnumber":"123","street":"Street","city":"City","postcode":"Postcode","place":"Place","hamlet":"Hamlet","suburb":"Suburb","subdistrict":"Subdistrict","district":"District","province":"Province","state":"State","country":"Country"}},"admin_level":{"label":"Admin Level"},"aerialway":{"label":"Type"},"aerialway/access":{"label":"Access","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aerialway/bubble":{"label":"Bubble"},"aerialway/capacity":{"label":"Capacity (per hour)","placeholder":"500, 2500, 5000..."},"aerialway/duration":{"label":"Duration (minutes)","placeholder":"1, 2, 3..."},"aerialway/heating":{"label":"Heated"},"aerialway/occupancy":{"label":"Occupancy","placeholder":"2, 4, 8..."},"aerialway/summer/access":{"label":"Access (summer)","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aeroway":{"label":"Type"},"amenity":{"label":"Type"},"area/highway":{"label":"Type"},"artist":{"label":"Artist"},"artwork_type":{"label":"Type"},"atm":{"label":"ATM"},"backrest":{"label":"Backrest"},"barrier":{"label":"Type"},"bench":{"label":"Bench"},"bicycle_parking":{"label":"Type"},"bin":{"label":"Waste Bin"},"boundary":{"label":"Type"},"brand":{"label":"Brand"},"building":{"label":"Building"},"building_area":{"label":"Building"},"capacity":{"label":"Capacity","placeholder":"50, 100, 200..."},"cardinal_direction":{"label":"Direction","options":{"N":"North","E":"East","S":"South","W":"West","NE":"Northeast","SE":"Southeast","SW":"Southwest","NW":"Northwest","NNE":"North-northeast","ENE":"East-northeast","ESE":"East-southeast","SSE":"South-southeast","SSW":"South-southwest","WSW":"West-southwest","WNW":"West-northwest","NNW":"North-northwest"}},"clock_direction":{"label":"Direction","options":{"clockwise":"Clockwise","anticlockwise":"Counterclockwise"}},"collection_times":{"label":"Collection Times"},"construction":{"label":"Type"},"content":{"label":"Contents"},"country":{"label":"Country"},"covered":{"label":"Covered"},"craft":{"label":"Type"},"crop":{"label":"Crop"},"crossing":{"label":"Type"},"cuisine":{"label":"Cuisine"},"currency_multi":{"label":"Currency Types"},"cycleway":{"label":"Bike Lanes","placeholder":"none","types":{"cycleway:left":"Left side","cycleway:right":"Right side"},"options":{"none":{"title":"None","description":"No bike lane"},"lane":{"title":"Standard bike lane","description":"A bike lane separated from auto traffic by a painted line"},"shared_lane":{"title":"Shared bike lane","description":"A bike lane with no separation from auto traffic"},"track":{"title":"Bike track","description":"A bike lane separated from traffic by a physical barrier"},"share_busway":{"title":"Bike lane shared with bus","description":"A bike lane shared with a bus lane"},"opposite_lane":{"title":"Opposite bike lane","description":"A bike lane that travels in the opposite direction of traffic"},"opposite":{"title":"Contraflow bike lane","description":"A bike lane that travels in both directions on a one-way street"}}},"delivery":{"label":"Delivery"},"denomination":{"label":"Denomination"},"denotation":{"label":"Denotation"},"description":{"label":"Description"},"diaper":{"label":"Diaper Changing Available"},"dock":{"label":"Type"},"drive_through":{"label":"Drive-Through"},"electrified":{"label":"Electrification","placeholder":"Contact Line, Electrified Rail...","options":{"contact_line":"Contact Line","rail":"Electrified Rail","yes":"Yes (unspecified)","no":"No"}},"elevation":{"label":"Elevation"},"emergency":{"label":"Emergency"},"entrance":{"label":"Type"},"except":{"label":"Exceptions"},"fax":{"label":"Fax","placeholder":"+31 42 123 4567"},"fee":{"label":"Fee"},"fire_hydrant/type":{"label":"Type","options":{"pillar":"Pillar/Aboveground","underground":"Underground","wall":"Wall","pond":"Pond"}},"fixme":{"label":"Fix Me"},"fuel":{"label":"Fuel"},"fuel_multi":{"label":"Fuel Types"},"gauge":{"label":"Gauge"},"gender":{"label":"Gender","placeholder":"Unknown","options":{"male":"Male","female":"Female","unisex":"Unisex"}},"generator/method":{"label":"Method"},"generator/source":{"label":"Source"},"generator/type":{"label":"Type"},"golf_hole":{"label":"Reference","placeholder":"Hole number (1-18)"},"handicap":{"label":"Handicap","placeholder":"1-18"},"handrail":{"label":"Handrail"},"highway":{"label":"Type"},"historic":{"label":"Type"},"hoops":{"label":"Hoops","placeholder":"1, 2, 4..."},"iata":{"label":"IATA"},"icao":{"label":"ICAO"},"incline":{"label":"Incline"},"incline_steps":{"label":"Incline","options":{"up":"Up","down":"Down"}},"information":{"label":"Type"},"internet_access":{"label":"Internet Access","options":{"yes":"Yes","no":"No","wlan":"Wifi","wired":"Wired","terminal":"Terminal"}},"lamp_type":{"label":"Type"},"landuse":{"label":"Type"},"lanes":{"label":"Lanes","placeholder":"1, 2, 3..."},"layer":{"label":"Layer"},"leaf_cycle":{"label":"Leaf Cycle","options":{"evergreen":"Evergreen","deciduous":"Deciduous","semi_evergreen":"Semi-Evergreen","semi_deciduous":"Semi-Deciduous","mixed":"Mixed"}},"leaf_cycle_singular":{"label":"Leaf Cycle","options":{"evergreen":"Evergreen","deciduous":"Deciduous","semi_evergreen":"Semi-Evergreen","semi_deciduous":"Semi-Deciduous"}},"leaf_type":{"label":"Leaf Type","options":{"broadleaved":"Broadleaved","needleleaved":"Needleleaved","mixed":"Mixed","leafless":"Leafless"}},"leaf_type_singular":{"label":"Leaf Type","options":{"broadleaved":"Broadleaved","needleleaved":"Needleleaved","leafless":"Leafless"}},"leisure":{"label":"Type"},"length":{"label":"Length (Meters)"},"level":{"label":"Level"},"levels":{"label":"Levels","placeholder":"2, 4, 6..."},"lit":{"label":"Lit"},"location":{"label":"Location"},"man_made":{"label":"Type"},"maxspeed":{"label":"Speed Limit","placeholder":"40, 50, 60..."},"maxstay":{"label":"Max Stay"},"mtb/scale":{"label":"Mountain Biking Difficulty","placeholder":"0, 1, 2, 3...","options":{"0":"0: Solid gravel/packed earth, no obstacles, wide curves","1":"1: Some loose surface, small obstacles, wide curves","2":"2: Much loose surface, large obstacles, easy hairpins","3":"3: Slippery surface, large obstacles, tight hairpins","4":"4: Loose surface or boulders, dangerous hairpins","5":"5: Maximum difficulty, boulder fields, landslides","6":"6: Not rideable except by the very best mountain bikers"}},"mtb/scale/imba":{"label":"IMBA Trail Difficulty","placeholder":"Easy, Medium, Difficult...","options":{"0":"Easiest (white circle)","1":"Easy (green circle)","2":"Medium (blue square)","3":"Difficult (black diamond)","4":"Extremely Difficult (double black diamond)"}},"mtb/scale/uphill":{"label":"Mountain Biking Uphill Difficulty","placeholder":"0, 1, 2, 3...","options":{"0":"0: Avg. incline <10%, gravel/packed earth, no obstacles","1":"1: Avg. incline <15%, gravel/packed earth, few small objects","2":"2: Avg. incline <20%, stable surface, fistsize rocks/roots","3":"3: Avg. incline <25%, variable surface, fistsize rocks/branches","4":"4: Avg. incline <30%, poor condition, big rocks/branches","5":"5: Very steep, bike generally needs to be pushed or carried"}},"name":{"label":"Name","placeholder":"Common name (if any)"},"natural":{"label":"Natural"},"network":{"label":"Network"},"note":{"label":"Note"},"office":{"label":"Type"},"oneway":{"label":"One Way","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"oneway_yes":{"label":"One Way","options":{"undefined":"Assumed to be Yes","yes":"Yes","no":"No"}},"opening_hours":{"label":"Hours"},"operator":{"label":"Operator"},"par":{"label":"Par","placeholder":"3, 4, 5..."},"park_ride":{"label":"Park and Ride"},"parking":{"label":"Type","options":{"surface":"Surface","multi-storey":"Multilevel","underground":"Underground","sheds":"Sheds","carports":"Carports","garage_boxes":"Garage Boxes","lane":"Roadside Lane"}},"payment_multi":{"label":"Payment Types"},"phone":{"label":"Phone","placeholder":"+31 42 123 4567"},"piste/difficulty":{"label":"Difficulty","placeholder":"Easy, Intermediate, Advanced...","options":{"novice":"Novice (instructional)","easy":"Easy (green circle)","intermediate":"Intermediate (blue square)","advanced":"Advanced (black diamond)","expert":"Expert (double black diamond)","freeride":"Freeride (off-piste)","extreme":"Extreme (climbing equipment required)"}},"piste/grooming":{"label":"Grooming","options":{"classic":"Classic","mogul":"Mogul","backcountry":"Backcountry","classic+skating":"Classic and Skating","scooter":"Scooter/Snowmobile","skating":"Skating"}},"piste/type":{"label":"Type","options":{"downhill":"Downhill","nordic":"Nordic","skitour":"Skitour","sled":"Sled","hike":"Hike","sleigh":"Sleigh","ice_skate":"Ice Skate","snow_park":"Snow Park","playground":"Playground"}},"place":{"label":"Type"},"population":{"label":"Population"},"power":{"label":"Type"},"power_supply":{"label":"Power Supply"},"railway":{"label":"Type"},"recycling_accepts":{"label":"Accepts"},"ref":{"label":"Reference"},"relation":{"label":"Type"},"religion":{"label":"Religion"},"restriction":{"label":"Type"},"restrictions":{"label":"Turn Restrictions"},"route":{"label":"Type"},"route_master":{"label":"Type"},"sac_scale":{"label":"Hiking Difficulty","placeholder":"Mountain Hiking, Alpine Hiking...","options":{"hiking":"T1: Hiking","mountain_hiking":"T2: Mountain Hiking","demanding_mountain_hiking":"T3: Demanding Mountain Hiking","alpine_hiking":"T4: Alpine Hiking","demanding_alpine_hiking":"T5: Demanding Alpine Hiking","difficult_alpine_hiking":"T6: Difficult Alpine Hiking"}},"sanitary_dump_station":{"label":"Toilet Disposal"},"seasonal":{"label":"Seasonal"},"service":{"label":"Type"},"service/bicycle/chain_tool":{"label":"Chain Tool","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service/bicycle/pump":{"label":"Air Pump","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service_rail":{"label":"Service Type","options":{"spur":"Spur","yard":"Yard","siding":"Siding","crossover":"Crossover"}},"shelter":{"label":"Shelter"},"shelter_type":{"label":"Type"},"shop":{"label":"Type"},"site":{"label":"Type"},"sloped_curb":{"label":"Sloped Curb"},"smoking":{"label":"Smoking","placeholder":"No, Separated, Yes...","options":{"no":"No smoking anywhere","separated":"In smoking areas, not physically isolated","isolated":"In smoking areas, physically isolated","outside":"Allowed outside","yes":"Allowed everywhere","dedicated":"Dedicated to smokers (e.g. smokers' club)"}},"smoothness":{"label":"Smoothness","placeholder":"Thin Rollers, Wheels, Off-Road...","options":{"excellent":"Thin Rollers: rollerblade, skateboard","good":"Thin Wheels: racing bike","intermediate":"Wheels: city bike, wheelchair, scooter","bad":"Robust Wheels: trekking bike, car, rickshaw","very_bad":"High Clearance: light duty off-road vehicle","horrible":"Off-Road: heavy duty off-road vehicle","very_horrible":"Specialized off-road: tractor, ATV","impassable":"Impassable / No wheeled vehicle"}},"social_facility_for":{"label":"People served","placeholder":"Homeless, Disabled, Child, etc"},"source":{"label":"Source"},"sport":{"label":"Sport"},"sport_ice":{"label":"Sport"},"sport_racing":{"label":"Sport"},"structure":{"label":"Structure","placeholder":"Unknown","options":{"bridge":"Bridge","tunnel":"Tunnel","embankment":"Embankment","cutting":"Cutting","ford":"Ford"}},"studio":{"label":"Type"},"substation":{"label":"Type"},"supervised":{"label":"Supervised"},"surface":{"label":"Surface"},"tactile_paving":{"label":"Tactile Paving"},"takeaway":{"label":"Takeaway","placeholder":"Yes, No, Takeaway Only...","options":{"yes":"Yes","no":"No","only":"Takeaway Only"}},"toilets/disposal":{"label":"Disposal","options":{"flush":"Flush","pitlatrine":"Pit/Latrine","chemical":"Chemical","bucket":"Bucket"}},"tourism":{"label":"Type"},"towertype":{"label":"Tower type"},"tracktype":{"label":"Track Type","placeholder":"Solid, Mostly Solid, Soft...","options":{"grade1":"Solid: paved or heavily compacted hardcore surface","grade2":"Mostly Solid: gravel/rock with some soft material mixed in","grade3":"Even mixture of hard and soft materials","grade4":"Mostly Soft: soil/sand/grass with some hard material mixed in","grade5":"Soft: soil/sand/grass"}},"traffic_signals":{"label":"Type"},"trail_visibility":{"label":"Trail Visibility","placeholder":"Excellent, Good, Bad...","options":{"excellent":"Excellent: unambiguous path or markers everywhere","good":"Good: markers visible, sometimes require searching","intermediate":"Intermediate: few markers, path mostly visible","bad":"Bad: no markers, path sometimes invisible/pathless","horrible":"Horrible: often pathless, some orientation skills required","no":"No: pathless, excellent orientation skills required"}},"trees":{"label":"Trees"},"tunnel":{"label":"Tunnel"},"vending":{"label":"Type of Goods"},"water":{"label":"Type"},"water_point":{"label":"Water Point"},"waterway":{"label":"Type"},"website":{"label":"Website","placeholder":"http://example.com/"},"wetland":{"label":"Type"},"wheelchair":{"label":"Wheelchair Access"},"width":{"label":"Width (Meters)"},"wikipedia":{"label":"Wikipedia"}},"presets":{"address":{"name":"Address","terms":""},"aerialway":{"name":"Aerialway","terms":"ski lift,funifor,funitel"},"aerialway/cable_car":{"name":"Cable Car","terms":"tramway,ropeway"},"aerialway/chair_lift":{"name":"Chair Lift","terms":""},"aerialway/gondola":{"name":"Gondola","terms":""},"aerialway/magic_carpet":{"name":"Magic Carpet Lift","terms":""},"aerialway/platter":{"name":"Platter Lift","terms":"button lift,poma lift"},"aerialway/pylon":{"name":"Aerialway Pylon","terms":""},"aerialway/rope_tow":{"name":"Rope Tow Lift","terms":"handle tow,bugel lift"},"aerialway/station":{"name":"Aerialway Station","terms":""},"aerialway/t-bar":{"name":"T-bar Lift","terms":""},"aeroway":{"name":"Aeroway","terms":""},"aeroway/aerodrome":{"name":"Airport","terms":"airplane,airport,aerodrome"},"aeroway/apron":{"name":"Apron","terms":"ramp"},"aeroway/gate":{"name":"Airport gate","terms":""},"aeroway/hangar":{"name":"Hangar","terms":""},"aeroway/helipad":{"name":"Helipad","terms":"helicopter,helipad,heliport"},"aeroway/runway":{"name":"Runway","terms":"landing strip"},"aeroway/taxiway":{"name":"Taxiway","terms":""},"aeroway/terminal":{"name":"Airport terminal","terms":"airport,aerodrome"},"amenity":{"name":"Amenity","terms":""},"amenity/arts_centre":{"name":"Arts Center","terms":""},"amenity/atm":{"name":"ATM","terms":"money,cash,machine"},"amenity/bank":{"name":"Bank","terms":"credit union,check,deposit,fund,investment,repository,reserve,safe,savings,stock,treasury,trust,vault"},"amenity/bar":{"name":"Bar","terms":"dive,beer,bier,booze"},"amenity/bbq":{"name":"Barbecue/Grill","terms":"bbq,grill"},"amenity/bench":{"name":"Bench","terms":"seat"},"amenity/bicycle_parking":{"name":"Bicycle Parking","terms":"bike"},"amenity/bicycle_rental":{"name":"Bicycle Rental","terms":"bike"},"amenity/bicycle_repair_station":{"name":"Bicycle Repair Tool Stand","terms":"bike,repair,chain,pump"},"amenity/biergarten":{"name":"Beer Garden","terms":"beer,bier,booze"},"amenity/boat_rental":{"name":"Boat Rental","terms":""},"amenity/bureau_de_change":{"name":"Currency Exchange","terms":"bureau de change,money changer"},"amenity/bus_station":{"name":"Bus Station","terms":""},"amenity/cafe":{"name":"Cafe","terms":"bistro,coffee,tea"},"amenity/car_rental":{"name":"Car Rental","terms":""},"amenity/car_sharing":{"name":"Car Sharing","terms":""},"amenity/car_wash":{"name":"Car Wash","terms":""},"amenity/casino":{"name":"Casino","terms":"gambling,roulette,craps,poker,blackjack"},"amenity/charging_station":{"name":"Charging Station","terms":"EV,Electric Vehicle,Supercharger"},"amenity/childcare":{"name":"Nursery/Childcare","terms":"daycare,orphanage,playgroup"},"amenity/cinema":{"name":"Cinema","terms":"drive-in,film,flick,movie,theater,picture,show,screen"},"amenity/clinic":{"name":"Clinic","terms":"medical,urgentcare"},"amenity/clock":{"name":"Clock","terms":""},"amenity/college":{"name":"College Grounds","terms":"university"},"amenity/community_centre":{"name":"Community Center","terms":"event,hall"},"amenity/compressed_air":{"name":"Compressed Air","terms":""},"amenity/courthouse":{"name":"Courthouse","terms":""},"amenity/dentist":{"name":"Dentist","terms":"tooth,teeth"},"amenity/doctors":{"name":"Doctor","terms":"medic*"},"amenity/dojo":{"name":"Dojo / Martial Arts Academy","terms":"martial arts,dojang"},"amenity/drinking_water":{"name":"Drinking Water","terms":"fountain,potable"},"amenity/embassy":{"name":"Embassy","terms":""},"amenity/fast_food":{"name":"Fast Food","terms":"restaurant"},"amenity/ferry_terminal":{"name":"Ferry Terminal","terms":""},"amenity/fire_station":{"name":"Fire Station","terms":""},"amenity/fountain":{"name":"Fountain","terms":""},"amenity/fuel":{"name":"Gas Station","terms":"petrol,fuel,gasoline,propane,diesel,lng,cng,biodiesel"},"amenity/grave_yard":{"name":"Graveyard","terms":""},"amenity/grit_bin":{"name":"Grit Bin","terms":"salt,sand"},"amenity/hospital":{"name":"Hospital Grounds","terms":"clinic,doctor,emergency room,health service,hospice,infirmary,institution,nursing home,sanatorium,sanitarium,sick,surgery,ward"},"amenity/hunting_stand":{"name":"Hunting Stand","terms":""},"amenity/kindergarten":{"name":"Preschool/Kindergarten Grounds","terms":"kindergarden,pre-school"},"amenity/library":{"name":"Library","terms":"book"},"amenity/marketplace":{"name":"Marketplace","terms":""},"amenity/motorcycle_parking":{"name":"Motorcycle Parking","terms":""},"amenity/nightclub":{"name":"Nightclub","terms":"disco*,night club,dancing,dance club"},"amenity/parking":{"name":"Car Parking","terms":""},"amenity/parking_entrance":{"name":"Parking Garage Entrance/Exit","terms":""},"amenity/parking_space":{"name":"Parking Space","terms":""},"amenity/pharmacy":{"name":"Pharmacy","terms":"drug,medicine"},"amenity/place_of_worship":{"name":"Place of Worship","terms":"abbey,basilica,bethel,cathedral,chancel,chantry,chapel,church,fold,house of God,house of prayer,house of worship,minster,mission,mosque,oratory,parish,sacellum,sanctuary,shrine,synagogue,tabernacle,temple"},"amenity/place_of_worship/buddhist":{"name":"Buddhist Temple","terms":"stupa,vihara,monastery,temple,pagoda,zendo,dojo"},"amenity/place_of_worship/christian":{"name":"Church","terms":"christian,abbey,basilica,bethel,cathedral,chancel,chantry,chapel,fold,house of God,house of prayer,house of worship,minster,mission,oratory,parish,sacellum,sanctuary,shrine,tabernacle,temple"},"amenity/place_of_worship/jewish":{"name":"Synagogue","terms":"jewish"},"amenity/place_of_worship/muslim":{"name":"Mosque","terms":"muslim"},"amenity/police":{"name":"Police","terms":"badge,constable,constabulary,cop,detective,fed,law,enforcement,officer,patrol"},"amenity/post_box":{"name":"Mailbox","terms":"letter,post"},"amenity/post_office":{"name":"Post Office","terms":"letter,mail"},"amenity/prison":{"name":"Prison Grounds","terms":"cell,jail"},"amenity/pub":{"name":"Pub","terms":"dive,beer,bier,booze"},"amenity/public_bookcase":{"name":"Public Bookcase","terms":"library,bookcrossing"},"amenity/ranger_station":{"name":"Ranger Station","terms":"visitor center,visitor centre,permit center,permit centre,backcountry office,warden office,warden center"},"amenity/recycling":{"name":"Recycling","terms":"can,bottle,garbage,scrap,trash"},"amenity/register_office":{"name":"Register Office","terms":""},"amenity/restaurant":{"name":"Restaurant","terms":"bar,breakfast,cafe,café,canteen,coffee,dine,dining,dinner,drive-in,eat,grill,lunch,table"},"amenity/sanitary_dump_station":{"name":"RV Toilet Disposal","terms":"Motor Home,Camper,Sanitary,Dump Station,Elsan,CDP,CTDP,Chemical Toilet"},"amenity/school":{"name":"School Grounds","terms":"academy,elementary school,middle school,high school"},"amenity/shelter":{"name":"Shelter","terms":"lean-to,gazebo,picnic"},"amenity/social_facility":{"name":"Social Facility","terms":""},"amenity/social_facility/food_bank":{"name":"Food Bank","terms":""},"amenity/social_facility/group_home":{"name":"Elderly Group Home","terms":"old,senior,living"},"amenity/social_facility/homeless_shelter":{"name":"Homeless Shelter","terms":"houseless,unhoused,displaced"},"amenity/studio":{"name":"Studio","terms":"recording,radio,television"},"amenity/swimming_pool":{"name":"Swimming Pool","terms":""},"amenity/taxi":{"name":"Taxi Stand","terms":"cab"},"amenity/telephone":{"name":"Telephone","terms":"phone"},"amenity/theatre":{"name":"Theater","terms":"theatre,performance,play,musical"},"amenity/toilets":{"name":"Toilets","terms":"bathroom,restroom,outhouse,privy,head,lavatory,latrine,water closet,WC,W.C."},"amenity/townhall":{"name":"Town Hall","terms":"village,city,government,courthouse,municipal"},"amenity/university":{"name":"University Grounds","terms":"college"},"amenity/vending_machine/cigarettes":{"name":"Cigarette Vending Machine","terms":"cigarette"},"amenity/vending_machine/condoms":{"name":"Condom Vending Machine","terms":"condom"},"amenity/vending_machine/drinks":{"name":"Drink Vending Machine","terms":"drink,soda,beverage,juice,pop"},"amenity/vending_machine/excrement_bags":{"name":"Excrement Bag Vending Machine","terms":"excrement bags,poop,dog,animal"},"amenity/vending_machine/news_papers":{"name":"Newspaper Vending Machine","terms":"newspaper"},"amenity/vending_machine/parcel_pickup_dropoff":{"name":"Parcel Pickup/Dropoff Vending Machine","terms":"parcel,mail,pickup"},"amenity/vending_machine/parking_tickets":{"name":"Parking Ticket Vending Machine","terms":"parking,ticket"},"amenity/vending_machine/public_transport_tickets":{"name":"Transit Ticket Vending Machine","terms":"bus,train,ferry,rail,ticket,transportation"},"amenity/vending_machine/sweets":{"name":"Snack Vending Machine","terms":"candy,gum,chip,pretzel,cookie,cracker"},"amenity/vending_machine/vending_machine":{"name":"Vending Machine","terms":""},"amenity/veterinary":{"name":"Veterinary","terms":"pet clinic,veterinarian,animal hospital,pet doctor"},"amenity/waste_basket":{"name":"Waste Basket","terms":"bin,rubbish,litter,trash,garbage"},"amenity/waste_disposal":{"name":"Garbage Dumpster","terms":"rubbish,litter,trash"},"amenity/water_point":{"name":"RV Drinking Water","terms":""},"area":{"name":"Area","terms":""},"area/highway":{"name":"Road Surface","terms":""},"barrier":{"name":"Barrier","terms":""},"barrier/block":{"name":"Block","terms":""},"barrier/bollard":{"name":"Bollard","terms":""},"barrier/cattle_grid":{"name":"Cattle Grid","terms":""},"barrier/city_wall":{"name":"City Wall","terms":""},"barrier/cycle_barrier":{"name":"Cycle Barrier","terms":""},"barrier/ditch":{"name":"Trench","terms":""},"barrier/entrance":{"name":"Entrance","terms":""},"barrier/fence":{"name":"Fence","terms":""},"barrier/gate":{"name":"Gate","terms":""},"barrier/hedge":{"name":"Hedge","terms":""},"barrier/kissing_gate":{"name":"Kissing Gate","terms":""},"barrier/lift_gate":{"name":"Lift Gate","terms":""},"barrier/retaining_wall":{"name":"Retaining Wall","terms":""},"barrier/stile":{"name":"Stile","terms":""},"barrier/toll_booth":{"name":"Toll Booth","terms":""},"barrier/wall":{"name":"Wall","terms":""},"boundary/administrative":{"name":"Administrative Boundary","terms":""},"building":{"name":"Building","terms":""},"building/apartments":{"name":"Apartments","terms":""},"building/barn":{"name":"Barn","terms":""},"building/bunker":{"name":"Bunker","terms":""},"building/cabin":{"name":"Cabin","terms":""},"building/cathedral":{"name":"Cathedral Building","terms":""},"building/chapel":{"name":"Chapel Building","terms":""},"building/church":{"name":"Church Building","terms":""},"building/college":{"name":"College Building","terms":"university"},"building/commercial":{"name":"Commercial Building","terms":""},"building/construction":{"name":"Building Under Construction","terms":""},"building/detached":{"name":"Detached House","terms":"home,single,family,residence,dwelling"},"building/dormitory":{"name":"Dormitory","terms":""},"building/entrance":{"name":"Entrance/Exit","terms":""},"building/garage":{"name":"Garage","terms":""},"building/garages":{"name":"Garages","terms":""},"building/greenhouse":{"name":"Greenhouse","terms":""},"building/hospital":{"name":"Hospital Building","terms":""},"building/hotel":{"name":"Hotel Building","terms":""},"building/house":{"name":"House","terms":"home,family,residence,dwelling"},"building/hut":{"name":"Hut","terms":""},"building/industrial":{"name":"Industrial Building","terms":""},"building/kindergarten":{"name":"Preschool/Kindergarten Building","terms":"kindergarden,pre-school"},"building/public":{"name":"Public Building","terms":""},"building/residential":{"name":"Residential Building","terms":""},"building/retail":{"name":"Retail Building","terms":""},"building/roof":{"name":"Roof","terms":""},"building/school":{"name":"School Building","terms":"academy,elementary school,middle school,high school"},"building/semidetached_house":{"name":"Semi-Detached House","terms":"home,double,duplex,twin,family,residence,dwelling"},"building/shed":{"name":"Shed","terms":""},"building/stable":{"name":"Stable","terms":""},"building/static_caravan":{"name":"Static Mobile Home","terms":""},"building/terrace":{"name":"Row Houses","terms":"home,terrace,brownstone,family,residence,dwelling"},"building/train_station":{"name":"Train Station","terms":""},"building/university":{"name":"University Building","terms":"college"},"building/warehouse":{"name":"Warehouse","terms":""},"craft":{"name":"Craft","terms":""},"craft/basket_maker":{"name":"Basket Maker","terms":""},"craft/beekeeper":{"name":"Beekeeper","terms":""},"craft/blacksmith":{"name":"Blacksmith","terms":""},"craft/boatbuilder":{"name":"Boat Builder","terms":""},"craft/bookbinder":{"name":"Bookbinder","terms":"book repair"},"craft/brewery":{"name":"Brewery","terms":"beer,bier"},"craft/carpenter":{"name":"Carpenter","terms":"woodworker"},"craft/carpet_layer":{"name":"Carpet Layer","terms":""},"craft/caterer":{"name":"Caterer","terms":""},"craft/clockmaker":{"name":"Clockmaker","terms":""},"craft/confectionery":{"name":"Confectionery","terms":"sweets,candy"},"craft/dressmaker":{"name":"Dressmaker","terms":"seamstress"},"craft/electrician":{"name":"Electrician","terms":"power,wire"},"craft/gardener":{"name":"Gardener","terms":"landscaper,grounds keeper"},"craft/glaziery":{"name":"Glaziery","terms":"glass,stained-glass,window"},"craft/handicraft":{"name":"Handicraft","terms":""},"craft/hvac":{"name":"HVAC","terms":"heat*,vent*,air conditioning"},"craft/insulator":{"name":"Insulator","terms":""},"craft/jeweler":{"name":"Jeweler","terms":""},"craft/key_cutter":{"name":"Key Cutter","terms":""},"craft/locksmith":{"name":"Locksmith","terms":""},"craft/metal_construction":{"name":"Metal Construction","terms":""},"craft/optician":{"name":"Optician","terms":""},"craft/painter":{"name":"Painter","terms":""},"craft/photographer":{"name":"Photographer","terms":""},"craft/photographic_laboratory":{"name":"Photographic Laboratory","terms":"film"},"craft/plasterer":{"name":"Plasterer","terms":""},"craft/plumber":{"name":"Plumber","terms":"pipe"},"craft/pottery":{"name":"Pottery","terms":"ceramic"},"craft/rigger":{"name":"Rigger","terms":""},"craft/roofer":{"name":"Roofer","terms":""},"craft/saddler":{"name":"Saddler","terms":""},"craft/sailmaker":{"name":"Sailmaker","terms":""},"craft/sawmill":{"name":"Sawmill","terms":"lumber"},"craft/scaffolder":{"name":"Scaffolder","terms":""},"craft/sculpter":{"name":"Sculpter","terms":""},"craft/shoemaker":{"name":"Shoemaker","terms":"cobbler"},"craft/stonemason":{"name":"Stonemason","terms":"masonry"},"craft/sweep":{"name":"Chimney Sweep","terms":""},"craft/tailor":{"name":"Tailor","terms":"clothes,suit"},"craft/tiler":{"name":"Tiler","terms":""},"craft/tinsmith":{"name":"Tinsmith","terms":""},"craft/upholsterer":{"name":"Upholsterer","terms":""},"craft/watchmaker":{"name":"Watchmaker","terms":""},"craft/window_construction":{"name":"Window Construction","terms":"glass"},"craft/winery":{"name":"Winery","terms":""},"embankment":{"name":"Embankment","terms":""},"emergency/ambulance_station":{"name":"Ambulance Station","terms":"EMS,EMT,rescue"},"emergency/fire_hydrant":{"name":"Fire Hydrant","terms":""},"emergency/phone":{"name":"Emergency Phone","terms":""},"entrance":{"name":"Entrance/Exit","terms":""},"footway/crossing":{"name":"Street Crossing","terms":""},"footway/crosswalk":{"name":"Pedestrian Crosswalk","terms":"zebra crossing"},"footway/sidewalk":{"name":"Sidewalk","terms":""},"ford":{"name":"Ford","terms":""},"golf/bunker":{"name":"Sand Trap","terms":"hazard,bunker"},"golf/fairway":{"name":"Fairway","terms":""},"golf/green":{"name":"Putting Green","terms":""},"golf/hole":{"name":"Golf Hole","terms":""},"golf/lateral_water_hazard":{"name":"Lateral Water Hazard","terms":""},"golf/rough":{"name":"Rough","terms":""},"golf/tee":{"name":"Tee Box","terms":"teeing ground"},"golf/water_hazard":{"name":"Water Hazard","terms":""},"highway":{"name":"Highway","terms":""},"highway/bridleway":{"name":"Bridle Path","terms":"bridleway,equestrian,horse"},"highway/bus_stop":{"name":"Bus Stop","terms":""},"highway/corridor":{"name":"Indoor Corridor","terms":"gallery,hall,hallway,indoor,passage,passageway"},"highway/crossing":{"name":"Street Crossing","terms":""},"highway/crosswalk":{"name":"Pedestrian Crosswalk","terms":"zebra crossing"},"highway/cycleway":{"name":"Cycle Path","terms":"bike"},"highway/footway":{"name":"Foot Path","terms":"hike,hiking,trackway,trail,walk"},"highway/living_street":{"name":"Living Street","terms":""},"highway/mini_roundabout":{"name":"Mini-Roundabout","terms":""},"highway/motorway":{"name":"Motorway","terms":""},"highway/motorway_junction":{"name":"Motorway Junction / Exit","terms":""},"highway/motorway_link":{"name":"Motorway Link","terms":"ramp,on ramp,off ramp"},"highway/path":{"name":"Path","terms":"hike,hiking,trackway,trail,walk"},"highway/pedestrian":{"name":"Pedestrian Street","terms":""},"highway/primary":{"name":"Primary Road","terms":""},"highway/primary_link":{"name":"Primary Link","terms":"ramp,on ramp,off ramp"},"highway/raceway":{"name":"Motor Raceway","terms":"auto*,race*,nascar"},"highway/residential":{"name":"Residential Road","terms":""},"highway/rest_area":{"name":"Rest Area","terms":"rest stop"},"highway/road":{"name":"Unknown Road","terms":""},"highway/secondary":{"name":"Secondary Road","terms":""},"highway/secondary_link":{"name":"Secondary Link","terms":"ramp,on ramp,off ramp"},"highway/service":{"name":"Service Road","terms":""},"highway/service/alley":{"name":"Alley","terms":""},"highway/service/drive-through":{"name":"Drive-Through","terms":""},"highway/service/driveway":{"name":"Driveway","terms":""},"highway/service/emergency_access":{"name":"Emergency Access","terms":""},"highway/service/parking_aisle":{"name":"Parking Aisle","terms":""},"highway/services":{"name":"Service Area","terms":"services,travel plaza,service station"},"highway/steps":{"name":"Steps","terms":"stairs,staircase"},"highway/stop":{"name":"Stop Sign","terms":"stop sign"},"highway/street_lamp":{"name":"Street Lamp","terms":"streetlight,street light,lamp,light,gaslight"},"highway/tertiary":{"name":"Tertiary Road","terms":""},"highway/tertiary_link":{"name":"Tertiary Link","terms":"ramp,on ramp,off ramp"},"highway/track":{"name":"Unmaintained Track Road","terms":"woods road,forest road,logging road,fire road,farm road,agricultural road,ranch road,carriage road,primitive,unmaintained,rut,offroad,4wd,4x4,four wheel drive,atv,quad,jeep,double track,two track"},"highway/traffic_signals":{"name":"Traffic Signals","terms":"light,stoplight,traffic light"},"highway/trunk":{"name":"Trunk Road","terms":""},"highway/trunk_link":{"name":"Trunk Link","terms":"ramp,on ramp,off ramp"},"highway/turning_circle":{"name":"Turning Circle","terms":"cul-de-sac"},"highway/unclassified":{"name":"Minor/Unclassified Road","terms":""},"historic":{"name":"Historic Site","terms":""},"historic/archaeological_site":{"name":"Archaeological Site","terms":""},"historic/boundary_stone":{"name":"Boundary Stone","terms":""},"historic/castle":{"name":"Castle","terms":""},"historic/memorial":{"name":"Memorial","terms":""},"historic/monument":{"name":"Monument","terms":""},"historic/ruins":{"name":"Ruins","terms":""},"historic/wayside_cross":{"name":"Wayside Cross","terms":""},"historic/wayside_shrine":{"name":"Wayside Shrine","terms":""},"junction":{"name":"Junction","terms":""},"landuse":{"name":"Land Use","terms":""},"landuse/allotments":{"name":"Community Garden","terms":"allotment,garden"},"landuse/basin":{"name":"Basin","terms":""},"landuse/cemetery":{"name":"Cemetery","terms":""},"landuse/churchyard":{"name":"Churchyard","terms":""},"landuse/commercial":{"name":"Commercial Area","terms":""},"landuse/construction":{"name":"Construction","terms":""},"landuse/farm":{"name":"Farmland","terms":""},"landuse/farmland":{"name":"Farmland","terms":""},"landuse/farmyard":{"name":"Farmyard","terms":""},"landuse/forest":{"name":"Forest","terms":"tree"},"landuse/garages":{"name":"Garages","terms":""},"landuse/grass":{"name":"Grass","terms":""},"landuse/industrial":{"name":"Industrial Area","terms":""},"landuse/landfill":{"name":"Landfill","terms":"dump"},"landuse/meadow":{"name":"Meadow","terms":""},"landuse/military":{"name":"Military Area","terms":""},"landuse/orchard":{"name":"Orchard","terms":""},"landuse/plant_nursery":{"name":"Plant Nursery","terms":"vivero"},"landuse/quarry":{"name":"Quarry","terms":""},"landuse/residential":{"name":"Residential Area","terms":""},"landuse/retail":{"name":"Retail Area","terms":""},"landuse/vineyard":{"name":"Vineyard","terms":""},"leisure":{"name":"Leisure","terms":""},"leisure/adult_gaming_centre":{"name":"Adult Gaming Center","terms":"gambling,slot machine"},"leisure/bird_hide":{"name":"Bird Hide","terms":"machan,ornithology"},"leisure/bowling_alley":{"name":"Bowling Alley","terms":""},"leisure/common":{"name":"Common","terms":"open space"},"leisure/dog_park":{"name":"Dog Park","terms":""},"leisure/firepit":{"name":"Firepit","terms":"fireplace,campfire"},"leisure/garden":{"name":"Garden","terms":""},"leisure/golf_course":{"name":"Golf Course","terms":"links"},"leisure/ice_rink":{"name":"Ice Rink","terms":"hockey,skating,curling"},"leisure/marina":{"name":"Marina","terms":"boat"},"leisure/nature_reserve":{"name":"Nature Reserve","terms":"protected,wildlife"},"leisure/park":{"name":"Park","terms":"esplanade,estate,forest,garden,grass,green,grounds,lawn,lot,meadow,parkland,place,playground,plaza,pleasure garden,recreation area,square,tract,village green,woodland"},"leisure/picnic_table":{"name":"Picnic Table","terms":"bench"},"leisure/pitch":{"name":"Sport Pitch","terms":"field"},"leisure/pitch/american_football":{"name":"American Football Field","terms":""},"leisure/pitch/baseball":{"name":"Baseball Diamond","terms":""},"leisure/pitch/basketball":{"name":"Basketball Court","terms":""},"leisure/pitch/rugby_league":{"name":"Rugby League Field","terms":""},"leisure/pitch/rugby_union":{"name":"Rugby Union Field","terms":""},"leisure/pitch/skateboard":{"name":"Skate Park","terms":""},"leisure/pitch/soccer":{"name":"Soccer Field","terms":""},"leisure/pitch/tennis":{"name":"Tennis Court","terms":""},"leisure/pitch/volleyball":{"name":"Volleyball Court","terms":""},"leisure/playground":{"name":"Playground","terms":"jungle gym,play area"},"leisure/running_track":{"name":"Running Track","terms":""},"leisure/slipway":{"name":"Slipway","terms":"boat launch,boat ramp"},"leisure/sports_centre":{"name":"Sports Center / Gym","terms":"gym"},"leisure/sports_centre/swimming":{"name":"Swimming Pool Facility","terms":"dive,water"},"leisure/stadium":{"name":"Stadium","terms":""},"leisure/swimming_pool":{"name":"Swimming Pool","terms":"dive,water"},"leisure/track":{"name":"Racetrack (non-Motorsport)","terms":""},"leisure/water_park":{"name":"Water Park","terms":"swim,pool,dive"},"line":{"name":"Line","terms":""},"man_made":{"name":"Man Made","terms":""},"man_made/adit":{"name":"Adit","terms":"entrance,underground,mine,cave"},"man_made/breakwater":{"name":"Breakwater","terms":""},"man_made/chimney":{"name":"Chimney","terms":""},"man_made/cutline":{"name":"Cut line","terms":""},"man_made/embankment":{"name":"Embankment","terms":""},"man_made/flagpole":{"name":"Flagpole","terms":""},"man_made/gasometer":{"name":"Gasometer","terms":"gas holder"},"man_made/groyne":{"name":"Groyne","terms":""},"man_made/lighthouse":{"name":"Lighthouse","terms":""},"man_made/mast":{"name":"Radio Mast","terms":"broadcast tower,cell phone tower,cell tower,guyed tower,mobile phone tower,radio tower,television tower,transmission mast,transmission tower,tv tower"},"man_made/observation":{"name":"Observation Tower","terms":"lookout tower,fire tower"},"man_made/petroleum_well":{"name":"Oil Well","terms":"drilling rig,oil derrick,oil drill,oil horse,oil rig,oil pump,petroleum well,pumpjack"},"man_made/pier":{"name":"Pier","terms":"dock"},"man_made/pipeline":{"name":"Pipeline","terms":""},"man_made/silo":{"name":"Silo","terms":"grain,corn,wheat"},"man_made/storage_tank":{"name":"Storage Tank","terms":"water,oil,gas,petrol"},"man_made/surveillance":{"name":"Surveillance","terms":""},"man_made/survey_point":{"name":"Survey Point","terms":""},"man_made/tower":{"name":"Tower","terms":""},"man_made/wastewater_plant":{"name":"Wastewater Plant","terms":"sewage*,water treatment plant,reclamation plant"},"man_made/water_tower":{"name":"Water Tower","terms":""},"man_made/water_well":{"name":"Water Well","terms":""},"man_made/water_works":{"name":"Water Works","terms":""},"man_made/works":{"name":"Works","terms":"car assembly plant,aluminium processing plant,brewery,furniture manufacture factory,oil refinery,plastic recycling"},"military/airfield":{"name":"Airfield","terms":""},"military/barracks":{"name":"Barracks","terms":""},"military/bunker":{"name":"Bunker","terms":""},"military/checkpoint":{"name":"Checkpoint","terms":""},"military/danger_area":{"name":"Danger Area","terms":""},"military/naval_base":{"name":"Naval Base","terms":""},"military/obstacle_course":{"name":"Obstacle Course","terms":""},"military/range":{"name":"Military Range","terms":""},"military/training_area":{"name":"Training area","terms":""},"natural":{"name":"Natural","terms":""},"natural/bay":{"name":"Bay","terms":""},"natural/beach":{"name":"Beach","terms":""},"natural/cave_entrance":{"name":"Cave Entrance","terms":"cavern,hollow,grotto,shelter,cavity"},"natural/cliff":{"name":"Cliff","terms":""},"natural/coastline":{"name":"Coastline","terms":"shore"},"natural/fell":{"name":"Fell","terms":""},"natural/glacier":{"name":"Glacier","terms":""},"natural/grassland":{"name":"Grassland","terms":""},"natural/heath":{"name":"Heath","terms":""},"natural/peak":{"name":"Peak","terms":"acme,aiguille,alp,climax,crest,crown,hill,mount,mountain,pinnacle,summit,tip,top"},"natural/saddle":{"name":"Saddle","terms":"pass,mountain pass,top"},"natural/scree":{"name":"Scree","terms":"loose rocks"},"natural/scrub":{"name":"Scrub","terms":"bush,shrubs"},"natural/spring":{"name":"Spring","terms":""},"natural/tree":{"name":"Tree","terms":""},"natural/tree_row":{"name":"Tree row","terms":""},"natural/volcano":{"name":"Volcano","terms":"mountain,crater"},"natural/water":{"name":"Water","terms":""},"natural/water/lake":{"name":"Lake","terms":"lakelet,loch,mere"},"natural/water/pond":{"name":"Pond","terms":"lakelet,millpond,tarn,pool,mere"},"natural/water/reservoir":{"name":"Reservoir","terms":""},"natural/wetland":{"name":"Wetland","terms":""},"natural/wood":{"name":"Wood","terms":"tree"},"office":{"name":"Office","terms":""},"office/accountant":{"name":"Accountant","terms":""},"office/administrative":{"name":"Administrative Office","terms":""},"office/architect":{"name":"Architect","terms":""},"office/company":{"name":"Company Office","terms":""},"office/educational_institution":{"name":"Educational Institution","terms":""},"office/employment_agency":{"name":"Employment Agency","terms":"job"},"office/estate_agent":{"name":"Real Estate Office","terms":""},"office/financial":{"name":"Financial Office","terms":""},"office/government":{"name":"Government Office","terms":""},"office/insurance":{"name":"Insurance Office","terms":""},"office/it":{"name":"IT Office","terms":""},"office/lawyer":{"name":"Law Office","terms":""},"office/newspaper":{"name":"Newspaper","terms":""},"office/ngo":{"name":"NGO Office","terms":""},"office/physician":{"name":"Physician","terms":""},"office/political_party":{"name":"Political Party","terms":""},"office/research":{"name":"Research Office","terms":""},"office/telecommunication":{"name":"Telecom Office","terms":""},"office/therapist":{"name":"Therapist","terms":""},"office/travel_agent":{"name":"Travel Agency","terms":""},"piste":{"name":"Piste/Ski Trail","terms":"ski,sled,sleigh,snowboard,nordic,downhill,snowmobile"},"place":{"name":"Place","terms":""},"place/city":{"name":"City","terms":""},"place/farm":{"name":"Farm","terms":""},"place/hamlet":{"name":"Hamlet","terms":""},"place/island":{"name":"Island","terms":"archipelago,atoll,bar,cay,isle,islet,key,reef"},"place/isolated_dwelling":{"name":"Isolated Dwelling","terms":""},"place/locality":{"name":"Locality","terms":""},"place/neighbourhood":{"name":"Neighborhood","terms":"neighbourhood"},"place/suburb":{"name":"Borough","terms":"Boro,Quarter"},"place/town":{"name":"Town","terms":""},"place/village":{"name":"Village","terms":""},"point":{"name":"Point","terms":""},"power":{"name":"Power","terms":""},"power/generator":{"name":"Power Generator","terms":""},"power/line":{"name":"Power Line","terms":""},"power/minor_line":{"name":"Minor Power Line","terms":""},"power/pole":{"name":"Power Pole","terms":""},"power/sub_station":{"name":"Substation","terms":""},"power/substation":{"name":"Substation","terms":""},"power/tower":{"name":"High-Voltage Tower","terms":""},"power/transformer":{"name":"Transformer","terms":""},"public_transport/platform":{"name":"Platform","terms":""},"public_transport/stop_position":{"name":"Stop Position","terms":""},"railway":{"name":"Railway","terms":""},"railway/abandoned":{"name":"Abandoned Railway","terms":""},"railway/disused":{"name":"Disused Railway","terms":""},"railway/funicular":{"name":"Funicular","terms":"venicular,cliff railway,cable car,cable railway,funicular railway"},"railway/halt":{"name":"Railway Halt","terms":"break,interrupt,rest,wait,interruption"},"railway/level_crossing":{"name":"Railway Crossing","terms":"crossing,railroad crossing,level crossing,grade crossing,road through railroad,train crossing"},"railway/monorail":{"name":"Monorail","terms":""},"railway/narrow_gauge":{"name":"Narrow Gauge Rail","terms":"narrow gauge railway,narrow gauge railroad"},"railway/platform":{"name":"Railway Platform","terms":""},"railway/rail":{"name":"Rail","terms":""},"railway/station":{"name":"Railway Station","terms":"train station,station"},"railway/subway":{"name":"Subway","terms":""},"railway/subway_entrance":{"name":"Subway Entrance","terms":""},"railway/tram":{"name":"Tram","terms":"streetcar"},"relation":{"name":"Relation","terms":""},"roundabout":{"name":"Roundabout","terms":""},"route/ferry":{"name":"Ferry Route","terms":""},"shop":{"name":"Shop","terms":""},"shop/alcohol":{"name":"Liquor Store","terms":"alcohol,beer,booze,wine"},"shop/anime":{"name":"Anime Shop","terms":""},"shop/antiques":{"name":"Antiques Shop","terms":""},"shop/art":{"name":"Art Store","terms":"art*,exhibit*,gallery"},"shop/baby_goods":{"name":"Baby Goods Store","terms":""},"shop/bag":{"name":"Bag/Luggage Store","terms":"handbag,purse"},"shop/bakery":{"name":"Bakery","terms":""},"shop/bathroom_furnishing":{"name":"Bathroom Furnishing Store","terms":""},"shop/beauty":{"name":"Beauty Shop","terms":"nail spa,spa,salon,tanning"},"shop/bed":{"name":"Bedding/Mattress Store","terms":""},"shop/beverages":{"name":"Beverage Store","terms":""},"shop/bicycle":{"name":"Bicycle Shop","terms":"bike,repair"},"shop/bookmaker":{"name":"Bookmaker","terms":""},"shop/books":{"name":"Book Store","terms":""},"shop/boutique":{"name":"Boutique","terms":""},"shop/butcher":{"name":"Butcher","terms":"meat"},"shop/candles":{"name":"Candle Shop","terms":""},"shop/car":{"name":"Car Dealership","terms":"auto"},"shop/car_parts":{"name":"Car Parts Store","terms":"auto"},"shop/car_repair":{"name":"Car Repair Shop","terms":"auto"},"shop/carpet":{"name":"Carpet Store","terms":"rug"},"shop/cheese":{"name":"Cheese Store","terms":""},"shop/chemist":{"name":"Chemist","terms":""},"shop/chocolate":{"name":"Chocolate Store","terms":""},"shop/clothes":{"name":"Clothing Store","terms":""},"shop/coffee":{"name":"Coffee Store","terms":""},"shop/computer":{"name":"Computer Store","terms":""},"shop/confectionery":{"name":"Candy Store","terms":""},"shop/convenience":{"name":"Convenience Store","terms":""},"shop/copyshop":{"name":"Copy Store","terms":""},"shop/cosmetics":{"name":"Cosmetics Store","terms":""},"shop/craft":{"name":"Arts and Crafts Store","terms":"art*,paint*,frame"},"shop/curtain":{"name":"Curtain Store","terms":"drape*,window"},"shop/dairy":{"name":"Dairy Store","terms":"milk,egg,cheese"},"shop/deli":{"name":"Deli","terms":"lunch,meat,sandwich"},"shop/department_store":{"name":"Department Store","terms":""},"shop/doityourself":{"name":"DIY Store","terms":""},"shop/dry_cleaning":{"name":"Dry Cleaner","terms":""},"shop/electronics":{"name":"Electronics Store","terms":"appliance,audio,computer,tv"},"shop/erotic":{"name":"Erotic Store","terms":"sex,porn"},"shop/fabric":{"name":"Fabric Store","terms":"sew"},"shop/farm":{"name":"Produce Stand","terms":"farm shop,farm stand"},"shop/fashion":{"name":"Fashion Store","terms":""},"shop/fishmonger":{"name":"Fishmonger","terms":""},"shop/florist":{"name":"Florist","terms":"flower"},"shop/frame":{"name":"Framing Shop","terms":"art*,paint*,photo*,frame"},"shop/funeral_directors":{"name":"Funeral Home","terms":"undertaker,memorial home"},"shop/furnace":{"name":"Furnace Store","terms":"oven,stove"},"shop/furniture":{"name":"Furniture Store","terms":"chair,sofa,table"},"shop/garden_centre":{"name":"Garden Center","terms":"landscape,mulch,shrub,tree"},"shop/gift":{"name":"Gift Shop","terms":""},"shop/greengrocer":{"name":"Greengrocer","terms":"fruit,vegetable"},"shop/hairdresser":{"name":"Hairdresser","terms":""},"shop/hardware":{"name":"Hardware Store","terms":""},"shop/hearing_aids":{"name":"Hearing Aids Store","terms":""},"shop/herbalist":{"name":"Herbalist","terms":""},"shop/hifi":{"name":"Hifi Store","terms":"stereo,video"},"shop/houseware":{"name":"Houseware Store","terms":"home,household"},"shop/interior_decoration":{"name":"Interior Decoration Store","terms":""},"shop/jewelry":{"name":"Jeweler","terms":"diamond,gem,ring"},"shop/kiosk":{"name":"News Kiosk","terms":""},"shop/kitchen":{"name":"Kitchen Design Store","terms":""},"shop/laundry":{"name":"Laundry","terms":""},"shop/leather":{"name":"Leather Store","terms":""},"shop/locksmith":{"name":"Locksmith","terms":"key,lockpick"},"shop/lottery":{"name":"Lottery Shop","terms":""},"shop/mall":{"name":"Mall","terms":""},"shop/massage":{"name":"Massage Shop","terms":""},"shop/medical_supply":{"name":"Medical Supply Store","terms":""},"shop/mobile_phone":{"name":"Mobile Phone Store","terms":""},"shop/money_lender":{"name":"Money Lender","terms":""},"shop/motorcycle":{"name":"Motorcycle Dealership","terms":""},"shop/music":{"name":"Music Store","terms":"CD,vinyl"},"shop/musical_instrument":{"name":"Musical Instrument Store","terms":""},"shop/newsagent":{"name":"Newspaper/Magazine Shop","terms":""},"shop/nutrition_supplements":{"name":"Nutrition Supplements Store","terms":""},"shop/optician":{"name":"Optician","terms":"eye,glasses"},"shop/organic":{"name":"Organic Goods Store","terms":""},"shop/outdoor":{"name":"Outdoors Store","terms":"camping,climbing,hiking"},"shop/paint":{"name":"Paint Store","terms":""},"shop/pawnbroker":{"name":"Pawn Shop","terms":""},"shop/pet":{"name":"Pet Store","terms":"cat,dog,fish"},"shop/photo":{"name":"Photography Store","terms":"camera,film"},"shop/pyrotechnics":{"name":"Fireworks Store","terms":""},"shop/radiotechnics":{"name":"Radio/Electronic Component Store","terms":""},"shop/religion":{"name":"Religious Store","terms":""},"shop/scuba_diving":{"name":"Scuba Diving Shop","terms":""},"shop/seafood":{"name":"Seafood Shop","terms":"fishmonger"},"shop/second_hand":{"name":"Consignment/Thrift Store","terms":"secondhand,second hand,resale,thrift,used"},"shop/shoes":{"name":"Shoe Store","terms":""},"shop/sports":{"name":"Sporting Goods Store","terms":""},"shop/stationery":{"name":"Stationery Store","terms":"card,paper"},"shop/storage_rental":{"name":"Storage Rental","terms":""},"shop/supermarket":{"name":"Supermarket","terms":"grocery,store,shop"},"shop/tailor":{"name":"Tailor","terms":"clothes,suit"},"shop/tattoo":{"name":"Tattoo Parlor","terms":""},"shop/tea":{"name":"Tea Store","terms":""},"shop/ticket":{"name":"Ticket Seller","terms":""},"shop/tobacco":{"name":"Tobacco Shop","terms":""},"shop/toys":{"name":"Toy Store","terms":""},"shop/travel_agency":{"name":"Travel Agency","terms":""},"shop/tyres":{"name":"Tire Store","terms":""},"shop/vacant":{"name":"Vacant Shop","terms":""},"shop/vacuum_cleaner":{"name":"Vacuum Cleaner Store","terms":""},"shop/variety_store":{"name":"Variety Store","terms":""},"shop/video":{"name":"Video Store","terms":"DVD"},"shop/video_games":{"name":"Video Game Store","terms":""},"shop/water_sports":{"name":"Watersport/Swim Shop","terms":""},"shop/weapons":{"name":"Weapon Shop","terms":"ammo,gun,knife,knives"},"shop/window_blind":{"name":"Window Blind Store","terms":""},"shop/wine":{"name":"Wine Shop","terms":""},"tourism":{"name":"Tourism","terms":""},"tourism/alpine_hut":{"name":"Alpine Hut","terms":""},"tourism/artwork":{"name":"Artwork","terms":"mural,sculpture,statue"},"tourism/attraction":{"name":"Tourist Attraction","terms":""},"tourism/camp_site":{"name":"Camp Site","terms":"Tent"},"tourism/caravan_site":{"name":"RV Park","terms":"Motor Home,Camper"},"tourism/chalet":{"name":"Chalet","terms":""},"tourism/gallery":{"name":"Art Gallery","terms":"art*,exhibit*,paint*,photo*,sculpt*"},"tourism/guest_house":{"name":"Guest House","terms":"B&B,Bed and Breakfast"},"tourism/hostel":{"name":"Hostel","terms":""},"tourism/hotel":{"name":"Hotel","terms":""},"tourism/information":{"name":"Information","terms":""},"tourism/motel":{"name":"Motel","terms":""},"tourism/museum":{"name":"Museum","terms":"art*,exhibit*,gallery,foundation,hall,institution,paint*,photo*,sculpt*"},"tourism/picnic_site":{"name":"Picnic Site","terms":"camp"},"tourism/theme_park":{"name":"Theme Park","terms":""},"tourism/viewpoint":{"name":"Viewpoint","terms":""},"tourism/zoo":{"name":"Zoo","terms":""},"traffic_calming/bump":{"name":"Speed Bump","terms":"speed hump"},"traffic_calming/hump":{"name":"Speed Hump","terms":"speed bump"},"traffic_calming/rumble_strip":{"name":"Rumble Strip","terms":"sleeper lines,audible lines,growlers"},"traffic_calming/table":{"name":"Raised Pedestrian Crossing","terms":"speed table,flat top hump"},"type/boundary":{"name":"Boundary","terms":""},"type/boundary/administrative":{"name":"Administrative Boundary","terms":""},"type/multipolygon":{"name":"Multipolygon","terms":""},"type/restriction":{"name":"Restriction","terms":""},"type/restriction/no_left_turn":{"name":"No Left Turn","terms":""},"type/restriction/no_right_turn":{"name":"No Right Turn","terms":""},"type/restriction/no_straight_on":{"name":"No Straight On","terms":""},"type/restriction/no_u_turn":{"name":"No U-turn","terms":""},"type/restriction/only_left_turn":{"name":"Left Turn Only","terms":""},"type/restriction/only_right_turn":{"name":"Right Turn Only","terms":""},"type/restriction/only_straight_on":{"name":"No Turns","terms":""},"type/route":{"name":"Route","terms":""},"type/route/bicycle":{"name":"Cycle Route","terms":""},"type/route/bus":{"name":"Bus Route","terms":""},"type/route/detour":{"name":"Detour Route","terms":""},"type/route/ferry":{"name":"Ferry Route","terms":""},"type/route/foot":{"name":"Foot Route","terms":""},"type/route/hiking":{"name":"Hiking Route","terms":""},"type/route/horse":{"name":"Riding Route","terms":""},"type/route/pipeline":{"name":"Pipeline Route","terms":""},"type/route/power":{"name":"Power Route","terms":""},"type/route/road":{"name":"Road Route","terms":""},"type/route/train":{"name":"Train Route","terms":""},"type/route/tram":{"name":"Tram Route","terms":""},"type/route_master":{"name":"Route Master","terms":""},"type/site":{"name":"Site","terms":""},"vertex":{"name":"Other","terms":""},"waterway":{"name":"Waterway","terms":""},"waterway/boatyard":{"name":"Boatyard","terms":""},"waterway/canal":{"name":"Canal","terms":""},"waterway/dam":{"name":"Dam","terms":""},"waterway/ditch":{"name":"Ditch","terms":""},"waterway/dock":{"name":"Wet Dock / Dry Dock","terms":"boat,ship,vessel,marine"},"waterway/drain":{"name":"Drain","terms":""},"waterway/fuel":{"name":"Marine Fuel Station","terms":"petrol,gas,diesel,boat"},"waterway/river":{"name":"River","terms":"beck,branch,brook,course,creek,estuary,rill,rivulet,run,runnel,stream,tributary,watercourse"},"waterway/riverbank":{"name":"Riverbank","terms":""},"waterway/sanitary_dump_station":{"name":"Marine Toilet Disposal","terms":"Boat,Watercraft,Sanitary,Dump Station,Pumpout,Pump out,Elsan,CDP,CTDP,Chemical Toilet"},"waterway/stream":{"name":"Stream","terms":"beck,branch,brook,burn,course,creek,current,drift,flood,flow,freshet,race,rill,rindle,rivulet,run,runnel,rush,spate,spritz,surge,tide,torrent,tributary,watercourse"},"waterway/water_point":{"name":"Marine Drinking Water","terms":""},"waterway/weir":{"name":"Weir","terms":""}}}},"suggestions":{"amenity":{"fuel":{"76":{"count":314},"Neste":{"count":189},"BP":{"count":2511},"Shell":{"count":8380},"Agip":{"count":2651},"Migrol":{"count":65},"Avia":{"count":897},"Texaco":{"count":680},"Total":{"count":2607},"Statoil":{"count":596},"Esso":{"count":3652},"Jet":{"count":441},"Avanti":{"count":90},"Sainsbury's":{"count":58},"OMV":{"count":701},"Aral":{"count":1339},"Tesco":{"count":197},"JET":{"count":180},"Morrisons":{"count":111},"United":{"count":91},"Canadian Tire":{"count":66},"Mobil":{"count":613},"Caltex":{"count":1001},"Sunoco":{"count":355},"Q8":{"count":1161},"ABC":{"count":79},"ARAL":{"count":375},"CEPSA":{"count":1018},"BFT":{"count":89},"Petron":{"count":878},"Intermarché":{"count":434},"Total Access":{"count":51},"Super U":{"count":124},"Auchan":{"count":53},"Elf":{"count":129},"Carrefour":{"count":205},"Station Service E. Leclerc":{"count":530},"Shell Express":{"count":131},"Hess":{"count":127},"Flying V":{"count":129},"bft":{"count":168},"Gulf":{"count":199},"PTT":{"count":191},"St1":{"count":100},"Teboil":{"count":115},"HEM":{"count":212},"GALP":{"count":626},"OK":{"count":163},"ÖMV":{"count":101},"Tinq":{"count":215},"OKQ8":{"count":186},"Repsol":{"count":424},"Westfalen":{"count":96},"Esso Express":{"count":98},"Raiffeisenbank":{"count":117},"Tamoil":{"count":866},"Engen":{"count":241},"Sasol":{"count":59},"Topaz":{"count":78},"LPG":{"count":174},"Coop":{"count":62},"Orlen":{"count":598},"Oilibya":{"count":68},"Tango":{"count":122},"Star":{"count":319},"Петрол":{"count":84},"Cepsa":{"count":96},"OIL!":{"count":63},"Ultramar":{"count":125},"Irving":{"count":87},"Lukoil":{"count":701},"Petro-Canada":{"count":489},"7-Eleven":{"count":488},"Agrola":{"count":69},"Husky":{"count":126},"Slovnaft":{"count":219},"Sheetz":{"count":134},"Mol":{"count":61},"Petronas":{"count":159},"Газпромнефть":{"count":748},"Лукойл":{"count":1477},"Elan":{"count":112},"Роснефть":{"count":638},"Turmöl":{"count":57},"Neste A24":{"count":55},"Marathon":{"count":189},"Valero":{"count":366},"Eni":{"count":236},"Chevron":{"count":954},"ТНК":{"count":520},"REPSOL":{"count":1603},"MOL":{"count":228},"Bliska":{"count":150},"Api":{"count":302},"Arco":{"count":179},"Pemex":{"count":423},"Exxon":{"count":506},"Coles Express":{"count":115},"Petrom":{"count":259},"PETRONOR":{"count":207},"Rompetrol":{"count":174},"Lotos":{"count":178},"ОМВ":{"count":60},"BR":{"count":129},"Copec":{"count":505},"Petrobras":{"count":270},"Liberty":{"count":55},"IP":{"count":871},"Erg":{"count":596},"Eneos":{"count":97},"Citgo":{"count":279},"Metano":{"count":208},"Сургутнефтегаз":{"count":61},"EKO":{"count":59},"Eko":{"count":58},"Indipend.":{"count":172},"IES":{"count":63},"TotalErg":{"count":89},"Cenex":{"count":115},"ПТК":{"count":82},"HP":{"count":79},"Phillips 66":{"count":216},"CARREFOUR":{"count":74},"ERG":{"count":76},"Speedway":{"count":148},"Benzina":{"count":96},"Татнефть":{"count":264},"Terpel":{"count":259},"WOG":{"count":189},"Seaoil":{"count":54},"АЗС":{"count":1077},"Kwik Trip":{"count":108},"Wawa":{"count":89},"Pertamina":{"count":186},"COSMO":{"count":64},"Z":{"count":76},"Indian Oil":{"count":183},"АГЗС":{"count":494},"INA":{"count":121},"JOMO":{"count":62},"Holiday":{"count":97},"YPF":{"count":70},"IDEMITSU":{"count":87},"ENEOS":{"count":736},"Bharat Petroleum":{"count":64},"CAMPSA":{"count":615},"Casey's General Store":{"count":190},"Башнефть":{"count":60},"Kangaroo":{"count":60},"コスモ石油 (COSMO)":{"count":136},"MEROIL":{"count":77},"1-2-3":{"count":71},"出光":{"count":228,"tags":{"name:en":"IDEMITSU"}},"НК Альянс":{"count":88},"Sinclair":{"count":100},"Conoco":{"count":189},"SPBU":{"count":54},"Макпетрол":{"count":109},"Circle K":{"count":166},"Posto Ipiranga":{"count":70},"Posto Shell":{"count":54},"Phoenix":{"count":144},"Ipiranga":{"count":119},"OKKO":{"count":85},"ОККО":{"count":119},"บางจาก":{"count":60},"QuikTrip":{"count":105},"Stewart's":{"count":63},"Posto BR":{"count":68},"ป ต ท":{"count":152},"ปตท":{"count":88},"ANP":{"count":80},"Kum & Go":{"count":80},"Petrolimex":{"count":55},"Sokimex":{"count":66},"Tela":{"count":154},"Posto":{"count":71},"H-E-B":{"count":182},"Укрнафта":{"count":58},"Татнефтепродукт":{"count":54},"Afriquia":{"count":88},"Murphy USA":{"count":67},"昭和シェル (Showa-shell)":{"count":94},"エネオス":{"count":53},"CNG":{"count":94}},"pub":{"Kings Arms":{"count":67},"The Ship":{"count":89},"The White Horse":{"count":204},"The White Hart":{"count":226},"Royal Oak":{"count":150},"The Red Lion":{"count":233},"The Kings Arms":{"count":58},"The Star":{"count":73},"The Anchor":{"count":64},"The Cross Keys":{"count":55},"The Wheatsheaf":{"count":117},"The Crown Inn":{"count":67},"The Kings Head":{"count":53},"The Castle":{"count":62},"The Railway":{"count":102},"The White Lion":{"count":118},"The Bell":{"count":121},"The Bull":{"count":68},"The Plough":{"count":179},"The George":{"count":110},"The Royal Oak":{"count":209},"The Fox":{"count":74},"Prince of Wales":{"count":77},"The Rising Sun":{"count":71},"The Prince of Wales":{"count":51},"The Crown":{"count":244},"The Chequers":{"count":66},"The Swan":{"count":152},"Rose and Crown":{"count":79},"The Victoria":{"count":67},"New Inn":{"count":90},"Royal Hotel":{"count":57},"Red Lion":{"count":207},"Cross Keys":{"count":61},"The Greyhound":{"count":96},"The Black Horse":{"count":94},"The New Inn":{"count":105},"Kings Head":{"count":59},"The Albion":{"count":51},"The Angel":{"count":52},"The Queens Head":{"count":52},"The Ship Inn":{"count":83},"Rose & Crown":{"count":51},"Queens Head":{"count":52},"Irish Pub":{"count":76}},"fast_food":{"Quick":{"count":484},"McDonald's":{"count":12376,"tags":{"cuisine":"burger"}},"Subway":{"count":5576,"tags":{"cuisine":"sandwich"}},"Burger King":{"count":3734,"tags":{"cuisine":"burger"}},"Ali Baba":{"count":61},"Hungry Jacks":{"count":173,"tags":{"cuisine":"burger"}},"Red Rooster":{"count":148},"KFC":{"count":3198,"tags":{"cuisine":"chicken"}},"Domino's Pizza":{"count":985,"tags":{"cuisine":"pizza"}},"Chowking":{"count":142},"Jollibee":{"count":396},"Hesburger":{"count":102},"肯德基":{"count":86},"Wendy's":{"count":1621,"tags":{"cuisine":"burger"}},"Tim Hortons":{"count":323},"Steers":{"count":151},"Hardee's":{"count":268,"tags":{"cuisine":"burger"}},"Arby's":{"count":782},"A&W":{"count":283},"Dairy Queen":{"count":791},"Hallo Pizza":{"count":76},"Fish & Chips":{"count":93},"Harvey's":{"count":90},"麥當勞":{"count":65},"Pizza Pizza":{"count":215},"Kotipizza":{"count":74},"Jack in the Box":{"count":546,"tags":{"cuisine":"burger"}},"Istanbul":{"count":56},"Kochlöffel":{"count":68},"Döner":{"count":228},"Telepizza":{"count":201},"Sibylla":{"count":61},"Carl's Jr.":{"count":298,"tags":{"cuisine":"burger"}},"Quiznos":{"count":266,"tags":{"cuisine":"sandwich"}},"Wimpy":{"count":141},"Sonic":{"count":566,"tags":{"cuisine":"burger"}},"Taco Bell":{"count":1423,"tags":{"cuisine":"mexican"}},"Pizza Nova":{"count":63},"Papa John's":{"count":304,"tags":{"cuisine":"pizza"}},"Nordsee":{"count":159},"Mr. Sub":{"count":103},"Макдоналдс":{"count":324,"tags":{"name:en":"McDonald's"}},"Asia Imbiss":{"count":111},"Chipotle":{"count":290,"tags":{"cuisine":"mexican"}},"マクドナルド":{"count":692,"tags":{"name:en":"McDonald's","cuisine":"burger"}},"In-N-Out Burger":{"count":65},"Jimmy John's":{"count":141},"Jamba Juice":{"count":68},"Робин Сдобин":{"count":82},"Baskin Robbins":{"count":74},"ケンタッキーフライドチキン":{"count":164,"tags":{"name:en":"KFC","cuisine":"chicken"}},"吉野家":{"count":191},"Taco Time":{"count":88},"松屋":{"count":281,"tags":{"name:en":"Matsuya"}},"Little Caesars":{"count":81},"El Pollo Loco":{"count":63},"Del Taco":{"count":141},"White Castle":{"count":80},"Boston Market":{"count":66},"Chick-fil-A":{"count":257,"tags":{"cuisine":"chicken"}},"Panda Express":{"count":238,"tags":{"cuisine":"chinese"}},"Whataburger":{"count":364},"Taco John's":{"count":78},"Теремок":{"count":68},"Culver's":{"count":425},"Five Guys":{"count":141},"Church's Chicken":{"count":95},"Popeye's":{"count":167,"tags":{"cuisine":"chicken"}},"Long John Silver's":{"count":93},"Pollo Campero":{"count":62},"Zaxby's":{"count":51},"すき家":{"count":276,"tags":{"name:en":"SUKIYA"}},"モスバーガー":{"count":257,"tags":{"name:en":"MOS BURGER"}},"Русский Аппетит":{"count":69},"なか卯":{"count":63}},"restaurant":{"Pizza Hut":{"count":1180,"tags":{"cuisine":"pizza"}},"Little Chef":{"count":64},"Adler":{"count":158},"Zur Krone":{"count":90},"Deutsches Haus":{"count":90},"Krone":{"count":171},"Akropolis":{"count":152},"Schützenhaus":{"count":124},"Kreuz":{"count":74},"Waldschänke":{"count":55},"La Piazza":{"count":69},"Lamm":{"count":66},"Zur Sonne":{"count":73},"Zur Linde":{"count":204},"Poseidon":{"count":110},"Shanghai":{"count":82},"Red Lobster":{"count":235},"Zum Löwen":{"count":84},"Swiss Chalet":{"count":107},"Olympia":{"count":74},"Wagamama":{"count":64},"Frankie & Benny's":{"count":66},"Hooters":{"count":103},"Sternen":{"count":78},"Hirschen":{"count":79},"Denny's":{"count":450},"Athen":{"count":68},"Sonne":{"count":126},"Hirsch":{"count":79},"Ratskeller":{"count":150},"La Cantina":{"count":56},"Gasthaus Krone":{"count":56},"El Greco":{"count":86},"Gasthof zur Post":{"count":79},"Nando's":{"count":246},"Löwen":{"count":112},"La Pataterie":{"count":51},"Bella Napoli":{"count":53},"Pizza Express":{"count":262},"Mandarin":{"count":65},"Hong Kong":{"count":83},"Zizzi":{"count":68},"Cracker Barrel":{"count":183},"Rhodos":{"count":81},"Lindenhof":{"count":79},"Milano":{"count":54},"Dolce Vita":{"count":77},"Kirchenwirt":{"count":81},"Kantine":{"count":52},"Ochsen":{"count":95},"Spur":{"count":62},"Mykonos":{"count":59},"Lotus":{"count":66},"Applebee's":{"count":531},"Flunch":{"count":72},"Zur Post":{"count":116},"China Town":{"count":76},"La Dolce Vita":{"count":73},"Waffle House":{"count":207},"Delphi":{"count":88},"Linde":{"count":103},"Outback Steakhouse":{"count":218},"Dionysos":{"count":69},"Kelsey's":{"count":57},"Boston Pizza":{"count":165},"Bella Italia":{"count":132},"Sizzler":{"count":53},"Grüner Baum":{"count":116},"Taj Mahal":{"count":104},"Rössli":{"count":68},"Wimpy":{"count":51},"Traube":{"count":65},"Adria":{"count":52},"Red Robin":{"count":185},"Roma":{"count":61},"San Marco":{"count":67},"Hellas":{"count":55},"La Perla":{"count":67},"Vips":{"count":53},"Panera Bread":{"count":218},"Da Vinci":{"count":54},"Hippopotamus":{"count":96},"Prezzo":{"count":75},"Courtepaille":{"count":106},"Hard Rock Cafe":{"count":70},"Panorama":{"count":61},"デニーズ":{"count":82},"Sportheim":{"count":65},"餃子の王将":{"count":57},"Bären":{"count":60},"Alte Post":{"count":60},"Pizzeria Roma":{"count":51},"China Garden":{"count":66},"Vapiano":{"count":82},"Mamma Mia":{"count":64},"Schwarzer Adler":{"count":57},"IHOP":{"count":317},"Chili's":{"count":328},"Asia":{"count":51},"Olive Garden":{"count":279},"TGI Friday's":{"count":159},"Friendly's":{"count":78},"Buffalo Grill":{"count":202},"Texas Roadhouse":{"count":110},"ガスト":{"count":230,"tags":{"name:en":"Gusto"}},"Sakura":{"count":75},"Mensa":{"count":99},"The Keg":{"count":53},"サイゼリヤ":{"count":93},"La Strada":{"count":52},"Village Inn":{"count":92},"Buffalo Wild Wings":{"count":176},"Peking":{"count":59},"Boston Market":{"count":61},"Round Table Pizza":{"count":53},"Jimmy John's":{"count":69},"California Pizza Kitchen":{"count":61},"Якитория":{"count":77},"Golden Corral":{"count":101},"Perkins":{"count":105},"Ruby Tuesday":{"count":162},"Shari's":{"count":65},"Bob Evans":{"count":129},"바다횟집 (Bada Fish Restaurant)":{"count":55},"Mang Inasal":{"count":84},"Евразия":{"count":102},"ジョナサン":{"count":59},"Longhorn Steakhouse":{"count":66}},"bank":{"Chase":{"count":721},"Commonwealth Bank":{"count":232},"Citibank":{"count":277},"HSBC":{"count":1102},"Barclays":{"count":965},"Westpac":{"count":208},"NAB":{"count":131},"ANZ":{"count":218},"Lloyds Bank":{"count":547},"Landbank":{"count":81},"Sparkasse":{"count":4555},"UCPB":{"count":92},"PNB":{"count":244},"Metrobank":{"count":269},"BDO":{"count":290},"Volksbank":{"count":2591},"BPI":{"count":415},"Postbank":{"count":443},"NatWest":{"count":628},"Raiffeisenbank":{"count":2119},"Yorkshire Bank":{"count":63},"ABSA":{"count":95},"Standard Bank":{"count":109},"FNB":{"count":97},"Deutsche Bank":{"count":855},"SEB":{"count":133},"Commerzbank":{"count":806},"Targobank":{"count":166},"ABN AMRO":{"count":130},"Handelsbanken":{"count":184},"Swedbank":{"count":223},"Kreissparkasse":{"count":600},"UniCredit Bank":{"count":408},"Monte dei Paschi di Siena":{"count":132},"Caja Rural":{"count":99},"Dresdner Bank":{"count":66},"Sparda-Bank":{"count":320},"VÚB":{"count":107},"Slovenská sporiteľňa":{"count":134},"Bank of Montreal":{"count":118},"KBC":{"count":203},"Royal Bank of Scotland":{"count":111},"TSB":{"count":80},"US Bank":{"count":256},"HypoVereinsbank":{"count":561},"Bank Austria":{"count":176},"ING":{"count":496},"Erste Bank":{"count":180},"CIBC":{"count":326},"Scotiabank":{"count":413},"Caisse d'Épargne":{"count":882},"Santander":{"count":1323},"Bank of Scotland":{"count":89},"TD Canada Trust":{"count":450},"BMO":{"count":169},"Danske Bank":{"count":131},"OTP":{"count":192},"Crédit Agricole":{"count":1239},"LCL":{"count":553},"VR-Bank":{"count":430},"ČSOB":{"count":160},"Česká spořitelna":{"count":212},"BNP":{"count":112},"Royal Bank":{"count":65},"Nationwide":{"count":209},"Halifax":{"count":225},"BAWAG PSK":{"count":102},"National Bank":{"count":84},"Nedbank":{"count":80},"First National Bank":{"count":85},"Nordea":{"count":319},"Rabobank":{"count":609},"Sparkasse KölnBonn":{"count":69},"Tatra banka":{"count":67},"Berliner Sparkasse":{"count":62},"Berliner Volksbank":{"count":77},"Wells Fargo":{"count":874},"Credit Suisse":{"count":71},"Société Générale":{"count":634},"Osuuspankki":{"count":75},"Sparkasse Aachen":{"count":56},"Hamburger Sparkasse":{"count":156},"Cassa di Risparmio del Veneto":{"count":68},"BNP Paribas":{"count":617},"Banque Populaire":{"count":433},"BNP Paribas Fortis":{"count":209},"Banco Popular":{"count":291},"Bancaja":{"count":55},"Banesto":{"count":208},"La Caixa":{"count":583},"Santander Consumer Bank":{"count":88},"BRD":{"count":191},"BCR":{"count":143},"Banca Transilvania":{"count":141},"BW-Bank":{"count":97},"Komerční banka":{"count":132},"Banco Pastor":{"count":64},"Stadtsparkasse":{"count":86},"Ulster Bank":{"count":86},"Sberbank":{"count":58},"CIC":{"count":427},"Bancpost":{"count":56},"Caja Madrid":{"count":115},"Maybank":{"count":94},"中国银行":{"count":85},"Unicredit Banca":{"count":243},"Crédit Mutuel":{"count":690},"BBVA":{"count":647},"Intesa San Paolo":{"count":69},"TD Bank":{"count":206},"Belfius":{"count":231},"Bank of America":{"count":924},"RBC":{"count":230},"Alpha Bank":{"count":123},"Сбербанк":{"count":4794},"Россельхозбанк":{"count":201},"Crédit du Nord":{"count":96},"BancoEstado":{"count":80},"Millennium Bank":{"count":414},"State Bank of India":{"count":151},"Беларусбанк":{"count":242},"ING Bank Śląski":{"count":67},"Caixa Geral de Depósitos":{"count":129},"Kreissparkasse Köln":{"count":65},"Banco BCI":{"count":51},"Banco de Chile":{"count":98},"ВТБ24":{"count":326},"UBS":{"count":134},"PKO BP":{"count":265},"Chinabank":{"count":55},"PSBank":{"count":59},"Union Bank":{"count":124},"China Bank":{"count":66},"RCBC":{"count":122},"Unicaja":{"count":83},"BBK":{"count":79},"Ibercaja":{"count":69},"RBS":{"count":143},"Commercial Bank of Ceylon PLC":{"count":79},"Bank of Ireland":{"count":109},"BNL":{"count":87},"Banco Santander":{"count":138},"Banco Itaú":{"count":111},"AIB":{"count":72},"BZ WBK":{"count":77},"Banco do Brasil":{"count":557},"Caixa Econômica Federal":{"count":184},"Fifth Third Bank":{"count":84},"Banca Popolare di Vicenza":{"count":81},"Wachovia":{"count":58},"OLB":{"count":53},"みずほ銀行":{"count":78},"BES":{"count":72},"ICICI Bank":{"count":91},"HDFC Bank":{"count":91},"La Banque Postale":{"count":67},"Pekao SA":{"count":56},"Oberbank":{"count":90},"Bradesco":{"count":295},"Oldenburgische Landesbank":{"count":56},"Bendigo Bank":{"count":93},"Argenta":{"count":86},"AXA":{"count":68},"Axis Bank":{"count":61},"Banco Nación":{"count":67},"GE Money Bank":{"count":72},"Альфа-Банк":{"count":185},"Белагропромбанк":{"count":70},"Caja Círculo":{"count":65},"Banco Galicia":{"count":51},"Eurobank":{"count":97},"Banca Intesa":{"count":62},"Canara Bank":{"count":92},"Cajamar":{"count":77},"Banamex":{"count":149},"Crédit Mutuel de Bretagne":{"count":335},"Davivienda":{"count":83},"Bank Spółdzielczy":{"count":159},"Credit Agricole":{"count":157},"Bankinter":{"count":59},"Banque Nationale":{"count":63},"Bank of the West":{"count":96},"Key Bank":{"count":155},"Western Union":{"count":88},"Citizens Bank":{"count":115},"ПриватБанк":{"count":513},"Security Bank":{"count":78},"Millenium":{"count":60},"Bankia":{"count":149},"三菱東京UFJ銀行":{"count":159},"Caixa":{"count":117},"Banco de Costa Rica":{"count":63},"SunTrust Bank":{"count":73},"Itaú":{"count":338},"PBZ":{"count":52},"中国工商银行":{"count":51},"Bancolombia":{"count":89},"Райффайзен Банк Аваль":{"count":64},"Bancomer":{"count":115},"Banorte":{"count":80},"Alior Bank":{"count":81},"BOC":{"count":51},"Банк Москвы":{"count":118},"ВТБ":{"count":59},"Getin Bank":{"count":55},"Caja Duero":{"count":57},"Regions Bank":{"count":62},"Росбанк":{"count":177},"Banco Estado":{"count":72},"BCI":{"count":68},"SunTrust":{"count":68},"PNC Bank":{"count":254},"신한은행":{"count":217,"tags":{"name:en":"Sinhan Bank"}},"우리은행":{"count":291,"tags":{"name:en":"Uri Bank"}},"국민은행":{"count":165,"tags":{"name:en":"Gungmin Bank"}},"중소기업은행":{"count":52,"tags":{"name:en":"Industrial Bank of Korea"}},"광주은행":{"count":51,"tags":{"name:en":"Gwangju Bank"}},"Газпромбанк":{"count":100},"M&T Bank":{"count":92},"Caja de Burgos":{"count":51},"Santander Totta":{"count":69},"УкрСиббанк":{"count":192},"Ощадбанк":{"count":364},"Уралсиб":{"count":85},"りそな銀行":{"count":225,"tags":{"name:en":"Mizuho Bank"}},"Ecobank":{"count":66},"Cajero Automatico Bancared":{"count":145},"Промсвязьбанк":{"count":93},"三井住友銀行":{"count":129},"Banco Provincia":{"count":67},"BB&T":{"count":147},"Возрождение":{"count":59},"Capital One":{"count":59},"横浜銀行":{"count":51},"Bank Mandiri":{"count":62},"Banco de la Nación":{"count":92},"Banco G&T Continental":{"count":62},"Peoples Bank":{"count":60},"工商银行":{"count":51},"Совкомбанк":{"count":55},"Provincial":{"count":56},"Banco de Desarrollo Banrural":{"count":73},"Banco Bradesco":{"count":65},"Bicentenario":{"count":182},"ლიბერთი ბანკი":{"count":54,"tags":{"name:en":"Liberty Bank"}},"Banesco":{"count":108},"Mercantil":{"count":75},"Bank BRI":{"count":53},"Del Tesoro":{"count":91},"하나은행":{"count":77},"CityCommerce Bank":{"count":71},"De Venezuela":{"count":117}},"car_rental":{"Europcar":{"count":291},"Budget":{"count":92},"Sixt":{"count":161},"Avis":{"count":282},"Hertz":{"count":293},"Enterprise":{"count":199},"stadtmobil CarSharing-Station":{"count":148}},"pharmacy":{"Rowlands Pharmacy":{"count":71},"Boots":{"count":840},"Marien-Apotheke":{"count":314},"Mercury Drug":{"count":426},"Löwen-Apotheke":{"count":356},"Superdrug":{"count":117},"Sonnen-Apotheke":{"count":311},"Rathaus-Apotheke":{"count":132},"Engel-Apotheke":{"count":123},"Hirsch-Apotheke":{"count":83},"Stern-Apotheke":{"count":67},"Lloyds Pharmacy":{"count":295},"Rosen-Apotheke":{"count":208},"Stadt-Apotheke":{"count":302},"Markt-Apotheke":{"count":164},"Аптека":{"count":1989},"Pharmasave":{"count":64},"Brunnen-Apotheke":{"count":53},"Shoppers Drug Mart":{"count":430},"Apotheke am Markt":{"count":60},"Alte Apotheke":{"count":88},"Neue Apotheke":{"count":109},"Gintarinė vaistinė":{"count":101},"Rats-Apotheke":{"count":84},"Adler Apotheke":{"count":313},"Pharmacie Centrale":{"count":64},"Walgreens":{"count":1619},"Rite Aid":{"count":745},"Apotheke":{"count":165},"Linden-Apotheke":{"count":211},"Bahnhof-Apotheke":{"count":66},"Burg-Apotheke":{"count":55},"Jean Coutu":{"count":62},"Pharmaprix":{"count":60},"Farmacias Ahumada":{"count":104},"Farmacia Comunale":{"count":113},"Farmacias Cruz Verde":{"count":86},"Cruz Verde":{"count":99},"Hubertus Apotheke":{"count":52},"CVS":{"count":1560},"Farmacias SalcoBrand":{"count":133},"Фармация":{"count":120},"Bären-Apotheke":{"count":74},"Clicks":{"count":113},"セイジョー":{"count":53},"マツモトキヨシ":{"count":115},"Dr. Max":{"count":51},"Вита":{"count":106},"Радуга":{"count":70},"サンドラッグ":{"count":61},"Apteka":{"count":366},"Первая помощь":{"count":74},"Ригла":{"count":113},"Имплозия":{"count":63},"Kinney Drugs":{"count":68},"Классика":{"count":67},"Ljekarna":{"count":53},"SalcoBrand":{"count":88},"Аптека 36,6":{"count":224},"Фармакор":{"count":75},"スギ薬局":{"count":84},"Аптечный пункт":{"count":148},"Невис":{"count":60},"トモズ (Tomod's)":{"count":83},"Eurovaistinė":{"count":65},"Farmacity":{"count":68},"аптека":{"count":96},"The Generics Pharmacy":{"count":95},"Farmatodo":{"count":123},"Duane Reade":{"count":61},"H-E-B":{"count":262},"Фармленд":{"count":82},"ドラッグてらしま (Drug Terashima)":{"count":96},"Арніка":{"count":125},"ავერსი (Aversi)":{"count":62},"Farmahorro":{"count":58}},"cafe":{"Starbucks":{"count":4238,"tags":{"cuisine":"coffee_shop"}},"Cafeteria":{"count":115},"Costa":{"count":618},"Caffè Nero":{"count":169},"Кафе":{"count":226},"Café Central":{"count":61},"Second Cup":{"count":193},"Dunkin Donuts":{"count":428,"tags":{"cuisine":"donut"}},"Espresso House":{"count":53},"Segafredo":{"count":69},"Coffee Time":{"count":94},"Cafe Coffee Day":{"count":120},"Eiscafe Venezia":{"count":180},"スターバックス":{"count":251,"tags":{"name:en":"Starbucks"}},"Шоколадница":{"count":145},"Pret A Manger":{"count":119},"Столовая":{"count":391},"Jamba Juice":{"count":53},"ドトール":{"count":164,"tags":{"name:en":"DOUTOR"}},"Tchibo":{"count":100},"Кофе Хауз":{"count":104},"Caribou Coffee":{"count":100},"Уют":{"count":51},"Шашлычная":{"count":58},"คาเฟ่ อเมซอน":{"count":62},"Traveler's Coffee":{"count":60},"カフェ・ド・クリエ":{"count":67,"tags":{"name:en":"Cafe de CRIE"}},"Cafe Amazon":{"count":65}}},"shop":{"supermarket":{"Budgens":{"count":88},"Morrisons":{"count":411},"Interspar":{"count":142},"Merkur":{"count":107},"Sainsbury's":{"count":547},"Lidl":{"count":7130},"Edeka":{"count":2293},"Coles":{"count":400},"Iceland":{"count":315},"Coop":{"count":2100},"Tesco":{"count":1297},"Woolworths":{"count":541},"Zielpunkt":{"count":239},"Nahkauf":{"count":170},"Billa":{"count":1432},"Kaufland":{"count":1004},"Plus":{"count":120},"ALDI":{"count":5172},"Checkers":{"count":128},"Tesco Metro":{"count":137},"NP":{"count":153},"Penny":{"count":1759},"Norma":{"count":1068},"Asda":{"count":225},"Netto":{"count":4379},"Rewe":{"count":2645},"Aldi Süd":{"count":594},"Real":{"count":246},"Tesco Express":{"count":406},"King Soopers":{"count":72},"Kiwi":{"count":167},"Pick n Pay":{"count":241},"ICA":{"count":192},"Tengelmann":{"count":188},"Carrefour":{"count":1640},"Waitrose":{"count":258},"Spar":{"count":2386},"Hofer":{"count":442},"M-Preis":{"count":76},"tegut":{"count":210},"Sainsbury's Local":{"count":118},"E-Center":{"count":66},"Aldi Nord":{"count":210},"nahkauf":{"count":84},"Meijer":{"count":76},"Safeway":{"count":410},"Costco":{"count":152},"Albert":{"count":185},"Jumbo":{"count":194},"Shoprite":{"count":244},"MPreis":{"count":54},"Penny Market":{"count":429},"Tesco Extra":{"count":123},"Albert Heijn":{"count":476},"IGA":{"count":363},"Super U":{"count":488},"Metro":{"count":260},"Neukauf":{"count":77},"Migros":{"count":459},"Marktkauf":{"count":121},"Delikatesy Centrum":{"count":59},"C1000":{"count":307},"Hoogvliet":{"count":53},"Food Basics":{"count":75},"Casino":{"count":264},"Penny Markt":{"count":466},"Giant":{"count":191},"COOP Jednota":{"count":73},"Rema 1000":{"count":368},"Kaufpark":{"count":96},"ALDI SÜD":{"count":113},"Simply Market":{"count":330},"Konzum":{"count":230},"Carrefour Express":{"count":353},"Eurospar":{"count":270},"Mercator":{"count":125},"Famila":{"count":130},"Hemköp":{"count":82},"real,-":{"count":81},"Markant":{"count":88},"Volg":{"count":135},"Leader Price":{"count":267},"Treff 3000":{"count":94},"SuperBrugsen":{"count":67},"Kaiser's":{"count":256},"K+K":{"count":106},"Unimarkt":{"count":86},"Carrefour City":{"count":126},"Sobeys":{"count":122},"S-Market":{"count":109},"Combi":{"count":55},"Denner":{"count":276},"Konsum":{"count":133},"Franprix":{"count":312},"Monoprix":{"count":198},"Diska":{"count":69},"PENNY":{"count":79},"Dia":{"count":835},"Giant Eagle":{"count":85},"NORMA":{"count":115},"AD Delhaize":{"count":63},"Auchan":{"count":152},"Mercadona":{"count":769},"Consum":{"count":130},"Carrefour Market":{"count":80},"Whole Foods":{"count":210,"tags":{"shop":"supermarket"}},"Pam":{"count":56},"sky":{"count":105},"Despar":{"count":146},"Eroski":{"count":208},"Costcutter":{"count":63},"Maxi":{"count":108},"Colruyt":{"count":180},"The Co-operative":{"count":64},"Intermarché":{"count":1210},"Delhaize":{"count":207},"CBA":{"count":176},"Shopi":{"count":53},"Walmart":{"count":644},"Kroger":{"count":317},"Albertsons":{"count":242},"Trader Joe's":{"count":235},"Feneberg":{"count":58},"denn's Biomarkt":{"count":52},"dm":{"count":114},"Kvickly":{"count":55},"Makro":{"count":140},"Dico":{"count":53},"Nah & Frisch":{"count":73},"Champion":{"count":59},"ICA Supermarket":{"count":51},"Fakta":{"count":235},"Магнит":{"count":1760},"Caprabo":{"count":103},"Famiglia Cooperativa":{"count":64},"Народная 7Я семьЯ":{"count":154},"Esselunga":{"count":85},"Maxima":{"count":102},"Petit Casino":{"count":111},"Wasgau":{"count":60},"Pingo Doce":{"count":253},"Match":{"count":140},"Profi":{"count":60},"Lider":{"count":65},"Unimarc":{"count":177},"The Co-operative Food":{"count":190},"Santa Isabel":{"count":128},"Седьмой континент":{"count":79},"HIT":{"count":59},"Rimi":{"count":106},"Conad":{"count":304},"Фуршет":{"count":76},"Willys":{"count":56},"Farmfoods":{"count":64},"U Express":{"count":51},"Фора":{"count":52},"Dunnes Stores":{"count":73},"Сільпо":{"count":125},"マルエツ":{"count":59},"Piggly Wiggly":{"count":57},"Crai":{"count":54},"El Árbol":{"count":73},"Centre Commercial E. Leclerc":{"count":549},"Foodland":{"count":100},"Super Brugsen":{"count":67},"Дикси":{"count":683},"Пятёрочка":{"count":1344},"Publix":{"count":339},"Føtex":{"count":66},"coop":{"count":73},"Fressnapf":{"count":69},"Coop Konsum":{"count":79},"Carrefour Contact":{"count":83},"No Frills":{"count":105},"Plodine":{"count":52},"ADEG":{"count":68},"Minipreço":{"count":111},"Biedronka":{"count":1335},"Eurospin":{"count":155},"Семья":{"count":62},"Gadis":{"count":53},"Евроопт":{"count":68},"Centra":{"count":51},"Квартал":{"count":82},"New World":{"count":69},"Countdown":{"count":95},"Reliance Fresh":{"count":61},"Stokrotka":{"count":98},"Coop Jednota":{"count":74},"Fred Meyer":{"count":64},"Irma":{"count":58},"Continente":{"count":75},"Price Chopper":{"count":99},"Game":{"count":52},"Soriana":{"count":93},"Alimerka":{"count":64},"Piotr i Paweł":{"count":53},"Перекресток":{"count":312},"Maxima X":{"count":117},"Карусель":{"count":55},"ALDI Nord":{"count":51},"Condis":{"count":67},"Sam's Club":{"count":138},"Копейка":{"count":87},"Géant Casino":{"count":54},"ASDA":{"count":180},"Intermarche":{"count":115},"Stop & Shop":{"count":66},"Food Lion":{"count":216},"Harris Teeter":{"count":92},"Foodworks":{"count":62},"Polo Market":{"count":86},"Лента":{"count":51},"西友 (SEIYU)":{"count":58},"H-E-B":{"count":293},"Атак":{"count":53},"Полушка":{"count":139},"Extra":{"count":82},"Lewiatan":{"count":94},"Sigma":{"count":51},"АТБ":{"count":322},"Społem":{"count":55},"Bodega Aurrera":{"count":82},"Tesco Lotus":{"count":77},"Мария-Ра":{"count":108},"Магнолия":{"count":72},"Магазин":{"count":120},"Монетка":{"count":174},"Hy-Vee":{"count":75},"Walmart Supercenter":{"count":133},"Hannaford":{"count":57},"Wegmans":{"count":83},"業務スーパー":{"count":61},"Norfa XL":{"count":55},"ヨークマート (YorkMart)":{"count":64},"Leclerc Drive":{"count":76}},"electronics":{"Media Markt":{"count":285},"Maplin":{"count":65},"Best Buy":{"count":345},"Future Shop":{"count":73},"Saturn":{"count":134},"Currys":{"count":80},"Radio Shack":{"count":269},"Euronics":{"count":115},"Expert":{"count":123},"Эльдорадо":{"count":184},"Darty":{"count":74},"М.Видео":{"count":89},"ヤマダ電機":{"count":51}},"convenience":{"Shell":{"count":255},"Spar":{"count":1119},"McColl's":{"count":100},"Tesco Express":{"count":426},"Sainsbury's Local":{"count":104},"Aral":{"count":56},"One Stop":{"count":146},"The Co-operative Food":{"count":115},"Londis":{"count":352},"7-Eleven":{"count":4440},"CBA":{"count":135},"Coop":{"count":678},"Sale":{"count":80},"Statoil":{"count":69},"Sheetz":{"count":54},"Konzum":{"count":173},"Siwa":{"count":216},"Mercator":{"count":57},"Esso":{"count":67},"COOP Jednota":{"count":181},"Mac's":{"count":152},"Alepa":{"count":62},"Hasty Market":{"count":54},"K-Market":{"count":54},"Costcutter":{"count":292},"Valintatalo":{"count":62},"Casino":{"count":90},"Franprix":{"count":61},"Circle K":{"count":289},"セブンイレブン":{"count":3011,"tags":{"name:en":"7-Eleven"}},"ローソン":{"count":1596,"tags":{"name:en":"LAWSON"}},"BP":{"count":163},"Tesco":{"count":55},"Petit Casino":{"count":233},"Volg":{"count":116},"Mace":{"count":115},"Mini Market":{"count":272},"Nisa Local":{"count":77},"Dorfladen":{"count":75},"Продукты":{"count":4285},"Mini Stop":{"count":228},"LAWSON":{"count":419},"デイリーヤマザキ":{"count":141},"Biedronka":{"count":83},"Надежда":{"count":56},"Mobil":{"count":66},"Nisa":{"count":51},"Premier":{"count":129},"ABC":{"count":152},"ミニストップ":{"count":316,"tags":{"name:en":"MINISTOP"}},"サンクス":{"count":560,"tags":{"name:en":"sunkus"}},"スリーエフ":{"count":88},"8 à Huit":{"count":61},"Tchibo":{"count":56},"Żabka":{"count":546},"Almacen":{"count":229},"Vival":{"count":194},"FamilyMart":{"count":529},"ファミリーマート":{"count":1608,"tags":{"name:en":"FamilyMart"}},"Carrefour City":{"count":57},"Sunkus":{"count":62},"Casey's General Store":{"count":95},"セブンイレブン(Seven-Eleven)":{"count":65},"Jednota":{"count":58},"Магазин":{"count":915},"Гастроном":{"count":152},"Centra":{"count":111},"Магнит":{"count":701},"サークルK":{"count":538,"tags":{"name:en":"Circle K"}},"Wawa":{"count":135},"Proxi":{"count":123},"Универсам":{"count":78},"Перекресток":{"count":51},"Groszek":{"count":65},"Select":{"count":62},"Večerka":{"count":51},"Potraviny":{"count":249},"Смак":{"count":78},"Эконом":{"count":55},"Березка":{"count":77},"Społem":{"count":93},"Carrefour Express":{"count":84},"Cumberland Farms":{"count":63},"Chevron":{"count":59},"Coop Jednota":{"count":66},"Tesco Lotus Express":{"count":67},"Kiosk":{"count":55},"Sklep spożywczy":{"count":130},"24 часа":{"count":58},"Минимаркет":{"count":102},"Oxxo":{"count":669},"Пятёрочка":{"count":398},"abc":{"count":74},"7/11":{"count":51},"Stewart's":{"count":255},"Продукти":{"count":171},"ローソンストア100 (LAWSON STORE 100)":{"count":85},"Дикси":{"count":119},"Радуга":{"count":86},"ローソンストア100":{"count":76},"เซเว่นอีเลฟเว่น":{"count":185},"Delikatesy Centrum":{"count":53},"Citgo":{"count":62},"Фортуна":{"count":51},"Kum & Go":{"count":59},"Мария-Ра":{"count":76},"Picard":{"count":57},"Four Square":{"count":52},"Визит":{"count":57},"Авоська":{"count":55},"Dollar General":{"count":127},"Studenac":{"count":76},"Central Convenience Store":{"count":55},"Монетка":{"count":62},"продукты":{"count":114},"Теремок":{"count":56},"Kwik Trip":{"count":69},"Кулинария":{"count":55},"全家":{"count":90},"Мечта":{"count":54},"Epicerie":{"count":102},"Кировский":{"count":67},"Food Mart":{"count":117},"Delikatesy":{"count":81},"ポプラ":{"count":54},"Lewiatan":{"count":135},"Продуктовый магазин":{"count":149},"Продуктовый":{"count":84},"セイコーマート (Seicomart)":{"count":72},"Виктория":{"count":70},"Весна":{"count":57},"Mini Market Non-Stop":{"count":60},"QuikTrip":{"count":75},"Копеечка":{"count":51},"Royal Farms":{"count":51},"Alfamart":{"count":103},"Indomaret":{"count":141},"магазин":{"count":171},"全家便利商店":{"count":156},"Boutique":{"count":59},"მარკეტი (Market)":{"count":144},"Stores":{"count":61}},"chemist":{"dm":{"count":939},"Müller":{"count":212},"Schlecker":{"count":187},"Etos":{"count":467},"Bipa":{"count":289},"Rossmann":{"count":1669},"DM Drogeriemarkt":{"count":55},"Ihr Platz":{"count":73},"Douglas":{"count":62},"Kruidvat":{"count":123}},"car_repair":{"Peugeot":{"count":83},"Kwik Fit":{"count":128},"ATU":{"count":261},"Midas":{"count":202},"Feu Vert":{"count":113},"Norauto":{"count":152},"Speedy":{"count":115},"Автозапчасти":{"count":212},"Renault":{"count":171},"Pit Stop":{"count":58},"Jiffy Lube":{"count":198},"Шиномонтаж":{"count":1157},"СТО":{"count":395},"O'Reilly Auto Parts":{"count":81},"Carglass":{"count":112},"шиномонтаж":{"count":62},"Citroen":{"count":51},"Euromaster":{"count":87},"Firestone":{"count":88},"Автосервис":{"count":361},"Advance Auto Parts":{"count":52},"Roady":{"count":56}},"furniture":{"IKEA":{"count":169},"Jysk":{"count":109},"Roller":{"count":78},"Dänisches Bettenlager":{"count":309},"Conforama":{"count":99},"Matratzen Concord":{"count":52},"Мебель":{"count":210},"But":{"count":63}},"doityourself":{"Hornbach":{"count":123},"B&Q":{"count":225},"Hubo":{"count":77},"Mr Bricolage":{"count":88},"Gamma":{"count":111},"OBI":{"count":422},"Lowes":{"count":1152},"Wickes":{"count":123},"Hagebau":{"count":59},"Max Bahr":{"count":79},"Castorama":{"count":153},"Rona":{"count":61},"Home Depot":{"count":865},"Toom Baumarkt":{"count":71},"Homebase":{"count":225},"Baumax":{"count":95},"Lagerhaus":{"count":79},"Bauhaus":{"count":186},"Canadian Tire":{"count":97},"Leroy Merlin":{"count":209},"Hellweg":{"count":58},"Brico":{"count":98},"Bricomarché":{"count":235},"Toom":{"count":67},"Hagebaumarkt":{"count":107},"Praktiker":{"count":122},"Menards":{"count":70},"Weldom":{"count":73},"Bunnings Warehouse":{"count":91},"Ace Hardware":{"count":147},"Home Hardware":{"count":72},"Хозтовары":{"count":86},"Стройматериалы":{"count":197},"Bricorama":{"count":60},"Point P":{"count":59}},"stationery":{"Staples":{"count":299},"McPaper":{"count":83},"Office Depot":{"count":98},"Канцтовары":{"count":63}},"car":{"Skoda":{"count":97},"BMW":{"count":149},"Citroen":{"count":277},"Renault":{"count":382},"Mercedes-Benz":{"count":235},"Volvo":{"count":96},"Ford":{"count":239},"Volkswagen":{"count":217},"Mazda":{"count":105},"Mitsubishi":{"count":73},"Fiat":{"count":93},"Автозапчасти":{"count":277},"Opel":{"count":165},"Audi":{"count":121},"Toyota":{"count":271},"Nissan":{"count":189},"Suzuki":{"count":75},"Honda":{"count":157},"Peugeot":{"count":308},"Шиномонтаж":{"count":259},"Hyundai":{"count":166},"Subaru":{"count":58},"Chevrolet":{"count":86},"Автомагазин":{"count":72}},"clothes":{"Matalan":{"count":90},"KiK":{"count":1219},"H&M":{"count":658},"Urban Outfitters":{"count":63},"Vögele":{"count":132},"Zeeman":{"count":121},"Takko":{"count":515},"Adler":{"count":55},"C&A":{"count":506},"Zara":{"count":217},"Vero Moda":{"count":95},"NKD":{"count":486},"Ernsting's family":{"count":312},"Winners":{"count":65},"River Island":{"count":59},"Next":{"count":176},"Gap":{"count":81},"Adidas":{"count":92},"Woolworths":{"count":117},"Mr Price":{"count":88},"Jet":{"count":61},"Pep":{"count":134},"Edgars":{"count":110},"Ackermans":{"count":91},"Truworths":{"count":65},"Ross":{"count":93},"Burton":{"count":51},"Dorothy Perkins":{"count":53},"Deichmann":{"count":61},"Lindex":{"count":73},"s.Oliver":{"count":56},"Cecil":{"count":51},"Dress Barn":{"count":52},"Old Navy":{"count":174},"Jack & Jones":{"count":52},"Pimkie":{"count":73},"Esprit":{"count":231},"Primark":{"count":92},"Bonita":{"count":155},"Mexx":{"count":67},"Gerry Weber":{"count":71},"Tally Weijl":{"count":70},"Mango":{"count":133},"TK Maxx":{"count":84},"Benetton":{"count":101},"Ulla Popken":{"count":61},"AWG":{"count":66},"Tommy Hilfiger":{"count":75},"New Yorker":{"count":180},"Orsay":{"count":73},"Jeans Fritz":{"count":51},"Charles Vögele":{"count":69},"New Look":{"count":126},"Lacoste":{"count":78},"Etam":{"count":53},"Kiabi":{"count":148},"Jack Wolfskin":{"count":60},"American Apparel":{"count":57},"Men's Wearhouse":{"count":54},"Intimissimi":{"count":52},"United Colors of Benetton":{"count":96},"Jules":{"count":63},"Second Hand":{"count":53},"AOKI":{"count":57},"Calzedonia":{"count":68},"洋服の青山":{"count":100},"Levi's":{"count":63},"Celio":{"count":74},"TJ Maxx":{"count":57},"Promod":{"count":82},"Street One":{"count":72},"ユニクロ":{"count":59},"Banana Republic":{"count":57},"Одежда":{"count":75},"Marshalls":{"count":56},"La Halle":{"count":62},"Peacocks":{"count":89},"しまむら":{"count":60}},"books":{"Bruna":{"count":58},"Waterstones":{"count":90},"Libro":{"count":57},"Barnes & Noble":{"count":267},"Weltbild":{"count":74},"Thalia":{"count":121},"Книги":{"count":112}},"department_store":{"Debenhams":{"count":67},"Canadian Tire":{"count":75},"Karstadt":{"count":64},"Walmart":{"count":517},"Kmart":{"count":143},"Target":{"count":574},"Galeria Kaufhof":{"count":61},"Marks & Spencer":{"count":66},"Big W":{"count":57},"Woolworth":{"count":78},"Универмаг":{"count":72},"Sears":{"count":235},"Walmart Supercenter":{"count":101},"Kohl's":{"count":153},"Macy's":{"count":147},"Sam's Club":{"count":54},"JCPenney":{"count":66}},"alcohol":{"Alko":{"count":145},"The Beer Store":{"count":150},"Systembolaget":{"count":210},"LCBO":{"count":239},"Ароматный мир":{"count":62},"Bargain Booze":{"count":62},"Nicolas":{"count":119},"BWS":{"count":70},"Botilleria":{"count":77},"SAQ":{"count":72},"Gall & Gall":{"count":512},"Живое пиво":{"count":70}},"bakery":{"Kamps":{"count":252},"Banette":{"count":52},"Bäckerei Schmidt":{"count":57},"Anker":{"count":73},"Hofpfisterei":{"count":111},"Greggs":{"count":276},"Oebel":{"count":57},"Boulangerie":{"count":266},"Stadtbäckerei":{"count":57},"Steinecke":{"count":145},"Ihle":{"count":76},"Goldilocks":{"count":59},"Dat Backhus":{"count":67},"K&U":{"count":61},"Der Beck":{"count":96},"Thürmann":{"count":54},"Backwerk":{"count":95},"Schäfer's":{"count":51},"Panaderia":{"count":168},"Goeken backen":{"count":51},"Stadtbäckerei Junge":{"count":51},"Boulangerie Patisserie":{"count":119},"Paul":{"count":81},"Хлеб":{"count":89},"Пекарня":{"count":52},"Кулиничи":{"count":51}},"sports":{"Sports Direct":{"count":57},"Decathlon":{"count":309},"Intersport":{"count":283},"Sports Authority":{"count":75},"Спортмастер":{"count":87},"Sport 2000":{"count":90},"Dick's Sporting Goods":{"count":77}},"variety_store":{"Tedi":{"count":157},"Dollarama":{"count":103},"Family Dollar":{"count":61},"Dollar Tree":{"count":110},"Dollar General":{"count":80}},"pet":{"Fressnapf":{"count":318},"PetSmart":{"count":177},"Das Futterhaus":{"count":69},"Pets at Home":{"count":62},"Petco":{"count":101},"Зоомагазин":{"count":100}},"shoes":{"Deichmann":{"count":622},"Reno":{"count":183},"Ecco":{"count":55},"Clarks":{"count":109},"La Halle aux Chaussures":{"count":69},"Brantano":{"count":71},"Geox":{"count":51},"Salamander":{"count":51},"Обувь":{"count":100},"Payless Shoe Source":{"count":67},"Famous Footwear":{"count":59},"Quick Schuh":{"count":72},"Shoe Zone":{"count":55},"Foot Locker":{"count":82},"Bata":{"count":101},"ЦентрОбувь":{"count":51}},"toys":{"La Grande Récré":{"count":56},"Toys R Us":{"count":151,"tags":{"shop":"toys"}},"Intertoys":{"count":57},"Детский мир":{"count":86},"Игрушки":{"count":58}},"travel_agency":{"Flight Centre":{"count":92},"Thomas Cook":{"count":119}},"jewelry":{"Bijou Brigitte":{"count":57},"Christ":{"count":57},"Swarovski":{"count":74}},"optician":{"Fielmann":{"count":232},"Apollo Optik":{"count":150},"Vision Express":{"count":58},"Оптика":{"count":182},"Optic 2000":{"count":98},"Alain Afflelou":{"count":73},"Specsavers":{"count":124},"Krys":{"count":77},"Atol":{"count":55}},"video":{"Blockbuster":{"count":184},"World of Video":{"count":64}},"mobile_phone":{"Билайн":{"count":128},"ソフトバンクショップ (SoftBank shop)":{"count":255},"Vodafone":{"count":355},"O2":{"count":208},"Carphone Warehouse":{"count":127},"Orange":{"count":246},"Verizon Wireless":{"count":125},"Sprint":{"count":109},"T-Mobile":{"count":175},"МТС":{"count":352},"Евросеть":{"count":506},"Bell":{"count":190},"The Phone House":{"count":83},"SFR":{"count":71},"Связной":{"count":439},"Мегафон":{"count":251},"AT&T":{"count":124},"ドコモショップ (docomo shop)":{"count":114},"au":{"count":65},"Movistar":{"count":77},"Bitė":{"count":72}},"hifi":{},"computer":{"PC World":{"count":55},"DNS":{"count":128}},"hairdresser":{"Klier":{"count":119},"Supercuts":{"count":106},"Hairkiller":{"count":51},"Great Clips":{"count":182},"Парикмахерская":{"count":510},"Стиль":{"count":51},"Franck Provost":{"count":70},"Салон красоты":{"count":70}},"hardware":{"1000 мелочей":{"count":61},"Хозтовары":{"count":151},"Стройматериалы":{"count":54}},"motorcycle":{"Yamaha":{"count":67},"Honda":{"count":69}}}},"addressFormats":[{"format":[["housenumber","street"],["city","postcode"]]},{"countryCodes":["gb"],"format":[["housename"],["housenumber","street"],["city","postcode"]]},{"countryCodes":["ie"],"format":[["housename"],["housenumber","street"],["city"],["postcode"]]},{"countryCodes":["ad","at","ba","be","ch","cz","de","dk","es","fi","gr","hr","is","it","li","nl","no","pl","pt","se","si","sk","sm","va"],"format":[["street","housenumber"],["postcode","city"]]},{"countryCodes":["fr","lu","mo"],"format":[["housenumber","street"],["postcode","city"]]},{"countryCodes":["br"],"format":[["street"],["housenumber","suburb"],["city","postcode"]]},{"countryCodes":["vn"],"format":[["housenumber","street"],["subdistrict"],["district"],["city"],["province","postcode"]]},{"countryCodes":["us"],"format":[["housenumber","street"],["city","state","postcode"]]},{"countryCodes":["ca"],"format":[["housenumber","street"],["city","province","postcode"]]}],"phoneFormats":{"us":"+1-202-555-1234","ca":"+1-226-555-1234","bs":"+1-242-555-1234","bb":"+1-246-555-1234","ai":"+1-264-555-1234","ag":"+1-268-555-1234","vg":"+1-284-555-1234","vi":"+1-340-555-1234","ky":"+1-345-555-1234","bm":"+1-441-555-1234","gd":"+1-473-555-1234","tc":"+1-649-555-1234","ms":"+1-664-555-1234","mp":"+1-670-555-1234","gu":"+1-671-555-1234","as":"+1-684-555-1234","sx":"+1-721-555-1234","lc":"+1-758-555-1234","dm":"+1-767-555-1234","vc":"+1-784-555-1234","pr":"+1-787-555-1234","do":"+1-809-555-1234","tt":"+1-868-555-1234","kn":"+1-869-555-1234","jm":"+1-876-555-1234","za":"+27 11 907 1111","nl":"+31 42 123 4567","fr":"+33 1 23 45 67 89","es":"+34 989 12 34 56","pt":"+351 211 123456","fi":"+358 40 123 4567","hu":"+36 1 123 45 67","hr":"+385 01 123 4567","si":"+386 31 123 4567","it":"+39 01 123 456","va":"+39 01 123 456","gb":"+44 207 123456","gg":"+44 207 123456","im":"+44 207 123456","je":"+44 207 123456","se":"+46 31 123 4567","no":"+47 22 12 34 56","sj":"+47 22 12 34 56","pl":"+48 42 123 4567","de":"+49 89 1234567","br":"+55 11 0982 1098","ru":"+7 495 1234567","kz":"+7 495 1234567","vn":"+84 1 234 5678","hk":"+852 12345678"},"driveLeft":{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-75.38818,18.62542],[-80.09033,20.32402],[-82.24365,19.26967],[-77.36572,16.42555],[-75.38818,18.62542]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-64.33594,32.86113],[-79.39819,27.21067],[-80.63965,23.71998],[-73.39966,20.40642],[-65.1709,19.20743],[-65.09399,16.23577],[-59.13391,16.80454],[-62.64404,15.16228],[-59.43054,14.85985],[-62.83081,13.64599],[-61.80359,10.73752],[-62.08649,10.04729],[-61.06201,9.85522],[-59.81369,8.31274],[-59.8027,8.27469],[-59.83498,8.22712],[-59.94141,8.21149],[-59.99771,8.15576],[-59.97986,8.13265],[-59.99771,8.12041],[-60.00183,8.07147],[-60.05127,8.02524],[-60.09933,8.03747],[-60.12268,8.02388],[-60.14053,7.98988],[-60.36163,7.83345],[-60.53467,7.81713],[-60.5896,7.6375],[-60.72144,7.54493],[-60.5896,7.31888],[-60.63904,7.24532],[-60.54703,7.12542],[-60.46875,7.20309],[-60.37262,7.18401],[-60.29984,7.1445],[-60.2916,7.06819],[-60.39871,6.95097],[-60.66513,6.83235],[-60.71869,6.75053],[-60.91232,6.81735],[-60.94254,6.72053],[-61.14441,6.72326],[-61.23093,6.5773],[-61.1554,6.45314],[-61.14441,6.20199],[-61.39709,5.95619],[-60.71045,5.20036],[-60.21606,5.23319],[-59.99634,5.06906],[-60.13916,4.51071],[-59.69971,4.40118],[-59.5459,3.93002],[-59.87549,3.56825],[-59.7876,3.37086],[-60.01831,2.83332],[-59.90845,2.38335],[-59.69971,2.2626],[-59.77661,1.87833],[-59.65302,1.85087],[-59.69147,1.75754],[-59.61456,1.71361],[-59.55139,1.73283],[-59.36188,1.49123],[-59.26575,1.39238],[-58.92242,1.30726],[-58.83728,1.17271],[-58.71918,1.23037],[-58.71094,1.29902],[-58.49121,1.26058],[-58.461,1.37591],[-58.50494,1.38689],[-58.51044,1.46102],[-58.38135,1.4775],[-58.32642,1.57359],[-58.00507,1.49946],[-57.99133,1.65321],[-57.79907,1.69165],[-57.70844,1.71087],[-57.54364,1.68341],[-57.41455,1.94421],[-57.10693,1.97715],[-56.8103,1.85636],[-56.48071,1.92225],[-55.90942,1.81244],[-55.90942,2.04302],[-56.14014,2.26534],[-55.94788,2.53701],[-55.70892,2.39981],[-55.37933,2.43274],[-55.19257,2.53976],[-54.98108,2.57268],[-54.88495,2.43548],[-54.71191,2.46293],[-54.69543,2.34767],[-54.58832,2.32846],[-54.43451,2.43548],[-54.20654,2.76748],[-54.17358,3.12955],[-53.96484,3.57921],[-54.33838,4.00674],[-54.44412,4.52577],[-54.46884,4.91036],[-54.36653,5.13061],[-54.27727,5.26191],[-54.19968,5.3084],[-54.01222,5.54457],[-54.0239,5.64605],[-53.86322,5.94936],[-64.33594,32.86113]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-14.5459,-5.92204],[11.79657,-17.27197],[12.03415,-17.14866],[12.38159,-17.22213],[12.58484,-17.22476],[13.01193,-16.97405],[13.24951,-17.00098],[13.38135,-16.98522],[13.93341,-17.38734],[18.42476,-17.38996],[18.47763,-17.46857],[18.62663,-17.64599],[18.79211,-17.76177],[18.92944,-17.82061],[19.33044,-17.84806],[19.66278,-17.8644],[19.74792,-17.90557],[19.85161,-17.87486],[20.10361,-17.90296],[20.18394,-17.88401],[20.34737,-17.88466],[20.44693,-17.91733],[20.51697,-17.96698],[20.83008,-18.03032],[20.95711,-17.97285],[21.16997,-17.93497],[21.42677,-18.0264],[23.45032,-17.63879],[22.00012,-16.38866],[21.99944,-13.00523],[24.03809,-12.99118],[24.03809,-10.91962],[24.43359,-11.09217],[24.45557,-11.48002],[25.37842,-11.19996],[25.42236,-11.60919],[26.96045,-11.97484],[27.18018,-11.60919],[28.125,-12.42048],[29.11377,-13.36824],[29.1687,-13.43771],[29.55872,-13.19716],[29.68506,-13.2239],[29.62463,-13.41099],[29.80591,-13.44305],[29.81415,-12.14809],[29.31152,-12.55456],[28.41064,-11.78133],[28.63037,-10.70379],[28.65234,-9.73071],[28.37219,-9.24309],[28.89748,-8.47916],[30.78644,-8.26857],[29.39941,-6.05316],[29.4873,-4.45595],[29.75922,-4.46759],[29.81415,-4.36421],[29.88007,-4.36832],[30.04074,-4.26699],[30.07919,-4.1629],[30.18356,-4.08311],[30.1918,-4.05126],[30.21566,-4.04595],[30.22923,-4.01136],[30.21326,-3.99612],[30.25978,-3.88755],[30.29274,-3.86288],[30.34424,-3.77245],[30.39848,-3.79095],[30.40878,-3.76765],[30.39548,-3.7304],[30.39054,-3.72821],[30.3896,-3.71918],[30.39093,-3.7101],[30.39514,-3.70444],[30.42028,-3.64963],[30.46886,-3.53501],[30.67108,-3.41335],[30.63297,-3.34892],[30.84206,-3.25535],[30.84549,-3.16108],[30.83485,-3.09698],[30.7933,-3.06235],[30.82111,-3.02258],[30.84515,-2.9739],[30.74764,-2.99618],[30.7037,-2.97013],[30.66422,-2.98967],[30.57632,-2.90738],[30.49393,-2.94441],[30.41016,-2.87172],[30.52002,-2.39432],[30.77545,-2.38883],[30.8606,-2.31199],[30.84961,-2.19398],[30.89081,-2.07322],[30.81116,-1.96068],[30.83862,-1.6587],[30.73425,-1.4418],[30.56259,-1.33884],[30.4541,-1.05737],[30.35797,-1.06287],[30.34149,-1.13152],[30.16571,-1.34296],[29.91852,-1.48024],[29.83887,-1.31824],[29.58344,-1.39238],[29.729,0.05493],[29.96796,0.5136],[29.9707,0.8569],[30.22339,0.92281],[30.24536,1.15349],[30.47745,1.20772],[31.30966,2.15693],[31.20255,2.22211],[31.20255,2.29278],[31.16409,2.27906],[31.13937,2.28318],[31.13113,2.26534],[31.07826,2.30033],[31.0714,2.34767],[31.00479,2.4005],[30.97183,2.40461],[30.94711,2.38746],[30.94849,2.36276],[30.9375,2.33532],[30.88531,2.34012],[30.83038,2.42176],[30.74112,2.43274],[30.76035,2.5864],[30.90179,2.88132],[30.76447,3.04178],[30.93613,3.40239],[30.94059,3.50588],[30.85236,3.48601],[30.90866,3.5936],[30.95055,3.63918],[30.94677,3.65391],[30.9866,3.70187],[31.00582,3.70701],[31.02058,3.69708],[31.16547,3.7954],[31.28838,3.79643],[31.52699,3.66282],[31.7038,3.72449],[31.82671,3.82794],[31.96198,3.65596],[31.95854,3.57099],[32.04987,3.59155],[32.07733,3.57099],[32.0842,3.53672],[32.20093,3.50657],[32.21672,3.56448],[32.19578,3.59977],[32.41516,3.74504],[32.72055,3.76782],[32.89307,3.81219],[33.02782,3.89371],[33.18146,3.7793],[33.51173,3.75258],[33.98758,4.23309],[34.05762,4.28342],[34.38721,4.61065],[35.94452,4.62023],[35.95688,4.53467],[36.04134,4.44568],[36.89621,4.4491],[38.14728,3.62992],[38.55927,3.62033],[38.92181,3.51068],[39.56039,3.43392],[39.87076,3.87522],[40.76752,4.28753],[41.16371,3.94372],[41.89774,3.97797],[41.31271,3.14463],[40.98896,2.82869],[40.99548,-0.84042],[41.72607,-1.81244],[41.57227,-48.80686],[-27.11426,-60.23981],[-65.78613,-52.5897],[-14.5459,-5.92204]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[31.33301,35.47856],[33.0249,33.75175],[35.44189,36.02245],[31.33301,35.47856]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[13.86475,36.46105],[15.271,35.8935],[14.17786,35.40248],[13.86475,36.46105]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[0.13184,61.89758],[2.41699,51.26191],[-2.03247,49.91763],[-1.90338,48.93333],[-20.12695,53.33087],[0.13184,61.89758]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[180,-35.35322],[161.41113,-19.68397],[167.00317,-12.26486],[180,-21.7697],[180,4.62023],[170.85938,3.77656],[127.08435,5.74171],[118.21289,2.76748],[119.52026,5.30977],[117.26807,7.51498],[102.6123,8.47781],[102.9158,11.74099],[102.76268,12.07357],[102.70226,12.17158],[102.74139,12.46474],[102.53128,12.68857],[102.49557,12.9256],[102.49763,13.0064],[102.4597,13.08199],[102.43412,13.09026],[102.39155,13.16407],[102.35481,13.29341],[102.35893,13.30945],[102.34503,13.34837],[102.35928,13.39797],[102.3567,13.48095],[102.36168,13.50582],[102.33559,13.53787],[102.33971,13.56023],[102.35498,13.5649],[102.36511,13.5785],[102.40047,13.5679],[102.42537,13.56891],[102.44614,13.56123],[102.48047,13.57091],[102.53849,13.56757],[102.5699,13.58526],[102.57806,13.60486],[102.62501,13.60845],[102.62132,13.61295],[102.60767,13.61562],[102.57231,13.63331],[102.56922,13.64082],[102.54879,13.658],[102.56252,13.68552],[102.5972,13.70803],[102.67084,13.74472],[102.68818,13.75172],[102.7014,13.7684],[102.73161,13.77082],[102.76543,13.85541],[102.78397,13.93207],[102.80388,13.94406],[102.81607,13.96639],[102.90705,14.02119],[102.89726,14.0535],[102.90095,14.0838],[102.92421,14.10744],[102.92378,14.12838],[102.94147,14.15035],[102.92953,14.17952],[103.17535,14.33774],[103.19939,14.32992],[103.68553,14.44],[103.94508,14.34157],[104.05756,14.34589],[104.06636,14.3419],[104.26025,14.37749],[104.50058,14.36984],[104.57817,14.36019],[104.6422,14.42387],[104.66632,14.40234],[104.68357,14.39877],[104.71138,14.43169],[104.72305,14.42188],[104.71687,14.40043],[104.75344,14.40459],[104.80408,14.43867],[104.83429,14.41573],[104.99239,14.3838],[105.05402,14.19783],[105.47905,14.49186],[105.60883,15.0005],[105.46703,15.13005],[105.48866,15.20237],[105.59269,15.2716],[105.58617,15.32823],[105.50308,15.31912],[105.46703,15.33948],[105.49175,15.37921],[105.59372,15.42869],[105.59372,15.50927],[105.60986,15.54871],[105.62616,15.56492],[105.62702,15.59129],[105.63518,15.62742],[105.63612,15.66056],[105.5975,15.72088],[105.49965,15.76681],[105.46291,15.74517],[105.43819,15.75459],[105.40489,15.79424],[105.34241,15.92039],[105.37811,15.98344],[105.39167,15.99136],[105.41931,15.98608],[105.42652,15.99764],[105.41468,16.01661],[105.21263,16.05076],[105.04955,16.10552],[105.01316,16.24401],[104.88235,16.37812],[104.8391,16.45782],[104.77936,16.49041],[104.73919,16.53287],[104.74228,16.62205],[104.76391,16.70953],[104.73953,16.80323],[104.76425,16.85088],[104.73782,16.90968],[104.744,17.0128],[104.81266,17.21853],[104.79841,17.39274],[104.70348,17.52833],[104.46384,17.65515],[104.34368,17.83564],[104.27776,17.8559],[104.22661,17.98069],[104.1116,18.10735],[104.06525,18.21174],[103.97392,18.33823],[103.9286,18.33237],[103.88809,18.29456],[103.85582,18.28673],[103.83659,18.32715],[103.79128,18.3467],[103.70201,18.34214],[103.60931,18.40405],[103.57292,18.40437],[103.51593,18.42978],[103.45963,18.42587],[103.41568,18.44802],[103.30479,18.43206],[103.24265,18.37082],[103.24333,18.34133],[103.29123,18.32357],[103.28899,18.29521],[103.23595,18.28299],[103.16608,18.25511],[103.02429,17.98135],[102.6535,17.83237],[102.40631,17.99963],[102.10968,18.22413],[101.548,17.81538],[101.30493,17.64991],[101.14563,17.46595],[100.95886,17.61654],[101.01757,17.88858],[101.18752,18.05121],[101.16863,18.10409],[101.18134,18.33595],[101.08727,18.38287],[101.05499,18.43988],[101.23215,18.73015],[101.35265,19.04524],[101.25927,19.12733],[101.2373,19.32637],[101.25824,19.58438],[101.11954,19.56836],[101.08898,19.58777],[101.08624,19.59715],[101.03165,19.6185],[100.89844,19.62125],[100.77827,19.49249],[100.63751,19.56432],[100.58258,19.49313],[100.47478,19.5944],[100.42929,19.67152],[100.43341,19.7024],[100.4147,19.7255],[100.40525,19.7646],[100.43907,19.80345],[100.45555,19.84843],[100.50636,19.87264],[100.51709,19.93027],[100.58653,20.1599],[100.56576,20.1757],[100.54945,20.17473],[100.52731,20.14379],[100.51065,20.14895],[100.48697,20.17956],[100.46774,20.196],[100.45246,20.20147],[100.45521,20.22129],[100.44783,20.23546],[100.41607,20.25286],[100.40594,20.28184],[100.38397,20.31082],[100.37556,20.35187],[100.36165,20.35638],[100.35736,20.37408],[100.33195,20.39902],[100.27805,20.40224],[100.25917,20.39677],[100.2475,20.37263],[100.22535,20.35509],[100.22346,20.31839],[100.16579,20.29988],[100.17162,20.24545],[100.10845,20.25221],[100.09266,20.2696],[100.09798,20.31485],[100.07961,20.3678],[99.9567,20.46417],[99.91636,20.44925],[99.90765,20.44977],[99.89121,20.44511],[99.87276,20.44406],[99.86212,20.44326],[99.80186,20.33948],[99.46472,20.3884],[99.56085,20.20035],[99.43691,20.08882],[99.27727,20.11623],[99.06921,20.1101],[98.97789,19.74538],[98.24387,19.68656],[97.85934,19.57014],[97.76733,18.57336],[97.39655,18.47179],[97.62451,18.30238],[97.73849,17.97743],[97.66502,17.87943],[97.90947,17.56745],[98.52951,16.82557],[98.51303,16.69276],[98.69293,16.26873],[98.87421,16.43609],[98.93394,16.3353],[98.84743,16.13356],[98.74512,16.12037],[98.58307,16.07287],[98.5762,15.79754],[98.54736,15.37557],[98.17383,15.15167],[98.3606,14.63674],[99.08295,13.89208],[99.16534,13.72204],[99.18182,13.00723],[99.39331,12.56797],[99.64153,11.78973],[99.32156,11.30266],[98.77859,10.67849],[98.80597,10.47642],[98.76657,10.40459],[98.74924,10.34194],[96.85547,6.40265],[92.42523,20.54794],[92.2728,20.96272],[92.18353,21.16072],[92.21718,21.23434],[92.19727,21.32968],[92.26318,21.36485],[92.27142,21.43326],[92.32635,21.41919],[92.34695,21.47032],[92.37717,21.48246],[92.42592,21.43453],[92.43073,21.3706],[92.50763,21.35654],[92.51793,21.38979],[92.55913,21.37892],[92.60376,21.24074],[92.68135,21.29129],[92.62642,21.43262],[92.60925,21.99908],[93.21899,22.2586],[93.1366,23.05446],[93.34534,23.05952],[93.43323,23.68729],[93.33572,24.07154],[93.4964,23.94359],[93.62549,24.00758],[94.15524,23.84675],[94.17068,23.92821],[94.25171,24.07405],[94.27952,24.17307],[94.28844,24.23037],[94.30175,24.2371],[94.32561,24.2731],[94.32587,24.32457],[94.58199,24.72282],[94.73648,25.13783],[94.78455,25.47799],[94.88823,25.58054],[95.14709,26.07035],[95.1004,26.51482],[96.3446,27.28393],[97.36359,27.91919],[96.14136,29.38218],[95.41626,29.08498],[94.79004,29.20971],[92.40051,27.81964],[91.6452,27.76376],[91.40625,28.0138],[89.57977,28.18824],[88.90961,27.3242],[88.77228,27.54602],[88.90961,27.884],[88.70361,28.1059],[88.12134,27.87793],[85.92957,27.94103],[81.62292,30.44157],[81.19995,30.03343],[78.75,31.50363],[78.77197,31.96148],[78.39844,32.50513],[78.95325,32.37068],[79.3158,32.49586],[79.16199,33.0363],[79.01367,34.33436],[78.26111,34.6558],[78.01392,35.46067],[76.16821,35.79108],[76.00342,36.5626],[75.16846,37.01133],[74.53674,36.99817],[72.52075,36.84006],[71.19141,36.08906],[71.65283,35.44725],[71.50452,34.9805],[70.98541,34.54955],[71.17905,34.36271],[71.09253,34.11522],[70.8783,33.97298],[70.51849,33.94336],[69.90807,34.04697],[69.86893,33.96614],[69.97055,33.75746],[70.13123,33.72662],[70.33997,33.342],[70.02136,33.13985],[69.5723,33.09614],[69.23859,32.46227],[69.31274,31.9172],[68.11798,31.75153],[66.39313,30.93875],[66.26404,29.80252],[62.49023,29.41089],[60.87524,29.86447],[61.5303,29.01174],[61.65802,28.77609],[61.96632,28.5405],[62.43256,28.42341],[62.6049,28.2548],[62.7951,28.28141],[62.86789,27.23448],[63.25996,27.2473],[63.32726,27.1334],[63.25241,27.09092],[63.25241,26.83755],[63.18787,26.83816],[63.18993,26.65237],[62.30896,26.51974],[62.21558,26.27371],[61.85303,26.22937],[61.83105,25.75042],[61.68274,25.67124],[56.55762,-21.18697],[180,-65],[180,-35.35322]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[138.75732,46.69467],[145.5249,44.48475],[145.27771,43.68376],[145.86548,43.39307],[142.84424,18.22935],[122.09106,24.37712],[138.75732,46.69467]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-180,7],[-148,7],[-151,-12],[-171.5,-10.75],[-171,-16],[-152,-19],[-180,-55],[-180,7]]]}}]}};