]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
1bacaee67de39594b56fd4714402897a3ec3d9bd
[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) {
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                 autocomplete();
6372                 render();
6373             });
6374         }
6375
6376         function nav(dir) {
6377             idx = Math.max(Math.min(idx + dir, suggestions.length - 1), 0);
6378             input.property('value', suggestions[idx].value);
6379             render();
6380             ensureVisible();
6381         }
6382
6383         function value() {
6384             var value = input.property('value'),
6385                 start = input.property('selectionStart'),
6386                 end = input.property('selectionEnd');
6387
6388             if (start && end) {
6389                 value = value.substring(0, start);
6390             }
6391
6392             return value;
6393         }
6394
6395         function fetch(v, cb) {
6396             fetcher.call(input, v, function(_) {
6397                 suggestions = _;
6398                 cb();
6399             });
6400         }
6401
6402         function autocomplete() {
6403             var v = caseSensitive ? value() : value().toLowerCase();
6404             idx = -1;
6405             if (!v) return;
6406
6407             for (var i = 0; i < suggestions.length; i++) {
6408                 var suggestion = suggestions[i].value,
6409                     compare = caseSensitive ? suggestion : suggestion.toLowerCase();
6410
6411                 if (compare.indexOf(v) === 0) {
6412                     idx = i;
6413                     input.property('value', suggestion);
6414                     input.node().setSelectionRange(v.length, suggestion.length);
6415                     return;
6416                 }
6417             }
6418         }
6419
6420         function render() {
6421             if (suggestions.length >= minItems && document.activeElement === input.node()) {
6422                 show();
6423             } else {
6424                 hide();
6425                 return;
6426             }
6427
6428             var options = container
6429                 .selectAll('a.combobox-option')
6430                 .data(suggestions, function(d) { return d.value; });
6431
6432             options.enter().append('a')
6433                 .attr('class', 'combobox-option')
6434                 .text(function(d) { return d.value; });
6435
6436             options
6437                 .attr('title', function(d) { return d.title; })
6438                 .classed('selected', function(d, i) { return i == idx; })
6439                 .on('mouseover', select)
6440                 .on('click', accept)
6441                 .order();
6442
6443             options.exit()
6444                 .remove();
6445
6446             var rect = input.node().getBoundingClientRect();
6447
6448             container.style({
6449                 'left': rect.left + 'px',
6450                 'width': rect.width + 'px',
6451                 'top': rect.height + rect.top + 'px'
6452             });
6453         }
6454
6455         function select(d, i) {
6456             idx = i;
6457             render();
6458         }
6459
6460         function ensureVisible() {
6461             var node = container.selectAll('a.selected').node();
6462             if (node) node.scrollIntoView();
6463         }
6464
6465         function accept(d) {
6466             if (!shown) return;
6467             input
6468                 .property('value', d.value)
6469                 .trigger('change');
6470             event.accept(d);
6471             hide();
6472         }
6473     };
6474
6475     combobox.fetcher = function(_) {
6476         if (!arguments.length) return fetcher;
6477         fetcher = _;
6478         return combobox;
6479     };
6480
6481     combobox.data = function(_) {
6482         if (!arguments.length) return data;
6483         data = _;
6484         return combobox;
6485     };
6486
6487     combobox.minItems = function(_) {
6488         if (!arguments.length) return minItems;
6489         minItems = _;
6490         return combobox;
6491     };
6492
6493     combobox.caseSensitive = function(_) {
6494         if (!arguments.length) return caseSensitive;
6495         caseSensitive = _;
6496         return combobox;
6497     };
6498
6499     return d3.rebind(combobox, event, 'on');
6500 };
6501
6502 d3.combobox.off = function(input) {
6503     data = null;
6504     fetcher = null;
6505
6506     input
6507         .on('focus.typeahead', null)
6508         .on('blur.typeahead', null)
6509         .on('keydown.typeahead', null)
6510         .on('keyup.typeahead', null)
6511         .on('input.typeahead', null)
6512         .each(function() {
6513             d3.select(this.parentNode).selectAll('.combobox-caret')
6514                 .filter(function(d) { return d === input.node(); })
6515                 .on('mousedown', null);
6516         });
6517
6518     d3.select(document.body)
6519         .on('scroll.combobox', null);
6520 };
6521 d3.geo.tile = function() {
6522   var size = [960, 500],
6523       scale = 256,
6524       scaleExtent = [0, 20],
6525       translate = [size[0] / 2, size[1] / 2],
6526       zoomDelta = 0;
6527
6528   function bound(_) {
6529       return Math.min(scaleExtent[1], Math.max(scaleExtent[0], _));
6530   }
6531
6532   function tile() {
6533     var z = Math.max(Math.log(scale) / Math.LN2 - 8, 0),
6534         z0 = bound(Math.round(z + zoomDelta)),
6535         k = Math.pow(2, z - z0 + 8),
6536         origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k],
6537         tiles = [],
6538         cols = d3.range(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))),
6539         rows = d3.range(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1])));
6540
6541     rows.forEach(function(y) {
6542       cols.forEach(function(x) {
6543         tiles.push([x, y, z0]);
6544       });
6545     });
6546
6547     tiles.translate = origin;
6548     tiles.scale = k;
6549
6550     return tiles;
6551   }
6552
6553   tile.scaleExtent = function(_) {
6554     if (!arguments.length) return scaleExtent;
6555     scaleExtent = _;
6556     return tile;
6557   };
6558
6559   tile.size = function(_) {
6560     if (!arguments.length) return size;
6561     size = _;
6562     return tile;
6563   };
6564
6565   tile.scale = function(_) {
6566     if (!arguments.length) return scale;
6567     scale = _;
6568     return tile;
6569   };
6570
6571   tile.translate = function(_) {
6572     if (!arguments.length) return translate;
6573     translate = _;
6574     return tile;
6575   };
6576
6577   tile.zoomDelta = function(_) {
6578     if (!arguments.length) return zoomDelta;
6579     zoomDelta = +_;
6580     return tile;
6581   };
6582
6583   return tile;
6584 };
6585 d3.jsonp = function (url, callback) {
6586   function rand() {
6587     var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
6588       c = '', i = -1;
6589     while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
6590     return c;
6591   }
6592
6593   function create(url) {
6594     var e = url.match(/callback=d3.jsonp.(\w+)/),
6595       c = e ? e[1] : rand();
6596     d3.jsonp[c] = function(data) {
6597       callback(data);
6598       delete d3.jsonp[c];
6599       script.remove();
6600     };
6601     return 'd3.jsonp.' + c;
6602   }
6603
6604   var cb = create(url),
6605     script = d3.select('head')
6606     .append('script')
6607     .attr('type', 'text/javascript')
6608     .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
6609 };
6610 /*
6611  * This code is licensed under the MIT license.
6612  *
6613  * Copyright © 2013, iD authors.
6614  *
6615  * Portions copyright © 2011, Keith Cirkel
6616  * See https://github.com/keithamus/jwerty
6617  *
6618  */
6619 d3.keybinding = function(namespace) {
6620     var bindings = [];
6621
6622     function matches(binding, event) {
6623         for (var p in binding.event) {
6624             if (event[p] != binding.event[p])
6625                 return false;
6626         }
6627
6628         return (!binding.capture) === (event.eventPhase !== Event.CAPTURING_PHASE);
6629     }
6630
6631     function capture() {
6632         for (var i = 0; i < bindings.length; i++) {
6633             var binding = bindings[i];
6634             if (matches(binding, d3.event)) {
6635                 binding.callback();
6636             }
6637         }
6638     }
6639
6640     function bubble() {
6641         var tagName = d3.select(d3.event.target).node().tagName;
6642         if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
6643             return;
6644         }
6645         capture();
6646     }
6647
6648     function keybinding(selection) {
6649         selection = selection || d3.select(document);
6650         selection.on('keydown.capture' + namespace, capture, true);
6651         selection.on('keydown.bubble' + namespace, bubble, false);
6652         return keybinding;
6653     }
6654
6655     keybinding.off = function(selection) {
6656         selection = selection || d3.select(document);
6657         selection.on('keydown.capture' + namespace, null);
6658         selection.on('keydown.bubble' + namespace, null);
6659         return keybinding;
6660     };
6661
6662     keybinding.on = function(code, callback, capture) {
6663         var binding = {
6664             event: {
6665                 keyCode: 0,
6666                 shiftKey: false,
6667                 ctrlKey: false,
6668                 altKey: false,
6669                 metaKey: false
6670             },
6671             capture: capture,
6672             callback: callback
6673         };
6674
6675         code = code.toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
6676
6677         for (var i = 0; i < code.length; i++) {
6678             // Normalise matching errors
6679             if (code[i] === '++') code[i] = '+';
6680
6681             if (code[i] in d3.keybinding.modifierCodes) {
6682                 binding.event[d3.keybinding.modifierProperties[d3.keybinding.modifierCodes[code[i]]]] = true;
6683             } else if (code[i] in d3.keybinding.keyCodes) {
6684                 binding.event.keyCode = d3.keybinding.keyCodes[code[i]];
6685             }
6686         }
6687
6688         bindings.push(binding);
6689
6690         return keybinding;
6691     };
6692
6693     return keybinding;
6694 };
6695
6696 (function () {
6697     d3.keybinding.modifierCodes = {
6698         // Shift key, ⇧
6699         '⇧': 16, shift: 16,
6700         // CTRL key, on Mac: ⌃
6701         '⌃': 17, ctrl: 17,
6702         // ALT key, on Mac: ⌥ (Alt)
6703         '⌥': 18, alt: 18, option: 18,
6704         // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
6705         '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
6706     };
6707
6708     d3.keybinding.modifierProperties = {
6709         16: 'shiftKey',
6710         17: 'ctrlKey',
6711         18: 'altKey',
6712         91: 'metaKey'
6713     };
6714
6715     d3.keybinding.keyCodes = {
6716         // Backspace key, on Mac: ⌫ (Backspace)
6717         '⌫': 8, backspace: 8,
6718         // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
6719         '⇥': 9, '⇆': 9, tab: 9,
6720         // Return key, ↩
6721         '↩': 13, 'return': 13, enter: 13, '⌅': 13,
6722         // Pause/Break key
6723         'pause': 19, 'pause-break': 19,
6724         // Caps Lock key, ⇪
6725         '⇪': 20, caps: 20, 'caps-lock': 20,
6726         // Escape key, on Mac: ⎋, on Windows: Esc
6727         '⎋': 27, escape: 27, esc: 27,
6728         // Space key
6729         space: 32,
6730         // Page-Up key, or pgup, on Mac: ↖
6731         '↖': 33, pgup: 33, 'page-up': 33,
6732         // Page-Down key, or pgdown, on Mac: ↘
6733         '↘': 34, pgdown: 34, 'page-down': 34,
6734         // END key, on Mac: ⇟
6735         '⇟': 35, end: 35,
6736         // HOME key, on Mac: ⇞
6737         '⇞': 36, home: 36,
6738         // Insert key, or ins
6739         ins: 45, insert: 45,
6740         // Delete key, on Mac: ⌦ (Delete)
6741         '⌦': 46, del: 46, 'delete': 46,
6742         // Left Arrow Key, or ←
6743         '←': 37, left: 37, 'arrow-left': 37,
6744         // Up Arrow Key, or ↑
6745         '↑': 38, up: 38, 'arrow-up': 38,
6746         // Right Arrow Key, or →
6747         '→': 39, right: 39, 'arrow-right': 39,
6748         // Up Arrow Key, or ↓
6749         '↓': 40, down: 40, 'arrow-down': 40,
6750         // odities, printing characters that come out wrong:
6751         // Firefox Equals
6752         'ffequals': 61,
6753         // Num-Multiply, or *
6754         '*': 106, star: 106, asterisk: 106, multiply: 106,
6755         // Num-Plus or +
6756         '+': 107, 'plus': 107,
6757         // Num-Subtract, or -
6758         '-': 109, subtract: 109,
6759         // Firefox Minus
6760         'ffplus': 171,
6761         // Firefox Minus
6762         'ffminus': 173,
6763         // Semicolon
6764         ';': 186, semicolon: 186,
6765         // = or equals
6766         '=': 187, 'equals': 187,
6767         // Comma, or ,
6768         ',': 188, comma: 188,
6769         'dash': 189, //???
6770         // Period, or ., or full-stop
6771         '.': 190, period: 190, 'full-stop': 190,
6772         // Slash, or /, or forward-slash
6773         '/': 191, slash: 191, 'forward-slash': 191,
6774         // Tick, or `, or back-quote
6775         '`': 192, tick: 192, 'back-quote': 192,
6776         // Open bracket, or [
6777         '[': 219, 'open-bracket': 219,
6778         // Back slash, or \
6779         '\\': 220, 'back-slash': 220,
6780         // Close backet, or ]
6781         ']': 221, 'close-bracket': 221,
6782         // Apostrophe, or Quote, or '
6783         '\'': 222, quote: 222, apostrophe: 222
6784     };
6785
6786     // NUMPAD 0-9
6787     var i = 95, n = 0;
6788     while (++i < 106) {
6789         d3.keybinding.keyCodes['num-' + n] = i;
6790         ++n;
6791     }
6792
6793     // 0-9
6794     i = 47; n = 0;
6795     while (++i < 58) {
6796         d3.keybinding.keyCodes[n] = i;
6797         ++n;
6798     }
6799
6800     // F1-F25
6801     i = 111; n = 1;
6802     while (++i < 136) {
6803         d3.keybinding.keyCodes['f' + n] = i;
6804         ++n;
6805     }
6806
6807     // a-z
6808     i = 64;
6809     while (++i < 91) {
6810         d3.keybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
6811     }
6812 })();
6813 d3.selection.prototype.one = function (type, listener, capture) {
6814     var target = this, typeOnce = type + ".once";
6815     function one() {
6816         target.on(typeOnce, null);
6817         listener.apply(this, arguments);
6818     }
6819     target.on(typeOnce, one, capture);
6820     return this;
6821 };
6822 d3.selection.prototype.dimensions = function (dimensions) {
6823     var refresh = (function(node) {
6824         var cr = node.getBoundingClientRect();
6825         prop = [cr.width, cr.height];
6826         this.property('__dimensions__', prop);
6827         return prop;
6828     }).bind(this);
6829
6830     var node = this.node();
6831
6832     if (!arguments.length) {
6833         if (!node) return [0,0];
6834         return this.property('__dimensions__') || refresh(node);
6835     }
6836     if (dimensions === null) {
6837         if (!node) return [0,0];
6838         return refresh(node);
6839     }
6840
6841     return this
6842         .property('__dimensions__', [dimensions[0], dimensions[1]])
6843         .attr({width: dimensions[0], height: dimensions[1]});
6844 };
6845 d3.selection.prototype.trigger = function (type) {
6846     this.each(function() {
6847         var evt = document.createEvent('HTMLEvents');
6848         evt.initEvent(type, true, true);
6849         this.dispatchEvent(evt);
6850     });
6851 };
6852 // Tooltips and svg mask used to highlight certain features
6853 d3.curtain = function() {
6854
6855     var event = d3.dispatch(),
6856         surface,
6857         tooltip,
6858         darkness;
6859
6860     function curtain(selection) {
6861
6862         surface = selection.append('svg')
6863             .attr('id', 'curtain')
6864             .style({
6865                 'z-index': 1000,
6866                 'pointer-events': 'none',
6867                 'position': 'absolute',
6868                 'top': 0,
6869                 'left': 0
6870             });
6871
6872         darkness = surface.append('path')
6873             .attr({
6874                 x: 0,
6875                 y: 0,
6876                 'class': 'curtain-darkness'
6877             });
6878
6879         d3.select(window).on('resize.curtain', resize);
6880
6881         tooltip = selection.append('div')
6882             .attr('class', 'tooltip')
6883             .style('z-index', 1002);
6884
6885         tooltip.append('div').attr('class', 'tooltip-arrow');
6886         tooltip.append('div').attr('class', 'tooltip-inner');
6887
6888         resize();
6889
6890         function resize() {
6891             surface.attr({
6892                 width: window.innerWidth,
6893                 height: window.innerHeight
6894             });
6895             curtain.cut(darkness.datum());
6896         }
6897     }
6898
6899     curtain.reveal = function(box, text, tooltipclass, duration) {
6900         if (typeof box === 'string') box = d3.select(box).node();
6901         if (box.getBoundingClientRect) box = box.getBoundingClientRect();
6902
6903         curtain.cut(box, duration);
6904
6905         if (text) {
6906             // pseudo markdown bold text hack
6907             var parts = text.split('**');
6908             var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
6909             if (parts[1]) html += '<span class="bold">' + parts[1] + '</span>';
6910
6911             var dimensions = tooltip.classed('in', true)
6912                 .select('.tooltip-inner')
6913                     .html(html)
6914                     .dimensions();
6915
6916             var pos;
6917
6918             var w = window.innerWidth,
6919                 h = window.innerHeight;
6920
6921             if (box.top + box.height < Math.min(100, box.width + box.left)) {
6922                 side = 'bottom';
6923                 pos = [box.left + box.width / 2 - dimensions[0]/ 2, box.top + box.height];
6924
6925             } else if (box.left + box.width + 300 < window.innerWidth) {
6926                 side = 'right';
6927                 pos = [box.left + box.width, box.top + box.height / 2 - dimensions[1] / 2];
6928
6929             } else if (box.left > 300) {
6930                 side = 'left';
6931                 pos = [box.left - 200, box.top + box.height / 2 - dimensions[1] / 2];
6932             } else {
6933                 side = 'bottom';
6934                 pos = [box.left, box.top + box.height];
6935             }
6936
6937             pos = [
6938                 Math.min(Math.max(10, pos[0]), w - dimensions[0] - 10),
6939                 Math.min(Math.max(10, pos[1]), h - dimensions[1] - 10)
6940             ];
6941
6942
6943             if (duration !== 0 || !tooltip.classed(side)) tooltip.call(iD.ui.Toggle(true));
6944
6945             tooltip
6946                 .style('top', pos[1] + 'px')
6947                 .style('left', pos[0] + 'px')
6948                 .attr('class', 'curtain-tooltip tooltip in ' + side + ' ' + tooltipclass)
6949                 .select('.tooltip-inner')
6950                     .html(html);
6951
6952         } else {
6953             tooltip.call(iD.ui.Toggle(false));
6954         }
6955     };
6956
6957     curtain.cut = function(datum, duration) {
6958         darkness.datum(datum);
6959
6960         (duration === 0 ? darkness : darkness.transition().duration(duration || 600))
6961             .attr('d', function(d) {
6962                 var string = "M 0,0 L 0," + window.innerHeight + " L " +
6963                     window.innerWidth + "," + window.innerHeight + "L" +
6964                     window.innerWidth + ",0 Z";
6965
6966                 if (!d) return string;
6967                 return string + 'M' +
6968                     d.left + ',' + d.top + 'L' +
6969                     d.left + ',' + (d.top + d.height) + 'L' +
6970                     (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
6971                     (d.left + d.width) + ',' + (d.top) + 'Z';
6972
6973             });
6974     };
6975
6976     curtain.remove = function() {
6977         surface.remove();
6978         tooltip.remove();
6979     };
6980
6981     return d3.rebind(curtain, event, 'on');
6982 };
6983 // Like selection.property('value', ...), but avoids no-op value sets,
6984 // which can result in layout/repaint thrashing in some situations.
6985 d3.selection.prototype.value = function(value) {
6986     function d3_selection_value(value) {
6987       function valueNull() {
6988         delete this.value;
6989       }
6990
6991       function valueConstant() {
6992         if (this.value !== value) this.value = value;
6993       }
6994
6995       function valueFunction() {
6996         var x = value.apply(this, arguments);
6997         if (x == null) delete this.value;
6998         else if (this.value !== x) this.value = x;
6999       }
7000
7001       return value == null
7002           ? valueNull : (typeof value === "function"
7003           ? valueFunction : valueConstant);
7004     }
7005
7006     if (!arguments.length) return this.property('value');
7007     return this.each(d3_selection_value(value));
7008 };
7009 // Copyright (c) 2006, 2008 Tony Garnock-Jones <tonyg@lshift.net>
7010 // Copyright (c) 2006, 2008 LShift Ltd. <query@lshift.net>
7011 //
7012 // Permission is hereby granted, free of charge, to any person
7013 // obtaining a copy of this software and associated documentation files
7014 // (the "Software"), to deal in the Software without restriction,
7015 // including without limitation the rights to use, copy, modify, merge,
7016 // publish, distribute, sublicense, and/or sell copies of the Software,
7017 // and to permit persons to whom the Software is furnished to do so,
7018 // subject to the following conditions:
7019 //
7020 // The above copyright notice and this permission notice shall be
7021 // included in all copies or substantial portions of the Software.
7022 //
7023 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
7024 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
7025 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
7026 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
7027 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
7028 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7029 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7030 // SOFTWARE.
7031
7032 // source:  https://bitbucket.org/lshift/synchrotron/src
7033
7034 Diff3 = (function() {
7035     'use strict';
7036
7037     var diff3 = {
7038         longest_common_subsequence: function(file1, file2) {
7039             /* Text diff algorithm following Hunt and McIlroy 1976.
7040              * J. W. Hunt and M. D. McIlroy, An algorithm for differential file
7041              * comparison, Bell Telephone Laboratories CSTR #41 (1976)
7042              * http://www.cs.dartmouth.edu/~doug/
7043              *
7044              * Expects two arrays of strings.
7045              */
7046             var equivalenceClasses;
7047             var file2indices;
7048             var newCandidate;
7049             var candidates;
7050             var line;
7051             var c, i, j, jX, r, s;
7052
7053             equivalenceClasses = {};
7054             for (j = 0; j < file2.length; j++) {
7055                 line = file2[j];
7056                 if (equivalenceClasses[line]) {
7057                     equivalenceClasses[line].push(j);
7058                 } else {
7059                     equivalenceClasses[line] = [j];
7060                 }
7061             }
7062
7063             candidates = [{file1index: -1,
7064                            file2index: -1,
7065                            chain: null}];
7066
7067             for (i = 0; i < file1.length; i++) {
7068                 line = file1[i];
7069                 file2indices = equivalenceClasses[line] || [];
7070
7071                 r = 0;
7072                 c = candidates[0];
7073
7074                 for (jX = 0; jX < file2indices.length; jX++) {
7075                     j = file2indices[jX];
7076
7077                     for (s = 0; s < candidates.length; s++) {
7078                         if ((candidates[s].file2index < j) &&
7079                             ((s == candidates.length - 1) ||
7080                              (candidates[s + 1].file2index > j)))
7081                             break;
7082                     }
7083
7084                     if (s < candidates.length) {
7085                         newCandidate = {file1index: i,
7086                                         file2index: j,
7087                                         chain: candidates[s]};
7088                         if (r == candidates.length) {
7089                             candidates.push(c);
7090                         } else {
7091                             candidates[r] = c;
7092                         }
7093                         r = s + 1;
7094                         c = newCandidate;
7095                         if (r == candidates.length) {
7096                             break; // no point in examining further (j)s
7097                         }
7098                     }
7099                 }
7100
7101                 candidates[r] = c;
7102             }
7103
7104             // At this point, we know the LCS: it's in the reverse of the
7105             // linked-list through .chain of
7106             // candidates[candidates.length - 1].
7107
7108             return candidates[candidates.length - 1];
7109         },
7110
7111         diff_comm: function(file1, file2) {
7112             // We apply the LCS to build a "comm"-style picture of the
7113             // differences between file1 and file2.
7114
7115             var result = [];
7116             var tail1 = file1.length;
7117             var tail2 = file2.length;
7118             var common = {common: []};
7119
7120             function processCommon() {
7121                 if (common.common.length) {
7122                     common.common.reverse();
7123                     result.push(common);
7124                     common = {common: []};
7125                 }
7126             }
7127
7128             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7129                  candidate !== null;
7130                  candidate = candidate.chain)
7131             {
7132                 var different = {file1: [], file2: []};
7133
7134                 while (--tail1 > candidate.file1index) {
7135                     different.file1.push(file1[tail1]);
7136                 }
7137
7138                 while (--tail2 > candidate.file2index) {
7139                     different.file2.push(file2[tail2]);
7140                 }
7141
7142                 if (different.file1.length || different.file2.length) {
7143                     processCommon();
7144                     different.file1.reverse();
7145                     different.file2.reverse();
7146                     result.push(different);
7147                 }
7148
7149                 if (tail1 >= 0) {
7150                     common.common.push(file1[tail1]);
7151                 }
7152             }
7153
7154             processCommon();
7155
7156             result.reverse();
7157             return result;
7158         },
7159
7160         diff_patch: function(file1, file2) {
7161             // We apply the LCD to build a JSON representation of a
7162             // diff(1)-style patch.
7163
7164             var result = [];
7165             var tail1 = file1.length;
7166             var tail2 = file2.length;
7167
7168             function chunkDescription(file, offset, length) {
7169                 var chunk = [];
7170                 for (var i = 0; i < length; i++) {
7171                     chunk.push(file[offset + i]);
7172                 }
7173                 return {offset: offset,
7174                         length: length,
7175                         chunk: chunk};
7176             }
7177
7178             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7179                  candidate !== null;
7180                  candidate = candidate.chain)
7181             {
7182                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7183                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7184                 tail1 = candidate.file1index;
7185                 tail2 = candidate.file2index;
7186
7187                 if (mismatchLength1 || mismatchLength2) {
7188                     result.push({file1: chunkDescription(file1,
7189                                                          candidate.file1index + 1,
7190                                                          mismatchLength1),
7191                                  file2: chunkDescription(file2,
7192                                                          candidate.file2index + 1,
7193                                                          mismatchLength2)});
7194                 }
7195             }
7196
7197             result.reverse();
7198             return result;
7199         },
7200
7201         strip_patch: function(patch) {
7202         // Takes the output of Diff3.diff_patch(), and removes
7203         // information from it. It can still be used by patch(),
7204         // below, but can no longer be inverted.
7205         var newpatch = [];
7206         for (var i = 0; i < patch.length; i++) {
7207             var chunk = patch[i];
7208             newpatch.push({file1: {offset: chunk.file1.offset,
7209                        length: chunk.file1.length},
7210                    file2: {chunk: chunk.file2.chunk}});
7211         }
7212         return newpatch;
7213         },
7214
7215         invert_patch: function(patch) {
7216             // Takes the output of Diff3.diff_patch(), and inverts the
7217             // sense of it, so that it can be applied to file2 to give
7218             // file1 rather than the other way around.
7219
7220             for (var i = 0; i < patch.length; i++) {
7221                 var chunk = patch[i];
7222                 var tmp = chunk.file1;
7223                 chunk.file1 = chunk.file2;
7224                 chunk.file2 = tmp;
7225             }
7226         },
7227
7228         patch: function (file, patch) {
7229             // Applies a patch to a file.
7230             //
7231             // Given file1 and file2, Diff3.patch(file1,
7232             // Diff3.diff_patch(file1, file2)) should give file2.
7233
7234             var result = [];
7235             var commonOffset = 0;
7236
7237             function copyCommon(targetOffset) {
7238                 while (commonOffset < targetOffset) {
7239                     result.push(file[commonOffset]);
7240                     commonOffset++;
7241                 }
7242             }
7243
7244             for (var chunkIndex = 0; chunkIndex < patch.length; chunkIndex++) {
7245                 var chunk = patch[chunkIndex];
7246                 copyCommon(chunk.file1.offset);
7247                 for (var lineIndex = 0; lineIndex < chunk.file2.chunk.length; lineIndex++) {
7248                     result.push(chunk.file2.chunk[lineIndex]);
7249                 }
7250                 commonOffset += chunk.file1.length;
7251             }
7252
7253             copyCommon(file.length);
7254             return result;
7255         },
7256
7257         diff_indices: function(file1, file2) {
7258             // We apply the LCS to give a simple representation of the
7259             // offsets and lengths of mismatched chunks in the input
7260             // files. This is used by diff3_merge_indices below.
7261
7262             var result = [];
7263             var tail1 = file1.length;
7264             var tail2 = file2.length;
7265
7266             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7267                  candidate !== null;
7268                  candidate = candidate.chain)
7269             {
7270                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7271                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7272                 tail1 = candidate.file1index;
7273                 tail2 = candidate.file2index;
7274
7275                 if (mismatchLength1 || mismatchLength2) {
7276                     result.push({file1: [tail1 + 1, mismatchLength1],
7277                                  file2: [tail2 + 1, mismatchLength2]});
7278                 }
7279             }
7280
7281             result.reverse();
7282             return result;
7283         },
7284
7285         diff3_merge_indices: function (a, o, b) {
7286             // Given three files, A, O, and B, where both A and B are
7287             // independently derived from O, returns a fairly complicated
7288             // internal representation of merge decisions it's taken. The
7289             // interested reader may wish to consult
7290             //
7291             // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce. "A
7292             // Formal Investigation of Diff3." In Arvind and Prasad,
7293             // editors, Foundations of Software Technology and Theoretical
7294             // Computer Science (FSTTCS), December 2007.
7295             //
7296             // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
7297             var i;
7298
7299             var m1 = Diff3.diff_indices(o, a);
7300             var m2 = Diff3.diff_indices(o, b);
7301
7302             var hunks = [];
7303             function addHunk(h, side) {
7304                 hunks.push([h.file1[0], side, h.file1[1], h.file2[0], h.file2[1]]);
7305             }
7306             for (i = 0; i < m1.length; i++) { addHunk(m1[i], 0); }
7307             for (i = 0; i < m2.length; i++) { addHunk(m2[i], 2); }
7308             hunks.sort();
7309
7310             var result = [];
7311             var commonOffset = 0;
7312             function copyCommon(targetOffset) {
7313                 if (targetOffset > commonOffset) {
7314                     result.push([1, commonOffset, targetOffset - commonOffset]);
7315                     commonOffset = targetOffset;
7316                 }
7317             }
7318
7319             for (var hunkIndex = 0; hunkIndex < hunks.length; hunkIndex++) {
7320                 var firstHunkIndex = hunkIndex;
7321                 var hunk = hunks[hunkIndex];
7322                 var regionLhs = hunk[0];
7323                 var regionRhs = regionLhs + hunk[2];
7324                 while (hunkIndex < hunks.length - 1) {
7325                     var maybeOverlapping = hunks[hunkIndex + 1];
7326                     var maybeLhs = maybeOverlapping[0];
7327                     if (maybeLhs > regionRhs) break;
7328                     regionRhs = maybeLhs + maybeOverlapping[2];
7329                     hunkIndex++;
7330                 }
7331
7332                 copyCommon(regionLhs);
7333                 if (firstHunkIndex == hunkIndex) {
7334             // The "overlap" was only one hunk long, meaning that
7335             // there's no conflict here. Either a and o were the
7336             // same, or b and o were the same.
7337                     if (hunk[4] > 0) {
7338                         result.push([hunk[1], hunk[3], hunk[4]]);
7339                     }
7340                 } else {
7341             // A proper conflict. Determine the extents of the
7342             // regions involved from a, o and b. Effectively merge
7343             // all the hunks on the left into one giant hunk, and
7344             // do the same for the right; then, correct for skew
7345             // in the regions of o that each side changed, and
7346             // report appropriate spans for the three sides.
7347             var regions = {
7348                 0: [a.length, -1, o.length, -1],
7349                 2: [b.length, -1, o.length, -1]
7350             };
7351                     for (i = firstHunkIndex; i <= hunkIndex; i++) {
7352                 hunk = hunks[i];
7353                         var side = hunk[1];
7354                 var r = regions[side];
7355                 var oLhs = hunk[0];
7356                 var oRhs = oLhs + hunk[2];
7357                         var abLhs = hunk[3];
7358                         var abRhs = abLhs + hunk[4];
7359                 r[0] = Math.min(abLhs, r[0]);
7360                 r[1] = Math.max(abRhs, r[1]);
7361                 r[2] = Math.min(oLhs, r[2]);
7362                 r[3] = Math.max(oRhs, r[3]);
7363                     }
7364             var aLhs = regions[0][0] + (regionLhs - regions[0][2]);
7365             var aRhs = regions[0][1] + (regionRhs - regions[0][3]);
7366             var bLhs = regions[2][0] + (regionLhs - regions[2][2]);
7367             var bRhs = regions[2][1] + (regionRhs - regions[2][3]);
7368                     result.push([-1,
7369                      aLhs,      aRhs      - aLhs,
7370                      regionLhs, regionRhs - regionLhs,
7371                      bLhs,      bRhs      - bLhs]);
7372                 }
7373                 commonOffset = regionRhs;
7374             }
7375
7376             copyCommon(o.length);
7377             return result;
7378         },
7379
7380         diff3_merge: function (a, o, b, excludeFalseConflicts) {
7381             // Applies the output of Diff3.diff3_merge_indices to actually
7382             // construct the merged file; the returned result alternates
7383             // between "ok" and "conflict" blocks.
7384
7385             var result = [];
7386             var files = [a, o, b];
7387             var indices = Diff3.diff3_merge_indices(a, o, b);
7388
7389             var okLines = [];
7390             function flushOk() {
7391                 if (okLines.length) {
7392                     result.push({ok: okLines});
7393                 }
7394                 okLines = [];
7395             }
7396             function pushOk(xs) {
7397                 for (var j = 0; j < xs.length; j++) {
7398                     okLines.push(xs[j]);
7399                 }
7400             }
7401
7402             function isTrueConflict(rec) {
7403                 if (rec[2] != rec[6]) return true;
7404                 var aoff = rec[1];
7405                 var boff = rec[5];
7406                 for (var j = 0; j < rec[2]; j++) {
7407                     if (a[j + aoff] != b[j + boff]) return true;
7408                 }
7409                 return false;
7410             }
7411
7412             for (var i = 0; i < indices.length; i++) {
7413                 var x = indices[i];
7414                 var side = x[0];
7415                 if (side == -1) {
7416                     if (excludeFalseConflicts && !isTrueConflict(x)) {
7417                         pushOk(files[0].slice(x[1], x[1] + x[2]));
7418                     } else {
7419                         flushOk();
7420                         result.push({conflict: {a: a.slice(x[1], x[1] + x[2]),
7421                                                 aIndex: x[1],
7422                                                 o: o.slice(x[3], x[3] + x[4]),
7423                                                 oIndex: x[3],
7424                                                 b: b.slice(x[5], x[5] + x[6]),
7425                                                 bIndex: x[5]}});
7426                     }
7427                 } else {
7428                     pushOk(files[side].slice(x[1], x[1] + x[2]));
7429                 }
7430             }
7431
7432             flushOk();
7433             return result;
7434         }
7435     };
7436     return diff3;
7437 })();
7438
7439 if (typeof module !== 'undefined') module.exports = Diff3;
7440 var JXON = new (function () {
7441   var
7442     sValueProp = "keyValue", sAttributesProp = "keyAttributes", sAttrPref = "@", /* you can customize these values */
7443     aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
7444
7445   function parseText (sValue) {
7446     if (rIsNull.test(sValue)) { return null; }
7447     if (rIsBool.test(sValue)) { return sValue.toLowerCase() === "true"; }
7448     if (isFinite(sValue)) { return parseFloat(sValue); }
7449     if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
7450     return sValue;
7451   }
7452
7453   function EmptyTree () { }
7454   EmptyTree.prototype.toString = function () { return "null"; };
7455   EmptyTree.prototype.valueOf = function () { return null; };
7456
7457   function objectify (vValue) {
7458     return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
7459   }
7460
7461   function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
7462     var
7463       nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
7464       bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
7465
7466     var
7467       sProp, vContent, nLength = 0, sCollectedTxt = "",
7468       vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
7469
7470     if (bChildren) {
7471       for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
7472         oNode = oParentNode.childNodes.item(nItem);
7473         if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is "CDATASection" (4) */
7474         else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is "Text" (3) */
7475         else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is "Element" (1) */
7476       }
7477     }
7478
7479     var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
7480
7481     if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
7482
7483     for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
7484       sProp = aCache[nElId].nodeName.toLowerCase();
7485       vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
7486       if (vResult.hasOwnProperty(sProp)) {
7487         if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
7488         vResult[sProp].push(vContent);
7489       } else {
7490         vResult[sProp] = vContent;
7491         nLength++;
7492       }
7493     }
7494
7495     if (bAttributes) {
7496       var
7497         nAttrLen = oParentNode.attributes.length,
7498         sAPrefix = bNesteAttr ? "" : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
7499
7500       for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
7501         oAttrib = oParentNode.attributes.item(nAttrib);
7502         oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
7503       }
7504
7505       if (bNesteAttr) {
7506         if (bFreeze) { Object.freeze(oAttrParent); }
7507         vResult[sAttributesProp] = oAttrParent;
7508         nLength -= nAttrLen - 1;
7509       }
7510     }
7511
7512     if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
7513       vResult[sValueProp] = vBuiltVal;
7514     } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
7515       vResult = vBuiltVal;
7516     }
7517
7518     if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
7519
7520     aCache.length = nLevelStart;
7521
7522     return vResult;
7523   }
7524
7525   function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
7526     var vValue, oChild;
7527
7528     if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
7529       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
7530     } else if (oParentObj.constructor === Date) {
7531       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));    
7532     }
7533
7534     for (var sName in oParentObj) {
7535       vValue = oParentObj[sName];
7536       if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
7537       if (sName === sValueProp) {
7538         if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
7539       } else if (sName === sAttributesProp) { /* verbosity level is 3 */
7540         for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
7541       } else if (sName.charAt(0) === sAttrPref) {
7542         oParentEl.setAttribute(sName.slice(1), vValue);
7543       } else if (vValue.constructor === Array) {
7544         for (var nItem = 0; nItem < vValue.length; nItem++) {
7545           oChild = oXMLDoc.createElement(sName);
7546           loadObjTree(oXMLDoc, oChild, vValue[nItem]);
7547           oParentEl.appendChild(oChild);
7548         }
7549       } else {
7550         oChild = oXMLDoc.createElement(sName);
7551         if (vValue instanceof Object) {
7552           loadObjTree(oXMLDoc, oChild, vValue);
7553         } else if (vValue !== null && vValue !== true) {
7554           oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
7555         }
7556         oParentEl.appendChild(oChild);
7557      }
7558    }
7559   }
7560
7561   this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
7562     var _nVerb = arguments.length > 1 && typeof nVerbosity === "number" ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
7563     return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);    
7564   };
7565
7566   this.unbuild = function (oObjTree) {    
7567     var oNewDoc = document.implementation.createDocument("", "", null);
7568     loadObjTree(oNewDoc, oNewDoc, oObjTree);
7569     return oNewDoc;
7570   };
7571
7572   this.stringify = function (oObjTree) {
7573     return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
7574   };
7575 })();
7576 // var myObject = JXON.build(doc);
7577 // we got our javascript object! try: alert(JSON.stringify(myObject));
7578
7579 // var newDoc = JXON.unbuild(myObject);
7580 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
7581 /**
7582  * @license
7583  * lodash 3.9.3 (Custom Build) <https://lodash.com/>
7584  * Build: `lodash --development --output js/lib/lodash.js include="any,assign,bind,chunk,clone,compact,contains,debounce,difference,each,every,extend,filter,find,first,forEach,forOwn,groupBy,indexOf,intersection,isEmpty,isEqual,isFunction,keys,last,map,omit,pairs,pluck,reject,some,throttle,union,uniq,unique,values,without,flatten,value,chain,cloneDeep,merge,pick,reduce" exports="global,node"`
7585  * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
7586  * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
7587  * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
7588  * Available under MIT license <https://lodash.com/license>
7589  */
7590 ;(function() {
7591
7592   /** Used as a safe reference for `undefined` in pre-ES5 environments. */
7593   var undefined;
7594
7595   /** Used as the semantic version number. */
7596   var VERSION = '3.9.3';
7597
7598   /** Used to compose bitmasks for wrapper metadata. */
7599   var BIND_FLAG = 1,
7600       BIND_KEY_FLAG = 2,
7601       CURRY_BOUND_FLAG = 4,
7602       CURRY_FLAG = 8,
7603       CURRY_RIGHT_FLAG = 16,
7604       PARTIAL_FLAG = 32,
7605       PARTIAL_RIGHT_FLAG = 64,
7606       ARY_FLAG = 128,
7607       REARG_FLAG = 256;
7608
7609   /** Used to detect when a function becomes hot. */
7610   var HOT_COUNT = 150,
7611       HOT_SPAN = 16;
7612
7613   /** Used to indicate the type of lazy iteratees. */
7614   var LAZY_DROP_WHILE_FLAG = 0,
7615       LAZY_FILTER_FLAG = 1,
7616       LAZY_MAP_FLAG = 2;
7617
7618   /** Used as the `TypeError` message for "Functions" methods. */
7619   var FUNC_ERROR_TEXT = 'Expected a function';
7620
7621   /** Used as the internal argument placeholder. */
7622   var PLACEHOLDER = '__lodash_placeholder__';
7623
7624   /** `Object#toString` result references. */
7625   var argsTag = '[object Arguments]',
7626       arrayTag = '[object Array]',
7627       boolTag = '[object Boolean]',
7628       dateTag = '[object Date]',
7629       errorTag = '[object Error]',
7630       funcTag = '[object Function]',
7631       mapTag = '[object Map]',
7632       numberTag = '[object Number]',
7633       objectTag = '[object Object]',
7634       regexpTag = '[object RegExp]',
7635       setTag = '[object Set]',
7636       stringTag = '[object String]',
7637       weakMapTag = '[object WeakMap]';
7638
7639   var arrayBufferTag = '[object ArrayBuffer]',
7640       float32Tag = '[object Float32Array]',
7641       float64Tag = '[object Float64Array]',
7642       int8Tag = '[object Int8Array]',
7643       int16Tag = '[object Int16Array]',
7644       int32Tag = '[object Int32Array]',
7645       uint8Tag = '[object Uint8Array]',
7646       uint8ClampedTag = '[object Uint8ClampedArray]',
7647       uint16Tag = '[object Uint16Array]',
7648       uint32Tag = '[object Uint32Array]';
7649
7650   /** Used to match property names within property paths. */
7651   var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,
7652       reIsPlainProp = /^\w*$/,
7653       rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g;
7654
7655   /**
7656    * Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special).
7657    * In addition to special characters the forward slash is escaped to allow for
7658    * easier `eval` use and `Function` compilation.
7659    */
7660   var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g,
7661       reHasRegExpChars = RegExp(reRegExpChars.source);
7662
7663   /** Used to match backslashes in property paths. */
7664   var reEscapeChar = /\\(\\)?/g;
7665
7666   /** Used to match `RegExp` flags from their coerced string values. */
7667   var reFlags = /\w*$/;
7668
7669   /** Used to detect host constructors (Safari > 5). */
7670   var reIsHostCtor = /^\[object .+?Constructor\]$/;
7671
7672   /** Used to detect unsigned integer values. */
7673   var reIsUint = /^\d+$/;
7674
7675   /** Used to fix the JScript `[[DontEnum]]` bug. */
7676   var shadowProps = [
7677     'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
7678     'toLocaleString', 'toString', 'valueOf'
7679   ];
7680
7681   /** Used to identify `toStringTag` values of typed arrays. */
7682   var typedArrayTags = {};
7683   typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
7684   typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
7685   typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
7686   typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
7687   typedArrayTags[uint32Tag] = true;
7688   typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
7689   typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
7690   typedArrayTags[dateTag] = typedArrayTags[errorTag] =
7691   typedArrayTags[funcTag] = typedArrayTags[mapTag] =
7692   typedArrayTags[numberTag] = typedArrayTags[objectTag] =
7693   typedArrayTags[regexpTag] = typedArrayTags[setTag] =
7694   typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
7695
7696   /** Used to identify `toStringTag` values supported by `_.clone`. */
7697   var cloneableTags = {};
7698   cloneableTags[argsTag] = cloneableTags[arrayTag] =
7699   cloneableTags[arrayBufferTag] = cloneableTags[boolTag] =
7700   cloneableTags[dateTag] = cloneableTags[float32Tag] =
7701   cloneableTags[float64Tag] = cloneableTags[int8Tag] =
7702   cloneableTags[int16Tag] = cloneableTags[int32Tag] =
7703   cloneableTags[numberTag] = cloneableTags[objectTag] =
7704   cloneableTags[regexpTag] = cloneableTags[stringTag] =
7705   cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
7706   cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
7707   cloneableTags[errorTag] = cloneableTags[funcTag] =
7708   cloneableTags[mapTag] = cloneableTags[setTag] =
7709   cloneableTags[weakMapTag] = false;
7710
7711   /** Used as an internal `_.debounce` options object by `_.throttle`. */
7712   var debounceOptions = {
7713     'leading': false,
7714     'maxWait': 0,
7715     'trailing': false
7716   };
7717
7718   /** Used to determine if values are of the language type `Object`. */
7719   var objectTypes = {
7720     'function': true,
7721     'object': true
7722   };
7723
7724   /** Detect free variable `exports`. */
7725   var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
7726
7727   /** Detect free variable `module`. */
7728   var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
7729
7730   /** Detect free variable `global` from Node.js. */
7731   var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global;
7732
7733   /** Detect free variable `self`. */
7734   var freeSelf = objectTypes[typeof self] && self && self.Object && self;
7735
7736   /** Detect free variable `window`. */
7737   var freeWindow = objectTypes[typeof window] && window && window.Object && window;
7738
7739   /** Detect the popular CommonJS extension `module.exports`. */
7740   var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
7741
7742   /**
7743    * Used as a reference to the global object.
7744    *
7745    * The `this` value is used if it's the global object to avoid Greasemonkey's
7746    * restricted `window` object, otherwise the `window` object is used.
7747    */
7748   var root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this;
7749
7750   /*--------------------------------------------------------------------------*/
7751
7752   /**
7753    * The base implementation of `_.findIndex` and `_.findLastIndex` without
7754    * support for callback shorthands and `this` binding.
7755    *
7756    * @private
7757    * @param {Array} array The array to search.
7758    * @param {Function} predicate The function invoked per iteration.
7759    * @param {boolean} [fromRight] Specify iterating from right to left.
7760    * @returns {number} Returns the index of the matched value, else `-1`.
7761    */
7762   function baseFindIndex(array, predicate, fromRight) {
7763     var length = array.length,
7764         index = fromRight ? length : -1;
7765
7766     while ((fromRight ? index-- : ++index < length)) {
7767       if (predicate(array[index], index, array)) {
7768         return index;
7769       }
7770     }
7771     return -1;
7772   }
7773
7774   /**
7775    * The base implementation of `_.indexOf` without support for binary searches.
7776    *
7777    * @private
7778    * @param {Array} array The array to search.
7779    * @param {*} value The value to search for.
7780    * @param {number} fromIndex The index to search from.
7781    * @returns {number} Returns the index of the matched value, else `-1`.
7782    */
7783   function baseIndexOf(array, value, fromIndex) {
7784     if (value !== value) {
7785       return indexOfNaN(array, fromIndex);
7786     }
7787     var index = fromIndex - 1,
7788         length = array.length;
7789
7790     while (++index < length) {
7791       if (array[index] === value) {
7792         return index;
7793       }
7794     }
7795     return -1;
7796   }
7797
7798   /**
7799    * The base implementation of `_.isFunction` without support for environments
7800    * with incorrect `typeof` results.
7801    *
7802    * @private
7803    * @param {*} value The value to check.
7804    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
7805    */
7806   function baseIsFunction(value) {
7807     // Avoid a Chakra JIT bug in compatibility modes of IE 11.
7808     // See https://github.com/jashkenas/underscore/issues/1621 for more details.
7809     return typeof value == 'function' || false;
7810   }
7811
7812   /**
7813    * Converts `value` to a string if it's not one. An empty string is returned
7814    * for `null` or `undefined` values.
7815    *
7816    * @private
7817    * @param {*} value The value to process.
7818    * @returns {string} Returns the string.
7819    */
7820   function baseToString(value) {
7821     if (typeof value == 'string') {
7822       return value;
7823     }
7824     return value == null ? '' : (value + '');
7825   }
7826
7827   /**
7828    * Gets the index at which the first occurrence of `NaN` is found in `array`.
7829    *
7830    * @private
7831    * @param {Array} array The array to search.
7832    * @param {number} fromIndex The index to search from.
7833    * @param {boolean} [fromRight] Specify iterating from right to left.
7834    * @returns {number} Returns the index of the matched `NaN`, else `-1`.
7835    */
7836   function indexOfNaN(array, fromIndex, fromRight) {
7837     var length = array.length,
7838         index = fromIndex + (fromRight ? 0 : -1);
7839
7840     while ((fromRight ? index-- : ++index < length)) {
7841       var other = array[index];
7842       if (other !== other) {
7843         return index;
7844       }
7845     }
7846     return -1;
7847   }
7848
7849   /**
7850    * Checks if `value` is a host object in IE < 9.
7851    *
7852    * @private
7853    * @param {*} value The value to check.
7854    * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
7855    */
7856   var isHostObject = (function() {
7857     try {
7858       Object({ 'toString': 0 } + '');
7859     } catch(e) {
7860       return function() { return false; };
7861     }
7862     return function(value) {
7863       // IE < 9 presents many host objects as `Object` objects that can coerce
7864       // to strings despite having improperly defined `toString` methods.
7865       return typeof value.toString != 'function' && typeof (value + '') == 'string';
7866     };
7867   }());
7868
7869   /**
7870    * Checks if `value` is object-like.
7871    *
7872    * @private
7873    * @param {*} value The value to check.
7874    * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
7875    */
7876   function isObjectLike(value) {
7877     return !!value && typeof value == 'object';
7878   }
7879
7880   /**
7881    * Replaces all `placeholder` elements in `array` with an internal placeholder
7882    * and returns an array of their indexes.
7883    *
7884    * @private
7885    * @param {Array} array The array to modify.
7886    * @param {*} placeholder The placeholder to replace.
7887    * @returns {Array} Returns the new array of placeholder indexes.
7888    */
7889   function replaceHolders(array, placeholder) {
7890     var index = -1,
7891         length = array.length,
7892         resIndex = -1,
7893         result = [];
7894
7895     while (++index < length) {
7896       if (array[index] === placeholder) {
7897         array[index] = PLACEHOLDER;
7898         result[++resIndex] = index;
7899       }
7900     }
7901     return result;
7902   }
7903
7904   /**
7905    * An implementation of `_.uniq` optimized for sorted arrays without support
7906    * for callback shorthands and `this` binding.
7907    *
7908    * @private
7909    * @param {Array} array The array to inspect.
7910    * @param {Function} [iteratee] The function invoked per iteration.
7911    * @returns {Array} Returns the new duplicate-value-free array.
7912    */
7913   function sortedUniq(array, iteratee) {
7914     var seen,
7915         index = -1,
7916         length = array.length,
7917         resIndex = -1,
7918         result = [];
7919
7920     while (++index < length) {
7921       var value = array[index],
7922           computed = iteratee ? iteratee(value, index, array) : value;
7923
7924       if (!index || seen !== computed) {
7925         seen = computed;
7926         result[++resIndex] = value;
7927       }
7928     }
7929     return result;
7930   }
7931
7932   /*--------------------------------------------------------------------------*/
7933
7934   /** Used for native method references. */
7935   var arrayProto = Array.prototype,
7936       errorProto = Error.prototype,
7937       objectProto = Object.prototype,
7938       stringProto = String.prototype;
7939
7940   /** Used to resolve the decompiled source of functions. */
7941   var fnToString = Function.prototype.toString;
7942
7943   /** Used to check objects for own properties. */
7944   var hasOwnProperty = objectProto.hasOwnProperty;
7945
7946   /**
7947    * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
7948    * of values.
7949    */
7950   var objToString = objectProto.toString;
7951
7952   /** Used to detect if a method is native. */
7953   var reIsNative = RegExp('^' +
7954     escapeRegExp(fnToString.call(hasOwnProperty))
7955     .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
7956   );
7957
7958   /** Native method references. */
7959   var ArrayBuffer = getNative(root, 'ArrayBuffer'),
7960       bufferSlice = getNative(ArrayBuffer && new ArrayBuffer(0), 'slice'),
7961       ceil = Math.ceil,
7962       floor = Math.floor,
7963       getPrototypeOf = getNative(Object, 'getPrototypeOf'),
7964       push = arrayProto.push,
7965       propertyIsEnumerable = objectProto.propertyIsEnumerable,
7966       Set = getNative(root, 'Set'),
7967       splice = arrayProto.splice,
7968       Uint8Array = getNative(root, 'Uint8Array'),
7969       WeakMap = getNative(root, 'WeakMap');
7970
7971   /** Used to clone array buffers. */
7972   var Float64Array = (function() {
7973     // Safari 5 errors when using an array buffer to initialize a typed array
7974     // where the array buffer's `byteLength` is not a multiple of the typed
7975     // array's `BYTES_PER_ELEMENT`.
7976     try {
7977       var func = getNative(root, 'Float64Array'),
7978           result = new func(new ArrayBuffer(10), 0, 1) && func;
7979     } catch(e) {}
7980     return result || null;
7981   }());
7982
7983   /* Native method references for those with the same name as other `lodash` methods. */
7984   var nativeCreate = getNative(Object, 'create'),
7985       nativeIsArray = getNative(Array, 'isArray'),
7986       nativeKeys = getNative(Object, 'keys'),
7987       nativeMax = Math.max,
7988       nativeMin = Math.min,
7989       nativeNow = getNative(Date, 'now');
7990
7991   /** Used as references for `-Infinity` and `Infinity`. */
7992   var POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
7993
7994   /** Used as references for the maximum length and index of an array. */
7995   var MAX_ARRAY_LENGTH = 4294967295,
7996       MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
7997       HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
7998
7999   /** Used as the size, in bytes, of each `Float64Array` element. */
8000   var FLOAT64_BYTES_PER_ELEMENT = Float64Array ? Float64Array.BYTES_PER_ELEMENT : 0;
8001
8002   /**
8003    * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer)
8004    * of an array-like value.
8005    */
8006   var MAX_SAFE_INTEGER = 9007199254740991;
8007
8008   /** Used to store function metadata. */
8009   var metaMap = WeakMap && new WeakMap;
8010
8011   /** Used to lookup unminified function names. */
8012   var realNames = {};
8013
8014   /** Used to lookup a type array constructors by `toStringTag`. */
8015   var ctorByTag = {};
8016   ctorByTag[float32Tag] = root.Float32Array;
8017   ctorByTag[float64Tag] = root.Float64Array;
8018   ctorByTag[int8Tag] = root.Int8Array;
8019   ctorByTag[int16Tag] = root.Int16Array;
8020   ctorByTag[int32Tag] = root.Int32Array;
8021   ctorByTag[uint8Tag] = root.Uint8Array;
8022   ctorByTag[uint8ClampedTag] = root.Uint8ClampedArray;
8023   ctorByTag[uint16Tag] = root.Uint16Array;
8024   ctorByTag[uint32Tag] = root.Uint32Array;
8025
8026   /** Used to avoid iterating over non-enumerable properties in IE < 9. */
8027   var nonEnumProps = {};
8028   nonEnumProps[arrayTag] = nonEnumProps[dateTag] = nonEnumProps[numberTag] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
8029   nonEnumProps[boolTag] = nonEnumProps[stringTag] = { 'constructor': true, 'toString': true, 'valueOf': true };
8030   nonEnumProps[errorTag] = nonEnumProps[funcTag] = nonEnumProps[regexpTag] = { 'constructor': true, 'toString': true };
8031   nonEnumProps[objectTag] = { 'constructor': true };
8032
8033   arrayEach(shadowProps, function(key) {
8034     for (var tag in nonEnumProps) {
8035       if (hasOwnProperty.call(nonEnumProps, tag)) {
8036         var props = nonEnumProps[tag];
8037         props[key] = hasOwnProperty.call(props, key);
8038       }
8039     }
8040   });
8041
8042   /*------------------------------------------------------------------------*/
8043
8044   /**
8045    * Creates a `lodash` object which wraps `value` to enable implicit chaining.
8046    * Methods that operate on and return arrays, collections, and functions can
8047    * be chained together. Methods that return a boolean or single value will
8048    * automatically end the chain returning the unwrapped value. Explicit chaining
8049    * may be enabled using `_.chain`. The execution of chained methods is lazy,
8050    * that is, execution is deferred until `_#value` is implicitly or explicitly
8051    * called.
8052    *
8053    * Lazy evaluation allows several methods to support shortcut fusion. Shortcut
8054    * fusion is an optimization that merges iteratees to avoid creating intermediate
8055    * arrays and reduce the number of iteratee executions.
8056    *
8057    * Chaining is supported in custom builds as long as the `_#value` method is
8058    * directly or indirectly included in the build.
8059    *
8060    * In addition to lodash methods, wrappers have `Array` and `String` methods.
8061    *
8062    * The wrapper `Array` methods are:
8063    * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`,
8064    * `splice`, and `unshift`
8065    *
8066    * The wrapper `String` methods are:
8067    * `replace` and `split`
8068    *
8069    * The wrapper methods that support shortcut fusion are:
8070    * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`,
8071    * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`,
8072    * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`,
8073    * and `where`
8074    *
8075    * The chainable wrapper methods are:
8076    * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`,
8077    * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`,
8078    * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defer`, `delay`,
8079    * `difference`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `fill`,
8080    * `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, `forEach`,
8081    * `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `functions`,
8082    * `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, `invoke`, `keys`,
8083    * `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
8084    * `memoize`, `merge`, `method`, `methodOf`, `mixin`, `negate`, `omit`, `once`,
8085    * `pairs`, `partial`, `partialRight`, `partition`, `pick`, `plant`, `pluck`,
8086    * `property`, `propertyOf`, `pull`, `pullAt`, `push`, `range`, `rearg`,
8087    * `reject`, `remove`, `rest`, `restParam`, `reverse`, `set`, `shuffle`,
8088    * `slice`, `sort`, `sortBy`, `sortByAll`, `sortByOrder`, `splice`, `spread`,
8089    * `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `tap`, `throttle`,
8090    * `thru`, `times`, `toArray`, `toPlainObject`, `transform`, `union`, `uniq`,
8091    * `unshift`, `unzip`, `unzipWith`, `values`, `valuesIn`, `where`, `without`,
8092    * `wrap`, `xor`, `zip`, `zipObject`, `zipWith`
8093    *
8094    * The wrapper methods that are **not** chainable by default are:
8095    * `add`, `attempt`, `camelCase`, `capitalize`, `clone`, `cloneDeep`, `deburr`,
8096    * `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`,
8097    * `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, `get`,
8098    * `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`, `inRange`, `isArguments`,
8099    * `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`,
8100    * `isFinite` `isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`,
8101    * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`,
8102    * `isTypedArray`, `join`, `kebabCase`, `last`, `lastIndexOf`, `lt`, `lte`,
8103    * `max`, `min`, `noConflict`, `noop`, `now`, `pad`, `padLeft`, `padRight`,
8104    * `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`,
8105    * `runInContext`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`,
8106    * `sortedLastIndex`, `startCase`, `startsWith`, `sum`, `template`, `trim`,
8107    * `trimLeft`, `trimRight`, `trunc`, `unescape`, `uniqueId`, `value`, and `words`
8108    *
8109    * The wrapper method `sample` will return a wrapped value when `n` is provided,
8110    * otherwise an unwrapped value is returned.
8111    *
8112    * @name _
8113    * @constructor
8114    * @category Chain
8115    * @param {*} value The value to wrap in a `lodash` instance.
8116    * @returns {Object} Returns the new `lodash` wrapper instance.
8117    * @example
8118    *
8119    * var wrapped = _([1, 2, 3]);
8120    *
8121    * // returns an unwrapped value
8122    * wrapped.reduce(function(total, n) {
8123    *   return total + n;
8124    * });
8125    * // => 6
8126    *
8127    * // returns a wrapped value
8128    * var squares = wrapped.map(function(n) {
8129    *   return n * n;
8130    * });
8131    *
8132    * _.isArray(squares);
8133    * // => false
8134    *
8135    * _.isArray(squares.value());
8136    * // => true
8137    */
8138   function lodash(value) {
8139     if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
8140       if (value instanceof LodashWrapper) {
8141         return value;
8142       }
8143       if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) {
8144         return wrapperClone(value);
8145       }
8146     }
8147     return new LodashWrapper(value);
8148   }
8149
8150   /**
8151    * The function whose prototype all chaining wrappers inherit from.
8152    *
8153    * @private
8154    */
8155   function baseLodash() {
8156     // No operation performed.
8157   }
8158
8159   /**
8160    * The base constructor for creating `lodash` wrapper objects.
8161    *
8162    * @private
8163    * @param {*} value The value to wrap.
8164    * @param {boolean} [chainAll] Enable chaining for all wrapper methods.
8165    * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value.
8166    */
8167   function LodashWrapper(value, chainAll, actions) {
8168     this.__wrapped__ = value;
8169     this.__actions__ = actions || [];
8170     this.__chain__ = !!chainAll;
8171   }
8172
8173   /**
8174    * An object environment feature flags.
8175    *
8176    * @static
8177    * @memberOf _
8178    * @type Object
8179    */
8180   var support = lodash.support = {};
8181
8182   (function(x) {
8183     var Ctor = function() { this.x = x; },
8184         object = { '0': x, 'length': x },
8185         props = [];
8186
8187     Ctor.prototype = { 'valueOf': x, 'y': x };
8188     for (var key in new Ctor) { props.push(key); }
8189
8190     /**
8191      * Detect if the `toStringTag` of `arguments` objects is resolvable
8192      * (all but Firefox < 4, IE < 9).
8193      *
8194      * @memberOf _.support
8195      * @type boolean
8196      */
8197     support.argsTag = objToString.call(arguments) == argsTag;
8198
8199     /**
8200      * Detect if `name` or `message` properties of `Error.prototype` are
8201      * enumerable by default (IE < 9, Safari < 5.1).
8202      *
8203      * @memberOf _.support
8204      * @type boolean
8205      */
8206     support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') ||
8207       propertyIsEnumerable.call(errorProto, 'name');
8208
8209     /**
8210      * Detect if `prototype` properties are enumerable by default.
8211      *
8212      * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
8213      * (if the prototype or a property on the prototype has been set)
8214      * incorrectly set the `[[Enumerable]]` value of a function's `prototype`
8215      * property to `true`.
8216      *
8217      * @memberOf _.support
8218      * @type boolean
8219      */
8220     support.enumPrototypes = propertyIsEnumerable.call(Ctor, 'prototype');
8221
8222     /**
8223      * Detect if properties shadowing those on `Object.prototype` are non-enumerable.
8224      *
8225      * In IE < 9 an object's own properties, shadowing non-enumerable ones,
8226      * are made non-enumerable as well (a.k.a the JScript `[[DontEnum]]` bug).
8227      *
8228      * @memberOf _.support
8229      * @type boolean
8230      */
8231     support.nonEnumShadows = !/valueOf/.test(props);
8232
8233     /**
8234      * Detect if own properties are iterated after inherited properties (IE < 9).
8235      *
8236      * @memberOf _.support
8237      * @type boolean
8238      */
8239     support.ownLast = props[0] != 'x';
8240
8241     /**
8242      * Detect if `Array#shift` and `Array#splice` augment array-like objects
8243      * correctly.
8244      *
8245      * Firefox < 10, compatibility modes of IE 8, and IE < 9 have buggy Array
8246      * `shift()` and `splice()` functions that fail to remove the last element,
8247      * `value[0]`, of array-like objects even though the "length" property is
8248      * set to `0`. The `shift()` method is buggy in compatibility modes of IE 8,
8249      * while `splice()` is buggy regardless of mode in IE < 9.
8250      *
8251      * @memberOf _.support
8252      * @type boolean
8253      */
8254     support.spliceObjects = (splice.call(object, 0, 1), !object[0]);
8255
8256     /**
8257      * Detect lack of support for accessing string characters by index.
8258      *
8259      * IE < 8 can't access characters by index. IE 8 can only access characters
8260      * by index on string literals, not string objects.
8261      *
8262      * @memberOf _.support
8263      * @type boolean
8264      */
8265     support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
8266   }(1, 0));
8267
8268   /*------------------------------------------------------------------------*/
8269
8270   /**
8271    * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
8272    *
8273    * @private
8274    * @param {*} value The value to wrap.
8275    */
8276   function LazyWrapper(value) {
8277     this.__wrapped__ = value;
8278     this.__actions__ = null;
8279     this.__dir__ = 1;
8280     this.__dropCount__ = 0;
8281     this.__filtered__ = false;
8282     this.__iteratees__ = null;
8283     this.__takeCount__ = POSITIVE_INFINITY;
8284     this.__views__ = null;
8285   }
8286
8287   /**
8288    * Creates a clone of the lazy wrapper object.
8289    *
8290    * @private
8291    * @name clone
8292    * @memberOf LazyWrapper
8293    * @returns {Object} Returns the cloned `LazyWrapper` object.
8294    */
8295   function lazyClone() {
8296     var actions = this.__actions__,
8297         iteratees = this.__iteratees__,
8298         views = this.__views__,
8299         result = new LazyWrapper(this.__wrapped__);
8300
8301     result.__actions__ = actions ? arrayCopy(actions) : null;
8302     result.__dir__ = this.__dir__;
8303     result.__filtered__ = this.__filtered__;
8304     result.__iteratees__ = iteratees ? arrayCopy(iteratees) : null;
8305     result.__takeCount__ = this.__takeCount__;
8306     result.__views__ = views ? arrayCopy(views) : null;
8307     return result;
8308   }
8309
8310   /**
8311    * Reverses the direction of lazy iteration.
8312    *
8313    * @private
8314    * @name reverse
8315    * @memberOf LazyWrapper
8316    * @returns {Object} Returns the new reversed `LazyWrapper` object.
8317    */
8318   function lazyReverse() {
8319     if (this.__filtered__) {
8320       var result = new LazyWrapper(this);
8321       result.__dir__ = -1;
8322       result.__filtered__ = true;
8323     } else {
8324       result = this.clone();
8325       result.__dir__ *= -1;
8326     }
8327     return result;
8328   }
8329
8330   /**
8331    * Extracts the unwrapped value from its lazy wrapper.
8332    *
8333    * @private
8334    * @name value
8335    * @memberOf LazyWrapper
8336    * @returns {*} Returns the unwrapped value.
8337    */
8338   function lazyValue() {
8339     var array = this.__wrapped__.value();
8340     if (!isArray(array)) {
8341       return baseWrapperValue(array, this.__actions__);
8342     }
8343     var dir = this.__dir__,
8344         isRight = dir < 0,
8345         view = getView(0, array.length, this.__views__),
8346         start = view.start,
8347         end = view.end,
8348         length = end - start,
8349         index = isRight ? end : (start - 1),
8350         takeCount = nativeMin(length, this.__takeCount__),
8351         iteratees = this.__iteratees__,
8352         iterLength = iteratees ? iteratees.length : 0,
8353         resIndex = 0,
8354         result = [];
8355
8356     outer:
8357     while (length-- && resIndex < takeCount) {
8358       index += dir;
8359
8360       var iterIndex = -1,
8361           value = array[index];
8362
8363       while (++iterIndex < iterLength) {
8364         var data = iteratees[iterIndex],
8365             iteratee = data.iteratee,
8366             type = data.type;
8367
8368         if (type == LAZY_DROP_WHILE_FLAG) {
8369           if (data.done && (isRight ? (index > data.index) : (index < data.index))) {
8370             data.count = 0;
8371             data.done = false;
8372           }
8373           data.index = index;
8374           if (!data.done) {
8375             var limit = data.limit;
8376             if (!(data.done = limit > -1 ? (data.count++ >= limit) : !iteratee(value))) {
8377               continue outer;
8378             }
8379           }
8380         } else {
8381           var computed = iteratee(value);
8382           if (type == LAZY_MAP_FLAG) {
8383             value = computed;
8384           } else if (!computed) {
8385             if (type == LAZY_FILTER_FLAG) {
8386               continue outer;
8387             } else {
8388               break outer;
8389             }
8390           }
8391         }
8392       }
8393       result[resIndex++] = value;
8394     }
8395     return result;
8396   }
8397
8398   /*------------------------------------------------------------------------*/
8399
8400   /**
8401    *
8402    * Creates a cache object to store unique values.
8403    *
8404    * @private
8405    * @param {Array} [values] The values to cache.
8406    */
8407   function SetCache(values) {
8408     var length = values ? values.length : 0;
8409
8410     this.data = { 'hash': nativeCreate(null), 'set': new Set };
8411     while (length--) {
8412       this.push(values[length]);
8413     }
8414   }
8415
8416   /**
8417    * Checks if `value` is in `cache` mimicking the return signature of
8418    * `_.indexOf` by returning `0` if the value is found, else `-1`.
8419    *
8420    * @private
8421    * @param {Object} cache The cache to search.
8422    * @param {*} value The value to search for.
8423    * @returns {number} Returns `0` if `value` is found, else `-1`.
8424    */
8425   function cacheIndexOf(cache, value) {
8426     var data = cache.data,
8427         result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value];
8428
8429     return result ? 0 : -1;
8430   }
8431
8432   /**
8433    * Adds `value` to the cache.
8434    *
8435    * @private
8436    * @name push
8437    * @memberOf SetCache
8438    * @param {*} value The value to cache.
8439    */
8440   function cachePush(value) {
8441     var data = this.data;
8442     if (typeof value == 'string' || isObject(value)) {
8443       data.set.add(value);
8444     } else {
8445       data.hash[value] = true;
8446     }
8447   }
8448
8449   /*------------------------------------------------------------------------*/
8450
8451   /**
8452    * Copies the values of `source` to `array`.
8453    *
8454    * @private
8455    * @param {Array} source The array to copy values from.
8456    * @param {Array} [array=[]] The array to copy values to.
8457    * @returns {Array} Returns `array`.
8458    */
8459   function arrayCopy(source, array) {
8460     var index = -1,
8461         length = source.length;
8462
8463     array || (array = Array(length));
8464     while (++index < length) {
8465       array[index] = source[index];
8466     }
8467     return array;
8468   }
8469
8470   /**
8471    * A specialized version of `_.forEach` for arrays without support for callback
8472    * shorthands and `this` binding.
8473    *
8474    * @private
8475    * @param {Array} array The array to iterate over.
8476    * @param {Function} iteratee The function invoked per iteration.
8477    * @returns {Array} Returns `array`.
8478    */
8479   function arrayEach(array, iteratee) {
8480     var index = -1,
8481         length = array.length;
8482
8483     while (++index < length) {
8484       if (iteratee(array[index], index, array) === false) {
8485         break;
8486       }
8487     }
8488     return array;
8489   }
8490
8491   /**
8492    * A specialized version of `_.every` for arrays without support for callback
8493    * shorthands and `this` binding.
8494    *
8495    * @private
8496    * @param {Array} array The array to iterate over.
8497    * @param {Function} predicate The function invoked per iteration.
8498    * @returns {boolean} Returns `true` if all elements pass the predicate check,
8499    *  else `false`.
8500    */
8501   function arrayEvery(array, predicate) {
8502     var index = -1,
8503         length = array.length;
8504
8505     while (++index < length) {
8506       if (!predicate(array[index], index, array)) {
8507         return false;
8508       }
8509     }
8510     return true;
8511   }
8512
8513   /**
8514    * A specialized version of `_.filter` for arrays without support for callback
8515    * shorthands and `this` binding.
8516    *
8517    * @private
8518    * @param {Array} array The array to iterate over.
8519    * @param {Function} predicate The function invoked per iteration.
8520    * @returns {Array} Returns the new filtered array.
8521    */
8522   function arrayFilter(array, predicate) {
8523     var index = -1,
8524         length = array.length,
8525         resIndex = -1,
8526         result = [];
8527
8528     while (++index < length) {
8529       var value = array[index];
8530       if (predicate(value, index, array)) {
8531         result[++resIndex] = value;
8532       }
8533     }
8534     return result;
8535   }
8536
8537   /**
8538    * A specialized version of `_.map` for arrays without support for callback
8539    * shorthands and `this` binding.
8540    *
8541    * @private
8542    * @param {Array} array The array to iterate over.
8543    * @param {Function} iteratee The function invoked per iteration.
8544    * @returns {Array} Returns the new mapped array.
8545    */
8546   function arrayMap(array, iteratee) {
8547     var index = -1,
8548         length = array.length,
8549         result = Array(length);
8550
8551     while (++index < length) {
8552       result[index] = iteratee(array[index], index, array);
8553     }
8554     return result;
8555   }
8556
8557   /**
8558    * A specialized version of `_.reduce` for arrays without support for callback
8559    * shorthands and `this` binding.
8560    *
8561    * @private
8562    * @param {Array} array The array to iterate over.
8563    * @param {Function} iteratee The function invoked per iteration.
8564    * @param {*} [accumulator] The initial value.
8565    * @param {boolean} [initFromArray] Specify using the first element of `array`
8566    *  as the initial value.
8567    * @returns {*} Returns the accumulated value.
8568    */
8569   function arrayReduce(array, iteratee, accumulator, initFromArray) {
8570     var index = -1,
8571         length = array.length;
8572
8573     if (initFromArray && length) {
8574       accumulator = array[++index];
8575     }
8576     while (++index < length) {
8577       accumulator = iteratee(accumulator, array[index], index, array);
8578     }
8579     return accumulator;
8580   }
8581
8582   /**
8583    * A specialized version of `_.some` for arrays without support for callback
8584    * shorthands and `this` binding.
8585    *
8586    * @private
8587    * @param {Array} array The array to iterate over.
8588    * @param {Function} predicate The function invoked per iteration.
8589    * @returns {boolean} Returns `true` if any element passes the predicate check,
8590    *  else `false`.
8591    */
8592   function arraySome(array, predicate) {
8593     var index = -1,
8594         length = array.length;
8595
8596     while (++index < length) {
8597       if (predicate(array[index], index, array)) {
8598         return true;
8599       }
8600     }
8601     return false;
8602   }
8603
8604   /**
8605    * A specialized version of `_.assign` for customizing assigned values without
8606    * support for argument juggling, multiple sources, and `this` binding `customizer`
8607    * functions.
8608    *
8609    * @private
8610    * @param {Object} object The destination object.
8611    * @param {Object} source The source object.
8612    * @param {Function} customizer The function to customize assigned values.
8613    * @returns {Object} Returns `object`.
8614    */
8615   function assignWith(object, source, customizer) {
8616     var index = -1,
8617         props = keys(source),
8618         length = props.length;
8619
8620     while (++index < length) {
8621       var key = props[index],
8622           value = object[key],
8623           result = customizer(value, source[key], key, object, source);
8624
8625       if ((result === result ? (result !== value) : (value === value)) ||
8626           (value === undefined && !(key in object))) {
8627         object[key] = result;
8628       }
8629     }
8630     return object;
8631   }
8632
8633   /**
8634    * The base implementation of `_.assign` without support for argument juggling,
8635    * multiple sources, and `customizer` functions.
8636    *
8637    * @private
8638    * @param {Object} object The destination object.
8639    * @param {Object} source The source object.
8640    * @returns {Object} Returns `object`.
8641    */
8642   function baseAssign(object, source) {
8643     return source == null
8644       ? object
8645       : baseCopy(source, keys(source), object);
8646   }
8647
8648   /**
8649    * Copies properties of `source` to `object`.
8650    *
8651    * @private
8652    * @param {Object} source The object to copy properties from.
8653    * @param {Array} props The property names to copy.
8654    * @param {Object} [object={}] The object to copy properties to.
8655    * @returns {Object} Returns `object`.
8656    */
8657   function baseCopy(source, props, object) {
8658     object || (object = {});
8659
8660     var index = -1,
8661         length = props.length;
8662
8663     while (++index < length) {
8664       var key = props[index];
8665       object[key] = source[key];
8666     }
8667     return object;
8668   }
8669
8670   /**
8671    * The base implementation of `_.callback` which supports specifying the
8672    * number of arguments to provide to `func`.
8673    *
8674    * @private
8675    * @param {*} [func=_.identity] The value to convert to a callback.
8676    * @param {*} [thisArg] The `this` binding of `func`.
8677    * @param {number} [argCount] The number of arguments to provide to `func`.
8678    * @returns {Function} Returns the callback.
8679    */
8680   function baseCallback(func, thisArg, argCount) {
8681     var type = typeof func;
8682     if (type == 'function') {
8683       return thisArg === undefined
8684         ? func
8685         : bindCallback(func, thisArg, argCount);
8686     }
8687     if (func == null) {
8688       return identity;
8689     }
8690     if (type == 'object') {
8691       return baseMatches(func);
8692     }
8693     return thisArg === undefined
8694       ? property(func)
8695       : baseMatchesProperty(func, thisArg);
8696   }
8697
8698   /**
8699    * The base implementation of `_.clone` without support for argument juggling
8700    * and `this` binding `customizer` functions.
8701    *
8702    * @private
8703    * @param {*} value The value to clone.
8704    * @param {boolean} [isDeep] Specify a deep clone.
8705    * @param {Function} [customizer] The function to customize cloning values.
8706    * @param {string} [key] The key of `value`.
8707    * @param {Object} [object] The object `value` belongs to.
8708    * @param {Array} [stackA=[]] Tracks traversed source objects.
8709    * @param {Array} [stackB=[]] Associates clones with source counterparts.
8710    * @returns {*} Returns the cloned value.
8711    */
8712   function baseClone(value, isDeep, customizer, key, object, stackA, stackB) {
8713     var result;
8714     if (customizer) {
8715       result = object ? customizer(value, key, object) : customizer(value);
8716     }
8717     if (result !== undefined) {
8718       return result;
8719     }
8720     if (!isObject(value)) {
8721       return value;
8722     }
8723     var isArr = isArray(value);
8724     if (isArr) {
8725       result = initCloneArray(value);
8726       if (!isDeep) {
8727         return arrayCopy(value, result);
8728       }
8729     } else {
8730       var tag = objToString.call(value),
8731           isFunc = tag == funcTag;
8732
8733       if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
8734         if (isHostObject(value)) {
8735           return object ? value : {};
8736         }
8737         result = initCloneObject(isFunc ? {} : value);
8738         if (!isDeep) {
8739           return baseAssign(result, value);
8740         }
8741       } else {
8742         return cloneableTags[tag]
8743           ? initCloneByTag(value, tag, isDeep)
8744           : (object ? value : {});
8745       }
8746     }
8747     // Check for circular references and return corresponding clone.
8748     stackA || (stackA = []);
8749     stackB || (stackB = []);
8750
8751     var length = stackA.length;
8752     while (length--) {
8753       if (stackA[length] == value) {
8754         return stackB[length];
8755       }
8756     }
8757     // Add the source value to the stack of traversed objects and associate it with its clone.
8758     stackA.push(value);
8759     stackB.push(result);
8760
8761     // Recursively populate clone (susceptible to call stack limits).
8762     (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) {
8763       result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB);
8764     });
8765     return result;
8766   }
8767
8768   /**
8769    * The base implementation of `_.create` without support for assigning
8770    * properties to the created object.
8771    *
8772    * @private
8773    * @param {Object} prototype The object to inherit from.
8774    * @returns {Object} Returns the new object.
8775    */
8776   var baseCreate = (function() {
8777     function object() {}
8778     return function(prototype) {
8779       if (isObject(prototype)) {
8780         object.prototype = prototype;
8781         var result = new object;
8782         object.prototype = null;
8783       }
8784       return result || {};
8785     };
8786   }());
8787
8788   /**
8789    * The base implementation of `_.difference` which accepts a single array
8790    * of values to exclude.
8791    *
8792    * @private
8793    * @param {Array} array The array to inspect.
8794    * @param {Array} values The values to exclude.
8795    * @returns {Array} Returns the new array of filtered values.
8796    */
8797   function baseDifference(array, values) {
8798     var length = array ? array.length : 0,
8799         result = [];
8800
8801     if (!length) {
8802       return result;
8803     }
8804     var index = -1,
8805         indexOf = getIndexOf(),
8806         isCommon = indexOf == baseIndexOf,
8807         cache = (isCommon && values.length >= 200) ? createCache(values) : null,
8808         valuesLength = values.length;
8809
8810     if (cache) {
8811       indexOf = cacheIndexOf;
8812       isCommon = false;
8813       values = cache;
8814     }
8815     outer:
8816     while (++index < length) {
8817       var value = array[index];
8818
8819       if (isCommon && value === value) {
8820         var valuesIndex = valuesLength;
8821         while (valuesIndex--) {
8822           if (values[valuesIndex] === value) {
8823             continue outer;
8824           }
8825         }
8826         result.push(value);
8827       }
8828       else if (indexOf(values, value, 0) < 0) {
8829         result.push(value);
8830       }
8831     }
8832     return result;
8833   }
8834
8835   /**
8836    * The base implementation of `_.forEach` without support for callback
8837    * shorthands and `this` binding.
8838    *
8839    * @private
8840    * @param {Array|Object|string} collection The collection to iterate over.
8841    * @param {Function} iteratee The function invoked per iteration.
8842    * @returns {Array|Object|string} Returns `collection`.
8843    */
8844   var baseEach = createBaseEach(baseForOwn);
8845
8846   /**
8847    * The base implementation of `_.every` without support for callback
8848    * shorthands and `this` binding.
8849    *
8850    * @private
8851    * @param {Array|Object|string} collection The collection to iterate over.
8852    * @param {Function} predicate The function invoked per iteration.
8853    * @returns {boolean} Returns `true` if all elements pass the predicate check,
8854    *  else `false`
8855    */
8856   function baseEvery(collection, predicate) {
8857     var result = true;
8858     baseEach(collection, function(value, index, collection) {
8859       result = !!predicate(value, index, collection);
8860       return result;
8861     });
8862     return result;
8863   }
8864
8865   /**
8866    * The base implementation of `_.filter` without support for callback
8867    * shorthands and `this` binding.
8868    *
8869    * @private
8870    * @param {Array|Object|string} collection The collection to iterate over.
8871    * @param {Function} predicate The function invoked per iteration.
8872    * @returns {Array} Returns the new filtered array.
8873    */
8874   function baseFilter(collection, predicate) {
8875     var result = [];
8876     baseEach(collection, function(value, index, collection) {
8877       if (predicate(value, index, collection)) {
8878         result.push(value);
8879       }
8880     });
8881     return result;
8882   }
8883
8884   /**
8885    * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`,
8886    * without support for callback shorthands and `this` binding, which iterates
8887    * over `collection` using the provided `eachFunc`.
8888    *
8889    * @private
8890    * @param {Array|Object|string} collection The collection to search.
8891    * @param {Function} predicate The function invoked per iteration.
8892    * @param {Function} eachFunc The function to iterate over `collection`.
8893    * @param {boolean} [retKey] Specify returning the key of the found element
8894    *  instead of the element itself.
8895    * @returns {*} Returns the found element or its key, else `undefined`.
8896    */
8897   function baseFind(collection, predicate, eachFunc, retKey) {
8898     var result;
8899     eachFunc(collection, function(value, key, collection) {
8900       if (predicate(value, key, collection)) {
8901         result = retKey ? key : value;
8902         return false;
8903       }
8904     });
8905     return result;
8906   }
8907
8908   /**
8909    * The base implementation of `_.flatten` with added support for restricting
8910    * flattening and specifying the start index.
8911    *
8912    * @private
8913    * @param {Array} array The array to flatten.
8914    * @param {boolean} [isDeep] Specify a deep flatten.
8915    * @param {boolean} [isStrict] Restrict flattening to arrays-like objects.
8916    * @returns {Array} Returns the new flattened array.
8917    */
8918   function baseFlatten(array, isDeep, isStrict) {
8919     var index = -1,
8920         length = array.length,
8921         resIndex = -1,
8922         result = [];
8923
8924     while (++index < length) {
8925       var value = array[index];
8926       if (isObjectLike(value) && isArrayLike(value) &&
8927           (isStrict || isArray(value) || isArguments(value))) {
8928         if (isDeep) {
8929           // Recursively flatten arrays (susceptible to call stack limits).
8930           value = baseFlatten(value, isDeep, isStrict);
8931         }
8932         var valIndex = -1,
8933             valLength = value.length;
8934
8935         while (++valIndex < valLength) {
8936           result[++resIndex] = value[valIndex];
8937         }
8938       } else if (!isStrict) {
8939         result[++resIndex] = value;
8940       }
8941     }
8942     return result;
8943   }
8944
8945   /**
8946    * The base implementation of `baseForIn` and `baseForOwn` which iterates
8947    * over `object` properties returned by `keysFunc` invoking `iteratee` for
8948    * each property. Iteratee functions may exit iteration early by explicitly
8949    * returning `false`.
8950    *
8951    * @private
8952    * @param {Object} object The object to iterate over.
8953    * @param {Function} iteratee The function invoked per iteration.
8954    * @param {Function} keysFunc The function to get the keys of `object`.
8955    * @returns {Object} Returns `object`.
8956    */
8957   var baseFor = createBaseFor();
8958
8959   /**
8960    * The base implementation of `_.forIn` without support for callback
8961    * shorthands and `this` binding.
8962    *
8963    * @private
8964    * @param {Object} object The object to iterate over.
8965    * @param {Function} iteratee The function invoked per iteration.
8966    * @returns {Object} Returns `object`.
8967    */
8968   function baseForIn(object, iteratee) {
8969     return baseFor(object, iteratee, keysIn);
8970   }
8971
8972   /**
8973    * The base implementation of `_.forOwn` without support for callback
8974    * shorthands and `this` binding.
8975    *
8976    * @private
8977    * @param {Object} object The object to iterate over.
8978    * @param {Function} iteratee The function invoked per iteration.
8979    * @returns {Object} Returns `object`.
8980    */
8981   function baseForOwn(object, iteratee) {
8982     return baseFor(object, iteratee, keys);
8983   }
8984
8985   /**
8986    * The base implementation of `_.functions` which creates an array of
8987    * `object` function property names filtered from those provided.
8988    *
8989    * @private
8990    * @param {Object} object The object to inspect.
8991    * @param {Array} props The property names to filter.
8992    * @returns {Array} Returns the new array of filtered property names.
8993    */
8994   function baseFunctions(object, props) {
8995     var index = -1,
8996         length = props.length,
8997         resIndex = -1,
8998         result = [];
8999
9000     while (++index < length) {
9001       var key = props[index];
9002       if (isFunction(object[key])) {
9003         result[++resIndex] = key;
9004       }
9005     }
9006     return result;
9007   }
9008
9009   /**
9010    * The base implementation of `get` without support for string paths
9011    * and default values.
9012    *
9013    * @private
9014    * @param {Object} object The object to query.
9015    * @param {Array} path The path of the property to get.
9016    * @param {string} [pathKey] The key representation of path.
9017    * @returns {*} Returns the resolved value.
9018    */
9019   function baseGet(object, path, pathKey) {
9020     if (object == null) {
9021       return;
9022     }
9023     object = toObject(object);
9024     if (pathKey !== undefined && pathKey in object) {
9025       path = [pathKey];
9026     }
9027     var index = 0,
9028         length = path.length;
9029
9030     while (object != null && index < length) {
9031       object = toObject(object)[path[index++]];
9032     }
9033     return (index && index == length) ? object : undefined;
9034   }
9035
9036   /**
9037    * The base implementation of `_.isEqual` without support for `this` binding
9038    * `customizer` functions.
9039    *
9040    * @private
9041    * @param {*} value The value to compare.
9042    * @param {*} other The other value to compare.
9043    * @param {Function} [customizer] The function to customize comparing values.
9044    * @param {boolean} [isLoose] Specify performing partial comparisons.
9045    * @param {Array} [stackA] Tracks traversed `value` objects.
9046    * @param {Array} [stackB] Tracks traversed `other` objects.
9047    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
9048    */
9049   function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) {
9050     if (value === other) {
9051       return true;
9052     }
9053     if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
9054       return value !== value && other !== other;
9055     }
9056     return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB);
9057   }
9058
9059   /**
9060    * A specialized version of `baseIsEqual` for arrays and objects which performs
9061    * deep comparisons and tracks traversed objects enabling objects with circular
9062    * references to be compared.
9063    *
9064    * @private
9065    * @param {Object} object The object to compare.
9066    * @param {Object} other The other object to compare.
9067    * @param {Function} equalFunc The function to determine equivalents of values.
9068    * @param {Function} [customizer] The function to customize comparing objects.
9069    * @param {boolean} [isLoose] Specify performing partial comparisons.
9070    * @param {Array} [stackA=[]] Tracks traversed `value` objects.
9071    * @param {Array} [stackB=[]] Tracks traversed `other` objects.
9072    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9073    */
9074   function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
9075     var objIsArr = isArray(object),
9076         othIsArr = isArray(other),
9077         objTag = arrayTag,
9078         othTag = arrayTag;
9079
9080     if (!objIsArr) {
9081       objTag = objToString.call(object);
9082       if (objTag == argsTag) {
9083         objTag = objectTag;
9084       } else if (objTag != objectTag) {
9085         objIsArr = isTypedArray(object);
9086       }
9087     }
9088     if (!othIsArr) {
9089       othTag = objToString.call(other);
9090       if (othTag == argsTag) {
9091         othTag = objectTag;
9092       } else if (othTag != objectTag) {
9093         othIsArr = isTypedArray(other);
9094       }
9095     }
9096     var objIsObj = objTag == objectTag && !isHostObject(object),
9097         othIsObj = othTag == objectTag && !isHostObject(other),
9098         isSameTag = objTag == othTag;
9099
9100     if (isSameTag && !(objIsArr || objIsObj)) {
9101       return equalByTag(object, other, objTag);
9102     }
9103     if (!isLoose) {
9104       var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
9105           othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
9106
9107       if (objIsWrapped || othIsWrapped) {
9108         return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB);
9109       }
9110     }
9111     if (!isSameTag) {
9112       return false;
9113     }
9114     // Assume cyclic values are equal.
9115     // For more information on detecting circular references see https://es5.github.io/#JO.
9116     stackA || (stackA = []);
9117     stackB || (stackB = []);
9118
9119     var length = stackA.length;
9120     while (length--) {
9121       if (stackA[length] == object) {
9122         return stackB[length] == other;
9123       }
9124     }
9125     // Add `object` and `other` to the stack of traversed objects.
9126     stackA.push(object);
9127     stackB.push(other);
9128
9129     var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB);
9130
9131     stackA.pop();
9132     stackB.pop();
9133
9134     return result;
9135   }
9136
9137   /**
9138    * The base implementation of `_.isMatch` without support for callback
9139    * shorthands and `this` binding.
9140    *
9141    * @private
9142    * @param {Object} object The object to inspect.
9143    * @param {Array} matchData The propery names, values, and compare flags to match.
9144    * @param {Function} [customizer] The function to customize comparing objects.
9145    * @returns {boolean} Returns `true` if `object` is a match, else `false`.
9146    */
9147   function baseIsMatch(object, matchData, customizer) {
9148     var index = matchData.length,
9149         length = index,
9150         noCustomizer = !customizer;
9151
9152     if (object == null) {
9153       return !length;
9154     }
9155     object = toObject(object);
9156     while (index--) {
9157       var data = matchData[index];
9158       if ((noCustomizer && data[2])
9159             ? data[1] !== object[data[0]]
9160             : !(data[0] in object)
9161           ) {
9162         return false;
9163       }
9164     }
9165     while (++index < length) {
9166       data = matchData[index];
9167       var key = data[0],
9168           objValue = object[key],
9169           srcValue = data[1];
9170
9171       if (noCustomizer && data[2]) {
9172         if (objValue === undefined && !(key in object)) {
9173           return false;
9174         }
9175       } else {
9176         var result = customizer ? customizer(objValue, srcValue, key) : undefined;
9177         if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, true) : result)) {
9178           return false;
9179         }
9180       }
9181     }
9182     return true;
9183   }
9184
9185   /**
9186    * The base implementation of `_.map` without support for callback shorthands
9187    * and `this` binding.
9188    *
9189    * @private
9190    * @param {Array|Object|string} collection The collection to iterate over.
9191    * @param {Function} iteratee The function invoked per iteration.
9192    * @returns {Array} Returns the new mapped array.
9193    */
9194   function baseMap(collection, iteratee) {
9195     var index = -1,
9196         result = isArrayLike(collection) ? Array(collection.length) : [];
9197
9198     baseEach(collection, function(value, key, collection) {
9199       result[++index] = iteratee(value, key, collection);
9200     });
9201     return result;
9202   }
9203
9204   /**
9205    * The base implementation of `_.matches` which does not clone `source`.
9206    *
9207    * @private
9208    * @param {Object} source The object of property values to match.
9209    * @returns {Function} Returns the new function.
9210    */
9211   function baseMatches(source) {
9212     var matchData = getMatchData(source);
9213     if (matchData.length == 1 && matchData[0][2]) {
9214       var key = matchData[0][0],
9215           value = matchData[0][1];
9216
9217       return function(object) {
9218         if (object == null) {
9219           return false;
9220         }
9221         object = toObject(object);
9222         return object[key] === value && (value !== undefined || (key in object));
9223       };
9224     }
9225     return function(object) {
9226       return baseIsMatch(object, matchData);
9227     };
9228   }
9229
9230   /**
9231    * The base implementation of `_.matchesProperty` which does not clone `srcValue`.
9232    *
9233    * @private
9234    * @param {string} path The path of the property to get.
9235    * @param {*} srcValue The value to compare.
9236    * @returns {Function} Returns the new function.
9237    */
9238   function baseMatchesProperty(path, srcValue) {
9239     var isArr = isArray(path),
9240         isCommon = isKey(path) && isStrictComparable(srcValue),
9241         pathKey = (path + '');
9242
9243     path = toPath(path);
9244     return function(object) {
9245       if (object == null) {
9246         return false;
9247       }
9248       var key = pathKey;
9249       object = toObject(object);
9250       if ((isArr || !isCommon) && !(key in object)) {
9251         object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
9252         if (object == null) {
9253           return false;
9254         }
9255         key = last(path);
9256         object = toObject(object);
9257       }
9258       return object[key] === srcValue
9259         ? (srcValue !== undefined || (key in object))
9260         : baseIsEqual(srcValue, object[key], undefined, true);
9261     };
9262   }
9263
9264   /**
9265    * The base implementation of `_.merge` without support for argument juggling,
9266    * multiple sources, and `this` binding `customizer` functions.
9267    *
9268    * @private
9269    * @param {Object} object The destination object.
9270    * @param {Object} source The source object.
9271    * @param {Function} [customizer] The function to customize merging properties.
9272    * @param {Array} [stackA=[]] Tracks traversed source objects.
9273    * @param {Array} [stackB=[]] Associates values with source counterparts.
9274    * @returns {Object} Returns `object`.
9275    */
9276   function baseMerge(object, source, customizer, stackA, stackB) {
9277     if (!isObject(object)) {
9278       return object;
9279     }
9280     var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)),
9281         props = isSrcArr ? null : keys(source);
9282
9283     arrayEach(props || source, function(srcValue, key) {
9284       if (props) {
9285         key = srcValue;
9286         srcValue = source[key];
9287       }
9288       if (isObjectLike(srcValue)) {
9289         stackA || (stackA = []);
9290         stackB || (stackB = []);
9291         baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
9292       }
9293       else {
9294         var value = object[key],
9295             result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
9296             isCommon = result === undefined;
9297
9298         if (isCommon) {
9299           result = srcValue;
9300         }
9301         if ((result !== undefined || (isSrcArr && !(key in object))) &&
9302             (isCommon || (result === result ? (result !== value) : (value === value)))) {
9303           object[key] = result;
9304         }
9305       }
9306     });
9307     return object;
9308   }
9309
9310   /**
9311    * A specialized version of `baseMerge` for arrays and objects which performs
9312    * deep merges and tracks traversed objects enabling objects with circular
9313    * references to be merged.
9314    *
9315    * @private
9316    * @param {Object} object The destination object.
9317    * @param {Object} source The source object.
9318    * @param {string} key The key of the value to merge.
9319    * @param {Function} mergeFunc The function to merge values.
9320    * @param {Function} [customizer] The function to customize merging properties.
9321    * @param {Array} [stackA=[]] Tracks traversed source objects.
9322    * @param {Array} [stackB=[]] Associates values with source counterparts.
9323    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9324    */
9325   function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) {
9326     var length = stackA.length,
9327         srcValue = source[key];
9328
9329     while (length--) {
9330       if (stackA[length] == srcValue) {
9331         object[key] = stackB[length];
9332         return;
9333       }
9334     }
9335     var value = object[key],
9336         result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
9337         isCommon = result === undefined;
9338
9339     if (isCommon) {
9340       result = srcValue;
9341       if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) {
9342         result = isArray(value)
9343           ? value
9344           : (isArrayLike(value) ? arrayCopy(value) : []);
9345       }
9346       else if (isPlainObject(srcValue) || isArguments(srcValue)) {
9347         result = isArguments(value)
9348           ? toPlainObject(value)
9349           : (isPlainObject(value) ? value : {});
9350       }
9351       else {
9352         isCommon = false;
9353       }
9354     }
9355     // Add the source value to the stack of traversed objects and associate
9356     // it with its merged value.
9357     stackA.push(srcValue);
9358     stackB.push(result);
9359
9360     if (isCommon) {
9361       // Recursively merge objects and arrays (susceptible to call stack limits).
9362       object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB);
9363     } else if (result === result ? (result !== value) : (value === value)) {
9364       object[key] = result;
9365     }
9366   }
9367
9368   /**
9369    * The base implementation of `_.property` without support for deep paths.
9370    *
9371    * @private
9372    * @param {string} key The key of the property to get.
9373    * @returns {Function} Returns the new function.
9374    */
9375   function baseProperty(key) {
9376     return function(object) {
9377       return object == null ? undefined : toObject(object)[key];
9378     };
9379   }
9380
9381   /**
9382    * A specialized version of `baseProperty` which supports deep paths.
9383    *
9384    * @private
9385    * @param {Array|string} path The path of the property to get.
9386    * @returns {Function} Returns the new function.
9387    */
9388   function basePropertyDeep(path) {
9389     var pathKey = (path + '');
9390     path = toPath(path);
9391     return function(object) {
9392       return baseGet(object, path, pathKey);
9393     };
9394   }
9395
9396   /**
9397    * The base implementation of `_.reduce` and `_.reduceRight` without support
9398    * for callback shorthands and `this` binding, which iterates over `collection`
9399    * using the provided `eachFunc`.
9400    *
9401    * @private
9402    * @param {Array|Object|string} collection The collection to iterate over.
9403    * @param {Function} iteratee The function invoked per iteration.
9404    * @param {*} accumulator The initial value.
9405    * @param {boolean} initFromCollection Specify using the first or last element
9406    *  of `collection` as the initial value.
9407    * @param {Function} eachFunc The function to iterate over `collection`.
9408    * @returns {*} Returns the accumulated value.
9409    */
9410   function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) {
9411     eachFunc(collection, function(value, index, collection) {
9412       accumulator = initFromCollection
9413         ? (initFromCollection = false, value)
9414         : iteratee(accumulator, value, index, collection);
9415     });
9416     return accumulator;
9417   }
9418
9419   /**
9420    * The base implementation of `setData` without support for hot loop detection.
9421    *
9422    * @private
9423    * @param {Function} func The function to associate metadata with.
9424    * @param {*} data The metadata.
9425    * @returns {Function} Returns `func`.
9426    */
9427   var baseSetData = !metaMap ? identity : function(func, data) {
9428     metaMap.set(func, data);
9429     return func;
9430   };
9431
9432   /**
9433    * The base implementation of `_.slice` without an iteratee call guard.
9434    *
9435    * @private
9436    * @param {Array} array The array to slice.
9437    * @param {number} [start=0] The start position.
9438    * @param {number} [end=array.length] The end position.
9439    * @returns {Array} Returns the slice of `array`.
9440    */
9441   function baseSlice(array, start, end) {
9442     var index = -1,
9443         length = array.length;
9444
9445     start = start == null ? 0 : (+start || 0);
9446     if (start < 0) {
9447       start = -start > length ? 0 : (length + start);
9448     }
9449     end = (end === undefined || end > length) ? length : (+end || 0);
9450     if (end < 0) {
9451       end += length;
9452     }
9453     length = start > end ? 0 : ((end - start) >>> 0);
9454     start >>>= 0;
9455
9456     var result = Array(length);
9457     while (++index < length) {
9458       result[index] = array[index + start];
9459     }
9460     return result;
9461   }
9462
9463   /**
9464    * The base implementation of `_.some` without support for callback shorthands
9465    * and `this` binding.
9466    *
9467    * @private
9468    * @param {Array|Object|string} collection The collection to iterate over.
9469    * @param {Function} predicate The function invoked per iteration.
9470    * @returns {boolean} Returns `true` if any element passes the predicate check,
9471    *  else `false`.
9472    */
9473   function baseSome(collection, predicate) {
9474     var result;
9475
9476     baseEach(collection, function(value, index, collection) {
9477       result = predicate(value, index, collection);
9478       return !result;
9479     });
9480     return !!result;
9481   }
9482
9483   /**
9484    * The base implementation of `_.uniq` without support for callback shorthands
9485    * and `this` binding.
9486    *
9487    * @private
9488    * @param {Array} array The array to inspect.
9489    * @param {Function} [iteratee] The function invoked per iteration.
9490    * @returns {Array} Returns the new duplicate-value-free array.
9491    */
9492   function baseUniq(array, iteratee) {
9493     var index = -1,
9494         indexOf = getIndexOf(),
9495         length = array.length,
9496         isCommon = indexOf == baseIndexOf,
9497         isLarge = isCommon && length >= 200,
9498         seen = isLarge ? createCache() : null,
9499         result = [];
9500
9501     if (seen) {
9502       indexOf = cacheIndexOf;
9503       isCommon = false;
9504     } else {
9505       isLarge = false;
9506       seen = iteratee ? [] : result;
9507     }
9508     outer:
9509     while (++index < length) {
9510       var value = array[index],
9511           computed = iteratee ? iteratee(value, index, array) : value;
9512
9513       if (isCommon && value === value) {
9514         var seenIndex = seen.length;
9515         while (seenIndex--) {
9516           if (seen[seenIndex] === computed) {
9517             continue outer;
9518           }
9519         }
9520         if (iteratee) {
9521           seen.push(computed);
9522         }
9523         result.push(value);
9524       }
9525       else if (indexOf(seen, computed, 0) < 0) {
9526         if (iteratee || isLarge) {
9527           seen.push(computed);
9528         }
9529         result.push(value);
9530       }
9531     }
9532     return result;
9533   }
9534
9535   /**
9536    * The base implementation of `_.values` and `_.valuesIn` which creates an
9537    * array of `object` property values corresponding to the property names
9538    * of `props`.
9539    *
9540    * @private
9541    * @param {Object} object The object to query.
9542    * @param {Array} props The property names to get values for.
9543    * @returns {Object} Returns the array of property values.
9544    */
9545   function baseValues(object, props) {
9546     var index = -1,
9547         length = props.length,
9548         result = Array(length);
9549
9550     while (++index < length) {
9551       result[index] = object[props[index]];
9552     }
9553     return result;
9554   }
9555
9556   /**
9557    * The base implementation of `wrapperValue` which returns the result of
9558    * performing a sequence of actions on the unwrapped `value`, where each
9559    * successive action is supplied the return value of the previous.
9560    *
9561    * @private
9562    * @param {*} value The unwrapped value.
9563    * @param {Array} actions Actions to peform to resolve the unwrapped value.
9564    * @returns {*} Returns the resolved value.
9565    */
9566   function baseWrapperValue(value, actions) {
9567     var result = value;
9568     if (result instanceof LazyWrapper) {
9569       result = result.value();
9570     }
9571     var index = -1,
9572         length = actions.length;
9573
9574     while (++index < length) {
9575       var args = [result],
9576           action = actions[index];
9577
9578       push.apply(args, action.args);
9579       result = action.func.apply(action.thisArg, args);
9580     }
9581     return result;
9582   }
9583
9584   /**
9585    * Performs a binary search of `array` to determine the index at which `value`
9586    * should be inserted into `array` in order to maintain its sort order.
9587    *
9588    * @private
9589    * @param {Array} array The sorted array to inspect.
9590    * @param {*} value The value to evaluate.
9591    * @param {boolean} [retHighest] Specify returning the highest qualified index.
9592    * @returns {number} Returns the index at which `value` should be inserted
9593    *  into `array`.
9594    */
9595   function binaryIndex(array, value, retHighest) {
9596     var low = 0,
9597         high = array ? array.length : low;
9598
9599     if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
9600       while (low < high) {
9601         var mid = (low + high) >>> 1,
9602             computed = array[mid];
9603
9604         if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) {
9605           low = mid + 1;
9606         } else {
9607           high = mid;
9608         }
9609       }
9610       return high;
9611     }
9612     return binaryIndexBy(array, value, identity, retHighest);
9613   }
9614
9615   /**
9616    * This function is like `binaryIndex` except that it invokes `iteratee` for
9617    * `value` and each element of `array` to compute their sort ranking. The
9618    * iteratee is invoked with one argument; (value).
9619    *
9620    * @private
9621    * @param {Array} array The sorted array to inspect.
9622    * @param {*} value The value to evaluate.
9623    * @param {Function} iteratee The function invoked per iteration.
9624    * @param {boolean} [retHighest] Specify returning the highest qualified index.
9625    * @returns {number} Returns the index at which `value` should be inserted
9626    *  into `array`.
9627    */
9628   function binaryIndexBy(array, value, iteratee, retHighest) {
9629     value = iteratee(value);
9630
9631     var low = 0,
9632         high = array ? array.length : 0,
9633         valIsNaN = value !== value,
9634         valIsNull = value === null,
9635         valIsUndef = value === undefined;
9636
9637     while (low < high) {
9638       var mid = floor((low + high) / 2),
9639           computed = iteratee(array[mid]),
9640           isDef = computed !== undefined,
9641           isReflexive = computed === computed;
9642
9643       if (valIsNaN) {
9644         var setLow = isReflexive || retHighest;
9645       } else if (valIsNull) {
9646         setLow = isReflexive && isDef && (retHighest || computed != null);
9647       } else if (valIsUndef) {
9648         setLow = isReflexive && (retHighest || isDef);
9649       } else if (computed == null) {
9650         setLow = false;
9651       } else {
9652         setLow = retHighest ? (computed <= value) : (computed < value);
9653       }
9654       if (setLow) {
9655         low = mid + 1;
9656       } else {
9657         high = mid;
9658       }
9659     }
9660     return nativeMin(high, MAX_ARRAY_INDEX);
9661   }
9662
9663   /**
9664    * A specialized version of `baseCallback` which only supports `this` binding
9665    * and specifying the number of arguments to provide to `func`.
9666    *
9667    * @private
9668    * @param {Function} func The function to bind.
9669    * @param {*} thisArg The `this` binding of `func`.
9670    * @param {number} [argCount] The number of arguments to provide to `func`.
9671    * @returns {Function} Returns the callback.
9672    */
9673   function bindCallback(func, thisArg, argCount) {
9674     if (typeof func != 'function') {
9675       return identity;
9676     }
9677     if (thisArg === undefined) {
9678       return func;
9679     }
9680     switch (argCount) {
9681       case 1: return function(value) {
9682         return func.call(thisArg, value);
9683       };
9684       case 3: return function(value, index, collection) {
9685         return func.call(thisArg, value, index, collection);
9686       };
9687       case 4: return function(accumulator, value, index, collection) {
9688         return func.call(thisArg, accumulator, value, index, collection);
9689       };
9690       case 5: return function(value, other, key, object, source) {
9691         return func.call(thisArg, value, other, key, object, source);
9692       };
9693     }
9694     return function() {
9695       return func.apply(thisArg, arguments);
9696     };
9697   }
9698
9699   /**
9700    * Creates a clone of the given array buffer.
9701    *
9702    * @private
9703    * @param {ArrayBuffer} buffer The array buffer to clone.
9704    * @returns {ArrayBuffer} Returns the cloned array buffer.
9705    */
9706   function bufferClone(buffer) {
9707     return bufferSlice.call(buffer, 0);
9708   }
9709   if (!bufferSlice) {
9710     // PhantomJS has `ArrayBuffer` and `Uint8Array` but not `Float64Array`.
9711     bufferClone = !(ArrayBuffer && Uint8Array) ? constant(null) : function(buffer) {
9712       var byteLength = buffer.byteLength,
9713           floatLength = Float64Array ? floor(byteLength / FLOAT64_BYTES_PER_ELEMENT) : 0,
9714           offset = floatLength * FLOAT64_BYTES_PER_ELEMENT,
9715           result = new ArrayBuffer(byteLength);
9716
9717       if (floatLength) {
9718         var view = new Float64Array(result, 0, floatLength);
9719         view.set(new Float64Array(buffer, 0, floatLength));
9720       }
9721       if (byteLength != offset) {
9722         view = new Uint8Array(result, offset);
9723         view.set(new Uint8Array(buffer, offset));
9724       }
9725       return result;
9726     };
9727   }
9728
9729   /**
9730    * Creates an array that is the composition of partially applied arguments,
9731    * placeholders, and provided arguments into a single array of arguments.
9732    *
9733    * @private
9734    * @param {Array|Object} args The provided arguments.
9735    * @param {Array} partials The arguments to prepend to those provided.
9736    * @param {Array} holders The `partials` placeholder indexes.
9737    * @returns {Array} Returns the new array of composed arguments.
9738    */
9739   function composeArgs(args, partials, holders) {
9740     var holdersLength = holders.length,
9741         argsIndex = -1,
9742         argsLength = nativeMax(args.length - holdersLength, 0),
9743         leftIndex = -1,
9744         leftLength = partials.length,
9745         result = Array(argsLength + leftLength);
9746
9747     while (++leftIndex < leftLength) {
9748       result[leftIndex] = partials[leftIndex];
9749     }
9750     while (++argsIndex < holdersLength) {
9751       result[holders[argsIndex]] = args[argsIndex];
9752     }
9753     while (argsLength--) {
9754       result[leftIndex++] = args[argsIndex++];
9755     }
9756     return result;
9757   }
9758
9759   /**
9760    * This function is like `composeArgs` except that the arguments composition
9761    * is tailored for `_.partialRight`.
9762    *
9763    * @private
9764    * @param {Array|Object} args The provided arguments.
9765    * @param {Array} partials The arguments to append to those provided.
9766    * @param {Array} holders The `partials` placeholder indexes.
9767    * @returns {Array} Returns the new array of composed arguments.
9768    */
9769   function composeArgsRight(args, partials, holders) {
9770     var holdersIndex = -1,
9771         holdersLength = holders.length,
9772         argsIndex = -1,
9773         argsLength = nativeMax(args.length - holdersLength, 0),
9774         rightIndex = -1,
9775         rightLength = partials.length,
9776         result = Array(argsLength + rightLength);
9777
9778     while (++argsIndex < argsLength) {
9779       result[argsIndex] = args[argsIndex];
9780     }
9781     var offset = argsIndex;
9782     while (++rightIndex < rightLength) {
9783       result[offset + rightIndex] = partials[rightIndex];
9784     }
9785     while (++holdersIndex < holdersLength) {
9786       result[offset + holders[holdersIndex]] = args[argsIndex++];
9787     }
9788     return result;
9789   }
9790
9791   /**
9792    * Creates a function that aggregates a collection, creating an accumulator
9793    * object composed from the results of running each element in the collection
9794    * through an iteratee.
9795    *
9796    * **Note:** This function is used to create `_.countBy`, `_.groupBy`, `_.indexBy`,
9797    * and `_.partition`.
9798    *
9799    * @private
9800    * @param {Function} setter The function to set keys and values of the accumulator object.
9801    * @param {Function} [initializer] The function to initialize the accumulator object.
9802    * @returns {Function} Returns the new aggregator function.
9803    */
9804   function createAggregator(setter, initializer) {
9805     return function(collection, iteratee, thisArg) {
9806       var result = initializer ? initializer() : {};
9807       iteratee = getCallback(iteratee, thisArg, 3);
9808
9809       if (isArray(collection)) {
9810         var index = -1,
9811             length = collection.length;
9812
9813         while (++index < length) {
9814           var value = collection[index];
9815           setter(result, value, iteratee(value, index, collection), collection);
9816         }
9817       } else {
9818         baseEach(collection, function(value, key, collection) {
9819           setter(result, value, iteratee(value, key, collection), collection);
9820         });
9821       }
9822       return result;
9823     };
9824   }
9825
9826   /**
9827    * Creates a function that assigns properties of source object(s) to a given
9828    * destination object.
9829    *
9830    * **Note:** This function is used to create `_.assign`, `_.defaults`, and `_.merge`.
9831    *
9832    * @private
9833    * @param {Function} assigner The function to assign values.
9834    * @returns {Function} Returns the new assigner function.
9835    */
9836   function createAssigner(assigner) {
9837     return restParam(function(object, sources) {
9838       var index = -1,
9839           length = object == null ? 0 : sources.length,
9840           customizer = length > 2 ? sources[length - 2] : undefined,
9841           guard = length > 2 ? sources[2] : undefined,
9842           thisArg = length > 1 ? sources[length - 1] : undefined;
9843
9844       if (typeof customizer == 'function') {
9845         customizer = bindCallback(customizer, thisArg, 5);
9846         length -= 2;
9847       } else {
9848         customizer = typeof thisArg == 'function' ? thisArg : undefined;
9849         length -= (customizer ? 1 : 0);
9850       }
9851       if (guard && isIterateeCall(sources[0], sources[1], guard)) {
9852         customizer = length < 3 ? undefined : customizer;
9853         length = 1;
9854       }
9855       while (++index < length) {
9856         var source = sources[index];
9857         if (source) {
9858           assigner(object, source, customizer);
9859         }
9860       }
9861       return object;
9862     });
9863   }
9864
9865   /**
9866    * Creates a `baseEach` or `baseEachRight` function.
9867    *
9868    * @private
9869    * @param {Function} eachFunc The function to iterate over a collection.
9870    * @param {boolean} [fromRight] Specify iterating from right to left.
9871    * @returns {Function} Returns the new base function.
9872    */
9873   function createBaseEach(eachFunc, fromRight) {
9874     return function(collection, iteratee) {
9875       var length = collection ? getLength(collection) : 0;
9876       if (!isLength(length)) {
9877         return eachFunc(collection, iteratee);
9878       }
9879       var index = fromRight ? length : -1,
9880           iterable = toObject(collection);
9881
9882       while ((fromRight ? index-- : ++index < length)) {
9883         if (iteratee(iterable[index], index, iterable) === false) {
9884           break;
9885         }
9886       }
9887       return collection;
9888     };
9889   }
9890
9891   /**
9892    * Creates a base function for `_.forIn` or `_.forInRight`.
9893    *
9894    * @private
9895    * @param {boolean} [fromRight] Specify iterating from right to left.
9896    * @returns {Function} Returns the new base function.
9897    */
9898   function createBaseFor(fromRight) {
9899     return function(object, iteratee, keysFunc) {
9900       var iterable = toObject(object),
9901           props = keysFunc(object),
9902           length = props.length,
9903           index = fromRight ? length : -1;
9904
9905       while ((fromRight ? index-- : ++index < length)) {
9906         var key = props[index];
9907         if (iteratee(iterable[key], key, iterable) === false) {
9908           break;
9909         }
9910       }
9911       return object;
9912     };
9913   }
9914
9915   /**
9916    * Creates a function that wraps `func` and invokes it with the `this`
9917    * binding of `thisArg`.
9918    *
9919    * @private
9920    * @param {Function} func The function to bind.
9921    * @param {*} [thisArg] The `this` binding of `func`.
9922    * @returns {Function} Returns the new bound function.
9923    */
9924   function createBindWrapper(func, thisArg) {
9925     var Ctor = createCtorWrapper(func);
9926
9927     function wrapper() {
9928       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
9929       return fn.apply(thisArg, arguments);
9930     }
9931     return wrapper;
9932   }
9933
9934   /**
9935    * Creates a `Set` cache object to optimize linear searches of large arrays.
9936    *
9937    * @private
9938    * @param {Array} [values] The values to cache.
9939    * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`.
9940    */
9941   var createCache = !(nativeCreate && Set) ? constant(null) : function(values) {
9942     return new SetCache(values);
9943   };
9944
9945   /**
9946    * Creates a function that produces an instance of `Ctor` regardless of
9947    * whether it was invoked as part of a `new` expression or by `call` or `apply`.
9948    *
9949    * @private
9950    * @param {Function} Ctor The constructor to wrap.
9951    * @returns {Function} Returns the new wrapped function.
9952    */
9953   function createCtorWrapper(Ctor) {
9954     return function() {
9955       // Use a `switch` statement to work with class constructors.
9956       // See https://people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-function-objects-call-thisargument-argumentslist
9957       // for more details.
9958       var args = arguments;
9959       switch (args.length) {
9960         case 0: return new Ctor;
9961         case 1: return new Ctor(args[0]);
9962         case 2: return new Ctor(args[0], args[1]);
9963         case 3: return new Ctor(args[0], args[1], args[2]);
9964         case 4: return new Ctor(args[0], args[1], args[2], args[3]);
9965         case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
9966       }
9967       var thisBinding = baseCreate(Ctor.prototype),
9968           result = Ctor.apply(thisBinding, args);
9969
9970       // Mimic the constructor's `return` behavior.
9971       // See https://es5.github.io/#x13.2.2 for more details.
9972       return isObject(result) ? result : thisBinding;
9973     };
9974   }
9975
9976   /**
9977    * Creates a `_.find` or `_.findLast` function.
9978    *
9979    * @private
9980    * @param {Function} eachFunc The function to iterate over a collection.
9981    * @param {boolean} [fromRight] Specify iterating from right to left.
9982    * @returns {Function} Returns the new find function.
9983    */
9984   function createFind(eachFunc, fromRight) {
9985     return function(collection, predicate, thisArg) {
9986       predicate = getCallback(predicate, thisArg, 3);
9987       if (isArray(collection)) {
9988         var index = baseFindIndex(collection, predicate, fromRight);
9989         return index > -1 ? collection[index] : undefined;
9990       }
9991       return baseFind(collection, predicate, eachFunc);
9992     };
9993   }
9994
9995   /**
9996    * Creates a function for `_.forEach` or `_.forEachRight`.
9997    *
9998    * @private
9999    * @param {Function} arrayFunc The function to iterate over an array.
10000    * @param {Function} eachFunc The function to iterate over a collection.
10001    * @returns {Function} Returns the new each function.
10002    */
10003   function createForEach(arrayFunc, eachFunc) {
10004     return function(collection, iteratee, thisArg) {
10005       return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
10006         ? arrayFunc(collection, iteratee)
10007         : eachFunc(collection, bindCallback(iteratee, thisArg, 3));
10008     };
10009   }
10010
10011   /**
10012    * Creates a function for `_.forOwn` or `_.forOwnRight`.
10013    *
10014    * @private
10015    * @param {Function} objectFunc The function to iterate over an object.
10016    * @returns {Function} Returns the new each function.
10017    */
10018   function createForOwn(objectFunc) {
10019     return function(object, iteratee, thisArg) {
10020       if (typeof iteratee != 'function' || thisArg !== undefined) {
10021         iteratee = bindCallback(iteratee, thisArg, 3);
10022       }
10023       return objectFunc(object, iteratee);
10024     };
10025   }
10026
10027   /**
10028    * Creates a function for `_.reduce` or `_.reduceRight`.
10029    *
10030    * @private
10031    * @param {Function} arrayFunc The function to iterate over an array.
10032    * @param {Function} eachFunc The function to iterate over a collection.
10033    * @returns {Function} Returns the new each function.
10034    */
10035   function createReduce(arrayFunc, eachFunc) {
10036     return function(collection, iteratee, accumulator, thisArg) {
10037       var initFromArray = arguments.length < 3;
10038       return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
10039         ? arrayFunc(collection, iteratee, accumulator, initFromArray)
10040         : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc);
10041     };
10042   }
10043
10044   /**
10045    * Creates a function that wraps `func` and invokes it with optional `this`
10046    * binding of, partial application, and currying.
10047    *
10048    * @private
10049    * @param {Function|string} func The function or method name to reference.
10050    * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
10051    * @param {*} [thisArg] The `this` binding of `func`.
10052    * @param {Array} [partials] The arguments to prepend to those provided to the new function.
10053    * @param {Array} [holders] The `partials` placeholder indexes.
10054    * @param {Array} [partialsRight] The arguments to append to those provided to the new function.
10055    * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
10056    * @param {Array} [argPos] The argument positions of the new function.
10057    * @param {number} [ary] The arity cap of `func`.
10058    * @param {number} [arity] The arity of `func`.
10059    * @returns {Function} Returns the new wrapped function.
10060    */
10061   function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
10062     var isAry = bitmask & ARY_FLAG,
10063         isBind = bitmask & BIND_FLAG,
10064         isBindKey = bitmask & BIND_KEY_FLAG,
10065         isCurry = bitmask & CURRY_FLAG,
10066         isCurryBound = bitmask & CURRY_BOUND_FLAG,
10067         isCurryRight = bitmask & CURRY_RIGHT_FLAG,
10068         Ctor = isBindKey ? null : createCtorWrapper(func);
10069
10070     function wrapper() {
10071       // Avoid `arguments` object use disqualifying optimizations by
10072       // converting it to an array before providing it to other functions.
10073       var length = arguments.length,
10074           index = length,
10075           args = Array(length);
10076
10077       while (index--) {
10078         args[index] = arguments[index];
10079       }
10080       if (partials) {
10081         args = composeArgs(args, partials, holders);
10082       }
10083       if (partialsRight) {
10084         args = composeArgsRight(args, partialsRight, holdersRight);
10085       }
10086       if (isCurry || isCurryRight) {
10087         var placeholder = wrapper.placeholder,
10088             argsHolders = replaceHolders(args, placeholder);
10089
10090         length -= argsHolders.length;
10091         if (length < arity) {
10092           var newArgPos = argPos ? arrayCopy(argPos) : null,
10093               newArity = nativeMax(arity - length, 0),
10094               newsHolders = isCurry ? argsHolders : null,
10095               newHoldersRight = isCurry ? null : argsHolders,
10096               newPartials = isCurry ? args : null,
10097               newPartialsRight = isCurry ? null : args;
10098
10099           bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
10100           bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
10101
10102           if (!isCurryBound) {
10103             bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
10104           }
10105           var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity],
10106               result = createHybridWrapper.apply(undefined, newData);
10107
10108           if (isLaziable(func)) {
10109             setData(result, newData);
10110           }
10111           result.placeholder = placeholder;
10112           return result;
10113         }
10114       }
10115       var thisBinding = isBind ? thisArg : this,
10116           fn = isBindKey ? thisBinding[func] : func;
10117
10118       if (argPos) {
10119         args = reorder(args, argPos);
10120       }
10121       if (isAry && ary < args.length) {
10122         args.length = ary;
10123       }
10124       if (this && this !== root && this instanceof wrapper) {
10125         fn = Ctor || createCtorWrapper(func);
10126       }
10127       return fn.apply(thisBinding, args);
10128     }
10129     return wrapper;
10130   }
10131
10132   /**
10133    * Creates a function that wraps `func` and invokes it with the optional `this`
10134    * binding of `thisArg` and the `partials` prepended to those provided to
10135    * the wrapper.
10136    *
10137    * @private
10138    * @param {Function} func The function to partially apply arguments to.
10139    * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
10140    * @param {*} thisArg The `this` binding of `func`.
10141    * @param {Array} partials The arguments to prepend to those provided to the new function.
10142    * @returns {Function} Returns the new bound function.
10143    */
10144   function createPartialWrapper(func, bitmask, thisArg, partials) {
10145     var isBind = bitmask & BIND_FLAG,
10146         Ctor = createCtorWrapper(func);
10147
10148     function wrapper() {
10149       // Avoid `arguments` object use disqualifying optimizations by
10150       // converting it to an array before providing it `func`.
10151       var argsIndex = -1,
10152           argsLength = arguments.length,
10153           leftIndex = -1,
10154           leftLength = partials.length,
10155           args = Array(argsLength + leftLength);
10156
10157       while (++leftIndex < leftLength) {
10158         args[leftIndex] = partials[leftIndex];
10159       }
10160       while (argsLength--) {
10161         args[leftIndex++] = arguments[++argsIndex];
10162       }
10163       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10164       return fn.apply(isBind ? thisArg : this, args);
10165     }
10166     return wrapper;
10167   }
10168
10169   /**
10170    * Creates a function that either curries or invokes `func` with optional
10171    * `this` binding and partially applied arguments.
10172    *
10173    * @private
10174    * @param {Function|string} func The function or method name to reference.
10175    * @param {number} bitmask The bitmask of flags.
10176    *  The bitmask may be composed of the following flags:
10177    *     1 - `_.bind`
10178    *     2 - `_.bindKey`
10179    *     4 - `_.curry` or `_.curryRight` of a bound function
10180    *     8 - `_.curry`
10181    *    16 - `_.curryRight`
10182    *    32 - `_.partial`
10183    *    64 - `_.partialRight`
10184    *   128 - `_.rearg`
10185    *   256 - `_.ary`
10186    * @param {*} [thisArg] The `this` binding of `func`.
10187    * @param {Array} [partials] The arguments to be partially applied.
10188    * @param {Array} [holders] The `partials` placeholder indexes.
10189    * @param {Array} [argPos] The argument positions of the new function.
10190    * @param {number} [ary] The arity cap of `func`.
10191    * @param {number} [arity] The arity of `func`.
10192    * @returns {Function} Returns the new wrapped function.
10193    */
10194   function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
10195     var isBindKey = bitmask & BIND_KEY_FLAG;
10196     if (!isBindKey && typeof func != 'function') {
10197       throw new TypeError(FUNC_ERROR_TEXT);
10198     }
10199     var length = partials ? partials.length : 0;
10200     if (!length) {
10201       bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
10202       partials = holders = null;
10203     }
10204     length -= (holders ? holders.length : 0);
10205     if (bitmask & PARTIAL_RIGHT_FLAG) {
10206       var partialsRight = partials,
10207           holdersRight = holders;
10208
10209       partials = holders = null;
10210     }
10211     var data = isBindKey ? null : getData(func),
10212         newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
10213
10214     if (data) {
10215       mergeData(newData, data);
10216       bitmask = newData[1];
10217       arity = newData[9];
10218     }
10219     newData[9] = arity == null
10220       ? (isBindKey ? 0 : func.length)
10221       : (nativeMax(arity - length, 0) || 0);
10222
10223     if (bitmask == BIND_FLAG) {
10224       var result = createBindWrapper(newData[0], newData[2]);
10225     } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) {
10226       result = createPartialWrapper.apply(undefined, newData);
10227     } else {
10228       result = createHybridWrapper.apply(undefined, newData);
10229     }
10230     var setter = data ? baseSetData : setData;
10231     return setter(result, newData);
10232   }
10233
10234   /**
10235    * A specialized version of `baseIsEqualDeep` for arrays with support for
10236    * partial deep comparisons.
10237    *
10238    * @private
10239    * @param {Array} array The array to compare.
10240    * @param {Array} other The other array to compare.
10241    * @param {Function} equalFunc The function to determine equivalents of values.
10242    * @param {Function} [customizer] The function to customize comparing arrays.
10243    * @param {boolean} [isLoose] Specify performing partial comparisons.
10244    * @param {Array} [stackA] Tracks traversed `value` objects.
10245    * @param {Array} [stackB] Tracks traversed `other` objects.
10246    * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
10247    */
10248   function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) {
10249     var index = -1,
10250         arrLength = array.length,
10251         othLength = other.length;
10252
10253     if (arrLength != othLength && !(isLoose && othLength > arrLength)) {
10254       return false;
10255     }
10256     // Ignore non-index properties.
10257     while (++index < arrLength) {
10258       var arrValue = array[index],
10259           othValue = other[index],
10260           result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined;
10261
10262       if (result !== undefined) {
10263         if (result) {
10264           continue;
10265         }
10266         return false;
10267       }
10268       // Recursively compare arrays (susceptible to call stack limits).
10269       if (isLoose) {
10270         if (!arraySome(other, function(othValue) {
10271               return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB);
10272             })) {
10273           return false;
10274         }
10275       } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) {
10276         return false;
10277       }
10278     }
10279     return true;
10280   }
10281
10282   /**
10283    * A specialized version of `baseIsEqualDeep` for comparing objects of
10284    * the same `toStringTag`.
10285    *
10286    * **Note:** This function only supports comparing values with tags of
10287    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
10288    *
10289    * @private
10290    * @param {Object} value The object to compare.
10291    * @param {Object} other The other object to compare.
10292    * @param {string} tag The `toStringTag` of the objects to compare.
10293    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
10294    */
10295   function equalByTag(object, other, tag) {
10296     switch (tag) {
10297       case boolTag:
10298       case dateTag:
10299         // Coerce dates and booleans to numbers, dates to milliseconds and booleans
10300         // to `1` or `0` treating invalid dates coerced to `NaN` as not equal.
10301         return +object == +other;
10302
10303       case errorTag:
10304         return object.name == other.name && object.message == other.message;
10305
10306       case numberTag:
10307         // Treat `NaN` vs. `NaN` as equal.
10308         return (object != +object)
10309           ? other != +other
10310           : object == +other;
10311
10312       case regexpTag:
10313       case stringTag:
10314         // Coerce regexes to strings and treat strings primitives and string
10315         // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details.
10316         return object == (other + '');
10317     }
10318     return false;
10319   }
10320
10321   /**
10322    * A specialized version of `baseIsEqualDeep` for objects with support for
10323    * partial deep comparisons.
10324    *
10325    * @private
10326    * @param {Object} object The object to compare.
10327    * @param {Object} other The other object to compare.
10328    * @param {Function} equalFunc The function to determine equivalents of values.
10329    * @param {Function} [customizer] The function to customize comparing values.
10330    * @param {boolean} [isLoose] Specify performing partial comparisons.
10331    * @param {Array} [stackA] Tracks traversed `value` objects.
10332    * @param {Array} [stackB] Tracks traversed `other` objects.
10333    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
10334    */
10335   function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
10336     var objProps = keys(object),
10337         objLength = objProps.length,
10338         othProps = keys(other),
10339         othLength = othProps.length;
10340
10341     if (objLength != othLength && !isLoose) {
10342       return false;
10343     }
10344     var index = objLength;
10345     while (index--) {
10346       var key = objProps[index];
10347       if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) {
10348         return false;
10349       }
10350     }
10351     var skipCtor = isLoose;
10352     while (++index < objLength) {
10353       key = objProps[index];
10354       var objValue = object[key],
10355           othValue = other[key],
10356           result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined;
10357
10358       // Recursively compare objects (susceptible to call stack limits).
10359       if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) {
10360         return false;
10361       }
10362       skipCtor || (skipCtor = key == 'constructor');
10363     }
10364     if (!skipCtor) {
10365       var objCtor = object.constructor,
10366           othCtor = other.constructor;
10367
10368       // Non `Object` object instances with different constructors are not equal.
10369       if (objCtor != othCtor &&
10370           ('constructor' in object && 'constructor' in other) &&
10371           !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
10372             typeof othCtor == 'function' && othCtor instanceof othCtor)) {
10373         return false;
10374       }
10375     }
10376     return true;
10377   }
10378
10379   /**
10380    * Gets the appropriate "callback" function. If the `_.callback` method is
10381    * customized this function returns the custom method, otherwise it returns
10382    * the `baseCallback` function. If arguments are provided the chosen function
10383    * is invoked with them and its result is returned.
10384    *
10385    * @private
10386    * @returns {Function} Returns the chosen function or its result.
10387    */
10388   function getCallback(func, thisArg, argCount) {
10389     var result = lodash.callback || callback;
10390     result = result === callback ? baseCallback : result;
10391     return argCount ? result(func, thisArg, argCount) : result;
10392   }
10393
10394   /**
10395    * Gets metadata for `func`.
10396    *
10397    * @private
10398    * @param {Function} func The function to query.
10399    * @returns {*} Returns the metadata for `func`.
10400    */
10401   var getData = !metaMap ? noop : function(func) {
10402     return metaMap.get(func);
10403   };
10404
10405   /**
10406    * Gets the name of `func`.
10407    *
10408    * @private
10409    * @param {Function} func The function to query.
10410    * @returns {string} Returns the function name.
10411    */
10412   function getFuncName(func) {
10413     var result = func.name,
10414         array = realNames[result],
10415         length = array ? array.length : 0;
10416
10417     while (length--) {
10418       var data = array[length],
10419           otherFunc = data.func;
10420       if (otherFunc == null || otherFunc == func) {
10421         return data.name;
10422       }
10423     }
10424     return result;
10425   }
10426
10427   /**
10428    * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
10429    * customized this function returns the custom method, otherwise it returns
10430    * the `baseIndexOf` function. If arguments are provided the chosen function
10431    * is invoked with them and its result is returned.
10432    *
10433    * @private
10434    * @returns {Function|number} Returns the chosen function or its result.
10435    */
10436   function getIndexOf(collection, target, fromIndex) {
10437     var result = lodash.indexOf || indexOf;
10438     result = result === indexOf ? baseIndexOf : result;
10439     return collection ? result(collection, target, fromIndex) : result;
10440   }
10441
10442   /**
10443    * Gets the "length" property value of `object`.
10444    *
10445    * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792)
10446    * that affects Safari on at least iOS 8.1-8.3 ARM64.
10447    *
10448    * @private
10449    * @param {Object} object The object to query.
10450    * @returns {*} Returns the "length" value.
10451    */
10452   var getLength = baseProperty('length');
10453
10454   /**
10455    * Gets the propery names, values, and compare flags of `object`.
10456    *
10457    * @private
10458    * @param {Object} object The object to query.
10459    * @returns {Array} Returns the match data of `object`.
10460    */
10461   function getMatchData(object) {
10462     var result = pairs(object),
10463         length = result.length;
10464
10465     while (length--) {
10466       result[length][2] = isStrictComparable(result[length][1]);
10467     }
10468     return result;
10469   }
10470
10471   /**
10472    * Gets the native function at `key` of `object`.
10473    *
10474    * @private
10475    * @param {Object} object The object to query.
10476    * @param {string} key The key of the method to get.
10477    * @returns {*} Returns the function if it's native, else `undefined`.
10478    */
10479   function getNative(object, key) {
10480     var value = object == null ? undefined : object[key];
10481     return isNative(value) ? value : undefined;
10482   }
10483
10484   /**
10485    * Gets the view, applying any `transforms` to the `start` and `end` positions.
10486    *
10487    * @private
10488    * @param {number} start The start of the view.
10489    * @param {number} end The end of the view.
10490    * @param {Array} [transforms] The transformations to apply to the view.
10491    * @returns {Object} Returns an object containing the `start` and `end`
10492    *  positions of the view.
10493    */
10494   function getView(start, end, transforms) {
10495     var index = -1,
10496         length = transforms ? transforms.length : 0;
10497
10498     while (++index < length) {
10499       var data = transforms[index],
10500           size = data.size;
10501
10502       switch (data.type) {
10503         case 'drop':      start += size; break;
10504         case 'dropRight': end -= size; break;
10505         case 'take':      end = nativeMin(end, start + size); break;
10506         case 'takeRight': start = nativeMax(start, end - size); break;
10507       }
10508     }
10509     return { 'start': start, 'end': end };
10510   }
10511
10512   /**
10513    * Initializes an array clone.
10514    *
10515    * @private
10516    * @param {Array} array The array to clone.
10517    * @returns {Array} Returns the initialized clone.
10518    */
10519   function initCloneArray(array) {
10520     var length = array.length,
10521         result = new array.constructor(length);
10522
10523     // Add array properties assigned by `RegExp#exec`.
10524     if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
10525       result.index = array.index;
10526       result.input = array.input;
10527     }
10528     return result;
10529   }
10530
10531   /**
10532    * Initializes an object clone.
10533    *
10534    * @private
10535    * @param {Object} object The object to clone.
10536    * @returns {Object} Returns the initialized clone.
10537    */
10538   function initCloneObject(object) {
10539     var Ctor = object.constructor;
10540     if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) {
10541       Ctor = Object;
10542     }
10543     return new Ctor;
10544   }
10545
10546   /**
10547    * Initializes an object clone based on its `toStringTag`.
10548    *
10549    * **Note:** This function only supports cloning values with tags of
10550    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
10551    *
10552    * @private
10553    * @param {Object} object The object to clone.
10554    * @param {string} tag The `toStringTag` of the object to clone.
10555    * @param {boolean} [isDeep] Specify a deep clone.
10556    * @returns {Object} Returns the initialized clone.
10557    */
10558   function initCloneByTag(object, tag, isDeep) {
10559     var Ctor = object.constructor;
10560     switch (tag) {
10561       case arrayBufferTag:
10562         return bufferClone(object);
10563
10564       case boolTag:
10565       case dateTag:
10566         return new Ctor(+object);
10567
10568       case float32Tag: case float64Tag:
10569       case int8Tag: case int16Tag: case int32Tag:
10570       case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
10571         // Safari 5 mobile incorrectly has `Object` as the constructor of typed arrays.
10572         if (Ctor instanceof Ctor) {
10573           Ctor = ctorByTag[tag];
10574         }
10575         var buffer = object.buffer;
10576         return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length);
10577
10578       case numberTag:
10579       case stringTag:
10580         return new Ctor(object);
10581
10582       case regexpTag:
10583         var result = new Ctor(object.source, reFlags.exec(object));
10584         result.lastIndex = object.lastIndex;
10585     }
10586     return result;
10587   }
10588
10589   /**
10590    * Checks if `value` is array-like.
10591    *
10592    * @private
10593    * @param {*} value The value to check.
10594    * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
10595    */
10596   function isArrayLike(value) {
10597     return value != null && isLength(getLength(value));
10598   }
10599
10600   /**
10601    * Checks if `value` is a valid array-like index.
10602    *
10603    * @private
10604    * @param {*} value The value to check.
10605    * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
10606    * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
10607    */
10608   function isIndex(value, length) {
10609     value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1;
10610     length = length == null ? MAX_SAFE_INTEGER : length;
10611     return value > -1 && value % 1 == 0 && value < length;
10612   }
10613
10614   /**
10615    * Checks if the provided arguments are from an iteratee call.
10616    *
10617    * @private
10618    * @param {*} value The potential iteratee value argument.
10619    * @param {*} index The potential iteratee index or key argument.
10620    * @param {*} object The potential iteratee object argument.
10621    * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`.
10622    */
10623   function isIterateeCall(value, index, object) {
10624     if (!isObject(object)) {
10625       return false;
10626     }
10627     var type = typeof index;
10628     if (type == 'number'
10629         ? (isArrayLike(object) && isIndex(index, object.length))
10630         : (type == 'string' && index in object)) {
10631       var other = object[index];
10632       return value === value ? (value === other) : (other !== other);
10633     }
10634     return false;
10635   }
10636
10637   /**
10638    * Checks if `value` is a property name and not a property path.
10639    *
10640    * @private
10641    * @param {*} value The value to check.
10642    * @param {Object} [object] The object to query keys on.
10643    * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
10644    */
10645   function isKey(value, object) {
10646     var type = typeof value;
10647     if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') {
10648       return true;
10649     }
10650     if (isArray(value)) {
10651       return false;
10652     }
10653     var result = !reIsDeepProp.test(value);
10654     return result || (object != null && value in toObject(object));
10655   }
10656
10657   /**
10658    * Checks if `func` has a lazy counterpart.
10659    *
10660    * @private
10661    * @param {Function} func The function to check.
10662    * @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`.
10663    */
10664   function isLaziable(func) {
10665     var funcName = getFuncName(func);
10666     if (!(funcName in LazyWrapper.prototype)) {
10667       return false;
10668     }
10669     var other = lodash[funcName];
10670     if (func === other) {
10671       return true;
10672     }
10673     var data = getData(other);
10674     return !!data && func === data[0];
10675   }
10676
10677   /**
10678    * Checks if `value` is a valid array-like length.
10679    *
10680    * **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength).
10681    *
10682    * @private
10683    * @param {*} value The value to check.
10684    * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
10685    */
10686   function isLength(value) {
10687     return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
10688   }
10689
10690   /**
10691    * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
10692    *
10693    * @private
10694    * @param {*} value The value to check.
10695    * @returns {boolean} Returns `true` if `value` if suitable for strict
10696    *  equality comparisons, else `false`.
10697    */
10698   function isStrictComparable(value) {
10699     return value === value && !isObject(value);
10700   }
10701
10702   /**
10703    * Merges the function metadata of `source` into `data`.
10704    *
10705    * Merging metadata reduces the number of wrappers required to invoke a function.
10706    * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
10707    * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg`
10708    * augment function arguments, making the order in which they are executed important,
10709    * preventing the merging of metadata. However, we make an exception for a safe
10710    * common case where curried functions have `_.ary` and or `_.rearg` applied.
10711    *
10712    * @private
10713    * @param {Array} data The destination metadata.
10714    * @param {Array} source The source metadata.
10715    * @returns {Array} Returns `data`.
10716    */
10717   function mergeData(data, source) {
10718     var bitmask = data[1],
10719         srcBitmask = source[1],
10720         newBitmask = bitmask | srcBitmask,
10721         isCommon = newBitmask < ARY_FLAG;
10722
10723     var isCombo =
10724       (srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) ||
10725       (srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) ||
10726       (srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG);
10727
10728     // Exit early if metadata can't be merged.
10729     if (!(isCommon || isCombo)) {
10730       return data;
10731     }
10732     // Use source `thisArg` if available.
10733     if (srcBitmask & BIND_FLAG) {
10734       data[2] = source[2];
10735       // Set when currying a bound function.
10736       newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG;
10737     }
10738     // Compose partial arguments.
10739     var value = source[3];
10740     if (value) {
10741       var partials = data[3];
10742       data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value);
10743       data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]);
10744     }
10745     // Compose partial right arguments.
10746     value = source[5];
10747     if (value) {
10748       partials = data[5];
10749       data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value);
10750       data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]);
10751     }
10752     // Use source `argPos` if available.
10753     value = source[7];
10754     if (value) {
10755       data[7] = arrayCopy(value);
10756     }
10757     // Use source `ary` if it's smaller.
10758     if (srcBitmask & ARY_FLAG) {
10759       data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
10760     }
10761     // Use source `arity` if one is not provided.
10762     if (data[9] == null) {
10763       data[9] = source[9];
10764     }
10765     // Use source `func` and merge bitmasks.
10766     data[0] = source[0];
10767     data[1] = newBitmask;
10768
10769     return data;
10770   }
10771
10772   /**
10773    * A specialized version of `_.pick` which picks `object` properties specified
10774    * by `props`.
10775    *
10776    * @private
10777    * @param {Object} object The source object.
10778    * @param {string[]} props The property names to pick.
10779    * @returns {Object} Returns the new object.
10780    */
10781   function pickByArray(object, props) {
10782     object = toObject(object);
10783
10784     var index = -1,
10785         length = props.length,
10786         result = {};
10787
10788     while (++index < length) {
10789       var key = props[index];
10790       if (key in object) {
10791         result[key] = object[key];
10792       }
10793     }
10794     return result;
10795   }
10796
10797   /**
10798    * A specialized version of `_.pick` which picks `object` properties `predicate`
10799    * returns truthy for.
10800    *
10801    * @private
10802    * @param {Object} object The source object.
10803    * @param {Function} predicate The function invoked per iteration.
10804    * @returns {Object} Returns the new object.
10805    */
10806   function pickByCallback(object, predicate) {
10807     var result = {};
10808     baseForIn(object, function(value, key, object) {
10809       if (predicate(value, key, object)) {
10810         result[key] = value;
10811       }
10812     });
10813     return result;
10814   }
10815
10816   /**
10817    * Reorder `array` according to the specified indexes where the element at
10818    * the first index is assigned as the first element, the element at
10819    * the second index is assigned as the second element, and so on.
10820    *
10821    * @private
10822    * @param {Array} array The array to reorder.
10823    * @param {Array} indexes The arranged array indexes.
10824    * @returns {Array} Returns `array`.
10825    */
10826   function reorder(array, indexes) {
10827     var arrLength = array.length,
10828         length = nativeMin(indexes.length, arrLength),
10829         oldArray = arrayCopy(array);
10830
10831     while (length--) {
10832       var index = indexes[length];
10833       array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
10834     }
10835     return array;
10836   }
10837
10838   /**
10839    * Sets metadata for `func`.
10840    *
10841    * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
10842    * period of time, it will trip its breaker and transition to an identity function
10843    * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070)
10844    * for more details.
10845    *
10846    * @private
10847    * @param {Function} func The function to associate metadata with.
10848    * @param {*} data The metadata.
10849    * @returns {Function} Returns `func`.
10850    */
10851   var setData = (function() {
10852     var count = 0,
10853         lastCalled = 0;
10854
10855     return function(key, value) {
10856       var stamp = now(),
10857           remaining = HOT_SPAN - (stamp - lastCalled);
10858
10859       lastCalled = stamp;
10860       if (remaining > 0) {
10861         if (++count >= HOT_COUNT) {
10862           return key;
10863         }
10864       } else {
10865         count = 0;
10866       }
10867       return baseSetData(key, value);
10868     };
10869   }());
10870
10871   /**
10872    * A fallback implementation of `_.isPlainObject` which checks if `value`
10873    * is an object created by the `Object` constructor or has a `[[Prototype]]`
10874    * of `null`.
10875    *
10876    * @private
10877    * @param {*} value The value to check.
10878    * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
10879    */
10880   function shimIsPlainObject(value) {
10881     var Ctor,
10882         support = lodash.support;
10883
10884     // Exit early for non `Object` objects.
10885     if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isHostObject(value)) ||
10886         (!hasOwnProperty.call(value, 'constructor') &&
10887           (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor))) ||
10888         (!support.argsTag && isArguments(value))) {
10889       return false;
10890     }
10891     // IE < 9 iterates inherited properties before own properties. If the first
10892     // iterated property is an object's own property then there are no inherited
10893     // enumerable properties.
10894     var result;
10895     if (support.ownLast) {
10896       baseForIn(value, function(subValue, key, object) {
10897         result = hasOwnProperty.call(object, key);
10898         return false;
10899       });
10900       return result !== false;
10901     }
10902     // In most environments an object's own properties are iterated before
10903     // its inherited properties. If the last iterated property is an object's
10904     // own property then there are no inherited enumerable properties.
10905     baseForIn(value, function(subValue, key) {
10906       result = key;
10907     });
10908     return result === undefined || hasOwnProperty.call(value, result);
10909   }
10910
10911   /**
10912    * A fallback implementation of `Object.keys` which creates an array of the
10913    * own enumerable property names of `object`.
10914    *
10915    * @private
10916    * @param {Object} object The object to query.
10917    * @returns {Array} Returns the array of property names.
10918    */
10919   function shimKeys(object) {
10920     var props = keysIn(object),
10921         propsLength = props.length,
10922         length = propsLength && object.length;
10923
10924     var allowIndexes = !!length && isLength(length) &&
10925       (isArray(object) || isArguments(object) || isString(object));
10926
10927     var index = -1,
10928         result = [];
10929
10930     while (++index < propsLength) {
10931       var key = props[index];
10932       if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) {
10933         result.push(key);
10934       }
10935     }
10936     return result;
10937   }
10938
10939   /**
10940    * Converts `value` to an object if it's not one.
10941    *
10942    * @private
10943    * @param {*} value The value to process.
10944    * @returns {Object} Returns the object.
10945    */
10946   function toObject(value) {
10947     if (lodash.support.unindexedChars && isString(value)) {
10948       var index = -1,
10949           length = value.length,
10950           result = Object(value);
10951
10952       while (++index < length) {
10953         result[index] = value.charAt(index);
10954       }
10955       return result;
10956     }
10957     return isObject(value) ? value : Object(value);
10958   }
10959
10960   /**
10961    * Converts `value` to property path array if it's not one.
10962    *
10963    * @private
10964    * @param {*} value The value to process.
10965    * @returns {Array} Returns the property path array.
10966    */
10967   function toPath(value) {
10968     if (isArray(value)) {
10969       return value;
10970     }
10971     var result = [];
10972     baseToString(value).replace(rePropName, function(match, number, quote, string) {
10973       result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
10974     });
10975     return result;
10976   }
10977
10978   /**
10979    * Creates a clone of `wrapper`.
10980    *
10981    * @private
10982    * @param {Object} wrapper The wrapper to clone.
10983    * @returns {Object} Returns the cloned wrapper.
10984    */
10985   function wrapperClone(wrapper) {
10986     return wrapper instanceof LazyWrapper
10987       ? wrapper.clone()
10988       : new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__, arrayCopy(wrapper.__actions__));
10989   }
10990
10991   /*------------------------------------------------------------------------*/
10992
10993   /**
10994    * Creates an array of elements split into groups the length of `size`.
10995    * If `collection` can't be split evenly, the final chunk will be the remaining
10996    * elements.
10997    *
10998    * @static
10999    * @memberOf _
11000    * @category Array
11001    * @param {Array} array The array to process.
11002    * @param {number} [size=1] The length of each chunk.
11003    * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
11004    * @returns {Array} Returns the new array containing chunks.
11005    * @example
11006    *
11007    * _.chunk(['a', 'b', 'c', 'd'], 2);
11008    * // => [['a', 'b'], ['c', 'd']]
11009    *
11010    * _.chunk(['a', 'b', 'c', 'd'], 3);
11011    * // => [['a', 'b', 'c'], ['d']]
11012    */
11013   function chunk(array, size, guard) {
11014     if (guard ? isIterateeCall(array, size, guard) : size == null) {
11015       size = 1;
11016     } else {
11017       size = nativeMax(+size || 1, 1);
11018     }
11019     var index = 0,
11020         length = array ? array.length : 0,
11021         resIndex = -1,
11022         result = Array(ceil(length / size));
11023
11024     while (index < length) {
11025       result[++resIndex] = baseSlice(array, index, (index += size));
11026     }
11027     return result;
11028   }
11029
11030   /**
11031    * Creates an array with all falsey values removed. The values `false`, `null`,
11032    * `0`, `""`, `undefined`, and `NaN` are falsey.
11033    *
11034    * @static
11035    * @memberOf _
11036    * @category Array
11037    * @param {Array} array The array to compact.
11038    * @returns {Array} Returns the new array of filtered values.
11039    * @example
11040    *
11041    * _.compact([0, 1, false, 2, '', 3]);
11042    * // => [1, 2, 3]
11043    */
11044   function compact(array) {
11045     var index = -1,
11046         length = array ? array.length : 0,
11047         resIndex = -1,
11048         result = [];
11049
11050     while (++index < length) {
11051       var value = array[index];
11052       if (value) {
11053         result[++resIndex] = value;
11054       }
11055     }
11056     return result;
11057   }
11058
11059   /**
11060    * Creates an array of unique `array` values not included in the other
11061    * provided arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11062    * for equality comparisons.
11063    *
11064    * @static
11065    * @memberOf _
11066    * @category Array
11067    * @param {Array} array The array to inspect.
11068    * @param {...Array} [values] The arrays of values to exclude.
11069    * @returns {Array} Returns the new array of filtered values.
11070    * @example
11071    *
11072    * _.difference([1, 2, 3], [4, 2]);
11073    * // => [1, 3]
11074    */
11075   var difference = restParam(function(array, values) {
11076     return isArrayLike(array)
11077       ? baseDifference(array, baseFlatten(values, false, true))
11078       : [];
11079   });
11080
11081   /**
11082    * Gets the first element of `array`.
11083    *
11084    * @static
11085    * @memberOf _
11086    * @alias head
11087    * @category Array
11088    * @param {Array} array The array to query.
11089    * @returns {*} Returns the first element of `array`.
11090    * @example
11091    *
11092    * _.first([1, 2, 3]);
11093    * // => 1
11094    *
11095    * _.first([]);
11096    * // => undefined
11097    */
11098   function first(array) {
11099     return array ? array[0] : undefined;
11100   }
11101
11102   /**
11103    * Flattens a nested array. If `isDeep` is `true` the array is recursively
11104    * flattened, otherwise it is only flattened a single level.
11105    *
11106    * @static
11107    * @memberOf _
11108    * @category Array
11109    * @param {Array} array The array to flatten.
11110    * @param {boolean} [isDeep] Specify a deep flatten.
11111    * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
11112    * @returns {Array} Returns the new flattened array.
11113    * @example
11114    *
11115    * _.flatten([1, [2, 3, [4]]]);
11116    * // => [1, 2, 3, [4]]
11117    *
11118    * // using `isDeep`
11119    * _.flatten([1, [2, 3, [4]]], true);
11120    * // => [1, 2, 3, 4]
11121    */
11122   function flatten(array, isDeep, guard) {
11123     var length = array ? array.length : 0;
11124     if (guard && isIterateeCall(array, isDeep, guard)) {
11125       isDeep = false;
11126     }
11127     return length ? baseFlatten(array, isDeep) : [];
11128   }
11129
11130   /**
11131    * Gets the index at which the first occurrence of `value` is found in `array`
11132    * using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11133    * for equality comparisons. If `fromIndex` is negative, it is used as the offset
11134    * from the end of `array`. If `array` is sorted providing `true` for `fromIndex`
11135    * performs a faster binary search.
11136    *
11137    * @static
11138    * @memberOf _
11139    * @category Array
11140    * @param {Array} array The array to search.
11141    * @param {*} value The value to search for.
11142    * @param {boolean|number} [fromIndex=0] The index to search from or `true`
11143    *  to perform a binary search on a sorted array.
11144    * @returns {number} Returns the index of the matched value, else `-1`.
11145    * @example
11146    *
11147    * _.indexOf([1, 2, 1, 2], 2);
11148    * // => 1
11149    *
11150    * // using `fromIndex`
11151    * _.indexOf([1, 2, 1, 2], 2, 2);
11152    * // => 3
11153    *
11154    * // performing a binary search
11155    * _.indexOf([1, 1, 2, 2], 2, true);
11156    * // => 2
11157    */
11158   function indexOf(array, value, fromIndex) {
11159     var length = array ? array.length : 0;
11160     if (!length) {
11161       return -1;
11162     }
11163     if (typeof fromIndex == 'number') {
11164       fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex;
11165     } else if (fromIndex) {
11166       var index = binaryIndex(array, value),
11167           other = array[index];
11168
11169       if (value === value ? (value === other) : (other !== other)) {
11170         return index;
11171       }
11172       return -1;
11173     }
11174     return baseIndexOf(array, value, fromIndex || 0);
11175   }
11176
11177   /**
11178    * Creates an array of unique values that are included in all of the provided
11179    * arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11180    * for equality comparisons.
11181    *
11182    * @static
11183    * @memberOf _
11184    * @category Array
11185    * @param {...Array} [arrays] The arrays to inspect.
11186    * @returns {Array} Returns the new array of shared values.
11187    * @example
11188    * _.intersection([1, 2], [4, 2], [2, 1]);
11189    * // => [2]
11190    */
11191   var intersection = restParam(function(arrays) {
11192     var othLength = arrays.length,
11193         othIndex = othLength,
11194         caches = Array(length),
11195         indexOf = getIndexOf(),
11196         isCommon = indexOf == baseIndexOf,
11197         result = [];
11198
11199     while (othIndex--) {
11200       var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : [];
11201       caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null;
11202     }
11203     var array = arrays[0],
11204         index = -1,
11205         length = array ? array.length : 0,
11206         seen = caches[0];
11207
11208     outer:
11209     while (++index < length) {
11210       value = array[index];
11211       if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) {
11212         var othIndex = othLength;
11213         while (--othIndex) {
11214           var cache = caches[othIndex];
11215           if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) {
11216             continue outer;
11217           }
11218         }
11219         if (seen) {
11220           seen.push(value);
11221         }
11222         result.push(value);
11223       }
11224     }
11225     return result;
11226   });
11227
11228   /**
11229    * Gets the last element of `array`.
11230    *
11231    * @static
11232    * @memberOf _
11233    * @category Array
11234    * @param {Array} array The array to query.
11235    * @returns {*} Returns the last element of `array`.
11236    * @example
11237    *
11238    * _.last([1, 2, 3]);
11239    * // => 3
11240    */
11241   function last(array) {
11242     var length = array ? array.length : 0;
11243     return length ? array[length - 1] : undefined;
11244   }
11245
11246   /**
11247    * Creates an array of unique values, in order, from all of the provided arrays
11248    * using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11249    * for equality comparisons.
11250    *
11251    * @static
11252    * @memberOf _
11253    * @category Array
11254    * @param {...Array} [arrays] The arrays to inspect.
11255    * @returns {Array} Returns the new array of combined values.
11256    * @example
11257    *
11258    * _.union([1, 2], [4, 2], [2, 1]);
11259    * // => [1, 2, 4]
11260    */
11261   var union = restParam(function(arrays) {
11262     return baseUniq(baseFlatten(arrays, false, true));
11263   });
11264
11265   /**
11266    * Creates a duplicate-free version of an array, using
11267    * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11268    * for equality comparisons, in which only the first occurence of each element
11269    * is kept. Providing `true` for `isSorted` performs a faster search algorithm
11270    * for sorted arrays. If an iteratee function is provided it is invoked for
11271    * each element in the array to generate the criterion by which uniqueness
11272    * is computed. The `iteratee` is bound to `thisArg` and invoked with three
11273    * arguments: (value, index, array).
11274    *
11275    * If a property name is provided for `iteratee` the created `_.property`
11276    * style callback returns the property value of the given element.
11277    *
11278    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11279    * style callback returns `true` for elements that have a matching property
11280    * value, else `false`.
11281    *
11282    * If an object is provided for `iteratee` the created `_.matches` style
11283    * callback returns `true` for elements that have the properties of the given
11284    * object, else `false`.
11285    *
11286    * @static
11287    * @memberOf _
11288    * @alias unique
11289    * @category Array
11290    * @param {Array} array The array to inspect.
11291    * @param {boolean} [isSorted] Specify the array is sorted.
11292    * @param {Function|Object|string} [iteratee] The function invoked per iteration.
11293    * @param {*} [thisArg] The `this` binding of `iteratee`.
11294    * @returns {Array} Returns the new duplicate-value-free array.
11295    * @example
11296    *
11297    * _.uniq([2, 1, 2]);
11298    * // => [2, 1]
11299    *
11300    * // using `isSorted`
11301    * _.uniq([1, 1, 2], true);
11302    * // => [1, 2]
11303    *
11304    * // using an iteratee function
11305    * _.uniq([1, 2.5, 1.5, 2], function(n) {
11306    *   return this.floor(n);
11307    * }, Math);
11308    * // => [1, 2.5]
11309    *
11310    * // using the `_.property` callback shorthand
11311    * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
11312    * // => [{ 'x': 1 }, { 'x': 2 }]
11313    */
11314   function uniq(array, isSorted, iteratee, thisArg) {
11315     var length = array ? array.length : 0;
11316     if (!length) {
11317       return [];
11318     }
11319     if (isSorted != null && typeof isSorted != 'boolean') {
11320       thisArg = iteratee;
11321       iteratee = isIterateeCall(array, isSorted, thisArg) ? null : isSorted;
11322       isSorted = false;
11323     }
11324     var callback = getCallback();
11325     if (!(iteratee == null && callback === baseCallback)) {
11326       iteratee = callback(iteratee, thisArg, 3);
11327     }
11328     return (isSorted && getIndexOf() == baseIndexOf)
11329       ? sortedUniq(array, iteratee)
11330       : baseUniq(array, iteratee);
11331   }
11332
11333   /**
11334    * Creates an array excluding all provided values using
11335    * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11336    * for equality comparisons.
11337    *
11338    * @static
11339    * @memberOf _
11340    * @category Array
11341    * @param {Array} array The array to filter.
11342    * @param {...*} [values] The values to exclude.
11343    * @returns {Array} Returns the new array of filtered values.
11344    * @example
11345    *
11346    * _.without([1, 2, 1, 3], 1, 2);
11347    * // => [3]
11348    */
11349   var without = restParam(function(array, values) {
11350     return isArrayLike(array)
11351       ? baseDifference(array, values)
11352       : [];
11353   });
11354
11355   /*------------------------------------------------------------------------*/
11356
11357   /**
11358    * Creates a `lodash` object that wraps `value` with explicit method
11359    * chaining enabled.
11360    *
11361    * @static
11362    * @memberOf _
11363    * @category Chain
11364    * @param {*} value The value to wrap.
11365    * @returns {Object} Returns the new `lodash` wrapper instance.
11366    * @example
11367    *
11368    * var users = [
11369    *   { 'user': 'barney',  'age': 36 },
11370    *   { 'user': 'fred',    'age': 40 },
11371    *   { 'user': 'pebbles', 'age': 1 }
11372    * ];
11373    *
11374    * var youngest = _.chain(users)
11375    *   .sortBy('age')
11376    *   .map(function(chr) {
11377    *     return chr.user + ' is ' + chr.age;
11378    *   })
11379    *   .first()
11380    *   .value();
11381    * // => 'pebbles is 1'
11382    */
11383   function chain(value) {
11384     var result = lodash(value);
11385     result.__chain__ = true;
11386     return result;
11387   }
11388
11389   /**
11390    * This method invokes `interceptor` and returns `value`. The interceptor is
11391    * bound to `thisArg` and invoked with one argument; (value). The purpose of
11392    * this method is to "tap into" a method chain in order to perform operations
11393    * on intermediate results within the chain.
11394    *
11395    * @static
11396    * @memberOf _
11397    * @category Chain
11398    * @param {*} value The value to provide to `interceptor`.
11399    * @param {Function} interceptor The function to invoke.
11400    * @param {*} [thisArg] The `this` binding of `interceptor`.
11401    * @returns {*} Returns `value`.
11402    * @example
11403    *
11404    * _([1, 2, 3])
11405    *  .tap(function(array) {
11406    *    array.pop();
11407    *  })
11408    *  .reverse()
11409    *  .value();
11410    * // => [2, 1]
11411    */
11412   function tap(value, interceptor, thisArg) {
11413     interceptor.call(thisArg, value);
11414     return value;
11415   }
11416
11417   /**
11418    * This method is like `_.tap` except that it returns the result of `interceptor`.
11419    *
11420    * @static
11421    * @memberOf _
11422    * @category Chain
11423    * @param {*} value The value to provide to `interceptor`.
11424    * @param {Function} interceptor The function to invoke.
11425    * @param {*} [thisArg] The `this` binding of `interceptor`.
11426    * @returns {*} Returns the result of `interceptor`.
11427    * @example
11428    *
11429    * _('  abc  ')
11430    *  .chain()
11431    *  .trim()
11432    *  .thru(function(value) {
11433    *    return [value];
11434    *  })
11435    *  .value();
11436    * // => ['abc']
11437    */
11438   function thru(value, interceptor, thisArg) {
11439     return interceptor.call(thisArg, value);
11440   }
11441
11442   /**
11443    * Enables explicit method chaining on the wrapper object.
11444    *
11445    * @name chain
11446    * @memberOf _
11447    * @category Chain
11448    * @returns {Object} Returns the new `lodash` wrapper instance.
11449    * @example
11450    *
11451    * var users = [
11452    *   { 'user': 'barney', 'age': 36 },
11453    *   { 'user': 'fred',   'age': 40 }
11454    * ];
11455    *
11456    * // without explicit chaining
11457    * _(users).first();
11458    * // => { 'user': 'barney', 'age': 36 }
11459    *
11460    * // with explicit chaining
11461    * _(users).chain()
11462    *   .first()
11463    *   .pick('user')
11464    *   .value();
11465    * // => { 'user': 'barney' }
11466    */
11467   function wrapperChain() {
11468     return chain(this);
11469   }
11470
11471   /**
11472    * Executes the chained sequence and returns the wrapped result.
11473    *
11474    * @name commit
11475    * @memberOf _
11476    * @category Chain
11477    * @returns {Object} Returns the new `lodash` wrapper instance.
11478    * @example
11479    *
11480    * var array = [1, 2];
11481    * var wrapper = _(array).push(3);
11482    *
11483    * console.log(array);
11484    * // => [1, 2]
11485    *
11486    * wrapper = wrapper.commit();
11487    * console.log(array);
11488    * // => [1, 2, 3]
11489    *
11490    * wrapper.last();
11491    * // => 3
11492    *
11493    * console.log(array);
11494    * // => [1, 2, 3]
11495    */
11496   function wrapperCommit() {
11497     return new LodashWrapper(this.value(), this.__chain__);
11498   }
11499
11500   /**
11501    * Creates a clone of the chained sequence planting `value` as the wrapped value.
11502    *
11503    * @name plant
11504    * @memberOf _
11505    * @category Chain
11506    * @returns {Object} Returns the new `lodash` wrapper instance.
11507    * @example
11508    *
11509    * var array = [1, 2];
11510    * var wrapper = _(array).map(function(value) {
11511    *   return Math.pow(value, 2);
11512    * });
11513    *
11514    * var other = [3, 4];
11515    * var otherWrapper = wrapper.plant(other);
11516    *
11517    * otherWrapper.value();
11518    * // => [9, 16]
11519    *
11520    * wrapper.value();
11521    * // => [1, 4]
11522    */
11523   function wrapperPlant(value) {
11524     var result,
11525         parent = this;
11526
11527     while (parent instanceof baseLodash) {
11528       var clone = wrapperClone(parent);
11529       if (result) {
11530         previous.__wrapped__ = clone;
11531       } else {
11532         result = clone;
11533       }
11534       var previous = clone;
11535       parent = parent.__wrapped__;
11536     }
11537     previous.__wrapped__ = value;
11538     return result;
11539   }
11540
11541   /**
11542    * Reverses the wrapped array so the first element becomes the last, the
11543    * second element becomes the second to last, and so on.
11544    *
11545    * **Note:** This method mutates the wrapped array.
11546    *
11547    * @name reverse
11548    * @memberOf _
11549    * @category Chain
11550    * @returns {Object} Returns the new reversed `lodash` wrapper instance.
11551    * @example
11552    *
11553    * var array = [1, 2, 3];
11554    *
11555    * _(array).reverse().value()
11556    * // => [3, 2, 1]
11557    *
11558    * console.log(array);
11559    * // => [3, 2, 1]
11560    */
11561   function wrapperReverse() {
11562     var value = this.__wrapped__;
11563     if (value instanceof LazyWrapper) {
11564       if (this.__actions__.length) {
11565         value = new LazyWrapper(this);
11566       }
11567       return new LodashWrapper(value.reverse(), this.__chain__);
11568     }
11569     return this.thru(function(value) {
11570       return value.reverse();
11571     });
11572   }
11573
11574   /**
11575    * Produces the result of coercing the unwrapped value to a string.
11576    *
11577    * @name toString
11578    * @memberOf _
11579    * @category Chain
11580    * @returns {string} Returns the coerced string value.
11581    * @example
11582    *
11583    * _([1, 2, 3]).toString();
11584    * // => '1,2,3'
11585    */
11586   function wrapperToString() {
11587     return (this.value() + '');
11588   }
11589
11590   /**
11591    * Executes the chained sequence to extract the unwrapped value.
11592    *
11593    * @name value
11594    * @memberOf _
11595    * @alias run, toJSON, valueOf
11596    * @category Chain
11597    * @returns {*} Returns the resolved unwrapped value.
11598    * @example
11599    *
11600    * _([1, 2, 3]).value();
11601    * // => [1, 2, 3]
11602    */
11603   function wrapperValue() {
11604     return baseWrapperValue(this.__wrapped__, this.__actions__);
11605   }
11606
11607   /*------------------------------------------------------------------------*/
11608
11609   /**
11610    * Checks if `predicate` returns truthy for **all** elements of `collection`.
11611    * The predicate is bound to `thisArg` and invoked with three arguments:
11612    * (value, index|key, collection).
11613    *
11614    * If a property name is provided for `predicate` the created `_.property`
11615    * style callback returns the property value of the given element.
11616    *
11617    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11618    * style callback returns `true` for elements that have a matching property
11619    * value, else `false`.
11620    *
11621    * If an object is provided for `predicate` the created `_.matches` style
11622    * callback returns `true` for elements that have the properties of the given
11623    * object, else `false`.
11624    *
11625    * @static
11626    * @memberOf _
11627    * @alias all
11628    * @category Collection
11629    * @param {Array|Object|string} collection The collection to iterate over.
11630    * @param {Function|Object|string} [predicate=_.identity] The function invoked
11631    *  per iteration.
11632    * @param {*} [thisArg] The `this` binding of `predicate`.
11633    * @returns {boolean} Returns `true` if all elements pass the predicate check,
11634    *  else `false`.
11635    * @example
11636    *
11637    * _.every([true, 1, null, 'yes'], Boolean);
11638    * // => false
11639    *
11640    * var users = [
11641    *   { 'user': 'barney', 'active': false },
11642    *   { 'user': 'fred',   'active': false }
11643    * ];
11644    *
11645    * // using the `_.matches` callback shorthand
11646    * _.every(users, { 'user': 'barney', 'active': false });
11647    * // => false
11648    *
11649    * // using the `_.matchesProperty` callback shorthand
11650    * _.every(users, 'active', false);
11651    * // => true
11652    *
11653    * // using the `_.property` callback shorthand
11654    * _.every(users, 'active');
11655    * // => false
11656    */
11657   function every(collection, predicate, thisArg) {
11658     var func = isArray(collection) ? arrayEvery : baseEvery;
11659     if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
11660       predicate = null;
11661     }
11662     if (typeof predicate != 'function' || thisArg !== undefined) {
11663       predicate = getCallback(predicate, thisArg, 3);
11664     }
11665     return func(collection, predicate);
11666   }
11667
11668   /**
11669    * Iterates over elements of `collection`, returning an array of all elements
11670    * `predicate` returns truthy for. The predicate is bound to `thisArg` and
11671    * invoked with three arguments: (value, index|key, collection).
11672    *
11673    * If a property name is provided for `predicate` the created `_.property`
11674    * style callback returns the property value of the given element.
11675    *
11676    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11677    * style callback returns `true` for elements that have a matching property
11678    * value, else `false`.
11679    *
11680    * If an object is provided for `predicate` the created `_.matches` style
11681    * callback returns `true` for elements that have the properties of the given
11682    * object, else `false`.
11683    *
11684    * @static
11685    * @memberOf _
11686    * @alias select
11687    * @category Collection
11688    * @param {Array|Object|string} collection The collection to iterate over.
11689    * @param {Function|Object|string} [predicate=_.identity] The function invoked
11690    *  per iteration.
11691    * @param {*} [thisArg] The `this` binding of `predicate`.
11692    * @returns {Array} Returns the new filtered array.
11693    * @example
11694    *
11695    * _.filter([4, 5, 6], function(n) {
11696    *   return n % 2 == 0;
11697    * });
11698    * // => [4, 6]
11699    *
11700    * var users = [
11701    *   { 'user': 'barney', 'age': 36, 'active': true },
11702    *   { 'user': 'fred',   'age': 40, 'active': false }
11703    * ];
11704    *
11705    * // using the `_.matches` callback shorthand
11706    * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user');
11707    * // => ['barney']
11708    *
11709    * // using the `_.matchesProperty` callback shorthand
11710    * _.pluck(_.filter(users, 'active', false), 'user');
11711    * // => ['fred']
11712    *
11713    * // using the `_.property` callback shorthand
11714    * _.pluck(_.filter(users, 'active'), 'user');
11715    * // => ['barney']
11716    */
11717   function filter(collection, predicate, thisArg) {
11718     var func = isArray(collection) ? arrayFilter : baseFilter;
11719     predicate = getCallback(predicate, thisArg, 3);
11720     return func(collection, predicate);
11721   }
11722
11723   /**
11724    * Iterates over elements of `collection`, returning the first element
11725    * `predicate` returns truthy for. The predicate is bound to `thisArg` and
11726    * invoked with three arguments: (value, index|key, collection).
11727    *
11728    * If a property name is provided for `predicate` the created `_.property`
11729    * style callback returns the property value of the given element.
11730    *
11731    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11732    * style callback returns `true` for elements that have a matching property
11733    * value, else `false`.
11734    *
11735    * If an object is provided for `predicate` the created `_.matches` style
11736    * callback returns `true` for elements that have the properties of the given
11737    * object, else `false`.
11738    *
11739    * @static
11740    * @memberOf _
11741    * @alias detect
11742    * @category Collection
11743    * @param {Array|Object|string} collection The collection to search.
11744    * @param {Function|Object|string} [predicate=_.identity] The function invoked
11745    *  per iteration.
11746    * @param {*} [thisArg] The `this` binding of `predicate`.
11747    * @returns {*} Returns the matched element, else `undefined`.
11748    * @example
11749    *
11750    * var users = [
11751    *   { 'user': 'barney',  'age': 36, 'active': true },
11752    *   { 'user': 'fred',    'age': 40, 'active': false },
11753    *   { 'user': 'pebbles', 'age': 1,  'active': true }
11754    * ];
11755    *
11756    * _.result(_.find(users, function(chr) {
11757    *   return chr.age < 40;
11758    * }), 'user');
11759    * // => 'barney'
11760    *
11761    * // using the `_.matches` callback shorthand
11762    * _.result(_.find(users, { 'age': 1, 'active': true }), 'user');
11763    * // => 'pebbles'
11764    *
11765    * // using the `_.matchesProperty` callback shorthand
11766    * _.result(_.find(users, 'active', false), 'user');
11767    * // => 'fred'
11768    *
11769    * // using the `_.property` callback shorthand
11770    * _.result(_.find(users, 'active'), 'user');
11771    * // => 'barney'
11772    */
11773   var find = createFind(baseEach);
11774
11775   /**
11776    * Iterates over elements of `collection` invoking `iteratee` for each element.
11777    * The `iteratee` is bound to `thisArg` and invoked with three arguments:
11778    * (value, index|key, collection). Iteratee functions may exit iteration early
11779    * by explicitly returning `false`.
11780    *
11781    * **Note:** As with other "Collections" methods, objects with a "length" property
11782    * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
11783    * may be used for object iteration.
11784    *
11785    * @static
11786    * @memberOf _
11787    * @alias each
11788    * @category Collection
11789    * @param {Array|Object|string} collection The collection to iterate over.
11790    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
11791    * @param {*} [thisArg] The `this` binding of `iteratee`.
11792    * @returns {Array|Object|string} Returns `collection`.
11793    * @example
11794    *
11795    * _([1, 2]).forEach(function(n) {
11796    *   console.log(n);
11797    * }).value();
11798    * // => logs each value from left to right and returns the array
11799    *
11800    * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) {
11801    *   console.log(n, key);
11802    * });
11803    * // => logs each value-key pair and returns the object (iteration order is not guaranteed)
11804    */
11805   var forEach = createForEach(arrayEach, baseEach);
11806
11807   /**
11808    * Creates an object composed of keys generated from the results of running
11809    * each element of `collection` through `iteratee`. The corresponding value
11810    * of each key is an array of the elements responsible for generating the key.
11811    * The `iteratee` is bound to `thisArg` and invoked with three arguments:
11812    * (value, index|key, collection).
11813    *
11814    * If a property name is provided for `iteratee` the created `_.property`
11815    * style callback returns the property value of the given element.
11816    *
11817    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11818    * style callback returns `true` for elements that have a matching property
11819    * value, else `false`.
11820    *
11821    * If an object is provided for `iteratee` the created `_.matches` style
11822    * callback returns `true` for elements that have the properties of the given
11823    * object, else `false`.
11824    *
11825    * @static
11826    * @memberOf _
11827    * @category Collection
11828    * @param {Array|Object|string} collection The collection to iterate over.
11829    * @param {Function|Object|string} [iteratee=_.identity] The function invoked
11830    *  per iteration.
11831    * @param {*} [thisArg] The `this` binding of `iteratee`.
11832    * @returns {Object} Returns the composed aggregate object.
11833    * @example
11834    *
11835    * _.groupBy([4.2, 6.1, 6.4], function(n) {
11836    *   return Math.floor(n);
11837    * });
11838    * // => { '4': [4.2], '6': [6.1, 6.4] }
11839    *
11840    * _.groupBy([4.2, 6.1, 6.4], function(n) {
11841    *   return this.floor(n);
11842    * }, Math);
11843    * // => { '4': [4.2], '6': [6.1, 6.4] }
11844    *
11845    * // using the `_.property` callback shorthand
11846    * _.groupBy(['one', 'two', 'three'], 'length');
11847    * // => { '3': ['one', 'two'], '5': ['three'] }
11848    */
11849   var groupBy = createAggregator(function(result, value, key) {
11850     if (hasOwnProperty.call(result, key)) {
11851       result[key].push(value);
11852     } else {
11853       result[key] = [value];
11854     }
11855   });
11856
11857   /**
11858    * Checks if `value` is in `collection` using
11859    * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11860    * for equality comparisons. If `fromIndex` is negative, it is used as the offset
11861    * from the end of `collection`.
11862    *
11863    * @static
11864    * @memberOf _
11865    * @alias contains, include
11866    * @category Collection
11867    * @param {Array|Object|string} collection The collection to search.
11868    * @param {*} target The value to search for.
11869    * @param {number} [fromIndex=0] The index to search from.
11870    * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`.
11871    * @returns {boolean} Returns `true` if a matching element is found, else `false`.
11872    * @example
11873    *
11874    * _.includes([1, 2, 3], 1);
11875    * // => true
11876    *
11877    * _.includes([1, 2, 3], 1, 2);
11878    * // => false
11879    *
11880    * _.includes({ 'user': 'fred', 'age': 40 }, 'fred');
11881    * // => true
11882    *
11883    * _.includes('pebbles', 'eb');
11884    * // => true
11885    */
11886   function includes(collection, target, fromIndex, guard) {
11887     var length = collection ? getLength(collection) : 0;
11888     if (!isLength(length)) {
11889       collection = values(collection);
11890       length = collection.length;
11891     }
11892     if (!length) {
11893       return false;
11894     }
11895     if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) {
11896       fromIndex = 0;
11897     } else {
11898       fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
11899     }
11900     return (typeof collection == 'string' || !isArray(collection) && isString(collection))
11901       ? (fromIndex < length && collection.indexOf(target, fromIndex) > -1)
11902       : (getIndexOf(collection, target, fromIndex) > -1);
11903   }
11904
11905   /**
11906    * Creates an array of values by running each element in `collection` through
11907    * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three
11908    * arguments: (value, index|key, collection).
11909    *
11910    * If a property name is provided for `iteratee` the created `_.property`
11911    * style callback returns the property value of the given element.
11912    *
11913    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11914    * style callback returns `true` for elements that have a matching property
11915    * value, else `false`.
11916    *
11917    * If an object is provided for `iteratee` the created `_.matches` style
11918    * callback returns `true` for elements that have the properties of the given
11919    * object, else `false`.
11920    *
11921    * Many lodash methods are guarded to work as iteratees for methods like
11922    * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
11923    *
11924    * The guarded methods are:
11925    * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`,
11926    * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`,
11927    * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`,
11928    * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`,
11929    * `sum`, `uniq`, and `words`
11930    *
11931    * @static
11932    * @memberOf _
11933    * @alias collect
11934    * @category Collection
11935    * @param {Array|Object|string} collection The collection to iterate over.
11936    * @param {Function|Object|string} [iteratee=_.identity] The function invoked
11937    *  per iteration.
11938    * @param {*} [thisArg] The `this` binding of `iteratee`.
11939    * @returns {Array} Returns the new mapped array.
11940    * @example
11941    *
11942    * function timesThree(n) {
11943    *   return n * 3;
11944    * }
11945    *
11946    * _.map([1, 2], timesThree);
11947    * // => [3, 6]
11948    *
11949    * _.map({ 'a': 1, 'b': 2 }, timesThree);
11950    * // => [3, 6] (iteration order is not guaranteed)
11951    *
11952    * var users = [
11953    *   { 'user': 'barney' },
11954    *   { 'user': 'fred' }
11955    * ];
11956    *
11957    * // using the `_.property` callback shorthand
11958    * _.map(users, 'user');
11959    * // => ['barney', 'fred']
11960    */
11961   function map(collection, iteratee, thisArg) {
11962     var func = isArray(collection) ? arrayMap : baseMap;
11963     iteratee = getCallback(iteratee, thisArg, 3);
11964     return func(collection, iteratee);
11965   }
11966
11967   /**
11968    * Gets the property value of `path` from all elements in `collection`.
11969    *
11970    * @static
11971    * @memberOf _
11972    * @category Collection
11973    * @param {Array|Object|string} collection The collection to iterate over.
11974    * @param {Array|string} path The path of the property to pluck.
11975    * @returns {Array} Returns the property values.
11976    * @example
11977    *
11978    * var users = [
11979    *   { 'user': 'barney', 'age': 36 },
11980    *   { 'user': 'fred',   'age': 40 }
11981    * ];
11982    *
11983    * _.pluck(users, 'user');
11984    * // => ['barney', 'fred']
11985    *
11986    * var userIndex = _.indexBy(users, 'user');
11987    * _.pluck(userIndex, 'age');
11988    * // => [36, 40] (iteration order is not guaranteed)
11989    */
11990   function pluck(collection, path) {
11991     return map(collection, property(path));
11992   }
11993
11994   /**
11995    * Reduces `collection` to a value which is the accumulated result of running
11996    * each element in `collection` through `iteratee`, where each successive
11997    * invocation is supplied the return value of the previous. If `accumulator`
11998    * is not provided the first element of `collection` is used as the initial
11999    * value. The `iteratee` is bound to `thisArg` and invoked with four arguments:
12000    * (accumulator, value, index|key, collection).
12001    *
12002    * Many lodash methods are guarded to work as iteratees for methods like
12003    * `_.reduce`, `_.reduceRight`, and `_.transform`.
12004    *
12005    * The guarded methods are:
12006    * `assign`, `defaults`, `includes`, `merge`, `sortByAll`, and `sortByOrder`
12007    *
12008    * @static
12009    * @memberOf _
12010    * @alias foldl, inject
12011    * @category Collection
12012    * @param {Array|Object|string} collection The collection to iterate over.
12013    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12014    * @param {*} [accumulator] The initial value.
12015    * @param {*} [thisArg] The `this` binding of `iteratee`.
12016    * @returns {*} Returns the accumulated value.
12017    * @example
12018    *
12019    * _.reduce([1, 2], function(total, n) {
12020    *   return total + n;
12021    * });
12022    * // => 3
12023    *
12024    * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) {
12025    *   result[key] = n * 3;
12026    *   return result;
12027    * }, {});
12028    * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed)
12029    */
12030   var reduce = createReduce(arrayReduce, baseEach);
12031
12032   /**
12033    * The opposite of `_.filter`; this method returns the elements of `collection`
12034    * that `predicate` does **not** return truthy for.
12035    *
12036    * @static
12037    * @memberOf _
12038    * @category Collection
12039    * @param {Array|Object|string} collection The collection to iterate over.
12040    * @param {Function|Object|string} [predicate=_.identity] The function invoked
12041    *  per iteration.
12042    * @param {*} [thisArg] The `this` binding of `predicate`.
12043    * @returns {Array} Returns the new filtered array.
12044    * @example
12045    *
12046    * _.reject([1, 2, 3, 4], function(n) {
12047    *   return n % 2 == 0;
12048    * });
12049    * // => [1, 3]
12050    *
12051    * var users = [
12052    *   { 'user': 'barney', 'age': 36, 'active': false },
12053    *   { 'user': 'fred',   'age': 40, 'active': true }
12054    * ];
12055    *
12056    * // using the `_.matches` callback shorthand
12057    * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user');
12058    * // => ['barney']
12059    *
12060    * // using the `_.matchesProperty` callback shorthand
12061    * _.pluck(_.reject(users, 'active', false), 'user');
12062    * // => ['fred']
12063    *
12064    * // using the `_.property` callback shorthand
12065    * _.pluck(_.reject(users, 'active'), 'user');
12066    * // => ['barney']
12067    */
12068   function reject(collection, predicate, thisArg) {
12069     var func = isArray(collection) ? arrayFilter : baseFilter;
12070     predicate = getCallback(predicate, thisArg, 3);
12071     return func(collection, function(value, index, collection) {
12072       return !predicate(value, index, collection);
12073     });
12074   }
12075
12076   /**
12077    * Checks if `predicate` returns truthy for **any** element of `collection`.
12078    * The function returns as soon as it finds a passing value and does not iterate
12079    * over the entire collection. The predicate is bound to `thisArg` and invoked
12080    * with three arguments: (value, index|key, collection).
12081    *
12082    * If a property name is provided for `predicate` the created `_.property`
12083    * style callback returns the property value of the given element.
12084    *
12085    * If a value is also provided for `thisArg` the created `_.matchesProperty`
12086    * style callback returns `true` for elements that have a matching property
12087    * value, else `false`.
12088    *
12089    * If an object is provided for `predicate` the created `_.matches` style
12090    * callback returns `true` for elements that have the properties of the given
12091    * object, else `false`.
12092    *
12093    * @static
12094    * @memberOf _
12095    * @alias any
12096    * @category Collection
12097    * @param {Array|Object|string} collection The collection to iterate over.
12098    * @param {Function|Object|string} [predicate=_.identity] The function invoked
12099    *  per iteration.
12100    * @param {*} [thisArg] The `this` binding of `predicate`.
12101    * @returns {boolean} Returns `true` if any element passes the predicate check,
12102    *  else `false`.
12103    * @example
12104    *
12105    * _.some([null, 0, 'yes', false], Boolean);
12106    * // => true
12107    *
12108    * var users = [
12109    *   { 'user': 'barney', 'active': true },
12110    *   { 'user': 'fred',   'active': false }
12111    * ];
12112    *
12113    * // using the `_.matches` callback shorthand
12114    * _.some(users, { 'user': 'barney', 'active': false });
12115    * // => false
12116    *
12117    * // using the `_.matchesProperty` callback shorthand
12118    * _.some(users, 'active', false);
12119    * // => true
12120    *
12121    * // using the `_.property` callback shorthand
12122    * _.some(users, 'active');
12123    * // => true
12124    */
12125   function some(collection, predicate, thisArg) {
12126     var func = isArray(collection) ? arraySome : baseSome;
12127     if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
12128       predicate = null;
12129     }
12130     if (typeof predicate != 'function' || thisArg !== undefined) {
12131       predicate = getCallback(predicate, thisArg, 3);
12132     }
12133     return func(collection, predicate);
12134   }
12135
12136   /*------------------------------------------------------------------------*/
12137
12138   /**
12139    * Gets the number of milliseconds that have elapsed since the Unix epoch
12140    * (1 January 1970 00:00:00 UTC).
12141    *
12142    * @static
12143    * @memberOf _
12144    * @category Date
12145    * @example
12146    *
12147    * _.defer(function(stamp) {
12148    *   console.log(_.now() - stamp);
12149    * }, _.now());
12150    * // => logs the number of milliseconds it took for the deferred function to be invoked
12151    */
12152   var now = nativeNow || function() {
12153     return new Date().getTime();
12154   };
12155
12156   /*------------------------------------------------------------------------*/
12157
12158   /**
12159    * Creates a function that invokes `func` with the `this` binding of `thisArg`
12160    * and prepends any additional `_.bind` arguments to those provided to the
12161    * bound function.
12162    *
12163    * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
12164    * may be used as a placeholder for partially applied arguments.
12165    *
12166    * **Note:** Unlike native `Function#bind` this method does not set the "length"
12167    * property of bound functions.
12168    *
12169    * @static
12170    * @memberOf _
12171    * @category Function
12172    * @param {Function} func The function to bind.
12173    * @param {*} thisArg The `this` binding of `func`.
12174    * @param {...*} [partials] The arguments to be partially applied.
12175    * @returns {Function} Returns the new bound function.
12176    * @example
12177    *
12178    * var greet = function(greeting, punctuation) {
12179    *   return greeting + ' ' + this.user + punctuation;
12180    * };
12181    *
12182    * var object = { 'user': 'fred' };
12183    *
12184    * var bound = _.bind(greet, object, 'hi');
12185    * bound('!');
12186    * // => 'hi fred!'
12187    *
12188    * // using placeholders
12189    * var bound = _.bind(greet, object, _, '!');
12190    * bound('hi');
12191    * // => 'hi fred!'
12192    */
12193   var bind = restParam(function(func, thisArg, partials) {
12194     var bitmask = BIND_FLAG;
12195     if (partials.length) {
12196       var holders = replaceHolders(partials, bind.placeholder);
12197       bitmask |= PARTIAL_FLAG;
12198     }
12199     return createWrapper(func, bitmask, thisArg, partials, holders);
12200   });
12201
12202   /**
12203    * Creates a debounced function that delays invoking `func` until after `wait`
12204    * milliseconds have elapsed since the last time the debounced function was
12205    * invoked. The debounced function comes with a `cancel` method to cancel
12206    * delayed invocations. Provide an options object to indicate that `func`
12207    * should be invoked on the leading and/or trailing edge of the `wait` timeout.
12208    * Subsequent calls to the debounced function return the result of the last
12209    * `func` invocation.
12210    *
12211    * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
12212    * on the trailing edge of the timeout only if the the debounced function is
12213    * invoked more than once during the `wait` timeout.
12214    *
12215    * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
12216    * for details over the differences between `_.debounce` and `_.throttle`.
12217    *
12218    * @static
12219    * @memberOf _
12220    * @category Function
12221    * @param {Function} func The function to debounce.
12222    * @param {number} [wait=0] The number of milliseconds to delay.
12223    * @param {Object} [options] The options object.
12224    * @param {boolean} [options.leading=false] Specify invoking on the leading
12225    *  edge of the timeout.
12226    * @param {number} [options.maxWait] The maximum time `func` is allowed to be
12227    *  delayed before it is invoked.
12228    * @param {boolean} [options.trailing=true] Specify invoking on the trailing
12229    *  edge of the timeout.
12230    * @returns {Function} Returns the new debounced function.
12231    * @example
12232    *
12233    * // avoid costly calculations while the window size is in flux
12234    * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
12235    *
12236    * // invoke `sendMail` when the click event is fired, debouncing subsequent calls
12237    * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
12238    *   'leading': true,
12239    *   'trailing': false
12240    * }));
12241    *
12242    * // ensure `batchLog` is invoked once after 1 second of debounced calls
12243    * var source = new EventSource('/stream');
12244    * jQuery(source).on('message', _.debounce(batchLog, 250, {
12245    *   'maxWait': 1000
12246    * }));
12247    *
12248    * // cancel a debounced call
12249    * var todoChanges = _.debounce(batchLog, 1000);
12250    * Object.observe(models.todo, todoChanges);
12251    *
12252    * Object.observe(models, function(changes) {
12253    *   if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
12254    *     todoChanges.cancel();
12255    *   }
12256    * }, ['delete']);
12257    *
12258    * // ...at some point `models.todo` is changed
12259    * models.todo.completed = true;
12260    *
12261    * // ...before 1 second has passed `models.todo` is deleted
12262    * // which cancels the debounced `todoChanges` call
12263    * delete models.todo;
12264    */
12265   function debounce(func, wait, options) {
12266     var args,
12267         maxTimeoutId,
12268         result,
12269         stamp,
12270         thisArg,
12271         timeoutId,
12272         trailingCall,
12273         lastCalled = 0,
12274         maxWait = false,
12275         trailing = true;
12276
12277     if (typeof func != 'function') {
12278       throw new TypeError(FUNC_ERROR_TEXT);
12279     }
12280     wait = wait < 0 ? 0 : (+wait || 0);
12281     if (options === true) {
12282       var leading = true;
12283       trailing = false;
12284     } else if (isObject(options)) {
12285       leading = options.leading;
12286       maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
12287       trailing = 'trailing' in options ? options.trailing : trailing;
12288     }
12289
12290     function cancel() {
12291       if (timeoutId) {
12292         clearTimeout(timeoutId);
12293       }
12294       if (maxTimeoutId) {
12295         clearTimeout(maxTimeoutId);
12296       }
12297       maxTimeoutId = timeoutId = trailingCall = undefined;
12298     }
12299
12300     function delayed() {
12301       var remaining = wait - (now() - stamp);
12302       if (remaining <= 0 || remaining > wait) {
12303         if (maxTimeoutId) {
12304           clearTimeout(maxTimeoutId);
12305         }
12306         var isCalled = trailingCall;
12307         maxTimeoutId = timeoutId = trailingCall = undefined;
12308         if (isCalled) {
12309           lastCalled = now();
12310           result = func.apply(thisArg, args);
12311           if (!timeoutId && !maxTimeoutId) {
12312             args = thisArg = null;
12313           }
12314         }
12315       } else {
12316         timeoutId = setTimeout(delayed, remaining);
12317       }
12318     }
12319
12320     function maxDelayed() {
12321       if (timeoutId) {
12322         clearTimeout(timeoutId);
12323       }
12324       maxTimeoutId = timeoutId = trailingCall = undefined;
12325       if (trailing || (maxWait !== wait)) {
12326         lastCalled = now();
12327         result = func.apply(thisArg, args);
12328         if (!timeoutId && !maxTimeoutId) {
12329           args = thisArg = null;
12330         }
12331       }
12332     }
12333
12334     function debounced() {
12335       args = arguments;
12336       stamp = now();
12337       thisArg = this;
12338       trailingCall = trailing && (timeoutId || !leading);
12339
12340       if (maxWait === false) {
12341         var leadingCall = leading && !timeoutId;
12342       } else {
12343         if (!maxTimeoutId && !leading) {
12344           lastCalled = stamp;
12345         }
12346         var remaining = maxWait - (stamp - lastCalled),
12347             isCalled = remaining <= 0 || remaining > maxWait;
12348
12349         if (isCalled) {
12350           if (maxTimeoutId) {
12351             maxTimeoutId = clearTimeout(maxTimeoutId);
12352           }
12353           lastCalled = stamp;
12354           result = func.apply(thisArg, args);
12355         }
12356         else if (!maxTimeoutId) {
12357           maxTimeoutId = setTimeout(maxDelayed, remaining);
12358         }
12359       }
12360       if (isCalled && timeoutId) {
12361         timeoutId = clearTimeout(timeoutId);
12362       }
12363       else if (!timeoutId && wait !== maxWait) {
12364         timeoutId = setTimeout(delayed, wait);
12365       }
12366       if (leadingCall) {
12367         isCalled = true;
12368         result = func.apply(thisArg, args);
12369       }
12370       if (isCalled && !timeoutId && !maxTimeoutId) {
12371         args = thisArg = null;
12372       }
12373       return result;
12374     }
12375     debounced.cancel = cancel;
12376     return debounced;
12377   }
12378
12379   /**
12380    * Creates a function that invokes `func` with the `this` binding of the
12381    * created function and arguments from `start` and beyond provided as an array.
12382    *
12383    * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
12384    *
12385    * @static
12386    * @memberOf _
12387    * @category Function
12388    * @param {Function} func The function to apply a rest parameter to.
12389    * @param {number} [start=func.length-1] The start position of the rest parameter.
12390    * @returns {Function} Returns the new function.
12391    * @example
12392    *
12393    * var say = _.restParam(function(what, names) {
12394    *   return what + ' ' + _.initial(names).join(', ') +
12395    *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
12396    * });
12397    *
12398    * say('hello', 'fred', 'barney', 'pebbles');
12399    * // => 'hello fred, barney, & pebbles'
12400    */
12401   function restParam(func, start) {
12402     if (typeof func != 'function') {
12403       throw new TypeError(FUNC_ERROR_TEXT);
12404     }
12405     start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0);
12406     return function() {
12407       var args = arguments,
12408           index = -1,
12409           length = nativeMax(args.length - start, 0),
12410           rest = Array(length);
12411
12412       while (++index < length) {
12413         rest[index] = args[start + index];
12414       }
12415       switch (start) {
12416         case 0: return func.call(this, rest);
12417         case 1: return func.call(this, args[0], rest);
12418         case 2: return func.call(this, args[0], args[1], rest);
12419       }
12420       var otherArgs = Array(start + 1);
12421       index = -1;
12422       while (++index < start) {
12423         otherArgs[index] = args[index];
12424       }
12425       otherArgs[start] = rest;
12426       return func.apply(this, otherArgs);
12427     };
12428   }
12429
12430   /**
12431    * Creates a throttled function that only invokes `func` at most once per
12432    * every `wait` milliseconds. The throttled function comes with a `cancel`
12433    * method to cancel delayed invocations. Provide an options object to indicate
12434    * that `func` should be invoked on the leading and/or trailing edge of the
12435    * `wait` timeout. Subsequent calls to the throttled function return the
12436    * result of the last `func` call.
12437    *
12438    * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
12439    * on the trailing edge of the timeout only if the the throttled function is
12440    * invoked more than once during the `wait` timeout.
12441    *
12442    * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
12443    * for details over the differences between `_.throttle` and `_.debounce`.
12444    *
12445    * @static
12446    * @memberOf _
12447    * @category Function
12448    * @param {Function} func The function to throttle.
12449    * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
12450    * @param {Object} [options] The options object.
12451    * @param {boolean} [options.leading=true] Specify invoking on the leading
12452    *  edge of the timeout.
12453    * @param {boolean} [options.trailing=true] Specify invoking on the trailing
12454    *  edge of the timeout.
12455    * @returns {Function} Returns the new throttled function.
12456    * @example
12457    *
12458    * // avoid excessively updating the position while scrolling
12459    * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
12460    *
12461    * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes
12462    * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
12463    *   'trailing': false
12464    * }));
12465    *
12466    * // cancel a trailing throttled call
12467    * jQuery(window).on('popstate', throttled.cancel);
12468    */
12469   function throttle(func, wait, options) {
12470     var leading = true,
12471         trailing = true;
12472
12473     if (typeof func != 'function') {
12474       throw new TypeError(FUNC_ERROR_TEXT);
12475     }
12476     if (options === false) {
12477       leading = false;
12478     } else if (isObject(options)) {
12479       leading = 'leading' in options ? !!options.leading : leading;
12480       trailing = 'trailing' in options ? !!options.trailing : trailing;
12481     }
12482     debounceOptions.leading = leading;
12483     debounceOptions.maxWait = +wait;
12484     debounceOptions.trailing = trailing;
12485     return debounce(func, wait, debounceOptions);
12486   }
12487
12488   /*------------------------------------------------------------------------*/
12489
12490   /**
12491    * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned,
12492    * otherwise they are assigned by reference. If `customizer` is provided it is
12493    * invoked to produce the cloned values. If `customizer` returns `undefined`
12494    * cloning is handled by the method instead. The `customizer` is bound to
12495    * `thisArg` and invoked with two argument; (value [, index|key, object]).
12496    *
12497    * **Note:** This method is loosely based on the
12498    * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
12499    * The enumerable properties of `arguments` objects and objects created by
12500    * constructors other than `Object` are cloned to plain `Object` objects. An
12501    * empty object is returned for uncloneable values such as functions, DOM nodes,
12502    * Maps, Sets, and WeakMaps.
12503    *
12504    * @static
12505    * @memberOf _
12506    * @category Lang
12507    * @param {*} value The value to clone.
12508    * @param {boolean} [isDeep] Specify a deep clone.
12509    * @param {Function} [customizer] The function to customize cloning values.
12510    * @param {*} [thisArg] The `this` binding of `customizer`.
12511    * @returns {*} Returns the cloned value.
12512    * @example
12513    *
12514    * var users = [
12515    *   { 'user': 'barney' },
12516    *   { 'user': 'fred' }
12517    * ];
12518    *
12519    * var shallow = _.clone(users);
12520    * shallow[0] === users[0];
12521    * // => true
12522    *
12523    * var deep = _.clone(users, true);
12524    * deep[0] === users[0];
12525    * // => false
12526    *
12527    * // using a customizer callback
12528    * var el = _.clone(document.body, function(value) {
12529    *   if (_.isElement(value)) {
12530    *     return value.cloneNode(false);
12531    *   }
12532    * });
12533    *
12534    * el === document.body
12535    * // => false
12536    * el.nodeName
12537    * // => BODY
12538    * el.childNodes.length;
12539    * // => 0
12540    */
12541   function clone(value, isDeep, customizer, thisArg) {
12542     if (isDeep && typeof isDeep != 'boolean' && isIterateeCall(value, isDeep, customizer)) {
12543       isDeep = false;
12544     }
12545     else if (typeof isDeep == 'function') {
12546       thisArg = customizer;
12547       customizer = isDeep;
12548       isDeep = false;
12549     }
12550     return typeof customizer == 'function'
12551       ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 1))
12552       : baseClone(value, isDeep);
12553   }
12554
12555   /**
12556    * Creates a deep clone of `value`. If `customizer` is provided it is invoked
12557    * to produce the cloned values. If `customizer` returns `undefined` cloning
12558    * is handled by the method instead. The `customizer` is bound to `thisArg`
12559    * and invoked with two argument; (value [, index|key, object]).
12560    *
12561    * **Note:** This method is loosely based on the
12562    * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
12563    * The enumerable properties of `arguments` objects and objects created by
12564    * constructors other than `Object` are cloned to plain `Object` objects. An
12565    * empty object is returned for uncloneable values such as functions, DOM nodes,
12566    * Maps, Sets, and WeakMaps.
12567    *
12568    * @static
12569    * @memberOf _
12570    * @category Lang
12571    * @param {*} value The value to deep clone.
12572    * @param {Function} [customizer] The function to customize cloning values.
12573    * @param {*} [thisArg] The `this` binding of `customizer`.
12574    * @returns {*} Returns the deep cloned value.
12575    * @example
12576    *
12577    * var users = [
12578    *   { 'user': 'barney' },
12579    *   { 'user': 'fred' }
12580    * ];
12581    *
12582    * var deep = _.cloneDeep(users);
12583    * deep[0] === users[0];
12584    * // => false
12585    *
12586    * // using a customizer callback
12587    * var el = _.cloneDeep(document.body, function(value) {
12588    *   if (_.isElement(value)) {
12589    *     return value.cloneNode(true);
12590    *   }
12591    * });
12592    *
12593    * el === document.body
12594    * // => false
12595    * el.nodeName
12596    * // => BODY
12597    * el.childNodes.length;
12598    * // => 20
12599    */
12600   function cloneDeep(value, customizer, thisArg) {
12601     return typeof customizer == 'function'
12602       ? baseClone(value, true, bindCallback(customizer, thisArg, 1))
12603       : baseClone(value, true);
12604   }
12605
12606   /**
12607    * Checks if `value` is classified as an `arguments` object.
12608    *
12609    * @static
12610    * @memberOf _
12611    * @category Lang
12612    * @param {*} value The value to check.
12613    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12614    * @example
12615    *
12616    * _.isArguments(function() { return arguments; }());
12617    * // => true
12618    *
12619    * _.isArguments([1, 2, 3]);
12620    * // => false
12621    */
12622   function isArguments(value) {
12623     return isObjectLike(value) && isArrayLike(value) && objToString.call(value) == argsTag;
12624   }
12625   // Fallback for environments without a `toStringTag` for `arguments` objects.
12626   if (!support.argsTag) {
12627     isArguments = function(value) {
12628       return isObjectLike(value) && isArrayLike(value) &&
12629         hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');
12630     };
12631   }
12632
12633   /**
12634    * Checks if `value` is classified as an `Array` object.
12635    *
12636    * @static
12637    * @memberOf _
12638    * @category Lang
12639    * @param {*} value The value to check.
12640    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12641    * @example
12642    *
12643    * _.isArray([1, 2, 3]);
12644    * // => true
12645    *
12646    * _.isArray(function() { return arguments; }());
12647    * // => false
12648    */
12649   var isArray = nativeIsArray || function(value) {
12650     return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag;
12651   };
12652
12653   /**
12654    * Checks if `value` is empty. A value is considered empty unless it is an
12655    * `arguments` object, array, string, or jQuery-like collection with a length
12656    * greater than `0` or an object with own enumerable properties.
12657    *
12658    * @static
12659    * @memberOf _
12660    * @category Lang
12661    * @param {Array|Object|string} value The value to inspect.
12662    * @returns {boolean} Returns `true` if `value` is empty, else `false`.
12663    * @example
12664    *
12665    * _.isEmpty(null);
12666    * // => true
12667    *
12668    * _.isEmpty(true);
12669    * // => true
12670    *
12671    * _.isEmpty(1);
12672    * // => true
12673    *
12674    * _.isEmpty([1, 2, 3]);
12675    * // => false
12676    *
12677    * _.isEmpty({ 'a': 1 });
12678    * // => false
12679    */
12680   function isEmpty(value) {
12681     if (value == null) {
12682       return true;
12683     }
12684     if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) ||
12685         (isObjectLike(value) && isFunction(value.splice)))) {
12686       return !value.length;
12687     }
12688     return !keys(value).length;
12689   }
12690
12691   /**
12692    * Performs a deep comparison between two values to determine if they are
12693    * equivalent. If `customizer` is provided it is invoked to compare values.
12694    * If `customizer` returns `undefined` comparisons are handled by the method
12695    * instead. The `customizer` is bound to `thisArg` and invoked with three
12696    * arguments: (value, other [, index|key]).
12697    *
12698    * **Note:** This method supports comparing arrays, booleans, `Date` objects,
12699    * numbers, `Object` objects, regexes, and strings. Objects are compared by
12700    * their own, not inherited, enumerable properties. Functions and DOM nodes
12701    * are **not** supported. Provide a customizer function to extend support
12702    * for comparing other values.
12703    *
12704    * @static
12705    * @memberOf _
12706    * @alias eq
12707    * @category Lang
12708    * @param {*} value The value to compare.
12709    * @param {*} other The other value to compare.
12710    * @param {Function} [customizer] The function to customize value comparisons.
12711    * @param {*} [thisArg] The `this` binding of `customizer`.
12712    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
12713    * @example
12714    *
12715    * var object = { 'user': 'fred' };
12716    * var other = { 'user': 'fred' };
12717    *
12718    * object == other;
12719    * // => false
12720    *
12721    * _.isEqual(object, other);
12722    * // => true
12723    *
12724    * // using a customizer callback
12725    * var array = ['hello', 'goodbye'];
12726    * var other = ['hi', 'goodbye'];
12727    *
12728    * _.isEqual(array, other, function(value, other) {
12729    *   if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) {
12730    *     return true;
12731    *   }
12732    * });
12733    * // => true
12734    */
12735   function isEqual(value, other, customizer, thisArg) {
12736     customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined;
12737     var result = customizer ? customizer(value, other) : undefined;
12738     return  result === undefined ? baseIsEqual(value, other, customizer) : !!result;
12739   }
12740
12741   /**
12742    * Checks if `value` is classified as a `Function` object.
12743    *
12744    * @static
12745    * @memberOf _
12746    * @category Lang
12747    * @param {*} value The value to check.
12748    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12749    * @example
12750    *
12751    * _.isFunction(_);
12752    * // => true
12753    *
12754    * _.isFunction(/abc/);
12755    * // => false
12756    */
12757   var isFunction = !(baseIsFunction(/x/) || (Uint8Array && !baseIsFunction(Uint8Array))) ? baseIsFunction : function(value) {
12758     // The use of `Object#toString` avoids issues with the `typeof` operator
12759     // in older versions of Chrome and Safari which return 'function' for regexes
12760     // and Safari 8 equivalents which return 'object' for typed array constructors.
12761     return objToString.call(value) == funcTag;
12762   };
12763
12764   /**
12765    * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
12766    * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
12767    *
12768    * @static
12769    * @memberOf _
12770    * @category Lang
12771    * @param {*} value The value to check.
12772    * @returns {boolean} Returns `true` if `value` is an object, else `false`.
12773    * @example
12774    *
12775    * _.isObject({});
12776    * // => true
12777    *
12778    * _.isObject([1, 2, 3]);
12779    * // => true
12780    *
12781    * _.isObject(1);
12782    * // => false
12783    */
12784   function isObject(value) {
12785     // Avoid a V8 JIT bug in Chrome 19-20.
12786     // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
12787     var type = typeof value;
12788     return !!value && (type == 'object' || type == 'function');
12789   }
12790
12791   /**
12792    * Checks if `value` is a native function.
12793    *
12794    * @static
12795    * @memberOf _
12796    * @category Lang
12797    * @param {*} value The value to check.
12798    * @returns {boolean} Returns `true` if `value` is a native function, else `false`.
12799    * @example
12800    *
12801    * _.isNative(Array.prototype.push);
12802    * // => true
12803    *
12804    * _.isNative(_);
12805    * // => false
12806    */
12807   function isNative(value) {
12808     if (value == null) {
12809       return false;
12810     }
12811     if (objToString.call(value) == funcTag) {
12812       return reIsNative.test(fnToString.call(value));
12813     }
12814     return isObjectLike(value) && (isHostObject(value) ? reIsNative : reIsHostCtor).test(value);
12815   }
12816
12817   /**
12818    * Checks if `value` is a plain object, that is, an object created by the
12819    * `Object` constructor or one with a `[[Prototype]]` of `null`.
12820    *
12821    * **Note:** This method assumes objects created by the `Object` constructor
12822    * have no inherited enumerable properties.
12823    *
12824    * @static
12825    * @memberOf _
12826    * @category Lang
12827    * @param {*} value The value to check.
12828    * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
12829    * @example
12830    *
12831    * function Foo() {
12832    *   this.a = 1;
12833    * }
12834    *
12835    * _.isPlainObject(new Foo);
12836    * // => false
12837    *
12838    * _.isPlainObject([1, 2, 3]);
12839    * // => false
12840    *
12841    * _.isPlainObject({ 'x': 0, 'y': 0 });
12842    * // => true
12843    *
12844    * _.isPlainObject(Object.create(null));
12845    * // => true
12846    */
12847   var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
12848     if (!(value && objToString.call(value) == objectTag) || (!lodash.support.argsTag && isArguments(value))) {
12849       return false;
12850     }
12851     var valueOf = getNative(value, 'valueOf'),
12852         objProto = valueOf && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
12853
12854     return objProto
12855       ? (value == objProto || getPrototypeOf(value) == objProto)
12856       : shimIsPlainObject(value);
12857   };
12858
12859   /**
12860    * Checks if `value` is classified as a `String` primitive or object.
12861    *
12862    * @static
12863    * @memberOf _
12864    * @category Lang
12865    * @param {*} value The value to check.
12866    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12867    * @example
12868    *
12869    * _.isString('abc');
12870    * // => true
12871    *
12872    * _.isString(1);
12873    * // => false
12874    */
12875   function isString(value) {
12876     return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag);
12877   }
12878
12879   /**
12880    * Checks if `value` is classified as a typed array.
12881    *
12882    * @static
12883    * @memberOf _
12884    * @category Lang
12885    * @param {*} value The value to check.
12886    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12887    * @example
12888    *
12889    * _.isTypedArray(new Uint8Array);
12890    * // => true
12891    *
12892    * _.isTypedArray([]);
12893    * // => false
12894    */
12895   function isTypedArray(value) {
12896     return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
12897   }
12898
12899   /**
12900    * Converts `value` to a plain object flattening inherited enumerable
12901    * properties of `value` to own properties of the plain object.
12902    *
12903    * @static
12904    * @memberOf _
12905    * @category Lang
12906    * @param {*} value The value to convert.
12907    * @returns {Object} Returns the converted plain object.
12908    * @example
12909    *
12910    * function Foo() {
12911    *   this.b = 2;
12912    * }
12913    *
12914    * Foo.prototype.c = 3;
12915    *
12916    * _.assign({ 'a': 1 }, new Foo);
12917    * // => { 'a': 1, 'b': 2 }
12918    *
12919    * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
12920    * // => { 'a': 1, 'b': 2, 'c': 3 }
12921    */
12922   function toPlainObject(value) {
12923     return baseCopy(value, keysIn(value));
12924   }
12925
12926   /*------------------------------------------------------------------------*/
12927
12928   /**
12929    * Assigns own enumerable properties of source object(s) to the destination
12930    * object. Subsequent sources overwrite property assignments of previous sources.
12931    * If `customizer` is provided it is invoked to produce the assigned values.
12932    * The `customizer` is bound to `thisArg` and invoked with five arguments:
12933    * (objectValue, sourceValue, key, object, source).
12934    *
12935    * **Note:** This method mutates `object` and is based on
12936    * [`Object.assign`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign).
12937    *
12938    * @static
12939    * @memberOf _
12940    * @alias extend
12941    * @category Object
12942    * @param {Object} object The destination object.
12943    * @param {...Object} [sources] The source objects.
12944    * @param {Function} [customizer] The function to customize assigned values.
12945    * @param {*} [thisArg] The `this` binding of `customizer`.
12946    * @returns {Object} Returns `object`.
12947    * @example
12948    *
12949    * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' });
12950    * // => { 'user': 'fred', 'age': 40 }
12951    *
12952    * // using a customizer callback
12953    * var defaults = _.partialRight(_.assign, function(value, other) {
12954    *   return _.isUndefined(value) ? other : value;
12955    * });
12956    *
12957    * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
12958    * // => { 'user': 'barney', 'age': 36 }
12959    */
12960   var assign = createAssigner(function(object, source, customizer) {
12961     return customizer
12962       ? assignWith(object, source, customizer)
12963       : baseAssign(object, source);
12964   });
12965
12966   /**
12967    * Iterates over own enumerable properties of an object invoking `iteratee`
12968    * for each property. The `iteratee` is bound to `thisArg` and invoked with
12969    * three arguments: (value, key, object). Iteratee functions may exit iteration
12970    * early by explicitly returning `false`.
12971    *
12972    * @static
12973    * @memberOf _
12974    * @category Object
12975    * @param {Object} object The object to iterate over.
12976    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12977    * @param {*} [thisArg] The `this` binding of `iteratee`.
12978    * @returns {Object} Returns `object`.
12979    * @example
12980    *
12981    * function Foo() {
12982    *   this.a = 1;
12983    *   this.b = 2;
12984    * }
12985    *
12986    * Foo.prototype.c = 3;
12987    *
12988    * _.forOwn(new Foo, function(value, key) {
12989    *   console.log(key);
12990    * });
12991    * // => logs 'a' and 'b' (iteration order is not guaranteed)
12992    */
12993   var forOwn = createForOwn(baseForOwn);
12994
12995   /**
12996    * Creates an array of the own enumerable property names of `object`.
12997    *
12998    * **Note:** Non-object values are coerced to objects. See the
12999    * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys)
13000    * for more details.
13001    *
13002    * @static
13003    * @memberOf _
13004    * @category Object
13005    * @param {Object} object The object to query.
13006    * @returns {Array} Returns the array of property names.
13007    * @example
13008    *
13009    * function Foo() {
13010    *   this.a = 1;
13011    *   this.b = 2;
13012    * }
13013    *
13014    * Foo.prototype.c = 3;
13015    *
13016    * _.keys(new Foo);
13017    * // => ['a', 'b'] (iteration order is not guaranteed)
13018    *
13019    * _.keys('hi');
13020    * // => ['0', '1']
13021    */
13022   var keys = !nativeKeys ? shimKeys : function(object) {
13023     var Ctor = object == null ? null : object.constructor;
13024     if ((typeof Ctor == 'function' && Ctor.prototype === object) ||
13025         (typeof object == 'function' ? lodash.support.enumPrototypes : isArrayLike(object))) {
13026       return shimKeys(object);
13027     }
13028     return isObject(object) ? nativeKeys(object) : [];
13029   };
13030
13031   /**
13032    * Creates an array of the own and inherited enumerable property names of `object`.
13033    *
13034    * **Note:** Non-object values are coerced to objects.
13035    *
13036    * @static
13037    * @memberOf _
13038    * @category Object
13039    * @param {Object} object The object to query.
13040    * @returns {Array} Returns the array of property names.
13041    * @example
13042    *
13043    * function Foo() {
13044    *   this.a = 1;
13045    *   this.b = 2;
13046    * }
13047    *
13048    * Foo.prototype.c = 3;
13049    *
13050    * _.keysIn(new Foo);
13051    * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
13052    */
13053   function keysIn(object) {
13054     if (object == null) {
13055       return [];
13056     }
13057     if (!isObject(object)) {
13058       object = Object(object);
13059     }
13060     var length = object.length,
13061         support = lodash.support;
13062
13063     length = (length && isLength(length) &&
13064       (isArray(object) || isArguments(object) || isString(object)) && length) || 0;
13065
13066     var Ctor = object.constructor,
13067         index = -1,
13068         proto = (isFunction(Ctor) && Ctor.prototype) || objectProto,
13069         isProto = proto === object,
13070         result = Array(length),
13071         skipIndexes = length > 0,
13072         skipErrorProps = support.enumErrorProps && (object === errorProto || object instanceof Error),
13073         skipProto = support.enumPrototypes && isFunction(object);
13074
13075     while (++index < length) {
13076       result[index] = (index + '');
13077     }
13078     // lodash skips the `constructor` property when it infers it is iterating
13079     // over a `prototype` object because IE < 9 can't set the `[[Enumerable]]`
13080     // attribute of an existing property and the `constructor` property of a
13081     // prototype defaults to non-enumerable.
13082     for (var key in object) {
13083       if (!(skipProto && key == 'prototype') &&
13084           !(skipErrorProps && (key == 'message' || key == 'name')) &&
13085           !(skipIndexes && isIndex(key, length)) &&
13086           !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
13087         result.push(key);
13088       }
13089     }
13090     if (support.nonEnumShadows && object !== objectProto) {
13091       var tag = object === stringProto ? stringTag : (object === errorProto ? errorTag : objToString.call(object)),
13092           nonEnums = nonEnumProps[tag] || nonEnumProps[objectTag];
13093
13094       if (tag == objectTag) {
13095         proto = objectProto;
13096       }
13097       length = shadowProps.length;
13098       while (length--) {
13099         key = shadowProps[length];
13100         var nonEnum = nonEnums[key];
13101         if (!(isProto && nonEnum) &&
13102             (nonEnum ? hasOwnProperty.call(object, key) : object[key] !== proto[key])) {
13103           result.push(key);
13104         }
13105       }
13106     }
13107     return result;
13108   }
13109
13110   /**
13111    * Recursively merges own enumerable properties of the source object(s), that
13112    * don't resolve to `undefined` into the destination object. Subsequent sources
13113    * overwrite property assignments of previous sources. If `customizer` is
13114    * provided it is invoked to produce the merged values of the destination and
13115    * source properties. If `customizer` returns `undefined` merging is handled
13116    * by the method instead. The `customizer` is bound to `thisArg` and invoked
13117    * with five arguments: (objectValue, sourceValue, key, object, source).
13118    *
13119    * @static
13120    * @memberOf _
13121    * @category Object
13122    * @param {Object} object The destination object.
13123    * @param {...Object} [sources] The source objects.
13124    * @param {Function} [customizer] The function to customize assigned values.
13125    * @param {*} [thisArg] The `this` binding of `customizer`.
13126    * @returns {Object} Returns `object`.
13127    * @example
13128    *
13129    * var users = {
13130    *   'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
13131    * };
13132    *
13133    * var ages = {
13134    *   'data': [{ 'age': 36 }, { 'age': 40 }]
13135    * };
13136    *
13137    * _.merge(users, ages);
13138    * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
13139    *
13140    * // using a customizer callback
13141    * var object = {
13142    *   'fruits': ['apple'],
13143    *   'vegetables': ['beet']
13144    * };
13145    *
13146    * var other = {
13147    *   'fruits': ['banana'],
13148    *   'vegetables': ['carrot']
13149    * };
13150    *
13151    * _.merge(object, other, function(a, b) {
13152    *   if (_.isArray(a)) {
13153    *     return a.concat(b);
13154    *   }
13155    * });
13156    * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
13157    */
13158   var merge = createAssigner(baseMerge);
13159
13160   /**
13161    * The opposite of `_.pick`; this method creates an object composed of the
13162    * own and inherited enumerable properties of `object` that are not omitted.
13163    *
13164    * @static
13165    * @memberOf _
13166    * @category Object
13167    * @param {Object} object The source object.
13168    * @param {Function|...(string|string[])} [predicate] The function invoked per
13169    *  iteration or property names to omit, specified as individual property
13170    *  names or arrays of property names.
13171    * @param {*} [thisArg] The `this` binding of `predicate`.
13172    * @returns {Object} Returns the new object.
13173    * @example
13174    *
13175    * var object = { 'user': 'fred', 'age': 40 };
13176    *
13177    * _.omit(object, 'age');
13178    * // => { 'user': 'fred' }
13179    *
13180    * _.omit(object, _.isNumber);
13181    * // => { 'user': 'fred' }
13182    */
13183   var omit = restParam(function(object, props) {
13184     if (object == null) {
13185       return {};
13186     }
13187     if (typeof props[0] != 'function') {
13188       var props = arrayMap(baseFlatten(props), String);
13189       return pickByArray(object, baseDifference(keysIn(object), props));
13190     }
13191     var predicate = bindCallback(props[0], props[1], 3);
13192     return pickByCallback(object, function(value, key, object) {
13193       return !predicate(value, key, object);
13194     });
13195   });
13196
13197   /**
13198    * Creates a two dimensional array of the key-value pairs for `object`,
13199    * e.g. `[[key1, value1], [key2, value2]]`.
13200    *
13201    * @static
13202    * @memberOf _
13203    * @category Object
13204    * @param {Object} object The object to query.
13205    * @returns {Array} Returns the new array of key-value pairs.
13206    * @example
13207    *
13208    * _.pairs({ 'barney': 36, 'fred': 40 });
13209    * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed)
13210    */
13211   function pairs(object) {
13212     object = toObject(object);
13213
13214     var index = -1,
13215         props = keys(object),
13216         length = props.length,
13217         result = Array(length);
13218
13219     while (++index < length) {
13220       var key = props[index];
13221       result[index] = [key, object[key]];
13222     }
13223     return result;
13224   }
13225
13226   /**
13227    * Creates an object composed of the picked `object` properties. Property
13228    * names may be specified as individual arguments or as arrays of property
13229    * names. If `predicate` is provided it is invoked for each property of `object`
13230    * picking the properties `predicate` returns truthy for. The predicate is
13231    * bound to `thisArg` and invoked with three arguments: (value, key, object).
13232    *
13233    * @static
13234    * @memberOf _
13235    * @category Object
13236    * @param {Object} object The source object.
13237    * @param {Function|...(string|string[])} [predicate] The function invoked per
13238    *  iteration or property names to pick, specified as individual property
13239    *  names or arrays of property names.
13240    * @param {*} [thisArg] The `this` binding of `predicate`.
13241    * @returns {Object} Returns the new object.
13242    * @example
13243    *
13244    * var object = { 'user': 'fred', 'age': 40 };
13245    *
13246    * _.pick(object, 'user');
13247    * // => { 'user': 'fred' }
13248    *
13249    * _.pick(object, _.isString);
13250    * // => { 'user': 'fred' }
13251    */
13252   var pick = restParam(function(object, props) {
13253     if (object == null) {
13254       return {};
13255     }
13256     return typeof props[0] == 'function'
13257       ? pickByCallback(object, bindCallback(props[0], props[1], 3))
13258       : pickByArray(object, baseFlatten(props));
13259   });
13260
13261   /**
13262    * Creates an array of the own enumerable property values of `object`.
13263    *
13264    * **Note:** Non-object values are coerced to objects.
13265    *
13266    * @static
13267    * @memberOf _
13268    * @category Object
13269    * @param {Object} object The object to query.
13270    * @returns {Array} Returns the array of property values.
13271    * @example
13272    *
13273    * function Foo() {
13274    *   this.a = 1;
13275    *   this.b = 2;
13276    * }
13277    *
13278    * Foo.prototype.c = 3;
13279    *
13280    * _.values(new Foo);
13281    * // => [1, 2] (iteration order is not guaranteed)
13282    *
13283    * _.values('hi');
13284    * // => ['h', 'i']
13285    */
13286   function values(object) {
13287     return baseValues(object, keys(object));
13288   }
13289
13290   /*------------------------------------------------------------------------*/
13291
13292   /**
13293    * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?",
13294    * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`.
13295    *
13296    * @static
13297    * @memberOf _
13298    * @category String
13299    * @param {string} [string=''] The string to escape.
13300    * @returns {string} Returns the escaped string.
13301    * @example
13302    *
13303    * _.escapeRegExp('[lodash](https://lodash.com/)');
13304    * // => '\[lodash\]\(https:\/\/lodash\.com\/\)'
13305    */
13306   function escapeRegExp(string) {
13307     string = baseToString(string);
13308     return (string && reHasRegExpChars.test(string))
13309       ? string.replace(reRegExpChars, '\\$&')
13310       : string;
13311   }
13312
13313   /*------------------------------------------------------------------------*/
13314
13315   /**
13316    * Creates a function that invokes `func` with the `this` binding of `thisArg`
13317    * and arguments of the created function. If `func` is a property name the
13318    * created callback returns the property value for a given element. If `func`
13319    * is an object the created callback returns `true` for elements that contain
13320    * the equivalent object properties, otherwise it returns `false`.
13321    *
13322    * @static
13323    * @memberOf _
13324    * @alias iteratee
13325    * @category Utility
13326    * @param {*} [func=_.identity] The value to convert to a callback.
13327    * @param {*} [thisArg] The `this` binding of `func`.
13328    * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
13329    * @returns {Function} Returns the callback.
13330    * @example
13331    *
13332    * var users = [
13333    *   { 'user': 'barney', 'age': 36 },
13334    *   { 'user': 'fred',   'age': 40 }
13335    * ];
13336    *
13337    * // wrap to create custom callback shorthands
13338    * _.callback = _.wrap(_.callback, function(callback, func, thisArg) {
13339    *   var match = /^(.+?)__([gl]t)(.+)$/.exec(func);
13340    *   if (!match) {
13341    *     return callback(func, thisArg);
13342    *   }
13343    *   return function(object) {
13344    *     return match[2] == 'gt'
13345    *       ? object[match[1]] > match[3]
13346    *       : object[match[1]] < match[3];
13347    *   };
13348    * });
13349    *
13350    * _.filter(users, 'age__gt36');
13351    * // => [{ 'user': 'fred', 'age': 40 }]
13352    */
13353   function callback(func, thisArg, guard) {
13354     if (guard && isIterateeCall(func, thisArg, guard)) {
13355       thisArg = null;
13356     }
13357     return isObjectLike(func)
13358       ? matches(func)
13359       : baseCallback(func, thisArg);
13360   }
13361
13362   /**
13363    * Creates a function that returns `value`.
13364    *
13365    * @static
13366    * @memberOf _
13367    * @category Utility
13368    * @param {*} value The value to return from the new function.
13369    * @returns {Function} Returns the new function.
13370    * @example
13371    *
13372    * var object = { 'user': 'fred' };
13373    * var getter = _.constant(object);
13374    *
13375    * getter() === object;
13376    * // => true
13377    */
13378   function constant(value) {
13379     return function() {
13380       return value;
13381     };
13382   }
13383
13384   /**
13385    * This method returns the first argument provided to it.
13386    *
13387    * @static
13388    * @memberOf _
13389    * @category Utility
13390    * @param {*} value Any value.
13391    * @returns {*} Returns `value`.
13392    * @example
13393    *
13394    * var object = { 'user': 'fred' };
13395    *
13396    * _.identity(object) === object;
13397    * // => true
13398    */
13399   function identity(value) {
13400     return value;
13401   }
13402
13403   /**
13404    * Creates a function that performs a deep comparison between a given object
13405    * and `source`, returning `true` if the given object has equivalent property
13406    * values, else `false`.
13407    *
13408    * **Note:** This method supports comparing arrays, booleans, `Date` objects,
13409    * numbers, `Object` objects, regexes, and strings. Objects are compared by
13410    * their own, not inherited, enumerable properties. For comparing a single
13411    * own or inherited property value see `_.matchesProperty`.
13412    *
13413    * @static
13414    * @memberOf _
13415    * @category Utility
13416    * @param {Object} source The object of property values to match.
13417    * @returns {Function} Returns the new function.
13418    * @example
13419    *
13420    * var users = [
13421    *   { 'user': 'barney', 'age': 36, 'active': true },
13422    *   { 'user': 'fred',   'age': 40, 'active': false }
13423    * ];
13424    *
13425    * _.filter(users, _.matches({ 'age': 40, 'active': false }));
13426    * // => [{ 'user': 'fred', 'age': 40, 'active': false }]
13427    */
13428   function matches(source) {
13429     return baseMatches(baseClone(source, true));
13430   }
13431
13432   /**
13433    * Adds all own enumerable function properties of a source object to the
13434    * destination object. If `object` is a function then methods are added to
13435    * its prototype as well.
13436    *
13437    * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
13438    * avoid conflicts caused by modifying the original.
13439    *
13440    * @static
13441    * @memberOf _
13442    * @category Utility
13443    * @param {Function|Object} [object=lodash] The destination object.
13444    * @param {Object} source The object of functions to add.
13445    * @param {Object} [options] The options object.
13446    * @param {boolean} [options.chain=true] Specify whether the functions added
13447    *  are chainable.
13448    * @returns {Function|Object} Returns `object`.
13449    * @example
13450    *
13451    * function vowels(string) {
13452    *   return _.filter(string, function(v) {
13453    *     return /[aeiou]/i.test(v);
13454    *   });
13455    * }
13456    *
13457    * _.mixin({ 'vowels': vowels });
13458    * _.vowels('fred');
13459    * // => ['e']
13460    *
13461    * _('fred').vowels().value();
13462    * // => ['e']
13463    *
13464    * _.mixin({ 'vowels': vowels }, { 'chain': false });
13465    * _('fred').vowels();
13466    * // => ['e']
13467    */
13468   function mixin(object, source, options) {
13469     if (options == null) {
13470       var isObj = isObject(source),
13471           props = isObj ? keys(source) : null,
13472           methodNames = (props && props.length) ? baseFunctions(source, props) : null;
13473
13474       if (!(methodNames ? methodNames.length : isObj)) {
13475         methodNames = false;
13476         options = source;
13477         source = object;
13478         object = this;
13479       }
13480     }
13481     if (!methodNames) {
13482       methodNames = baseFunctions(source, keys(source));
13483     }
13484     var chain = true,
13485         index = -1,
13486         isFunc = isFunction(object),
13487         length = methodNames.length;
13488
13489     if (options === false) {
13490       chain = false;
13491     } else if (isObject(options) && 'chain' in options) {
13492       chain = options.chain;
13493     }
13494     while (++index < length) {
13495       var methodName = methodNames[index],
13496           func = source[methodName];
13497
13498       object[methodName] = func;
13499       if (isFunc) {
13500         object.prototype[methodName] = (function(func) {
13501           return function() {
13502             var chainAll = this.__chain__;
13503             if (chain || chainAll) {
13504               var result = object(this.__wrapped__),
13505                   actions = result.__actions__ = arrayCopy(this.__actions__);
13506
13507               actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
13508               result.__chain__ = chainAll;
13509               return result;
13510             }
13511             var args = [this.value()];
13512             push.apply(args, arguments);
13513             return func.apply(object, args);
13514           };
13515         }(func));
13516       }
13517     }
13518     return object;
13519   }
13520
13521   /**
13522    * A no-operation function that returns `undefined` regardless of the
13523    * arguments it receives.
13524    *
13525    * @static
13526    * @memberOf _
13527    * @category Utility
13528    * @example
13529    *
13530    * var object = { 'user': 'fred' };
13531    *
13532    * _.noop(object) === undefined;
13533    * // => true
13534    */
13535   function noop() {
13536     // No operation performed.
13537   }
13538
13539   /**
13540    * Creates a function that returns the property value at `path` on a
13541    * given object.
13542    *
13543    * @static
13544    * @memberOf _
13545    * @category Utility
13546    * @param {Array|string} path The path of the property to get.
13547    * @returns {Function} Returns the new function.
13548    * @example
13549    *
13550    * var objects = [
13551    *   { 'a': { 'b': { 'c': 2 } } },
13552    *   { 'a': { 'b': { 'c': 1 } } }
13553    * ];
13554    *
13555    * _.map(objects, _.property('a.b.c'));
13556    * // => [2, 1]
13557    *
13558    * _.pluck(_.sortBy(objects, _.property(['a', 'b', 'c'])), 'a.b.c');
13559    * // => [1, 2]
13560    */
13561   function property(path) {
13562     return isKey(path) ? baseProperty(path) : basePropertyDeep(path);
13563   }
13564
13565   /*------------------------------------------------------------------------*/
13566
13567   // Ensure wrappers are instances of `baseLodash`.
13568   lodash.prototype = baseLodash.prototype;
13569
13570   LodashWrapper.prototype = baseCreate(baseLodash.prototype);
13571   LodashWrapper.prototype.constructor = LodashWrapper;
13572
13573   LazyWrapper.prototype = baseCreate(baseLodash.prototype);
13574   LazyWrapper.prototype.constructor = LazyWrapper;
13575
13576   // Add functions to the `Set` cache.
13577   SetCache.prototype.push = cachePush;
13578
13579   // Add functions that return wrapped values when chaining.
13580   lodash.assign = assign;
13581   lodash.bind = bind;
13582   lodash.callback = callback;
13583   lodash.chain = chain;
13584   lodash.chunk = chunk;
13585   lodash.compact = compact;
13586   lodash.constant = constant;
13587   lodash.debounce = debounce;
13588   lodash.difference = difference;
13589   lodash.filter = filter;
13590   lodash.flatten = flatten;
13591   lodash.forEach = forEach;
13592   lodash.forOwn = forOwn;
13593   lodash.groupBy = groupBy;
13594   lodash.intersection = intersection;
13595   lodash.keys = keys;
13596   lodash.keysIn = keysIn;
13597   lodash.map = map;
13598   lodash.matches = matches;
13599   lodash.merge = merge;
13600   lodash.mixin = mixin;
13601   lodash.omit = omit;
13602   lodash.pairs = pairs;
13603   lodash.pick = pick;
13604   lodash.pluck = pluck;
13605   lodash.property = property;
13606   lodash.reject = reject;
13607   lodash.restParam = restParam;
13608   lodash.tap = tap;
13609   lodash.throttle = throttle;
13610   lodash.thru = thru;
13611   lodash.toPlainObject = toPlainObject;
13612   lodash.union = union;
13613   lodash.uniq = uniq;
13614   lodash.values = values;
13615   lodash.without = without;
13616
13617   // Add aliases.
13618   lodash.collect = map;
13619   lodash.each = forEach;
13620   lodash.extend = assign;
13621   lodash.iteratee = callback;
13622   lodash.select = filter;
13623   lodash.unique = uniq;
13624
13625   // Add functions to `lodash.prototype`.
13626   mixin(lodash, lodash);
13627
13628   /*------------------------------------------------------------------------*/
13629
13630   // Add functions that return unwrapped values when chaining.
13631   lodash.clone = clone;
13632   lodash.cloneDeep = cloneDeep;
13633   lodash.escapeRegExp = escapeRegExp;
13634   lodash.every = every;
13635   lodash.find = find;
13636   lodash.first = first;
13637   lodash.identity = identity;
13638   lodash.includes = includes;
13639   lodash.indexOf = indexOf;
13640   lodash.isArguments = isArguments;
13641   lodash.isArray = isArray;
13642   lodash.isEmpty = isEmpty;
13643   lodash.isEqual = isEqual;
13644   lodash.isFunction = isFunction;
13645   lodash.isNative = isNative;
13646   lodash.isObject = isObject;
13647   lodash.isPlainObject = isPlainObject;
13648   lodash.isString = isString;
13649   lodash.isTypedArray = isTypedArray;
13650   lodash.last = last;
13651   lodash.noop = noop;
13652   lodash.now = now;
13653   lodash.reduce = reduce;
13654   lodash.some = some;
13655
13656   // Add aliases.
13657   lodash.all = every;
13658   lodash.any = some;
13659   lodash.contains = includes;
13660   lodash.eq = isEqual;
13661   lodash.detect = find;
13662   lodash.foldl = reduce;
13663   lodash.head = first;
13664   lodash.include = includes;
13665   lodash.inject = reduce;
13666
13667   mixin(lodash, (function() {
13668     var source = {};
13669     baseForOwn(lodash, function(func, methodName) {
13670       if (!lodash.prototype[methodName]) {
13671         source[methodName] = func;
13672       }
13673     });
13674     return source;
13675   }()), false);
13676
13677   /*------------------------------------------------------------------------*/
13678
13679   lodash.prototype.sample = function(n) {
13680     if (!this.__chain__ && n == null) {
13681       return sample(this.value());
13682     }
13683     return this.thru(function(value) {
13684       return sample(value, n);
13685     });
13686   };
13687
13688   /*------------------------------------------------------------------------*/
13689
13690   /**
13691    * The semantic version number.
13692    *
13693    * @static
13694    * @memberOf _
13695    * @type string
13696    */
13697   lodash.VERSION = VERSION;
13698
13699   // Assign default placeholders.
13700   bind.placeholder = lodash;
13701
13702   // Add `LazyWrapper` methods that accept an `iteratee` value.
13703   arrayEach(['dropWhile', 'filter', 'map', 'takeWhile'], function(methodName, type) {
13704     var isFilter = type != LAZY_MAP_FLAG,
13705         isDropWhile = type == LAZY_DROP_WHILE_FLAG;
13706
13707     LazyWrapper.prototype[methodName] = function(iteratee, thisArg) {
13708       var filtered = this.__filtered__,
13709           result = (filtered && isDropWhile) ? new LazyWrapper(this) : this.clone(),
13710           iteratees = result.__iteratees__ || (result.__iteratees__ = []);
13711
13712       iteratees.push({
13713         'done': false,
13714         'count': 0,
13715         'index': 0,
13716         'iteratee': getCallback(iteratee, thisArg, 1),
13717         'limit': -1,
13718         'type': type
13719       });
13720
13721       result.__filtered__ = filtered || isFilter;
13722       return result;
13723     };
13724   });
13725
13726   // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
13727   arrayEach(['drop', 'take'], function(methodName, index) {
13728     var whileName = methodName + 'While';
13729
13730     LazyWrapper.prototype[methodName] = function(n) {
13731       var filtered = this.__filtered__,
13732           result = (filtered && !index) ? this.dropWhile() : this.clone();
13733
13734       n = n == null ? 1 : nativeMax(floor(n) || 0, 0);
13735       if (filtered) {
13736         if (index) {
13737           result.__takeCount__ = nativeMin(result.__takeCount__, n);
13738         } else {
13739           last(result.__iteratees__).limit = n;
13740         }
13741       } else {
13742         var views = result.__views__ || (result.__views__ = []);
13743         views.push({ 'size': n, 'type': methodName + (result.__dir__ < 0 ? 'Right' : '') });
13744       }
13745       return result;
13746     };
13747
13748     LazyWrapper.prototype[methodName + 'Right'] = function(n) {
13749       return this.reverse()[methodName](n).reverse();
13750     };
13751
13752     LazyWrapper.prototype[methodName + 'RightWhile'] = function(predicate, thisArg) {
13753       return this.reverse()[whileName](predicate, thisArg).reverse();
13754     };
13755   });
13756
13757   // Add `LazyWrapper` methods for `_.first` and `_.last`.
13758   arrayEach(['first', 'last'], function(methodName, index) {
13759     var takeName = 'take' + (index ? 'Right' : '');
13760
13761     LazyWrapper.prototype[methodName] = function() {
13762       return this[takeName](1).value()[0];
13763     };
13764   });
13765
13766   // Add `LazyWrapper` methods for `_.initial` and `_.rest`.
13767   arrayEach(['initial', 'rest'], function(methodName, index) {
13768     var dropName = 'drop' + (index ? '' : 'Right');
13769
13770     LazyWrapper.prototype[methodName] = function() {
13771       return this[dropName](1);
13772     };
13773   });
13774
13775   // Add `LazyWrapper` methods for `_.pluck` and `_.where`.
13776   arrayEach(['pluck', 'where'], function(methodName, index) {
13777     var operationName = index ? 'filter' : 'map',
13778         createCallback = index ? baseMatches : property;
13779
13780     LazyWrapper.prototype[methodName] = function(value) {
13781       return this[operationName](createCallback(value));
13782     };
13783   });
13784
13785   LazyWrapper.prototype.compact = function() {
13786     return this.filter(identity);
13787   };
13788
13789   LazyWrapper.prototype.reject = function(predicate, thisArg) {
13790     predicate = getCallback(predicate, thisArg, 1);
13791     return this.filter(function(value) {
13792       return !predicate(value);
13793     });
13794   };
13795
13796   LazyWrapper.prototype.slice = function(start, end) {
13797     start = start == null ? 0 : (+start || 0);
13798
13799     var result = this;
13800     if (start < 0) {
13801       result = this.takeRight(-start);
13802     } else if (start) {
13803       result = this.drop(start);
13804     }
13805     if (end !== undefined) {
13806       end = (+end || 0);
13807       result = end < 0 ? result.dropRight(-end) : result.take(end - start);
13808     }
13809     return result;
13810   };
13811
13812   LazyWrapper.prototype.toArray = function() {
13813     return this.drop(0);
13814   };
13815
13816   // Add `LazyWrapper` methods to `lodash.prototype`.
13817   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
13818     var lodashFunc = lodash[methodName];
13819     if (!lodashFunc) {
13820       return;
13821     }
13822     var checkIteratee = /^(?:filter|map|reject)|While$/.test(methodName),
13823         retUnwrapped = /^(?:first|last)$/.test(methodName);
13824
13825     lodash.prototype[methodName] = function() {
13826       var args = arguments,
13827           chainAll = this.__chain__,
13828           value = this.__wrapped__,
13829           isHybrid = !!this.__actions__.length,
13830           isLazy = value instanceof LazyWrapper,
13831           iteratee = args[0],
13832           useLazy = isLazy || isArray(value);
13833
13834       if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
13835         // avoid lazy use if the iteratee has a "length" value other than `1`
13836         isLazy = useLazy = false;
13837       }
13838       var onlyLazy = isLazy && !isHybrid;
13839       if (retUnwrapped && !chainAll) {
13840         return onlyLazy
13841           ? func.call(value)
13842           : lodashFunc.call(lodash, this.value());
13843       }
13844       var interceptor = function(value) {
13845         var otherArgs = [value];
13846         push.apply(otherArgs, args);
13847         return lodashFunc.apply(lodash, otherArgs);
13848       };
13849       if (useLazy) {
13850         var wrapper = onlyLazy ? value : new LazyWrapper(this),
13851             result = func.apply(wrapper, args);
13852
13853         if (!retUnwrapped && (isHybrid || result.__actions__)) {
13854           var actions = result.__actions__ || (result.__actions__ = []);
13855           actions.push({ 'func': thru, 'args': [interceptor], 'thisArg': lodash });
13856         }
13857         return new LodashWrapper(result, chainAll);
13858       }
13859       return this.thru(interceptor);
13860     };
13861   });
13862
13863   // Add `Array` and `String` methods to `lodash.prototype`.
13864   arrayEach(['concat', 'join', 'pop', 'push', 'replace', 'shift', 'sort', 'splice', 'split', 'unshift'], function(methodName) {
13865     var protoFunc = (/^(?:replace|split)$/.test(methodName) ? stringProto : arrayProto)[methodName],
13866         chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
13867         fixObjects = !support.spliceObjects && /^(?:pop|shift|splice)$/.test(methodName),
13868         retUnwrapped = /^(?:join|pop|replace|shift)$/.test(methodName);
13869
13870     // Avoid array-like object bugs with `Array#shift` and `Array#splice` in
13871     // IE < 9, Firefox < 10, and RingoJS.
13872     var func = !fixObjects ? protoFunc : function() {
13873       var result = protoFunc.apply(this, arguments);
13874       if (this.length === 0) {
13875         delete this[0];
13876       }
13877       return result;
13878     };
13879
13880     lodash.prototype[methodName] = function() {
13881       var args = arguments;
13882       if (retUnwrapped && !this.__chain__) {
13883         return func.apply(this.value(), args);
13884       }
13885       return this[chainName](function(value) {
13886         return func.apply(value, args);
13887       });
13888     };
13889   });
13890
13891   // Map minified function names to their real names.
13892   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
13893     var lodashFunc = lodash[methodName];
13894     if (lodashFunc) {
13895       var key = lodashFunc.name,
13896           names = realNames[key] || (realNames[key] = []);
13897
13898       names.push({ 'name': methodName, 'func': lodashFunc });
13899     }
13900   });
13901
13902   realNames[createHybridWrapper(null, BIND_KEY_FLAG).name] = [{ 'name': 'wrapper', 'func': null }];
13903
13904   // Add functions to the lazy wrapper.
13905   LazyWrapper.prototype.clone = lazyClone;
13906   LazyWrapper.prototype.reverse = lazyReverse;
13907   LazyWrapper.prototype.value = lazyValue;
13908
13909   // Add chaining functions to the `lodash` wrapper.
13910   lodash.prototype.chain = wrapperChain;
13911   lodash.prototype.commit = wrapperCommit;
13912   lodash.prototype.plant = wrapperPlant;
13913   lodash.prototype.reverse = wrapperReverse;
13914   lodash.prototype.toString = wrapperToString;
13915   lodash.prototype.run = lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
13916
13917   // Add function aliases to the `lodash` wrapper.
13918   lodash.prototype.collect = lodash.prototype.map;
13919   lodash.prototype.head = lodash.prototype.first;
13920   lodash.prototype.select = lodash.prototype.filter;
13921   lodash.prototype.tail = lodash.prototype.rest;
13922
13923   /*--------------------------------------------------------------------------*/
13924
13925   if (freeExports && freeModule) {
13926     // Export for Node.js or RingoJS.
13927     if (moduleExports) {
13928       (freeModule.exports = lodash)._ = lodash;
13929     }
13930   }
13931   else {
13932     // Export for a browser or Rhino.
13933     root._ = lodash;
13934   }
13935 }.call(this));
13936 (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;
13937 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){
13938 'use strict';
13939
13940 var ohauth = require('ohauth'),
13941     xtend = require('xtend'),
13942     store = require('store');
13943
13944 // # osm-auth
13945 //
13946 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
13947 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
13948 // does not support custom headers, which this uses everywhere.
13949 module.exports = function(o) {
13950
13951     var oauth = {};
13952
13953     // authenticated users will also have a request token secret, but it's
13954     // not used in transactions with the server
13955     oauth.authenticated = function() {
13956         return !!(token('oauth_token') && token('oauth_token_secret'));
13957     };
13958
13959     oauth.logout = function() {
13960         token('oauth_token', '');
13961         token('oauth_token_secret', '');
13962         token('oauth_request_token_secret', '');
13963         return oauth;
13964     };
13965
13966     // TODO: detect lack of click event
13967     oauth.authenticate = function(callback) {
13968         if (oauth.authenticated()) return callback();
13969
13970         oauth.logout();
13971
13972         // ## Getting a request token
13973         var params = timenonce(getAuth(o)),
13974             url = o.url + '/oauth/request_token',
13975             timer;
13976
13977         params.oauth_signature = ohauth.signature(
13978             o.oauth_secret, '',
13979             ohauth.baseString('POST', url, params));
13980
13981         if (!o.singlepage) {
13982             // Create a 600x550 popup window in the center of the screen
13983             var w = 600, h = 550,
13984                 settings = [
13985                     ['width', w], ['height', h],
13986                     ['left', screen.width / 2 - w / 2],
13987                     ['top', screen.height / 2 - h / 2]].map(function(x) {
13988                         return x.join('=');
13989                     }).join(','),
13990                 popup = window.open('about:blank', 'oauth_window', settings);
13991
13992
13993             timer = setInterval(function() {
13994                 if (popup.closed) {
13995                     o.done();
13996                     clearInterval(timer);
13997                     callback('not authenticated', null);
13998                 }
13999             }, 100);
14000         }
14001
14002         // Request a request token. When this is complete, the popup
14003         // window is redirected to OSM's authorization page.
14004         ohauth.xhr('POST', url, params, null, {}, reqTokenDone);
14005         o.loading();
14006
14007         function reqTokenDone(err, xhr) {
14008             o.done();
14009             if (err) {
14010                 if (timer) clearInterval(timer);
14011                 return callback(err);
14012             }
14013             var resp = ohauth.stringQs(xhr.response);
14014             token('oauth_request_token_secret', resp.oauth_token_secret);
14015             var authorize_url = o.url + '/oauth/authorize?' + ohauth.qsString({
14016                 oauth_token: resp.oauth_token,
14017                 oauth_callback: location.href.replace('index.html', '')
14018                     .replace(/#.*/, '') + o.landing
14019             });
14020
14021             if (o.singlepage) {
14022                 location.href = authorize_url;
14023             } else {
14024                 popup.location = authorize_url;
14025             }
14026         }
14027
14028         // Called by a function in a landing page, in the popup window. The
14029         // window closes itself.
14030         window.authComplete = function(token) {
14031             if (timer) clearInterval(timer);
14032             var oauth_token = ohauth.stringQs(token.split('?')[1]);
14033             get_access_token(oauth_token.oauth_token);
14034             delete window.authComplete;
14035         };
14036
14037         // ## Getting an request token
14038         //
14039         // At this point we have an `oauth_token`, brought in from a function
14040         // call on a landing page popup.
14041         function get_access_token(oauth_token) {
14042             var url = o.url + '/oauth/access_token',
14043                 params = timenonce(getAuth(o)),
14044                 request_token_secret = token('oauth_request_token_secret');
14045             params.oauth_token = oauth_token;
14046             params.oauth_signature = ohauth.signature(
14047                 o.oauth_secret,
14048                 request_token_secret,
14049                 ohauth.baseString('POST', url, params));
14050
14051             // ## Getting an access token
14052             //
14053             // The final token required for authentication. At this point
14054             // we have a `request token secret`
14055             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
14056             o.loading();
14057         }
14058
14059         function accessTokenDone(err, xhr) {
14060             o.done();
14061             if (timer) clearInterval(timer);
14062             if (err) return callback(err);
14063             var access_token = ohauth.stringQs(xhr.response);
14064             token('oauth_token', access_token.oauth_token);
14065             token('oauth_token_secret', access_token.oauth_token_secret);
14066             callback(null, oauth);
14067         }
14068     };
14069
14070     oauth.bootstrapToken = function(oauth_token, callback) {
14071         // ## Getting an request token
14072         // At this point we have an `oauth_token`, brought in from a function
14073         // call on a landing page popup.
14074         function get_access_token(oauth_token) {
14075             var url = o.url + '/oauth/access_token',
14076                 params = timenonce(getAuth(o)),
14077                 request_token_secret = token('oauth_request_token_secret');
14078             params.oauth_token = oauth_token;
14079             params.oauth_signature = ohauth.signature(
14080                 o.oauth_secret,
14081                 request_token_secret,
14082                 ohauth.baseString('POST', url, params));
14083
14084             // ## Getting an access token
14085             // The final token required for authentication. At this point
14086             // we have a `request token secret`
14087             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
14088             o.loading();
14089         }
14090
14091         function accessTokenDone(err, xhr) {
14092             o.done();
14093             if (err) return callback(err);
14094             var access_token = ohauth.stringQs(xhr.response);
14095             token('oauth_token', access_token.oauth_token);
14096             token('oauth_token_secret', access_token.oauth_token_secret);
14097             callback(null, oauth);
14098         }
14099
14100         get_access_token(oauth_token);
14101     };
14102
14103     // # xhr
14104     //
14105     // A single XMLHttpRequest wrapper that does authenticated calls if the
14106     // user has logged in.
14107     oauth.xhr = function(options, callback) {
14108         if (!oauth.authenticated()) {
14109             if (o.auto) return oauth.authenticate(run);
14110             else return callback('not authenticated', null);
14111         } else return run();
14112
14113         function run() {
14114             var params = timenonce(getAuth(o)),
14115                 url = o.url + options.path,
14116                 oauth_token_secret = token('oauth_token_secret');
14117
14118             // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
14119             if ((!options.options || !options.options.header ||
14120                 options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
14121                 options.content) {
14122                 params = xtend(params, ohauth.stringQs(options.content));
14123             }
14124
14125             params.oauth_token = token('oauth_token');
14126             params.oauth_signature = ohauth.signature(
14127                 o.oauth_secret,
14128                 oauth_token_secret,
14129                 ohauth.baseString(options.method, url, params));
14130
14131             ohauth.xhr(options.method,
14132                 url, params, options.content, options.options, done);
14133         }
14134
14135         function done(err, xhr) {
14136             if (err) return callback(err);
14137             else if (xhr.responseXML) return callback(err, xhr.responseXML);
14138             else return callback(err, xhr.response);
14139         }
14140     };
14141
14142     // pre-authorize this object, if we can just get a token and token_secret
14143     // from the start
14144     oauth.preauth = function(c) {
14145         if (!c) return;
14146         if (c.oauth_token) token('oauth_token', c.oauth_token);
14147         if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
14148         return oauth;
14149     };
14150
14151     oauth.options = function(_) {
14152         if (!arguments.length) return o;
14153
14154         o = _;
14155
14156         o.url = o.url || 'http://www.openstreetmap.org';
14157         o.landing = o.landing || 'land.html';
14158
14159         o.singlepage = o.singlepage || false;
14160
14161         // Optional loading and loading-done functions for nice UI feedback.
14162         // by default, no-ops
14163         o.loading = o.loading || function() {};
14164         o.done = o.done || function() {};
14165
14166         return oauth.preauth(o);
14167     };
14168
14169     // 'stamp' an authentication object from `getAuth()`
14170     // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
14171     // and timestamp
14172     function timenonce(o) {
14173         o.oauth_timestamp = ohauth.timestamp();
14174         o.oauth_nonce = ohauth.nonce();
14175         return o;
14176     }
14177
14178     // get/set tokens. These are prefixed with the base URL so that `osm-auth`
14179     // can be used with multiple APIs and the keys in `localStorage`
14180     // will not clash
14181     var token;
14182
14183     if (store.enabled) {
14184         token = function (x, y) {
14185             if (arguments.length === 1) return store.get(o.url + x);
14186             else if (arguments.length === 2) return store.set(o.url + x, y);
14187         };
14188     } else {
14189         var storage = {};
14190         token = function (x, y) {
14191             if (arguments.length === 1) return storage[o.url + x];
14192             else if (arguments.length === 2) return storage[o.url + x] = y;
14193         };
14194     }
14195
14196     // Get an authentication object. If you just add and remove properties
14197     // from a single object, you'll need to use `delete` to make sure that
14198     // it doesn't contain undesired properties for authentication
14199     function getAuth(o) {
14200         return {
14201             oauth_consumer_key: o.oauth_consumer_key,
14202             oauth_signature_method: "HMAC-SHA1"
14203         };
14204     }
14205
14206     // potentially pre-authorize
14207     oauth.options(o);
14208
14209     return oauth;
14210 };
14211
14212 },{"ohauth":2,"store":3,"xtend":4}],3:[function(require,module,exports){
14213 (function(global){;(function(win){
14214         var store = {},
14215                 doc = win.document,
14216                 localStorageName = 'localStorage',
14217                 storage
14218
14219         store.disabled = false
14220         store.set = function(key, value) {}
14221         store.get = function(key) {}
14222         store.remove = function(key) {}
14223         store.clear = function() {}
14224         store.transact = function(key, defaultVal, transactionFn) {
14225                 var val = store.get(key)
14226                 if (transactionFn == null) {
14227                         transactionFn = defaultVal
14228                         defaultVal = null
14229                 }
14230                 if (typeof val == 'undefined') { val = defaultVal || {} }
14231                 transactionFn(val)
14232                 store.set(key, val)
14233         }
14234         store.getAll = function() {}
14235         store.forEach = function() {}
14236
14237         store.serialize = function(value) {
14238                 return JSON.stringify(value)
14239         }
14240         store.deserialize = function(value) {
14241                 if (typeof value != 'string') { return undefined }
14242                 try { return JSON.parse(value) }
14243                 catch(e) { return value || undefined }
14244         }
14245
14246         // Functions to encapsulate questionable FireFox 3.6.13 behavior
14247         // when about.config::dom.storage.enabled === false
14248         // See https://github.com/marcuswestin/store.js/issues#issue/13
14249         function isLocalStorageNameSupported() {
14250                 try { return (localStorageName in win && win[localStorageName]) }
14251                 catch(err) { return false }
14252         }
14253
14254         if (isLocalStorageNameSupported()) {
14255                 storage = win[localStorageName]
14256                 store.set = function(key, val) {
14257                         if (val === undefined) { return store.remove(key) }
14258                         storage.setItem(key, store.serialize(val))
14259                         return val
14260                 }
14261                 store.get = function(key) { return store.deserialize(storage.getItem(key)) }
14262                 store.remove = function(key) { storage.removeItem(key) }
14263                 store.clear = function() { storage.clear() }
14264                 store.getAll = function() {
14265                         var ret = {}
14266                         store.forEach(function(key, val) {
14267                                 ret[key] = val
14268                         })
14269                         return ret
14270                 }
14271                 store.forEach = function(callback) {
14272                         for (var i=0; i<storage.length; i++) {
14273                                 var key = storage.key(i)
14274                                 callback(key, store.get(key))
14275                         }
14276                 }
14277         } else if (doc.documentElement.addBehavior) {
14278                 var storageOwner,
14279                         storageContainer
14280                 // Since #userData storage applies only to specific paths, we need to
14281                 // somehow link our data to a specific path.  We choose /favicon.ico
14282                 // as a pretty safe option, since all browsers already make a request to
14283                 // this URL anyway and being a 404 will not hurt us here.  We wrap an
14284                 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
14285                 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
14286                 // since the iframe access rules appear to allow direct access and
14287                 // manipulation of the document element, even for a 404 page.  This
14288                 // document can be used instead of the current document (which would
14289                 // have been limited to the current path) to perform #userData storage.
14290                 try {
14291                         storageContainer = new ActiveXObject('htmlfile')
14292                         storageContainer.open()
14293                         storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></iframe>')
14294                         storageContainer.close()
14295                         storageOwner = storageContainer.w.frames[0].document
14296                         storage = storageOwner.createElement('div')
14297                 } catch(e) {
14298                         // somehow ActiveXObject instantiation failed (perhaps some special
14299                         // security settings or otherwse), fall back to per-path storage
14300                         storage = doc.createElement('div')
14301                         storageOwner = doc.body
14302                 }
14303                 function withIEStorage(storeFunction) {
14304                         return function() {
14305                                 var args = Array.prototype.slice.call(arguments, 0)
14306                                 args.unshift(storage)
14307                                 // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
14308                                 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
14309                                 storageOwner.appendChild(storage)
14310                                 storage.addBehavior('#default#userData')
14311                                 storage.load(localStorageName)
14312                                 var result = storeFunction.apply(store, args)
14313                                 storageOwner.removeChild(storage)
14314                                 return result
14315                         }
14316                 }
14317
14318                 // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
14319                 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
14320                 function ieKeyFix(key) {
14321                         return key.replace(forbiddenCharsRegex, '___')
14322                 }
14323                 store.set = withIEStorage(function(storage, key, val) {
14324                         key = ieKeyFix(key)
14325                         if (val === undefined) { return store.remove(key) }
14326                         storage.setAttribute(key, store.serialize(val))
14327                         storage.save(localStorageName)
14328                         return val
14329                 })
14330                 store.get = withIEStorage(function(storage, key) {
14331                         key = ieKeyFix(key)
14332                         return store.deserialize(storage.getAttribute(key))
14333                 })
14334                 store.remove = withIEStorage(function(storage, key) {
14335                         key = ieKeyFix(key)
14336                         storage.removeAttribute(key)
14337                         storage.save(localStorageName)
14338                 })
14339                 store.clear = withIEStorage(function(storage) {
14340                         var attributes = storage.XMLDocument.documentElement.attributes
14341                         storage.load(localStorageName)
14342                         for (var i=0, attr; attr=attributes[i]; i++) {
14343                                 storage.removeAttribute(attr.name)
14344                         }
14345                         storage.save(localStorageName)
14346                 })
14347                 store.getAll = function(storage) {
14348                         var ret = {}
14349                         store.forEach(function(key, val) {
14350                                 ret[key] = val
14351                         })
14352                         return ret
14353                 }
14354                 store.forEach = withIEStorage(function(storage, callback) {
14355                         var attributes = storage.XMLDocument.documentElement.attributes
14356                         for (var i=0, attr; attr=attributes[i]; ++i) {
14357                                 callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
14358                         }
14359                 })
14360         }
14361
14362         try {
14363                 var testKey = '__storejs__'
14364                 store.set(testKey, testKey)
14365                 if (store.get(testKey) != testKey) { store.disabled = true }
14366                 store.remove(testKey)
14367         } catch(e) {
14368                 store.disabled = true
14369         }
14370         store.enabled = !store.disabled
14371
14372         if (typeof module != 'undefined' && module.exports) { module.exports = store }
14373         else if (typeof define === 'function' && define.amd) { define(store) }
14374         else { win.store = store }
14375
14376 })(this.window || global);
14377
14378 })(window)
14379 },{}],5:[function(require,module,exports){
14380 module.exports = hasKeys
14381
14382 function hasKeys(source) {
14383     return source !== null &&
14384         (typeof source === "object" ||
14385         typeof source === "function")
14386 }
14387
14388 },{}],4:[function(require,module,exports){
14389 var Keys = require("object-keys")
14390 var hasKeys = require("./has-keys")
14391
14392 module.exports = extend
14393
14394 function extend() {
14395     var target = {}
14396
14397     for (var i = 0; i < arguments.length; i++) {
14398         var source = arguments[i]
14399
14400         if (!hasKeys(source)) {
14401             continue
14402         }
14403
14404         var keys = Keys(source)
14405
14406         for (var j = 0; j < keys.length; j++) {
14407             var name = keys[j]
14408             target[name] = source[name]
14409         }
14410     }
14411
14412     return target
14413 }
14414
14415 },{"./has-keys":5,"object-keys":6}],7:[function(require,module,exports){
14416 (function(global){/**
14417  * jsHashes - A fast and independent hashing library pure JavaScript implemented (ES3 compliant) for both server and client side
14418  *
14419  * @class Hashes
14420  * @author Tomas Aparicio <tomas@rijndael-project.com>
14421  * @license New BSD (see LICENSE file)
14422  * @version 1.0.4
14423  *
14424  * Algorithms specification:
14425  *
14426  * MD5 <http://www.ietf.org/rfc/rfc1321.txt>
14427  * RIPEMD-160 <http://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
14428  * SHA1   <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14429  * SHA256 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14430  * SHA512 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14431  * HMAC <http://www.ietf.org/rfc/rfc2104.txt>
14432  *
14433  */
14434 (function(){
14435   var Hashes;
14436
14437   // private helper methods
14438   function utf8Encode(str) {
14439     var  x, y, output = '', i = -1, l;
14440
14441     if (str && str.length) {
14442       l = str.length;
14443       while ((i+=1) < l) {
14444         /* Decode utf-16 surrogate pairs */
14445         x = str.charCodeAt(i);
14446         y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
14447         if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
14448             x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
14449             i += 1;
14450         }
14451         /* Encode output as utf-8 */
14452         if (x <= 0x7F) {
14453             output += String.fromCharCode(x);
14454         } else if (x <= 0x7FF) {
14455             output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
14456                         0x80 | ( x & 0x3F));
14457         } else if (x <= 0xFFFF) {
14458             output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
14459                         0x80 | ((x >>> 6 ) & 0x3F),
14460                         0x80 | ( x & 0x3F));
14461         } else if (x <= 0x1FFFFF) {
14462             output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
14463                         0x80 | ((x >>> 12) & 0x3F),
14464                         0x80 | ((x >>> 6 ) & 0x3F),
14465                         0x80 | ( x & 0x3F));
14466         }
14467       }
14468     }
14469     return output;
14470   }
14471
14472   function utf8Decode(str) {
14473     var i, ac, c1, c2, c3, arr = [], l;
14474     i = ac = c1 = c2 = c3 = 0;
14475
14476     if (str && str.length) {
14477       l = str.length;
14478       str += '';
14479
14480       while (i < l) {
14481           c1 = str.charCodeAt(i);
14482           ac += 1;
14483           if (c1 < 128) {
14484               arr[ac] = String.fromCharCode(c1);
14485               i+=1;
14486           } else if (c1 > 191 && c1 < 224) {
14487               c2 = str.charCodeAt(i + 1);
14488               arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
14489               i += 2;
14490           } else {
14491               c2 = str.charCodeAt(i + 1);
14492               c3 = str.charCodeAt(i + 2);
14493               arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
14494               i += 3;
14495           }
14496       }
14497     }
14498     return arr.join('');
14499   }
14500
14501   /**
14502    * Add integers, wrapping at 2^32. This uses 16-bit operations internally
14503    * to work around bugs in some JS interpreters.
14504    */
14505   function safe_add(x, y) {
14506     var lsw = (x & 0xFFFF) + (y & 0xFFFF),
14507         msw = (x >> 16) + (y >> 16) + (lsw >> 16);
14508     return (msw << 16) | (lsw & 0xFFFF);
14509   }
14510
14511   /**
14512    * Bitwise rotate a 32-bit number to the left.
14513    */
14514   function bit_rol(num, cnt) {
14515     return (num << cnt) | (num >>> (32 - cnt));
14516   }
14517
14518   /**
14519    * Convert a raw string to a hex string
14520    */
14521   function rstr2hex(input, hexcase) {
14522     var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
14523         output = '', x, i = 0, l = input.length;
14524     for (; i < l; i+=1) {
14525       x = input.charCodeAt(i);
14526       output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
14527     }
14528     return output;
14529   }
14530
14531   /**
14532    * Encode a string as utf-16
14533    */
14534   function str2rstr_utf16le(input) {
14535     var i, l = input.length, output = '';
14536     for (i = 0; i < l; i+=1) {
14537       output += String.fromCharCode( input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF);
14538     }
14539     return output;
14540   }
14541
14542   function str2rstr_utf16be(input) {
14543     var i, l = input.length, output = '';
14544     for (i = 0; i < l; i+=1) {
14545       output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF);
14546     }
14547     return output;
14548   }
14549
14550   /**
14551    * Convert an array of big-endian words to a string
14552    */
14553   function binb2rstr(input) {
14554     var i, l = input.length * 32, output = '';
14555     for (i = 0; i < l; i += 8) {
14556         output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
14557     }
14558     return output;
14559   }
14560
14561   /**
14562    * Convert an array of little-endian words to a string
14563    */
14564   function binl2rstr(input) {
14565     var i, l = input.length * 32, output = '';
14566     for (i = 0;i < l; i += 8) {
14567       output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
14568     }
14569     return output;
14570   }
14571
14572   /**
14573    * Convert a raw string to an array of little-endian words
14574    * Characters >255 have their high-byte silently ignored.
14575    */
14576   function rstr2binl(input) {
14577     var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
14578     for (i = 0; i < lo; i+=1) {
14579       output[i] = 0;
14580     }
14581     for (i = 0; i < l; i += 8) {
14582       output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
14583     }
14584     return output;
14585   }
14586
14587   /**
14588    * Convert a raw string to an array of big-endian words
14589    * Characters >255 have their high-byte silently ignored.
14590    */
14591    function rstr2binb(input) {
14592       var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
14593       for (i = 0; i < lo; i+=1) {
14594             output[i] = 0;
14595         }
14596       for (i = 0; i < l; i += 8) {
14597             output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
14598         }
14599       return output;
14600    }
14601
14602   /**
14603    * Convert a raw string to an arbitrary string encoding
14604    */
14605   function rstr2any(input, encoding) {
14606     var divisor = encoding.length,
14607         remainders = Array(),
14608         i, q, x, ld, quotient, dividend, output, full_length;
14609
14610     /* Convert to an array of 16-bit big-endian values, forming the dividend */
14611     dividend = Array(Math.ceil(input.length / 2));
14612     ld = dividend.length;
14613     for (i = 0; i < ld; i+=1) {
14614       dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
14615     }
14616
14617     /**
14618      * Repeatedly perform a long division. The binary array forms the dividend,
14619      * the length of the encoding is the divisor. Once computed, the quotient
14620      * forms the dividend for the next step. We stop when the dividend is zerHashes.
14621      * All remainders are stored for later use.
14622      */
14623     while(dividend.length > 0) {
14624       quotient = Array();
14625       x = 0;
14626       for (i = 0; i < dividend.length; i+=1) {
14627         x = (x << 16) + dividend[i];
14628         q = Math.floor(x / divisor);
14629         x -= q * divisor;
14630         if (quotient.length > 0 || q > 0) {
14631           quotient[quotient.length] = q;
14632         }
14633       }
14634       remainders[remainders.length] = x;
14635       dividend = quotient;
14636     }
14637
14638     /* Convert the remainders to the output string */
14639     output = '';
14640     for (i = remainders.length - 1; i >= 0; i--) {
14641       output += encoding.charAt(remainders[i]);
14642     }
14643
14644     /* Append leading zero equivalents */
14645     full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
14646     for (i = output.length; i < full_length; i+=1) {
14647       output = encoding[0] + output;
14648     }
14649     return output;
14650   }
14651
14652   /**
14653    * Convert a raw string to a base-64 string
14654    */
14655   function rstr2b64(input, b64pad) {
14656     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
14657         output = '',
14658         len = input.length, i, j, triplet;
14659     b64pad= b64pad || '=';
14660     for (i = 0; i < len; i += 3) {
14661       triplet = (input.charCodeAt(i) << 16)
14662             | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
14663             | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
14664       for (j = 0; j < 4; j+=1) {
14665         if (i * 8 + j * 6 > input.length * 8) {
14666           output += b64pad;
14667         } else {
14668           output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
14669         }
14670        }
14671     }
14672     return output;
14673   }
14674
14675   Hashes = {
14676   /**
14677    * @property {String} version
14678    * @readonly
14679    */
14680   VERSION : '1.0.3',
14681   /**
14682    * @member Hashes
14683    * @class Base64
14684    * @constructor
14685    */
14686   Base64 : function () {
14687     // private properties
14688     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
14689         pad = '=', // default pad according with the RFC standard
14690         url = false, // URL encoding support @todo
14691         utf8 = true; // by default enable UTF-8 support encoding
14692
14693     // public method for encoding
14694     this.encode = function (input) {
14695       var i, j, triplet,
14696           output = '',
14697           len = input.length;
14698
14699       pad = pad || '=';
14700       input = (utf8) ? utf8Encode(input) : input;
14701
14702       for (i = 0; i < len; i += 3) {
14703         triplet = (input.charCodeAt(i) << 16)
14704               | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
14705               | (i + 2 < len ? input.charCodeAt(i+2) : 0);
14706         for (j = 0; j < 4; j+=1) {
14707           if (i * 8 + j * 6 > len * 8) {
14708               output += pad;
14709           } else {
14710               output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
14711           }
14712         }
14713       }
14714       return output;
14715     };
14716
14717     // public method for decoding
14718     this.decode = function (input) {
14719       // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
14720       var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
14721         dec = '',
14722         arr = [];
14723       if (!input) { return input; }
14724
14725       i = ac = 0;
14726       input = input.replace(new RegExp('\\'+pad,'gi'),''); // use '='
14727       //input += '';
14728
14729       do { // unpack four hexets into three octets using index points in b64
14730         h1 = tab.indexOf(input.charAt(i+=1));
14731         h2 = tab.indexOf(input.charAt(i+=1));
14732         h3 = tab.indexOf(input.charAt(i+=1));
14733         h4 = tab.indexOf(input.charAt(i+=1));
14734
14735         bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
14736
14737         o1 = bits >> 16 & 0xff;
14738         o2 = bits >> 8 & 0xff;
14739         o3 = bits & 0xff;
14740         ac += 1;
14741
14742         if (h3 === 64) {
14743           arr[ac] = String.fromCharCode(o1);
14744         } else if (h4 === 64) {
14745           arr[ac] = String.fromCharCode(o1, o2);
14746         } else {
14747           arr[ac] = String.fromCharCode(o1, o2, o3);
14748         }
14749       } while (i < input.length);
14750
14751       dec = arr.join('');
14752       dec = (utf8) ? utf8Decode(dec) : dec;
14753
14754       return dec;
14755     };
14756
14757     // set custom pad string
14758     this.setPad = function (str) {
14759         pad = str || pad;
14760         return this;
14761     };
14762     // set custom tab string characters
14763     this.setTab = function (str) {
14764         tab = str || tab;
14765         return this;
14766     };
14767     this.setUTF8 = function (bool) {
14768         if (typeof bool === 'boolean') {
14769           utf8 = bool;
14770         }
14771         return this;
14772     };
14773   },
14774
14775   /**
14776    * CRC-32 calculation
14777    * @member Hashes
14778    * @method CRC32
14779    * @static
14780    * @param {String} str Input String
14781    * @return {String}
14782    */
14783   CRC32 : function (str) {
14784     var crc = 0, x = 0, y = 0, table, i, iTop;
14785     str = utf8Encode(str);
14786
14787     table = [
14788         '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
14789         '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
14790         '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
14791         '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
14792         'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
14793         '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
14794         'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
14795         '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
14796         'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
14797         '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
14798         'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
14799         '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
14800         'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
14801         '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
14802         '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
14803         '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
14804         '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
14805         'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
14806         '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
14807         'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
14808         '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
14809         'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
14810         '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
14811         'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
14812         '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
14813         'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
14814     ].join('');
14815
14816     crc = crc ^ (-1);
14817     for (i = 0, iTop = str.length; i < iTop; i+=1 ) {
14818         y = ( crc ^ str.charCodeAt( i ) ) & 0xFF;
14819         x = '0x' + table.substr( y * 9, 8 );
14820         crc = ( crc >>> 8 ) ^ x;
14821     }
14822     // always return a positive number (that's what >>> 0 does)
14823     return (crc ^ (-1)) >>> 0;
14824   },
14825   /**
14826    * @member Hashes
14827    * @class MD5
14828    * @constructor
14829    * @param {Object} [config]
14830    *
14831    * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
14832    * Digest Algorithm, as defined in RFC 1321.
14833    * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
14834    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
14835    * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
14836    */
14837   MD5 : function (options) {
14838     /**
14839      * Private config properties. You may need to tweak these to be compatible with
14840      * the server-side, but the defaults work in most cases.
14841      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
14842      */
14843     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
14844         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
14845         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
14846
14847     // privileged (public) methods
14848     this.hex = function (s) {
14849       return rstr2hex(rstr(s, utf8), hexcase);
14850     };
14851     this.b64 = function (s) {
14852       return rstr2b64(rstr(s), b64pad);
14853     };
14854     this.any = function(s, e) {
14855       return rstr2any(rstr(s, utf8), e);
14856     };
14857     this.hex_hmac = function (k, d) {
14858       return rstr2hex(rstr_hmac(k, d), hexcase);
14859     };
14860     this.b64_hmac = function (k, d) {
14861       return rstr2b64(rstr_hmac(k,d), b64pad);
14862     };
14863     this.any_hmac = function (k, d, e) {
14864       return rstr2any(rstr_hmac(k, d), e);
14865     };
14866     /**
14867      * Perform a simple self-test to see if the VM is working
14868      * @return {String} Hexadecimal hash sample
14869      */
14870     this.vm_test = function () {
14871       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
14872     };
14873     /**
14874      * Enable/disable uppercase hexadecimal returned string
14875      * @param {Boolean}
14876      * @return {Object} this
14877      */
14878     this.setUpperCase = function (a) {
14879       if (typeof a === 'boolean' ) {
14880         hexcase = a;
14881       }
14882       return this;
14883     };
14884     /**
14885      * Defines a base64 pad string
14886      * @param {String} Pad
14887      * @return {Object} this
14888      */
14889     this.setPad = function (a) {
14890       b64pad = a || b64pad;
14891       return this;
14892     };
14893     /**
14894      * Defines a base64 pad string
14895      * @param {Boolean}
14896      * @return {Object} [this]
14897      */
14898     this.setUTF8 = function (a) {
14899       if (typeof a === 'boolean') {
14900         utf8 = a;
14901       }
14902       return this;
14903     };
14904
14905     // private methods
14906
14907     /**
14908      * Calculate the MD5 of a raw string
14909      */
14910     function rstr(s) {
14911       s = (utf8) ? utf8Encode(s): s;
14912       return binl2rstr(binl(rstr2binl(s), s.length * 8));
14913     }
14914
14915     /**
14916      * Calculate the HMAC-MD5, of a key and some data (raw strings)
14917      */
14918     function rstr_hmac(key, data) {
14919       var bkey, ipad, opad, hash, i;
14920
14921       key = (utf8) ? utf8Encode(key) : key;
14922       data = (utf8) ? utf8Encode(data) : data;
14923       bkey = rstr2binl(key);
14924       if (bkey.length > 16) {
14925         bkey = binl(bkey, key.length * 8);
14926       }
14927
14928       ipad = Array(16), opad = Array(16);
14929       for (i = 0; i < 16; i+=1) {
14930           ipad[i] = bkey[i] ^ 0x36363636;
14931           opad[i] = bkey[i] ^ 0x5C5C5C5C;
14932       }
14933       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
14934       return binl2rstr(binl(opad.concat(hash), 512 + 128));
14935     }
14936
14937     /**
14938      * Calculate the MD5 of an array of little-endian words, and a bit length.
14939      */
14940     function binl(x, len) {
14941       var i, olda, oldb, oldc, oldd,
14942           a =  1732584193,
14943           b = -271733879,
14944           c = -1732584194,
14945           d =  271733878;
14946
14947       /* append padding */
14948       x[len >> 5] |= 0x80 << ((len) % 32);
14949       x[(((len + 64) >>> 9) << 4) + 14] = len;
14950
14951       for (i = 0; i < x.length; i += 16) {
14952         olda = a;
14953         oldb = b;
14954         oldc = c;
14955         oldd = d;
14956
14957         a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
14958         d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
14959         c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
14960         b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
14961         a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
14962         d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
14963         c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
14964         b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
14965         a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
14966         d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
14967         c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
14968         b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
14969         a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
14970         d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
14971         c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
14972         b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
14973
14974         a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
14975         d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
14976         c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
14977         b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
14978         a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
14979         d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
14980         c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
14981         b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
14982         a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
14983         d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
14984         c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
14985         b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
14986         a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
14987         d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
14988         c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
14989         b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
14990
14991         a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
14992         d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
14993         c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
14994         b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
14995         a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
14996         d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
14997         c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
14998         b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
14999         a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
15000         d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
15001         c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
15002         b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
15003         a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
15004         d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
15005         c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
15006         b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
15007
15008         a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
15009         d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
15010         c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
15011         b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
15012         a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
15013         d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
15014         c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
15015         b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
15016         a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
15017         d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
15018         c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
15019         b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
15020         a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
15021         d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
15022         c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
15023         b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
15024
15025         a = safe_add(a, olda);
15026         b = safe_add(b, oldb);
15027         c = safe_add(c, oldc);
15028         d = safe_add(d, oldd);
15029       }
15030       return Array(a, b, c, d);
15031     }
15032
15033     /**
15034      * These functions implement the four basic operations the algorithm uses.
15035      */
15036     function md5_cmn(q, a, b, x, s, t) {
15037       return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
15038     }
15039     function md5_ff(a, b, c, d, x, s, t) {
15040       return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
15041     }
15042     function md5_gg(a, b, c, d, x, s, t) {
15043       return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
15044     }
15045     function md5_hh(a, b, c, d, x, s, t) {
15046       return md5_cmn(b ^ c ^ d, a, b, x, s, t);
15047     }
15048     function md5_ii(a, b, c, d, x, s, t) {
15049       return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
15050     }
15051   },
15052   /**
15053    * @member Hashes
15054    * @class Hashes.SHA1
15055    * @param {Object} [config]
15056    * @constructor
15057    *
15058    * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
15059    * Version 2.2 Copyright Paul Johnston 2000 - 2009.
15060    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15061    * See http://pajhome.org.uk/crypt/md5 for details.
15062    */
15063   SHA1 : function (options) {
15064    /**
15065      * Private config properties. You may need to tweak these to be compatible with
15066      * the server-side, but the defaults work in most cases.
15067      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
15068      */
15069     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
15070         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
15071         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
15072
15073     // public methods
15074     this.hex = function (s) {
15075         return rstr2hex(rstr(s, utf8), hexcase);
15076     };
15077     this.b64 = function (s) {
15078         return rstr2b64(rstr(s, utf8), b64pad);
15079     };
15080     this.any = function (s, e) {
15081         return rstr2any(rstr(s, utf8), e);
15082     };
15083     this.hex_hmac = function (k, d) {
15084         return rstr2hex(rstr_hmac(k, d));
15085     };
15086     this.b64_hmac = function (k, d) {
15087         return rstr2b64(rstr_hmac(k, d), b64pad);
15088     };
15089     this.any_hmac = function (k, d, e) {
15090         return rstr2any(rstr_hmac(k, d), e);
15091     };
15092     /**
15093      * Perform a simple self-test to see if the VM is working
15094      * @return {String} Hexadecimal hash sample
15095      * @public
15096      */
15097     this.vm_test = function () {
15098       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15099     };
15100     /**
15101      * @description Enable/disable uppercase hexadecimal returned string
15102      * @param {boolean}
15103      * @return {Object} this
15104      * @public
15105      */
15106     this.setUpperCase = function (a) {
15107         if (typeof a === 'boolean') {
15108         hexcase = a;
15109       }
15110         return this;
15111     };
15112     /**
15113      * @description Defines a base64 pad string
15114      * @param {string} Pad
15115      * @return {Object} this
15116      * @public
15117      */
15118     this.setPad = function (a) {
15119       b64pad = a || b64pad;
15120         return this;
15121     };
15122     /**
15123      * @description Defines a base64 pad string
15124      * @param {boolean}
15125      * @return {Object} this
15126      * @public
15127      */
15128     this.setUTF8 = function (a) {
15129         if (typeof a === 'boolean') {
15130         utf8 = a;
15131       }
15132         return this;
15133     };
15134
15135     // private methods
15136
15137     /**
15138          * Calculate the SHA-512 of a raw string
15139          */
15140         function rstr(s) {
15141       s = (utf8) ? utf8Encode(s) : s;
15142       return binb2rstr(binb(rstr2binb(s), s.length * 8));
15143         }
15144
15145     /**
15146      * Calculate the HMAC-SHA1 of a key and some data (raw strings)
15147      */
15148     function rstr_hmac(key, data) {
15149         var bkey, ipad, opad, i, hash;
15150         key = (utf8) ? utf8Encode(key) : key;
15151         data = (utf8) ? utf8Encode(data) : data;
15152         bkey = rstr2binb(key);
15153
15154         if (bkey.length > 16) {
15155         bkey = binb(bkey, key.length * 8);
15156       }
15157         ipad = Array(16), opad = Array(16);
15158         for (i = 0; i < 16; i+=1) {
15159                 ipad[i] = bkey[i] ^ 0x36363636;
15160                 opad[i] = bkey[i] ^ 0x5C5C5C5C;
15161         }
15162         hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
15163         return binb2rstr(binb(opad.concat(hash), 512 + 160));
15164     }
15165
15166     /**
15167      * Calculate the SHA-1 of an array of big-endian words, and a bit length
15168      */
15169     function binb(x, len) {
15170       var i, j, t, olda, oldb, oldc, oldd, olde,
15171           w = Array(80),
15172           a =  1732584193,
15173           b = -271733879,
15174           c = -1732584194,
15175           d =  271733878,
15176           e = -1009589776;
15177
15178       /* append padding */
15179       x[len >> 5] |= 0x80 << (24 - len % 32);
15180       x[((len + 64 >> 9) << 4) + 15] = len;
15181
15182       for (i = 0; i < x.length; i += 16) {
15183         olda = a,
15184         oldb = b;
15185         oldc = c;
15186         oldd = d;
15187         olde = e;
15188
15189         for (j = 0; j < 80; j+=1)       {
15190           if (j < 16) {
15191             w[j] = x[i + j];
15192           } else {
15193             w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
15194           }
15195           t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
15196                                            safe_add(safe_add(e, w[j]), sha1_kt(j)));
15197           e = d;
15198           d = c;
15199           c = bit_rol(b, 30);
15200           b = a;
15201           a = t;
15202         }
15203
15204         a = safe_add(a, olda);
15205         b = safe_add(b, oldb);
15206         c = safe_add(c, oldc);
15207         d = safe_add(d, oldd);
15208         e = safe_add(e, olde);
15209       }
15210       return Array(a, b, c, d, e);
15211     }
15212
15213     /**
15214      * Perform the appropriate triplet combination function for the current
15215      * iteration
15216      */
15217     function sha1_ft(t, b, c, d) {
15218       if (t < 20) { return (b & c) | ((~b) & d); }
15219       if (t < 40) { return b ^ c ^ d; }
15220       if (t < 60) { return (b & c) | (b & d) | (c & d); }
15221       return b ^ c ^ d;
15222     }
15223
15224     /**
15225      * Determine the appropriate additive constant for the current iteration
15226      */
15227     function sha1_kt(t) {
15228       return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
15229                  (t < 60) ? -1894007588 : -899497514;
15230     }
15231   },
15232   /**
15233    * @class Hashes.SHA256
15234    * @param {config}
15235    *
15236    * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
15237    * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
15238    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15239    * See http://pajhome.org.uk/crypt/md5 for details.
15240    * Also http://anmar.eu.org/projects/jssha2/
15241    */
15242   SHA256 : function (options) {
15243     /**
15244      * Private properties configuration variables. You may need to tweak these to be compatible with
15245      * the server-side, but the defaults work in most cases.
15246      * @see this.setUpperCase() method
15247      * @see this.setPad() method
15248      */
15249     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase  */
15250               b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', /* base-64 pad character. Default '=' for strict RFC compliance   */
15251               utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15252               sha256_K;
15253
15254     /* privileged (public) methods */
15255     this.hex = function (s) {
15256       return rstr2hex(rstr(s, utf8));
15257     };
15258     this.b64 = function (s) {
15259       return rstr2b64(rstr(s, utf8), b64pad);
15260     };
15261     this.any = function (s, e) {
15262       return rstr2any(rstr(s, utf8), e);
15263     };
15264     this.hex_hmac = function (k, d) {
15265       return rstr2hex(rstr_hmac(k, d));
15266     };
15267     this.b64_hmac = function (k, d) {
15268       return rstr2b64(rstr_hmac(k, d), b64pad);
15269     };
15270     this.any_hmac = function (k, d, e) {
15271       return rstr2any(rstr_hmac(k, d), e);
15272     };
15273     /**
15274      * Perform a simple self-test to see if the VM is working
15275      * @return {String} Hexadecimal hash sample
15276      * @public
15277      */
15278     this.vm_test = function () {
15279       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15280     };
15281     /**
15282      * Enable/disable uppercase hexadecimal returned string
15283      * @param {boolean}
15284      * @return {Object} this
15285      * @public
15286      */
15287     this.setUpperCase = function (a) {
15288       if (typeof a === 'boolean') {
15289         hexcase = a;
15290       }
15291       return this;
15292     };
15293     /**
15294      * @description Defines a base64 pad string
15295      * @param {string} Pad
15296      * @return {Object} this
15297      * @public
15298      */
15299     this.setPad = function (a) {
15300       b64pad = a || b64pad;
15301       return this;
15302     };
15303     /**
15304      * Defines a base64 pad string
15305      * @param {boolean}
15306      * @return {Object} this
15307      * @public
15308      */
15309     this.setUTF8 = function (a) {
15310       if (typeof a === 'boolean') {
15311         utf8 = a;
15312       }
15313       return this;
15314     };
15315
15316     // private methods
15317
15318     /**
15319      * Calculate the SHA-512 of a raw string
15320      */
15321     function rstr(s, utf8) {
15322       s = (utf8) ? utf8Encode(s) : s;
15323       return binb2rstr(binb(rstr2binb(s), s.length * 8));
15324     }
15325
15326     /**
15327      * Calculate the HMAC-sha256 of a key and some data (raw strings)
15328      */
15329     function rstr_hmac(key, data) {
15330       key = (utf8) ? utf8Encode(key) : key;
15331       data = (utf8) ? utf8Encode(data) : data;
15332       var hash, i = 0,
15333           bkey = rstr2binb(key),
15334           ipad = Array(16),
15335           opad = Array(16);
15336
15337       if (bkey.length > 16) { bkey = binb(bkey, key.length * 8); }
15338
15339       for (; i < 16; i+=1) {
15340         ipad[i] = bkey[i] ^ 0x36363636;
15341         opad[i] = bkey[i] ^ 0x5C5C5C5C;
15342       }
15343
15344       hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
15345       return binb2rstr(binb(opad.concat(hash), 512 + 256));
15346     }
15347
15348     /*
15349      * Main sha256 function, with its support functions
15350      */
15351     function sha256_S (X, n) {return ( X >>> n ) | (X << (32 - n));}
15352     function sha256_R (X, n) {return ( X >>> n );}
15353     function sha256_Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
15354     function sha256_Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
15355     function sha256_Sigma0256(x) {return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));}
15356     function sha256_Sigma1256(x) {return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));}
15357     function sha256_Gamma0256(x) {return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));}
15358     function sha256_Gamma1256(x) {return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));}
15359     function sha256_Sigma0512(x) {return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39));}
15360     function sha256_Sigma1512(x) {return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41));}
15361     function sha256_Gamma0512(x) {return (sha256_S(x, 1)  ^ sha256_S(x, 8) ^ sha256_R(x, 7));}
15362     function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));}
15363
15364     sha256_K = [
15365       1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,
15366       -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
15367       1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
15368       264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
15369       -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
15370       113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
15371       1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885,
15372       -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
15373       430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
15374       1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,
15375       -1866530822, -1538233109, -1090935817, -965641998
15376     ];
15377
15378     function binb(m, l) {
15379       var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
15380                  1359893119, -1694144372, 528734635, 1541459225];
15381       var W = new Array(64);
15382       var a, b, c, d, e, f, g, h;
15383       var i, j, T1, T2;
15384
15385       /* append padding */
15386       m[l >> 5] |= 0x80 << (24 - l % 32);
15387       m[((l + 64 >> 9) << 4) + 15] = l;
15388
15389       for (i = 0; i < m.length; i += 16)
15390       {
15391       a = HASH[0];
15392       b = HASH[1];
15393       c = HASH[2];
15394       d = HASH[3];
15395       e = HASH[4];
15396       f = HASH[5];
15397       g = HASH[6];
15398       h = HASH[7];
15399
15400       for (j = 0; j < 64; j+=1)
15401       {
15402         if (j < 16) {
15403           W[j] = m[j + i];
15404         } else {
15405           W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
15406                           sha256_Gamma0256(W[j - 15])), W[j - 16]);
15407         }
15408
15409         T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
15410                                   sha256_K[j]), W[j]);
15411         T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
15412         h = g;
15413         g = f;
15414         f = e;
15415         e = safe_add(d, T1);
15416         d = c;
15417         c = b;
15418         b = a;
15419         a = safe_add(T1, T2);
15420       }
15421
15422       HASH[0] = safe_add(a, HASH[0]);
15423       HASH[1] = safe_add(b, HASH[1]);
15424       HASH[2] = safe_add(c, HASH[2]);
15425       HASH[3] = safe_add(d, HASH[3]);
15426       HASH[4] = safe_add(e, HASH[4]);
15427       HASH[5] = safe_add(f, HASH[5]);
15428       HASH[6] = safe_add(g, HASH[6]);
15429       HASH[7] = safe_add(h, HASH[7]);
15430       }
15431       return HASH;
15432     }
15433
15434   },
15435
15436   /**
15437    * @class Hashes.SHA512
15438    * @param {config}
15439    *
15440    * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
15441    * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
15442    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15443    * See http://pajhome.org.uk/crypt/md5 for details.
15444    */
15445   SHA512 : function (options) {
15446     /**
15447      * Private properties configuration variables. You may need to tweak these to be compatible with
15448      * the server-side, but the defaults work in most cases.
15449      * @see this.setUpperCase() method
15450      * @see this.setPad() method
15451      */
15452     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false , /* hexadecimal output case format. false - lowercase; true - uppercase  */
15453         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
15454         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15455         sha512_k;
15456
15457     /* privileged (public) methods */
15458     this.hex = function (s) {
15459       return rstr2hex(rstr(s));
15460     };
15461     this.b64 = function (s) {
15462       return rstr2b64(rstr(s), b64pad);
15463     };
15464     this.any = function (s, e) {
15465       return rstr2any(rstr(s), e);
15466     };
15467     this.hex_hmac = function (k, d) {
15468       return rstr2hex(rstr_hmac(k, d));
15469     };
15470     this.b64_hmac = function (k, d) {
15471       return rstr2b64(rstr_hmac(k, d), b64pad);
15472     };
15473     this.any_hmac = function (k, d, e) {
15474       return rstr2any(rstr_hmac(k, d), e);
15475     };
15476     /**
15477      * Perform a simple self-test to see if the VM is working
15478      * @return {String} Hexadecimal hash sample
15479      * @public
15480      */
15481     this.vm_test = function () {
15482       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15483     };
15484     /**
15485      * @description Enable/disable uppercase hexadecimal returned string
15486      * @param {boolean}
15487      * @return {Object} this
15488      * @public
15489      */
15490     this.setUpperCase = function (a) {
15491       if (typeof a === 'boolean') {
15492         hexcase = a;
15493       }
15494       return this;
15495     };
15496     /**
15497      * @description Defines a base64 pad string
15498      * @param {string} Pad
15499      * @return {Object} this
15500      * @public
15501      */
15502     this.setPad = function (a) {
15503       b64pad = a || b64pad;
15504       return this;
15505     };
15506     /**
15507      * @description Defines a base64 pad string
15508      * @param {boolean}
15509      * @return {Object} this
15510      * @public
15511      */
15512     this.setUTF8 = function (a) {
15513       if (typeof a === 'boolean') {
15514         utf8 = a;
15515       }
15516       return this;
15517     };
15518
15519     /* private methods */
15520
15521     /**
15522      * Calculate the SHA-512 of a raw string
15523      */
15524     function rstr(s) {
15525       s = (utf8) ? utf8Encode(s) : s;
15526       return binb2rstr(binb(rstr2binb(s), s.length * 8));
15527     }
15528     /*
15529      * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
15530      */
15531     function rstr_hmac(key, data) {
15532       key = (utf8) ? utf8Encode(key) : key;
15533       data = (utf8) ? utf8Encode(data) : data;
15534
15535       var hash, i = 0,
15536           bkey = rstr2binb(key),
15537           ipad = Array(32), opad = Array(32);
15538
15539       if (bkey.length > 32) { bkey = binb(bkey, key.length * 8); }
15540
15541       for (; i < 32; i+=1) {
15542         ipad[i] = bkey[i] ^ 0x36363636;
15543         opad[i] = bkey[i] ^ 0x5C5C5C5C;
15544       }
15545
15546       hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
15547       return binb2rstr(binb(opad.concat(hash), 1024 + 512));
15548     }
15549
15550     /**
15551      * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
15552      */
15553     function binb(x, len) {
15554       var j, i, l,
15555           W = new Array(80),
15556           hash = new Array(16),
15557           //Initial hash values
15558           H = [
15559             new int64(0x6a09e667, -205731576),
15560             new int64(-1150833019, -2067093701),
15561             new int64(0x3c6ef372, -23791573),
15562             new int64(-1521486534, 0x5f1d36f1),
15563             new int64(0x510e527f, -1377402159),
15564             new int64(-1694144372, 0x2b3e6c1f),
15565             new int64(0x1f83d9ab, -79577749),
15566             new int64(0x5be0cd19, 0x137e2179)
15567           ],
15568           T1 = new int64(0, 0),
15569           T2 = new int64(0, 0),
15570           a = new int64(0,0),
15571           b = new int64(0,0),
15572           c = new int64(0,0),
15573           d = new int64(0,0),
15574           e = new int64(0,0),
15575           f = new int64(0,0),
15576           g = new int64(0,0),
15577           h = new int64(0,0),
15578           //Temporary variables not specified by the document
15579           s0 = new int64(0, 0),
15580           s1 = new int64(0, 0),
15581           Ch = new int64(0, 0),
15582           Maj = new int64(0, 0),
15583           r1 = new int64(0, 0),
15584           r2 = new int64(0, 0),
15585           r3 = new int64(0, 0);
15586
15587       if (sha512_k === undefined) {
15588           //SHA512 constants
15589           sha512_k = [
15590             new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
15591             new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
15592             new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
15593             new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
15594             new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
15595             new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
15596             new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
15597             new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
15598             new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
15599             new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
15600             new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
15601             new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
15602             new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
15603             new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
15604             new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
15605             new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
15606             new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
15607             new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
15608             new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
15609             new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
15610             new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
15611             new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
15612             new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
15613             new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
15614             new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
15615             new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
15616             new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
15617             new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
15618             new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
15619             new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
15620             new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
15621             new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
15622             new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
15623             new int64(-354779690, -840897762), new int64(-176337025, -294727304),
15624             new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
15625             new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
15626             new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
15627             new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
15628             new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
15629             new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
15630           ];
15631       }
15632
15633       for (i=0; i<80; i+=1) {
15634         W[i] = new int64(0, 0);
15635       }
15636
15637       // append padding to the source string. The format is described in the FIPS.
15638       x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
15639       x[((len + 128 >> 10)<< 5) + 31] = len;
15640       l = x.length;
15641       for (i = 0; i<l; i+=32) { //32 dwords is the block size
15642         int64copy(a, H[0]);
15643         int64copy(b, H[1]);
15644         int64copy(c, H[2]);
15645         int64copy(d, H[3]);
15646         int64copy(e, H[4]);
15647         int64copy(f, H[5]);
15648         int64copy(g, H[6]);
15649         int64copy(h, H[7]);
15650
15651         for (j=0; j<16; j+=1) {
15652           W[j].h = x[i + 2*j];
15653           W[j].l = x[i + 2*j + 1];
15654         }
15655
15656         for (j=16; j<80; j+=1) {
15657           //sigma1
15658           int64rrot(r1, W[j-2], 19);
15659           int64revrrot(r2, W[j-2], 29);
15660           int64shr(r3, W[j-2], 6);
15661           s1.l = r1.l ^ r2.l ^ r3.l;
15662           s1.h = r1.h ^ r2.h ^ r3.h;
15663           //sigma0
15664           int64rrot(r1, W[j-15], 1);
15665           int64rrot(r2, W[j-15], 8);
15666           int64shr(r3, W[j-15], 7);
15667           s0.l = r1.l ^ r2.l ^ r3.l;
15668           s0.h = r1.h ^ r2.h ^ r3.h;
15669
15670           int64add4(W[j], s1, W[j-7], s0, W[j-16]);
15671         }
15672
15673         for (j = 0; j < 80; j+=1) {
15674           //Ch
15675           Ch.l = (e.l & f.l) ^ (~e.l & g.l);
15676           Ch.h = (e.h & f.h) ^ (~e.h & g.h);
15677
15678           //Sigma1
15679           int64rrot(r1, e, 14);
15680           int64rrot(r2, e, 18);
15681           int64revrrot(r3, e, 9);
15682           s1.l = r1.l ^ r2.l ^ r3.l;
15683           s1.h = r1.h ^ r2.h ^ r3.h;
15684
15685           //Sigma0
15686           int64rrot(r1, a, 28);
15687           int64revrrot(r2, a, 2);
15688           int64revrrot(r3, a, 7);
15689           s0.l = r1.l ^ r2.l ^ r3.l;
15690           s0.h = r1.h ^ r2.h ^ r3.h;
15691
15692           //Maj
15693           Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
15694           Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
15695
15696           int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
15697           int64add(T2, s0, Maj);
15698
15699           int64copy(h, g);
15700           int64copy(g, f);
15701           int64copy(f, e);
15702           int64add(e, d, T1);
15703           int64copy(d, c);
15704           int64copy(c, b);
15705           int64copy(b, a);
15706           int64add(a, T1, T2);
15707         }
15708         int64add(H[0], H[0], a);
15709         int64add(H[1], H[1], b);
15710         int64add(H[2], H[2], c);
15711         int64add(H[3], H[3], d);
15712         int64add(H[4], H[4], e);
15713         int64add(H[5], H[5], f);
15714         int64add(H[6], H[6], g);
15715         int64add(H[7], H[7], h);
15716       }
15717
15718       //represent the hash as an array of 32-bit dwords
15719       for (i=0; i<8; i+=1) {
15720         hash[2*i] = H[i].h;
15721         hash[2*i + 1] = H[i].l;
15722       }
15723       return hash;
15724     }
15725
15726     //A constructor for 64-bit numbers
15727     function int64(h, l) {
15728       this.h = h;
15729       this.l = l;
15730       //this.toString = int64toString;
15731     }
15732
15733     //Copies src into dst, assuming both are 64-bit numbers
15734     function int64copy(dst, src) {
15735       dst.h = src.h;
15736       dst.l = src.l;
15737     }
15738
15739     //Right-rotates a 64-bit number by shift
15740     //Won't handle cases of shift>=32
15741     //The function revrrot() is for that
15742     function int64rrot(dst, x, shift) {
15743       dst.l = (x.l >>> shift) | (x.h << (32-shift));
15744       dst.h = (x.h >>> shift) | (x.l << (32-shift));
15745     }
15746
15747     //Reverses the dwords of the source and then rotates right by shift.
15748     //This is equivalent to rotation by 32+shift
15749     function int64revrrot(dst, x, shift) {
15750       dst.l = (x.h >>> shift) | (x.l << (32-shift));
15751       dst.h = (x.l >>> shift) | (x.h << (32-shift));
15752     }
15753
15754     //Bitwise-shifts right a 64-bit number by shift
15755     //Won't handle shift>=32, but it's never needed in SHA512
15756     function int64shr(dst, x, shift) {
15757       dst.l = (x.l >>> shift) | (x.h << (32-shift));
15758       dst.h = (x.h >>> shift);
15759     }
15760
15761     //Adds two 64-bit numbers
15762     //Like the original implementation, does not rely on 32-bit operations
15763     function int64add(dst, x, y) {
15764        var w0 = (x.l & 0xffff) + (y.l & 0xffff);
15765        var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
15766        var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
15767        var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
15768        dst.l = (w0 & 0xffff) | (w1 << 16);
15769        dst.h = (w2 & 0xffff) | (w3 << 16);
15770     }
15771
15772     //Same, except with 4 addends. Works faster than adding them one by one.
15773     function int64add4(dst, a, b, c, d) {
15774        var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
15775        var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
15776        var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
15777        var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
15778        dst.l = (w0 & 0xffff) | (w1 << 16);
15779        dst.h = (w2 & 0xffff) | (w3 << 16);
15780     }
15781
15782     //Same, except with 5 addends
15783     function int64add5(dst, a, b, c, d, e) {
15784       var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
15785           w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
15786           w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
15787           w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
15788        dst.l = (w0 & 0xffff) | (w1 << 16);
15789        dst.h = (w2 & 0xffff) | (w3 << 16);
15790     }
15791   },
15792   /**
15793    * @class Hashes.RMD160
15794    * @constructor
15795    * @param {Object} [config]
15796    *
15797    * A JavaScript implementation of the RIPEMD-160 Algorithm
15798    * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
15799    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15800    * See http://pajhome.org.uk/crypt/md5 for details.
15801    * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
15802    */
15803   RMD160 : function (options) {
15804     /**
15805      * Private properties configuration variables. You may need to tweak these to be compatible with
15806      * the server-side, but the defaults work in most cases.
15807      * @see this.setUpperCase() method
15808      * @see this.setPad() method
15809      */
15810     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,   /* hexadecimal output case format. false - lowercase; true - uppercase  */
15811         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
15812         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15813         rmd160_r1 = [
15814            0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
15815            7,  4, 13,  1, 10,  6, 15,  3, 12,  0,  9,  5,  2, 14, 11,  8,
15816            3, 10, 14,  4,  9, 15,  8,  1,  2,  7,  0,  6, 13, 11,  5, 12,
15817            1,  9, 11, 10,  0,  8, 12,  4, 13,  3,  7, 15, 14,  5,  6,  2,
15818            4,  0,  5,  9,  7, 12,  2, 10, 14,  1,  3,  8, 11,  6, 15, 13
15819         ],
15820         rmd160_r2 = [
15821            5, 14,  7,  0,  9,  2, 11,  4, 13,  6, 15,  8,  1, 10,  3, 12,
15822            6, 11,  3,  7,  0, 13,  5, 10, 14, 15,  8, 12,  4,  9,  1,  2,
15823           15,  5,  1,  3,  7, 14,  6,  9, 11,  8, 12,  2, 10,  0,  4, 13,
15824            8,  6,  4,  1,  3, 11, 15,  0,  5, 12,  2, 13,  9,  7, 10, 14,
15825           12, 15, 10,  4,  1,  5,  8,  7,  6,  2, 13, 14,  0,  3,  9, 11
15826         ],
15827         rmd160_s1 = [
15828           11, 14, 15, 12,  5,  8,  7,  9, 11, 13, 14, 15,  6,  7,  9,  8,
15829            7,  6,  8, 13, 11,  9,  7, 15,  7, 12, 15,  9, 11,  7, 13, 12,
15830           11, 13,  6,  7, 14,  9, 13, 15, 14,  8, 13,  6,  5, 12,  7,  5,
15831           11, 12, 14, 15, 14, 15,  9,  8,  9, 14,  5,  6,  8,  6,  5, 12,
15832            9, 15,  5, 11,  6,  8, 13, 12,  5, 12, 13, 14, 11,  8,  5,  6
15833         ],
15834         rmd160_s2 = [
15835            8,  9,  9, 11, 13, 15, 15,  5,  7,  7,  8, 11, 14, 14, 12,  6,
15836            9, 13, 15,  7, 12,  8,  9, 11,  7,  7, 12,  7,  6, 15, 13, 11,
15837            9,  7, 15, 11,  8,  6,  6, 14, 12, 13,  5, 14, 13, 13,  7,  5,
15838           15,  5,  8, 11, 14, 14,  6, 14,  6,  9, 12,  9, 12,  5, 15,  8,
15839            8,  5, 12,  9, 12,  5, 14,  6,  8, 13,  6,  5, 15, 13, 11, 11
15840         ];
15841
15842     /* privileged (public) methods */
15843     this.hex = function (s) {
15844       return rstr2hex(rstr(s, utf8));
15845     };
15846     this.b64 = function (s) {
15847       return rstr2b64(rstr(s, utf8), b64pad);
15848     };
15849     this.any = function (s, e) {
15850       return rstr2any(rstr(s, utf8), e);
15851     };
15852     this.hex_hmac = function (k, d) {
15853       return rstr2hex(rstr_hmac(k, d));
15854     };
15855     this.b64_hmac = function (k, d) {
15856       return rstr2b64(rstr_hmac(k, d), b64pad);
15857     };
15858     this.any_hmac = function (k, d, e) {
15859       return rstr2any(rstr_hmac(k, d), e);
15860     };
15861     /**
15862      * Perform a simple self-test to see if the VM is working
15863      * @return {String} Hexadecimal hash sample
15864      * @public
15865      */
15866     this.vm_test = function () {
15867       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15868     };
15869     /**
15870      * @description Enable/disable uppercase hexadecimal returned string
15871      * @param {boolean}
15872      * @return {Object} this
15873      * @public
15874      */
15875     this.setUpperCase = function (a) {
15876       if (typeof a === 'boolean' ) { hexcase = a; }
15877       return this;
15878     };
15879     /**
15880      * @description Defines a base64 pad string
15881      * @param {string} Pad
15882      * @return {Object} this
15883      * @public
15884      */
15885     this.setPad = function (a) {
15886       if (typeof a !== 'undefined' ) { b64pad = a; }
15887       return this;
15888     };
15889     /**
15890      * @description Defines a base64 pad string
15891      * @param {boolean}
15892      * @return {Object} this
15893      * @public
15894      */
15895     this.setUTF8 = function (a) {
15896       if (typeof a === 'boolean') { utf8 = a; }
15897       return this;
15898     };
15899
15900     /* private methods */
15901
15902     /**
15903      * Calculate the rmd160 of a raw string
15904      */
15905     function rstr(s) {
15906       s = (utf8) ? utf8Encode(s) : s;
15907       return binl2rstr(binl(rstr2binl(s), s.length * 8));
15908     }
15909
15910     /**
15911      * Calculate the HMAC-rmd160 of a key and some data (raw strings)
15912      */
15913     function rstr_hmac(key, data) {
15914       key = (utf8) ? utf8Encode(key) : key;
15915       data = (utf8) ? utf8Encode(data) : data;
15916       var i, hash,
15917           bkey = rstr2binl(key),
15918           ipad = Array(16), opad = Array(16);
15919
15920       if (bkey.length > 16) {
15921         bkey = binl(bkey, key.length * 8);
15922       }
15923
15924       for (i = 0; i < 16; i+=1) {
15925         ipad[i] = bkey[i] ^ 0x36363636;
15926         opad[i] = bkey[i] ^ 0x5C5C5C5C;
15927       }
15928       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
15929       return binl2rstr(binl(opad.concat(hash), 512 + 160));
15930     }
15931
15932     /**
15933      * Convert an array of little-endian words to a string
15934      */
15935     function binl2rstr(input) {
15936       var i, output = '', l = input.length * 32;
15937       for (i = 0; i < l; i += 8) {
15938         output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
15939       }
15940       return output;
15941     }
15942
15943     /**
15944      * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
15945      */
15946     function binl(x, len) {
15947       var T, j, i, l,
15948           h0 = 0x67452301,
15949           h1 = 0xefcdab89,
15950           h2 = 0x98badcfe,
15951           h3 = 0x10325476,
15952           h4 = 0xc3d2e1f0,
15953           A1, B1, C1, D1, E1,
15954           A2, B2, C2, D2, E2;
15955
15956       /* append padding */
15957       x[len >> 5] |= 0x80 << (len % 32);
15958       x[(((len + 64) >>> 9) << 4) + 14] = len;
15959       l = x.length;
15960
15961       for (i = 0; i < l; i+=16) {
15962         A1 = A2 = h0; B1 = B2 = h1; C1 = C2 = h2; D1 = D2 = h3; E1 = E2 = h4;
15963         for (j = 0; j <= 79; j+=1) {
15964           T = safe_add(A1, rmd160_f(j, B1, C1, D1));
15965           T = safe_add(T, x[i + rmd160_r1[j]]);
15966           T = safe_add(T, rmd160_K1(j));
15967           T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
15968           A1 = E1; E1 = D1; D1 = bit_rol(C1, 10); C1 = B1; B1 = T;
15969           T = safe_add(A2, rmd160_f(79-j, B2, C2, D2));
15970           T = safe_add(T, x[i + rmd160_r2[j]]);
15971           T = safe_add(T, rmd160_K2(j));
15972           T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
15973           A2 = E2; E2 = D2; D2 = bit_rol(C2, 10); C2 = B2; B2 = T;
15974         }
15975
15976         T = safe_add(h1, safe_add(C1, D2));
15977         h1 = safe_add(h2, safe_add(D1, E2));
15978         h2 = safe_add(h3, safe_add(E1, A2));
15979         h3 = safe_add(h4, safe_add(A1, B2));
15980         h4 = safe_add(h0, safe_add(B1, C2));
15981         h0 = T;
15982       }
15983       return [h0, h1, h2, h3, h4];
15984     }
15985
15986     // specific algorithm methods
15987     function rmd160_f(j, x, y, z) {
15988       return ( 0 <= j && j <= 15) ? (x ^ y ^ z) :
15989          (16 <= j && j <= 31) ? (x & y) | (~x & z) :
15990          (32 <= j && j <= 47) ? (x | ~y) ^ z :
15991          (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
15992          (64 <= j && j <= 79) ? x ^ (y | ~z) :
15993          'rmd160_f: j out of range';
15994     }
15995
15996     function rmd160_K1(j) {
15997       return ( 0 <= j && j <= 15) ? 0x00000000 :
15998          (16 <= j && j <= 31) ? 0x5a827999 :
15999          (32 <= j && j <= 47) ? 0x6ed9eba1 :
16000          (48 <= j && j <= 63) ? 0x8f1bbcdc :
16001          (64 <= j && j <= 79) ? 0xa953fd4e :
16002          'rmd160_K1: j out of range';
16003     }
16004
16005     function rmd160_K2(j){
16006       return ( 0 <= j && j <= 15) ? 0x50a28be6 :
16007          (16 <= j && j <= 31) ? 0x5c4dd124 :
16008          (32 <= j && j <= 47) ? 0x6d703ef3 :
16009          (48 <= j && j <= 63) ? 0x7a6d76e9 :
16010          (64 <= j && j <= 79) ? 0x00000000 :
16011          'rmd160_K2: j out of range';
16012     }
16013   }
16014 };
16015
16016   // exposes Hashes
16017   (function( window, undefined ) {
16018     var freeExports = false;
16019     if (typeof exports === 'object' ) {
16020       freeExports = exports;
16021       if (exports && typeof global === 'object' && global && global === global.global ) { window = global; }
16022     }
16023
16024     if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
16025       // define as an anonymous module, so, through path mapping, it can be aliased
16026       define(function () { return Hashes; });
16027     }
16028     else if ( freeExports ) {
16029       // in Node.js or RingoJS v0.8.0+
16030       if ( typeof module === 'object' && module && module.exports === freeExports ) {
16031         module.exports = Hashes;
16032       }
16033       // in Narwhal or RingoJS v0.7.0-
16034       else {
16035         freeExports.Hashes = Hashes;
16036       }
16037     }
16038     else {
16039       // in a browser or Rhino
16040       window.Hashes = Hashes;
16041     }
16042   }( this ));
16043 }()); // IIFE
16044
16045 })(window)
16046 },{}],2:[function(require,module,exports){
16047 'use strict';
16048
16049 var hashes = require('jshashes'),
16050     xtend = require('xtend'),
16051     sha1 = new hashes.SHA1();
16052
16053 var ohauth = {};
16054
16055 ohauth.qsString = function(obj) {
16056     return Object.keys(obj).sort().map(function(key) {
16057         return ohauth.percentEncode(key) + '=' +
16058             ohauth.percentEncode(obj[key]);
16059     }).join('&');
16060 };
16061
16062 ohauth.stringQs = function(str) {
16063     return str.split('&').reduce(function(obj, pair){
16064         var parts = pair.split('=');
16065         obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
16066             '' : decodeURIComponent(parts[1]);
16067         return obj;
16068     }, {});
16069 };
16070
16071 ohauth.rawxhr = function(method, url, data, headers, callback) {
16072     var xhr = new XMLHttpRequest(),
16073         twoHundred = /^20\d$/;
16074     xhr.onreadystatechange = function() {
16075         if (4 == xhr.readyState && 0 !== xhr.status) {
16076             if (twoHundred.test(xhr.status)) callback(null, xhr);
16077             else return callback(xhr, null);
16078         }
16079     };
16080     xhr.onerror = function(e) { return callback(e, null); };
16081     xhr.open(method, url, true);
16082     for (var h in headers) xhr.setRequestHeader(h, headers[h]);
16083     xhr.send(data);
16084 };
16085
16086 ohauth.xhr = function(method, url, auth, data, options, callback) {
16087     var headers = (options && options.header) || {
16088         'Content-Type': 'application/x-www-form-urlencoded'
16089     };
16090     headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
16091     ohauth.rawxhr(method, url, data, headers, callback);
16092 };
16093
16094 ohauth.nonce = function() {
16095     for (var o = ''; o.length < 6;) {
16096         o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
16097     }
16098     return o;
16099 };
16100
16101 ohauth.authHeader = function(obj) {
16102     return Object.keys(obj).sort().map(function(key) {
16103         return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
16104     }).join(', ');
16105 };
16106
16107 ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
16108
16109 ohauth.percentEncode = function(s) {
16110     return encodeURIComponent(s)
16111         .replace(/\!/g, '%21').replace(/\'/g, '%27')
16112         .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
16113 };
16114
16115 ohauth.baseString = function(method, url, params) {
16116     if (params.oauth_signature) delete params.oauth_signature;
16117     return [
16118         method,
16119         ohauth.percentEncode(url),
16120         ohauth.percentEncode(ohauth.qsString(params))].join('&');
16121 };
16122
16123 ohauth.signature = function(oauth_secret, token_secret, baseString) {
16124     return sha1.b64_hmac(
16125         ohauth.percentEncode(oauth_secret) + '&' +
16126         ohauth.percentEncode(token_secret),
16127         baseString);
16128 };
16129
16130 /**
16131  * Takes an options object for configuration (consumer_key,
16132  * consumer_secret, version, signature_method, token) and returns a
16133  * function that generates the Authorization header for given data.
16134  *
16135  * The returned function takes these parameters:
16136  * - method: GET/POST/...
16137  * - uri: full URI with protocol, port, path and query string
16138  * - extra_params: any extra parameters (that are passed in the POST data),
16139  *   can be an object or a from-urlencoded string.
16140  *
16141  * Returned function returns full OAuth header with "OAuth" string in it.
16142  */
16143
16144 ohauth.headerGenerator = function(options) {
16145     options = options || {};
16146     var consumer_key = options.consumer_key || '',
16147         consumer_secret = options.consumer_secret || '',
16148         signature_method = options.signature_method || 'HMAC-SHA1',
16149         version = options.version || '1.0',
16150         token = options.token || '';
16151
16152     return function(method, uri, extra_params) {
16153         method = method.toUpperCase();
16154         if (typeof extra_params === 'string' && extra_params.length > 0) {
16155             extra_params = ohauth.stringQs(extra_params);
16156         }
16157
16158         var uri_parts = uri.split('?', 2),
16159         base_uri = uri_parts[0];
16160
16161         var query_params = uri_parts.length === 2 ?
16162             ohauth.stringQs(uri_parts[1]) : {};
16163
16164         var oauth_params = {
16165             oauth_consumer_key: consumer_key,
16166             oauth_signature_method: signature_method,
16167             oauth_version: version,
16168             oauth_timestamp: ohauth.timestamp(),
16169             oauth_nonce: ohauth.nonce()
16170         };
16171
16172         if (token) oauth_params.oauth_token = token;
16173
16174         var all_params = xtend({}, oauth_params, query_params, extra_params),
16175             base_str = ohauth.baseString(method, base_uri, all_params);
16176
16177         oauth_params.oauth_signature = ohauth.signature(consumer_secret, token, base_str);
16178
16179         return 'OAuth ' + ohauth.authHeader(oauth_params);
16180     };
16181 };
16182
16183 module.exports = ohauth;
16184
16185 },{"jshashes":7,"xtend":4}],6:[function(require,module,exports){
16186 module.exports = Object.keys || require('./shim');
16187
16188
16189 },{"./shim":8}],8:[function(require,module,exports){
16190 (function () {
16191         "use strict";
16192
16193         // modified from https://github.com/kriskowal/es5-shim
16194         var has = Object.prototype.hasOwnProperty,
16195                 is = require('is'),
16196                 forEach = require('foreach'),
16197                 hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'),
16198                 dontEnums = [
16199                         "toString",
16200                         "toLocaleString",
16201                         "valueOf",
16202                         "hasOwnProperty",
16203                         "isPrototypeOf",
16204                         "propertyIsEnumerable",
16205                         "constructor"
16206                 ],
16207                 keysShim;
16208
16209         keysShim = function keys(object) {
16210                 if (!is.object(object) && !is.array(object)) {
16211                         throw new TypeError("Object.keys called on a non-object");
16212                 }
16213
16214                 var name, theKeys = [];
16215                 for (name in object) {
16216                         if (has.call(object, name)) {
16217                                 theKeys.push(name);
16218                         }
16219                 }
16220
16221                 if (hasDontEnumBug) {
16222                         forEach(dontEnums, function (dontEnum) {
16223                                 if (has.call(object, dontEnum)) {
16224                                         theKeys.push(dontEnum);
16225                                 }
16226                         });
16227                 }
16228                 return theKeys;
16229         };
16230
16231         module.exports = keysShim;
16232 }());
16233
16234
16235 },{"is":9,"foreach":10}],9:[function(require,module,exports){
16236
16237 /**!
16238  * is
16239  * the definitive JavaScript type testing library
16240  *
16241  * @copyright 2013 Enrico Marino
16242  * @license MIT
16243  */
16244
16245 var objProto = Object.prototype;
16246 var owns = objProto.hasOwnProperty;
16247 var toString = objProto.toString;
16248 var isActualNaN = function (value) {
16249   return value !== value;
16250 };
16251 var NON_HOST_TYPES = {
16252   "boolean": 1,
16253   "number": 1,
16254   "string": 1,
16255   "undefined": 1
16256 };
16257
16258 /**
16259  * Expose `is`
16260  */
16261
16262 var is = module.exports = {};
16263
16264 /**
16265  * Test general.
16266  */
16267
16268 /**
16269  * is.type
16270  * Test if `value` is a type of `type`.
16271  *
16272  * @param {Mixed} value value to test
16273  * @param {String} type type
16274  * @return {Boolean} true if `value` is a type of `type`, false otherwise
16275  * @api public
16276  */
16277
16278 is.a =
16279 is.type = function (value, type) {
16280   return typeof value === type;
16281 };
16282
16283 /**
16284  * is.defined
16285  * Test if `value` is defined.
16286  *
16287  * @param {Mixed} value value to test
16288  * @return {Boolean} true if 'value' is defined, false otherwise
16289  * @api public
16290  */
16291
16292 is.defined = function (value) {
16293   return value !== undefined;
16294 };
16295
16296 /**
16297  * is.empty
16298  * Test if `value` is empty.
16299  *
16300  * @param {Mixed} value value to test
16301  * @return {Boolean} true if `value` is empty, false otherwise
16302  * @api public
16303  */
16304
16305 is.empty = function (value) {
16306   var type = toString.call(value);
16307   var key;
16308
16309   if ('[object Array]' === type || '[object Arguments]' === type) {
16310     return value.length === 0;
16311   }
16312
16313   if ('[object Object]' === type) {
16314     for (key in value) if (owns.call(value, key)) return false;
16315     return true;
16316   }
16317
16318   if ('[object String]' === type) {
16319     return '' === value;
16320   }
16321
16322   return false;
16323 };
16324
16325 /**
16326  * is.equal
16327  * Test if `value` is equal to `other`.
16328  *
16329  * @param {Mixed} value value to test
16330  * @param {Mixed} other value to compare with
16331  * @return {Boolean} true if `value` is equal to `other`, false otherwise
16332  */
16333
16334 is.equal = function (value, other) {
16335   var type = toString.call(value)
16336   var key;
16337
16338   if (type !== toString.call(other)) {
16339     return false;
16340   }
16341
16342   if ('[object Object]' === type) {
16343     for (key in value) {
16344       if (!is.equal(value[key], other[key])) {
16345         return false;
16346       }
16347     }
16348     return true;
16349   }
16350
16351   if ('[object Array]' === type) {
16352     key = value.length;
16353     if (key !== other.length) {
16354       return false;
16355     }
16356     while (--key) {
16357       if (!is.equal(value[key], other[key])) {
16358         return false;
16359       }
16360     }
16361     return true;
16362   }
16363
16364   if ('[object Function]' === type) {
16365     return value.prototype === other.prototype;
16366   }
16367
16368   if ('[object Date]' === type) {
16369     return value.getTime() === other.getTime();
16370   }
16371
16372   return value === other;
16373 };
16374
16375 /**
16376  * is.hosted
16377  * Test if `value` is hosted by `host`.
16378  *
16379  * @param {Mixed} value to test
16380  * @param {Mixed} host host to test with
16381  * @return {Boolean} true if `value` is hosted by `host`, false otherwise
16382  * @api public
16383  */
16384
16385 is.hosted = function (value, host) {
16386   var type = typeof host[value];
16387   return type === 'object' ? !!host[value] : !NON_HOST_TYPES[type];
16388 };
16389
16390 /**
16391  * is.instance
16392  * Test if `value` is an instance of `constructor`.
16393  *
16394  * @param {Mixed} value value to test
16395  * @return {Boolean} true if `value` is an instance of `constructor`
16396  * @api public
16397  */
16398
16399 is.instance = is['instanceof'] = function (value, constructor) {
16400   return value instanceof constructor;
16401 };
16402
16403 /**
16404  * is.null
16405  * Test if `value` is null.
16406  *
16407  * @param {Mixed} value value to test
16408  * @return {Boolean} true if `value` is null, false otherwise
16409  * @api public
16410  */
16411
16412 is['null'] = function (value) {
16413   return value === null;
16414 };
16415
16416 /**
16417  * is.undefined
16418  * Test if `value` is undefined.
16419  *
16420  * @param {Mixed} value value to test
16421  * @return {Boolean} true if `value` is undefined, false otherwise
16422  * @api public
16423  */
16424
16425 is.undefined = function (value) {
16426   return value === undefined;
16427 };
16428
16429 /**
16430  * Test arguments.
16431  */
16432
16433 /**
16434  * is.arguments
16435  * Test if `value` is an arguments object.
16436  *
16437  * @param {Mixed} value value to test
16438  * @return {Boolean} true if `value` is an arguments object, false otherwise
16439  * @api public
16440  */
16441
16442 is.arguments = function (value) {
16443   var isStandardArguments = '[object Arguments]' === toString.call(value);
16444   var isOldArguments = !is.array(value) && is.arraylike(value) && is.object(value) && is.fn(value.callee);
16445   return isStandardArguments || isOldArguments;
16446 };
16447
16448 /**
16449  * Test array.
16450  */
16451
16452 /**
16453  * is.array
16454  * Test if 'value' is an array.
16455  *
16456  * @param {Mixed} value value to test
16457  * @return {Boolean} true if `value` is an array, false otherwise
16458  * @api public
16459  */
16460
16461 is.array = function (value) {
16462   return '[object Array]' === toString.call(value);
16463 };
16464
16465 /**
16466  * is.arguments.empty
16467  * Test if `value` is an empty arguments object.
16468  *
16469  * @param {Mixed} value value to test
16470  * @return {Boolean} true if `value` is an empty arguments object, false otherwise
16471  * @api public
16472  */
16473 is.arguments.empty = function (value) {
16474   return is.arguments(value) && value.length === 0;
16475 };
16476
16477 /**
16478  * is.array.empty
16479  * Test if `value` is an empty array.
16480  *
16481  * @param {Mixed} value value to test
16482  * @return {Boolean} true if `value` is an empty array, false otherwise
16483  * @api public
16484  */
16485 is.array.empty = function (value) {
16486   return is.array(value) && value.length === 0;
16487 };
16488
16489 /**
16490  * is.arraylike
16491  * Test if `value` is an arraylike object.
16492  *
16493  * @param {Mixed} value value to test
16494  * @return {Boolean} true if `value` is an arguments object, false otherwise
16495  * @api public
16496  */
16497
16498 is.arraylike = function (value) {
16499   return !!value && !is.boolean(value)
16500     && owns.call(value, 'length')
16501     && isFinite(value.length)
16502     && is.number(value.length)
16503     && value.length >= 0;
16504 };
16505
16506 /**
16507  * Test boolean.
16508  */
16509
16510 /**
16511  * is.boolean
16512  * Test if `value` is a boolean.
16513  *
16514  * @param {Mixed} value value to test
16515  * @return {Boolean} true if `value` is a boolean, false otherwise
16516  * @api public
16517  */
16518
16519 is.boolean = function (value) {
16520   return '[object Boolean]' === toString.call(value);
16521 };
16522
16523 /**
16524  * is.false
16525  * Test if `value` is false.
16526  *
16527  * @param {Mixed} value value to test
16528  * @return {Boolean} true if `value` is false, false otherwise
16529  * @api public
16530  */
16531
16532 is['false'] = function (value) {
16533   return is.boolean(value) && (value === false || value.valueOf() === false);
16534 };
16535
16536 /**
16537  * is.true
16538  * Test if `value` is true.
16539  *
16540  * @param {Mixed} value value to test
16541  * @return {Boolean} true if `value` is true, false otherwise
16542  * @api public
16543  */
16544
16545 is['true'] = function (value) {
16546   return is.boolean(value) && (value === true || value.valueOf() === true);
16547 };
16548
16549 /**
16550  * Test date.
16551  */
16552
16553 /**
16554  * is.date
16555  * Test if `value` is a date.
16556  *
16557  * @param {Mixed} value value to test
16558  * @return {Boolean} true if `value` is a date, false otherwise
16559  * @api public
16560  */
16561
16562 is.date = function (value) {
16563   return '[object Date]' === toString.call(value);
16564 };
16565
16566 /**
16567  * Test element.
16568  */
16569
16570 /**
16571  * is.element
16572  * Test if `value` is an html element.
16573  *
16574  * @param {Mixed} value value to test
16575  * @return {Boolean} true if `value` is an HTML Element, false otherwise
16576  * @api public
16577  */
16578
16579 is.element = function (value) {
16580   return value !== undefined
16581     && typeof HTMLElement !== 'undefined'
16582     && value instanceof HTMLElement
16583     && value.nodeType === 1;
16584 };
16585
16586 /**
16587  * Test error.
16588  */
16589
16590 /**
16591  * is.error
16592  * Test if `value` is an error object.
16593  *
16594  * @param {Mixed} value value to test
16595  * @return {Boolean} true if `value` is an error object, false otherwise
16596  * @api public
16597  */
16598
16599 is.error = function (value) {
16600   return '[object Error]' === toString.call(value);
16601 };
16602
16603 /**
16604  * Test function.
16605  */
16606
16607 /**
16608  * is.fn / is.function (deprecated)
16609  * Test if `value` is a function.
16610  *
16611  * @param {Mixed} value value to test
16612  * @return {Boolean} true if `value` is a function, false otherwise
16613  * @api public
16614  */
16615
16616 is.fn = is['function'] = function (value) {
16617   var isAlert = typeof window !== 'undefined' && value === window.alert;
16618   return isAlert || '[object Function]' === toString.call(value);
16619 };
16620
16621 /**
16622  * Test number.
16623  */
16624
16625 /**
16626  * is.number
16627  * Test if `value` is a number.
16628  *
16629  * @param {Mixed} value value to test
16630  * @return {Boolean} true if `value` is a number, false otherwise
16631  * @api public
16632  */
16633
16634 is.number = function (value) {
16635   return '[object Number]' === toString.call(value);
16636 };
16637
16638 /**
16639  * is.infinite
16640  * Test if `value` is positive or negative infinity.
16641  *
16642  * @param {Mixed} value value to test
16643  * @return {Boolean} true if `value` is positive or negative Infinity, false otherwise
16644  * @api public
16645  */
16646 is.infinite = function (value) {
16647   return value === Infinity || value === -Infinity;
16648 };
16649
16650 /**
16651  * is.decimal
16652  * Test if `value` is a decimal number.
16653  *
16654  * @param {Mixed} value value to test
16655  * @return {Boolean} true if `value` is a decimal number, false otherwise
16656  * @api public
16657  */
16658
16659 is.decimal = function (value) {
16660   return is.number(value) && !isActualNaN(value) && value % 1 !== 0;
16661 };
16662
16663 /**
16664  * is.divisibleBy
16665  * Test if `value` is divisible by `n`.
16666  *
16667  * @param {Number} value value to test
16668  * @param {Number} n dividend
16669  * @return {Boolean} true if `value` is divisible by `n`, false otherwise
16670  * @api public
16671  */
16672
16673 is.divisibleBy = function (value, n) {
16674   var isDividendInfinite = is.infinite(value);
16675   var isDivisorInfinite = is.infinite(n);
16676   var isNonZeroNumber = is.number(value) && !isActualNaN(value) && is.number(n) && !isActualNaN(n) && n !== 0;
16677   return isDividendInfinite || isDivisorInfinite || (isNonZeroNumber && value % n === 0);
16678 };
16679
16680 /**
16681  * is.int
16682  * Test if `value` is an integer.
16683  *
16684  * @param value to test
16685  * @return {Boolean} true if `value` is an integer, false otherwise
16686  * @api public
16687  */
16688
16689 is.int = function (value) {
16690   return is.number(value) && !isActualNaN(value) && value % 1 === 0;
16691 };
16692
16693 /**
16694  * is.maximum
16695  * Test if `value` is greater than 'others' values.
16696  *
16697  * @param {Number} value value to test
16698  * @param {Array} others values to compare with
16699  * @return {Boolean} true if `value` is greater than `others` values
16700  * @api public
16701  */
16702
16703 is.maximum = function (value, others) {
16704   if (isActualNaN(value)) {
16705     throw new TypeError('NaN is not a valid value');
16706   } else if (!is.arraylike(others)) {
16707     throw new TypeError('second argument must be array-like');
16708   }
16709   var len = others.length;
16710
16711   while (--len >= 0) {
16712     if (value < others[len]) {
16713       return false;
16714     }
16715   }
16716
16717   return true;
16718 };
16719
16720 /**
16721  * is.minimum
16722  * Test if `value` is less than `others` values.
16723  *
16724  * @param {Number} value value to test
16725  * @param {Array} others values to compare with
16726  * @return {Boolean} true if `value` is less than `others` values
16727  * @api public
16728  */
16729
16730 is.minimum = function (value, others) {
16731   if (isActualNaN(value)) {
16732     throw new TypeError('NaN is not a valid value');
16733   } else if (!is.arraylike(others)) {
16734     throw new TypeError('second argument must be array-like');
16735   }
16736   var len = others.length;
16737
16738   while (--len >= 0) {
16739     if (value > others[len]) {
16740       return false;
16741     }
16742   }
16743
16744   return true;
16745 };
16746
16747 /**
16748  * is.nan
16749  * Test if `value` is not a number.
16750  *
16751  * @param {Mixed} value value to test
16752  * @return {Boolean} true if `value` is not a number, false otherwise
16753  * @api public
16754  */
16755
16756 is.nan = function (value) {
16757   return !is.number(value) || value !== value;
16758 };
16759
16760 /**
16761  * is.even
16762  * Test if `value` is an even number.
16763  *
16764  * @param {Number} value value to test
16765  * @return {Boolean} true if `value` is an even number, false otherwise
16766  * @api public
16767  */
16768
16769 is.even = function (value) {
16770   return is.infinite(value) || (is.number(value) && value === value && value % 2 === 0);
16771 };
16772
16773 /**
16774  * is.odd
16775  * Test if `value` is an odd number.
16776  *
16777  * @param {Number} value value to test
16778  * @return {Boolean} true if `value` is an odd number, false otherwise
16779  * @api public
16780  */
16781
16782 is.odd = function (value) {
16783   return is.infinite(value) || (is.number(value) && value === value && value % 2 !== 0);
16784 };
16785
16786 /**
16787  * is.ge
16788  * Test if `value` is greater than or equal to `other`.
16789  *
16790  * @param {Number} value value to test
16791  * @param {Number} other value to compare with
16792  * @return {Boolean}
16793  * @api public
16794  */
16795
16796 is.ge = function (value, other) {
16797   if (isActualNaN(value) || isActualNaN(other)) {
16798     throw new TypeError('NaN is not a valid value');
16799   }
16800   return !is.infinite(value) && !is.infinite(other) && value >= other;
16801 };
16802
16803 /**
16804  * is.gt
16805  * Test if `value` is greater than `other`.
16806  *
16807  * @param {Number} value value to test
16808  * @param {Number} other value to compare with
16809  * @return {Boolean}
16810  * @api public
16811  */
16812
16813 is.gt = function (value, other) {
16814   if (isActualNaN(value) || isActualNaN(other)) {
16815     throw new TypeError('NaN is not a valid value');
16816   }
16817   return !is.infinite(value) && !is.infinite(other) && value > other;
16818 };
16819
16820 /**
16821  * is.le
16822  * Test if `value` is less than or equal to `other`.
16823  *
16824  * @param {Number} value value to test
16825  * @param {Number} other value to compare with
16826  * @return {Boolean} if 'value' is less than or equal to 'other'
16827  * @api public
16828  */
16829
16830 is.le = function (value, other) {
16831   if (isActualNaN(value) || isActualNaN(other)) {
16832     throw new TypeError('NaN is not a valid value');
16833   }
16834   return !is.infinite(value) && !is.infinite(other) && value <= other;
16835 };
16836
16837 /**
16838  * is.lt
16839  * Test if `value` is less than `other`.
16840  *
16841  * @param {Number} value value to test
16842  * @param {Number} other value to compare with
16843  * @return {Boolean} if `value` is less than `other`
16844  * @api public
16845  */
16846
16847 is.lt = function (value, other) {
16848   if (isActualNaN(value) || isActualNaN(other)) {
16849     throw new TypeError('NaN is not a valid value');
16850   }
16851   return !is.infinite(value) && !is.infinite(other) && value < other;
16852 };
16853
16854 /**
16855  * is.within
16856  * Test if `value` is within `start` and `finish`.
16857  *
16858  * @param {Number} value value to test
16859  * @param {Number} start lower bound
16860  * @param {Number} finish upper bound
16861  * @return {Boolean} true if 'value' is is within 'start' and 'finish'
16862  * @api public
16863  */
16864 is.within = function (value, start, finish) {
16865   if (isActualNaN(value) || isActualNaN(start) || isActualNaN(finish)) {
16866     throw new TypeError('NaN is not a valid value');
16867   } else if (!is.number(value) || !is.number(start) || !is.number(finish)) {
16868     throw new TypeError('all arguments must be numbers');
16869   }
16870   var isAnyInfinite = is.infinite(value) || is.infinite(start) || is.infinite(finish);
16871   return isAnyInfinite || (value >= start && value <= finish);
16872 };
16873
16874 /**
16875  * Test object.
16876  */
16877
16878 /**
16879  * is.object
16880  * Test if `value` is an object.
16881  *
16882  * @param {Mixed} value value to test
16883  * @return {Boolean} true if `value` is an object, false otherwise
16884  * @api public
16885  */
16886
16887 is.object = function (value) {
16888   return value && '[object Object]' === toString.call(value);
16889 };
16890
16891 /**
16892  * is.hash
16893  * Test if `value` is a hash - a plain object literal.
16894  *
16895  * @param {Mixed} value value to test
16896  * @return {Boolean} true if `value` is a hash, false otherwise
16897  * @api public
16898  */
16899
16900 is.hash = function (value) {
16901   return is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval;
16902 };
16903
16904 /**
16905  * Test regexp.
16906  */
16907
16908 /**
16909  * is.regexp
16910  * Test if `value` is a regular expression.
16911  *
16912  * @param {Mixed} value value to test
16913  * @return {Boolean} true if `value` is a regexp, false otherwise
16914  * @api public
16915  */
16916
16917 is.regexp = function (value) {
16918   return '[object RegExp]' === toString.call(value);
16919 };
16920
16921 /**
16922  * Test string.
16923  */
16924
16925 /**
16926  * is.string
16927  * Test if `value` is a string.
16928  *
16929  * @param {Mixed} value value to test
16930  * @return {Boolean} true if 'value' is a string, false otherwise
16931  * @api public
16932  */
16933
16934 is.string = function (value) {
16935   return '[object String]' === toString.call(value);
16936 };
16937
16938
16939 },{}],10:[function(require,module,exports){
16940
16941 var hasOwn = Object.prototype.hasOwnProperty;
16942 var toString = Object.prototype.toString;
16943
16944 module.exports = function forEach (obj, fn, ctx) {
16945     if (toString.call(fn) !== '[object Function]') {
16946         throw new TypeError('iterator must be a function');
16947     }
16948     var l = obj.length;
16949     if (l === +l) {
16950         for (var i = 0; i < l; i++) {
16951             fn.call(ctx, obj[i], i, obj);
16952         }
16953     } else {
16954         for (var k in obj) {
16955             if (hasOwn.call(obj, k)) {
16956                 fn.call(ctx, obj[k], k, obj);
16957             }
16958         }
16959     }
16960 };
16961
16962
16963 },{}]},{},[1])(1)
16964 });
16965 ;
16966 /*
16967  (c) 2015, Vladimir Agafonkin
16968  RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
16969  https://github.com/mourner/rbush
16970 */
16971
16972 (function () {
16973 'use strict';
16974
16975 function rbush(maxEntries, format) {
16976
16977     // jshint newcap: false, validthis: true
16978     if (!(this instanceof rbush)) return new rbush(maxEntries, format);
16979
16980     // max entries in a node is 9 by default; min node fill is 40% for best performance
16981     this._maxEntries = Math.max(4, maxEntries || 9);
16982     this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
16983
16984     if (format) {
16985         this._initFormat(format);
16986     }
16987
16988     this.clear();
16989 }
16990
16991 rbush.prototype = {
16992
16993     all: function () {
16994         return this._all(this.data, []);
16995     },
16996
16997     search: function (bbox) {
16998
16999         var node = this.data,
17000             result = [],
17001             toBBox = this.toBBox;
17002
17003         if (!intersects(bbox, node.bbox)) return result;
17004
17005         var nodesToSearch = [],
17006             i, len, child, childBBox;
17007
17008         while (node) {
17009             for (i = 0, len = node.children.length; i < len; i++) {
17010
17011                 child = node.children[i];
17012                 childBBox = node.leaf ? toBBox(child) : child.bbox;
17013
17014                 if (intersects(bbox, childBBox)) {
17015                     if (node.leaf) result.push(child);
17016                     else if (contains(bbox, childBBox)) this._all(child, result);
17017                     else nodesToSearch.push(child);
17018                 }
17019             }
17020             node = nodesToSearch.pop();
17021         }
17022
17023         return result;
17024     },
17025
17026     collides: function (bbox) {
17027
17028         var node = this.data,
17029             toBBox = this.toBBox;
17030
17031         if (!intersects(bbox, node.bbox)) return false;
17032
17033         var nodesToSearch = [],
17034             i, len, child, childBBox;
17035
17036         while (node) {
17037             for (i = 0, len = node.children.length; i < len; i++) {
17038
17039                 child = node.children[i];
17040                 childBBox = node.leaf ? toBBox(child) : child.bbox;
17041
17042                 if (intersects(bbox, childBBox)) {
17043                     if (node.leaf || contains(bbox, childBBox)) return true;
17044                     nodesToSearch.push(child);
17045                 }
17046             }
17047             node = nodesToSearch.pop();
17048         }
17049
17050         return false;
17051     },
17052
17053     load: function (data) {
17054         if (!(data && data.length)) return this;
17055
17056         if (data.length < this._minEntries) {
17057             for (var i = 0, len = data.length; i < len; i++) {
17058                 this.insert(data[i]);
17059             }
17060             return this;
17061         }
17062
17063         // recursively build the tree with the given data from stratch using OMT algorithm
17064         var node = this._build(data.slice(), 0, data.length - 1, 0);
17065
17066         if (!this.data.children.length) {
17067             // save as is if tree is empty
17068             this.data = node;
17069
17070         } else if (this.data.height === node.height) {
17071             // split root if trees have the same height
17072             this._splitRoot(this.data, node);
17073
17074         } else {
17075             if (this.data.height < node.height) {
17076                 // swap trees if inserted one is bigger
17077                 var tmpNode = this.data;
17078                 this.data = node;
17079                 node = tmpNode;
17080             }
17081
17082             // insert the small tree into the large tree at appropriate level
17083             this._insert(node, this.data.height - node.height - 1, true);
17084         }
17085
17086         return this;
17087     },
17088
17089     insert: function (item) {
17090         if (item) this._insert(item, this.data.height - 1);
17091         return this;
17092     },
17093
17094     clear: function () {
17095         this.data = {
17096             children: [],
17097             height: 1,
17098             bbox: empty(),
17099             leaf: true
17100         };
17101         return this;
17102     },
17103
17104     remove: function (item) {
17105         if (!item) return this;
17106
17107         var node = this.data,
17108             bbox = this.toBBox(item),
17109             path = [],
17110             indexes = [],
17111             i, parent, index, goingUp;
17112
17113         // depth-first iterative tree traversal
17114         while (node || path.length) {
17115
17116             if (!node) { // go up
17117                 node = path.pop();
17118                 parent = path[path.length - 1];
17119                 i = indexes.pop();
17120                 goingUp = true;
17121             }
17122
17123             if (node.leaf) { // check current node
17124                 index = node.children.indexOf(item);
17125
17126                 if (index !== -1) {
17127                     // item found, remove the item and condense tree upwards
17128                     node.children.splice(index, 1);
17129                     path.push(node);
17130                     this._condense(path);
17131                     return this;
17132                 }
17133             }
17134
17135             if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
17136                 path.push(node);
17137                 indexes.push(i);
17138                 i = 0;
17139                 parent = node;
17140                 node = node.children[0];
17141
17142             } else if (parent) { // go right
17143                 i++;
17144                 node = parent.children[i];
17145                 goingUp = false;
17146
17147             } else node = null; // nothing found
17148         }
17149
17150         return this;
17151     },
17152
17153     toBBox: function (item) { return item; },
17154
17155     compareMinX: function (a, b) { return a[0] - b[0]; },
17156     compareMinY: function (a, b) { return a[1] - b[1]; },
17157
17158     toJSON: function () { return this.data; },
17159
17160     fromJSON: function (data) {
17161         this.data = data;
17162         return this;
17163     },
17164
17165     _all: function (node, result) {
17166         var nodesToSearch = [];
17167         while (node) {
17168             if (node.leaf) result.push.apply(result, node.children);
17169             else nodesToSearch.push.apply(nodesToSearch, node.children);
17170
17171             node = nodesToSearch.pop();
17172         }
17173         return result;
17174     },
17175
17176     _build: function (items, left, right, height) {
17177
17178         var N = right - left + 1,
17179             M = this._maxEntries,
17180             node;
17181
17182         if (N <= M) {
17183             // reached leaf level; return leaf
17184             node = {
17185                 children: items.slice(left, right + 1),
17186                 height: 1,
17187                 bbox: null,
17188                 leaf: true
17189             };
17190             calcBBox(node, this.toBBox);
17191             return node;
17192         }
17193
17194         if (!height) {
17195             // target height of the bulk-loaded tree
17196             height = Math.ceil(Math.log(N) / Math.log(M));
17197
17198             // target number of root entries to maximize storage utilization
17199             M = Math.ceil(N / Math.pow(M, height - 1));
17200         }
17201
17202         node = {
17203             children: [],
17204             height: height,
17205             bbox: null,
17206             leaf: false
17207         };
17208
17209         // split the items into M mostly square tiles
17210
17211         var N2 = Math.ceil(N / M),
17212             N1 = N2 * Math.ceil(Math.sqrt(M)),
17213             i, j, right2, right3;
17214
17215         multiSelect(items, left, right, N1, this.compareMinX);
17216
17217         for (i = left; i <= right; i += N1) {
17218
17219             right2 = Math.min(i + N1 - 1, right);
17220
17221             multiSelect(items, i, right2, N2, this.compareMinY);
17222
17223             for (j = i; j <= right2; j += N2) {
17224
17225                 right3 = Math.min(j + N2 - 1, right2);
17226
17227                 // pack each entry recursively
17228                 node.children.push(this._build(items, j, right3, height - 1));
17229             }
17230         }
17231
17232         calcBBox(node, this.toBBox);
17233
17234         return node;
17235     },
17236
17237     _chooseSubtree: function (bbox, node, level, path) {
17238
17239         var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
17240
17241         while (true) {
17242             path.push(node);
17243
17244             if (node.leaf || path.length - 1 === level) break;
17245
17246             minArea = minEnlargement = Infinity;
17247
17248             for (i = 0, len = node.children.length; i < len; i++) {
17249                 child = node.children[i];
17250                 area = bboxArea(child.bbox);
17251                 enlargement = enlargedArea(bbox, child.bbox) - area;
17252
17253                 // choose entry with the least area enlargement
17254                 if (enlargement < minEnlargement) {
17255                     minEnlargement = enlargement;
17256                     minArea = area < minArea ? area : minArea;
17257                     targetNode = child;
17258
17259                 } else if (enlargement === minEnlargement) {
17260                     // otherwise choose one with the smallest area
17261                     if (area < minArea) {
17262                         minArea = area;
17263                         targetNode = child;
17264                     }
17265                 }
17266             }
17267
17268             node = targetNode;
17269         }
17270
17271         return node;
17272     },
17273
17274     _insert: function (item, level, isNode) {
17275
17276         var toBBox = this.toBBox,
17277             bbox = isNode ? item.bbox : toBBox(item),
17278             insertPath = [];
17279
17280         // find the best node for accommodating the item, saving all nodes along the path too
17281         var node = this._chooseSubtree(bbox, this.data, level, insertPath);
17282
17283         // put the item into the node
17284         node.children.push(item);
17285         extend(node.bbox, bbox);
17286
17287         // split on node overflow; propagate upwards if necessary
17288         while (level >= 0) {
17289             if (insertPath[level].children.length > this._maxEntries) {
17290                 this._split(insertPath, level);
17291                 level--;
17292             } else break;
17293         }
17294
17295         // adjust bboxes along the insertion path
17296         this._adjustParentBBoxes(bbox, insertPath, level);
17297     },
17298
17299     // split overflowed node into two
17300     _split: function (insertPath, level) {
17301
17302         var node = insertPath[level],
17303             M = node.children.length,
17304             m = this._minEntries;
17305
17306         this._chooseSplitAxis(node, m, M);
17307
17308         var splitIndex = this._chooseSplitIndex(node, m, M);
17309
17310         var newNode = {
17311             children: node.children.splice(splitIndex, node.children.length - splitIndex),
17312             height: node.height,
17313             bbox: null,
17314             leaf: false
17315         };
17316
17317         if (node.leaf) newNode.leaf = true;
17318
17319         calcBBox(node, this.toBBox);
17320         calcBBox(newNode, this.toBBox);
17321
17322         if (level) insertPath[level - 1].children.push(newNode);
17323         else this._splitRoot(node, newNode);
17324     },
17325
17326     _splitRoot: function (node, newNode) {
17327         // split root node
17328         this.data = {
17329             children: [node, newNode],
17330             height: node.height + 1,
17331             bbox: null,
17332             leaf: false
17333         };
17334         calcBBox(this.data, this.toBBox);
17335     },
17336
17337     _chooseSplitIndex: function (node, m, M) {
17338
17339         var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
17340
17341         minOverlap = minArea = Infinity;
17342
17343         for (i = m; i <= M - m; i++) {
17344             bbox1 = distBBox(node, 0, i, this.toBBox);
17345             bbox2 = distBBox(node, i, M, this.toBBox);
17346
17347             overlap = intersectionArea(bbox1, bbox2);
17348             area = bboxArea(bbox1) + bboxArea(bbox2);
17349
17350             // choose distribution with minimum overlap
17351             if (overlap < minOverlap) {
17352                 minOverlap = overlap;
17353                 index = i;
17354
17355                 minArea = area < minArea ? area : minArea;
17356
17357             } else if (overlap === minOverlap) {
17358                 // otherwise choose distribution with minimum area
17359                 if (area < minArea) {
17360                     minArea = area;
17361                     index = i;
17362                 }
17363             }
17364         }
17365
17366         return index;
17367     },
17368
17369     // sorts node children by the best axis for split
17370     _chooseSplitAxis: function (node, m, M) {
17371
17372         var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
17373             compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
17374             xMargin = this._allDistMargin(node, m, M, compareMinX),
17375             yMargin = this._allDistMargin(node, m, M, compareMinY);
17376
17377         // if total distributions margin value is minimal for x, sort by minX,
17378         // otherwise it's already sorted by minY
17379         if (xMargin < yMargin) node.children.sort(compareMinX);
17380     },
17381
17382     // total margin of all possible split distributions where each node is at least m full
17383     _allDistMargin: function (node, m, M, compare) {
17384
17385         node.children.sort(compare);
17386
17387         var toBBox = this.toBBox,
17388             leftBBox = distBBox(node, 0, m, toBBox),
17389             rightBBox = distBBox(node, M - m, M, toBBox),
17390             margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
17391             i, child;
17392
17393         for (i = m; i < M - m; i++) {
17394             child = node.children[i];
17395             extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
17396             margin += bboxMargin(leftBBox);
17397         }
17398
17399         for (i = M - m - 1; i >= m; i--) {
17400             child = node.children[i];
17401             extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
17402             margin += bboxMargin(rightBBox);
17403         }
17404
17405         return margin;
17406     },
17407
17408     _adjustParentBBoxes: function (bbox, path, level) {
17409         // adjust bboxes along the given tree path
17410         for (var i = level; i >= 0; i--) {
17411             extend(path[i].bbox, bbox);
17412         }
17413     },
17414
17415     _condense: function (path) {
17416         // go through the path, removing empty nodes and updating bboxes
17417         for (var i = path.length - 1, siblings; i >= 0; i--) {
17418             if (path[i].children.length === 0) {
17419                 if (i > 0) {
17420                     siblings = path[i - 1].children;
17421                     siblings.splice(siblings.indexOf(path[i]), 1);
17422
17423                 } else this.clear();
17424
17425             } else calcBBox(path[i], this.toBBox);
17426         }
17427     },
17428
17429     _initFormat: function (format) {
17430         // data format (minX, minY, maxX, maxY accessors)
17431
17432         // uses eval-type function compilation instead of just accepting a toBBox function
17433         // because the algorithms are very sensitive to sorting functions performance,
17434         // so they should be dead simple and without inner calls
17435
17436         // jshint evil: true
17437
17438         var compareArr = ['return a', ' - b', ';'];
17439
17440         this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
17441         this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
17442
17443         this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
17444     }
17445 };
17446
17447
17448 // calculate node's bbox from bboxes of its children
17449 function calcBBox(node, toBBox) {
17450     node.bbox = distBBox(node, 0, node.children.length, toBBox);
17451 }
17452
17453 // min bounding rectangle of node children from k to p-1
17454 function distBBox(node, k, p, toBBox) {
17455     var bbox = empty();
17456
17457     for (var i = k, child; i < p; i++) {
17458         child = node.children[i];
17459         extend(bbox, node.leaf ? toBBox(child) : child.bbox);
17460     }
17461
17462     return bbox;
17463 }
17464
17465 function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
17466
17467 function extend(a, b) {
17468     a[0] = Math.min(a[0], b[0]);
17469     a[1] = Math.min(a[1], b[1]);
17470     a[2] = Math.max(a[2], b[2]);
17471     a[3] = Math.max(a[3], b[3]);
17472     return a;
17473 }
17474
17475 function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
17476 function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
17477
17478 function bboxArea(a)   { return (a[2] - a[0]) * (a[3] - a[1]); }
17479 function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
17480
17481 function enlargedArea(a, b) {
17482     return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
17483            (Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
17484 }
17485
17486 function intersectionArea(a, b) {
17487     var minX = Math.max(a[0], b[0]),
17488         minY = Math.max(a[1], b[1]),
17489         maxX = Math.min(a[2], b[2]),
17490         maxY = Math.min(a[3], b[3]);
17491
17492     return Math.max(0, maxX - minX) *
17493            Math.max(0, maxY - minY);
17494 }
17495
17496 function contains(a, b) {
17497     return a[0] <= b[0] &&
17498            a[1] <= b[1] &&
17499            b[2] <= a[2] &&
17500            b[3] <= a[3];
17501 }
17502
17503 function intersects(a, b) {
17504     return b[0] <= a[2] &&
17505            b[1] <= a[3] &&
17506            b[2] >= a[0] &&
17507            b[3] >= a[1];
17508 }
17509
17510 // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
17511 // combines selection algorithm with binary divide & conquer approach
17512
17513 function multiSelect(arr, left, right, n, compare) {
17514     var stack = [left, right],
17515         mid;
17516
17517     while (stack.length) {
17518         right = stack.pop();
17519         left = stack.pop();
17520
17521         if (right - left <= n) continue;
17522
17523         mid = left + Math.ceil((right - left) / n / 2) * n;
17524         select(arr, left, right, mid, compare);
17525
17526         stack.push(left, mid, mid, right);
17527     }
17528 }
17529
17530 // Floyd-Rivest selection algorithm:
17531 // sort an array between left and right (inclusive) so that the smallest k elements come first (unordered)
17532 function select(arr, left, right, k, compare) {
17533     var n, i, z, s, sd, newLeft, newRight, t, j;
17534
17535     while (right > left) {
17536         if (right - left > 600) {
17537             n = right - left + 1;
17538             i = k - left + 1;
17539             z = Math.log(n);
17540             s = 0.5 * Math.exp(2 * z / 3);
17541             sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1);
17542             newLeft = Math.max(left, Math.floor(k - i * s / n + sd));
17543             newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd));
17544             select(arr, newLeft, newRight, k, compare);
17545         }
17546
17547         t = arr[k];
17548         i = left;
17549         j = right;
17550
17551         swap(arr, left, k);
17552         if (compare(arr[right], t) > 0) swap(arr, left, right);
17553
17554         while (i < j) {
17555             swap(arr, i, j);
17556             i++;
17557             j--;
17558             while (compare(arr[i], t) < 0) i++;
17559             while (compare(arr[j], t) > 0) j--;
17560         }
17561
17562         if (compare(arr[left], t) === 0) swap(arr, left, j);
17563         else {
17564             j++;
17565             swap(arr, j, right);
17566         }
17567
17568         if (j <= k) left = j + 1;
17569         if (k <= j) right = j - 1;
17570     }
17571 }
17572
17573 function swap(arr, i, j) {
17574     var tmp = arr[i];
17575     arr[i] = arr[j];
17576     arr[j] = tmp;
17577 }
17578
17579
17580 // export as AMD/CommonJS module or global variable
17581 if (typeof define === 'function' && define.amd) define('rbush', function () { return rbush; });
17582 else if (typeof module !== 'undefined') module.exports = rbush;
17583 else if (typeof self !== 'undefined') self.rbush = rbush;
17584 else window.rbush = rbush;
17585
17586 })();
17587 (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;
17588 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){
17589 module.exports = element;
17590 module.exports.pair = pair;
17591 module.exports.format = format;
17592 module.exports.formatPair = formatPair;
17593
17594 function element(x, dims) {
17595     return search(x, dims).val;
17596 }
17597
17598 function formatPair(x) {
17599     return format(x.lat, 'lat') + ' ' + format(x.lon, 'lon');
17600 }
17601
17602 // Is 0 North or South?
17603 function format(x, dim) {
17604     var dirs = {
17605             lat: ['N', 'S'],
17606             lon: ['E', 'W']
17607         }[dim] || '',
17608         dir = dirs[x >= 0 ? 0 : 1],
17609         abs = Math.abs(x),
17610         whole = Math.floor(abs),
17611         fraction = abs - whole,
17612         fractionMinutes = fraction * 60,
17613         minutes = Math.floor(fractionMinutes),
17614         seconds = Math.floor((fractionMinutes - minutes) * 60);
17615
17616     return whole + '° ' +
17617         (minutes ? minutes + "' " : '') +
17618         (seconds ? seconds + '" ' : '') + dir;
17619 }
17620
17621 function search(x, dims, r) {
17622     if (!dims) dims = 'NSEW';
17623     if (typeof x !== 'string') return { val: null, regex: r };
17624     r = r || /[\s\,]*([\-|\—|\―]?[0-9.]+)°? *(?:([0-9.]+)['’′‘] *)?(?:([0-9.]+)(?:''|"|”|″) *)?([NSEW])?/gi;
17625     var m = r.exec(x);
17626     if (!m) return { val: null, regex: r };
17627     else if (m[4] && dims.indexOf(m[4]) === -1) return { val: null, regex: r };
17628     else return {
17629         val: (((m[1]) ? parseFloat(m[1]) : 0) +
17630             ((m[2] ? parseFloat(m[2]) / 60 : 0)) +
17631             ((m[3] ? parseFloat(m[3]) / 3600 : 0))) *
17632             ((m[4] && m[4] === 'S' || m[4] === 'W') ? -1 : 1),
17633         regex: r,
17634         raw: m[0],
17635         dim: m[4]
17636     };
17637 }
17638
17639 function pair(x, dims) {
17640     x = x.trim();
17641     var one = search(x, dims);
17642     if (one.val === null) return null;
17643     var two = search(x, dims, one.regex);
17644     if (two.val === null) return null;
17645     // null if one/two are not contiguous.
17646     if (one.raw + two.raw !== x) return null;
17647     if (one.dim) return swapdim(one.val, two.val, one.dim);
17648     else return [one.val, two.val];
17649 }
17650
17651 function swapdim(a, b, dim) {
17652     if (dim == 'N' || dim == 'S') return [a, b];
17653     if (dim == 'W' || dim == 'E') return [b, a];
17654 }
17655
17656 },{}]},{},[1])
17657 (1)
17658 });
17659 ;toGeoJSON = (function() {
17660     'use strict';
17661
17662     var removeSpace = (/\s*/g),
17663         trimSpace = (/^\s*|\s*$/g),
17664         splitSpace = (/\s+/);
17665     // generate a short, numeric hash of a string
17666     function okhash(x) {
17667         if (!x || !x.length) return 0;
17668         for (var i = 0, h = 0; i < x.length; i++) {
17669             h = ((h << 5) - h) + x.charCodeAt(i) | 0;
17670         } return h;
17671     }
17672     // all Y children of X
17673     function get(x, y) { return x.getElementsByTagName(y); }
17674     function attr(x, y) { return x.getAttribute(y); }
17675     function attrf(x, y) { return parseFloat(attr(x, y)); }
17676     // one Y child of X, if any, otherwise null
17677     function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
17678     // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
17679     function norm(el) { if (el.normalize) { el.normalize(); } return el; }
17680     // cast array x into numbers
17681     function numarray(x) {
17682         for (var j = 0, o = []; j < x.length; j++) o[j] = parseFloat(x[j]);
17683         return o;
17684     }
17685     function clean(x) {
17686         var o = {};
17687         for (var i in x) if (x[i]) o[i] = x[i];
17688         return o;
17689     }
17690     // get the content of a text node, if any
17691     function nodeVal(x) { if (x) {norm(x);} return x && x.firstChild && x.firstChild.nodeValue; }
17692     // get one coordinate from a coordinate array, if any
17693     function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
17694     // get all coordinates from a coordinate array as [[],[]]
17695     function coord(v) {
17696         var coords = v.replace(trimSpace, '').split(splitSpace),
17697             o = [];
17698         for (var i = 0; i < coords.length; i++) {
17699             o.push(coord1(coords[i]));
17700         }
17701         return o;
17702     }
17703     function coordPair(x) { return [attrf(x, 'lon'), attrf(x, 'lat')]; }
17704
17705     // create a new feature collection parent object
17706     function fc() {
17707         return {
17708             type: 'FeatureCollection',
17709             features: []
17710         };
17711     }
17712
17713     var styleSupport = false;
17714     if (typeof XMLSerializer !== 'undefined') {
17715         var serializer = new XMLSerializer();
17716         styleSupport = true;
17717     }
17718     function xml2str(str) { return serializer.serializeToString(str); }
17719
17720     var t = {
17721         kml: function(doc, o) {
17722             o = o || {};
17723
17724             var gj = fc(),
17725                 // styleindex keeps track of hashed styles in order to match features
17726                 styleIndex = {},
17727                 // atomic geospatial types supported by KML - MultiGeometry is
17728                 // handled separately
17729                 geotypes = ['Polygon', 'LineString', 'Point', 'Track'],
17730                 // all root placemarks in the file
17731                 placemarks = get(doc, 'Placemark'),
17732                 styles = get(doc, 'Style');
17733
17734             if (styleSupport) for (var k = 0; k < styles.length; k++) {
17735                 styleIndex['#' + attr(styles[k], 'id')] = okhash(xml2str(styles[k])).toString(16);
17736             }
17737             for (var j = 0; j < placemarks.length; j++) {
17738                 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
17739             }
17740             function gxCoord(v) { return numarray(v.split(' ')); }
17741             function gxCoords(root) {
17742                 var elems = get(root, 'coord', 'gx'), coords = [];
17743                 for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
17744                 return coords;
17745             }
17746             function getGeometry(root) {
17747                 var geomNode, geomNodes, i, j, k, geoms = [];
17748                 if (get1(root, 'MultiGeometry')) return getGeometry(get1(root, 'MultiGeometry'));
17749                 if (get1(root, 'MultiTrack')) return getGeometry(get1(root, 'MultiTrack'));
17750                 for (i = 0; i < geotypes.length; i++) {
17751                     geomNodes = get(root, geotypes[i]);
17752                     if (geomNodes) {
17753                         for (j = 0; j < geomNodes.length; j++) {
17754                             geomNode = geomNodes[j];
17755                             if (geotypes[i] == 'Point') {
17756                                 geoms.push({
17757                                     type: 'Point',
17758                                     coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
17759                                 });
17760                             } else if (geotypes[i] == 'LineString') {
17761                                 geoms.push({
17762                                     type: 'LineString',
17763                                     coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
17764                                 });
17765                             } else if (geotypes[i] == 'Polygon') {
17766                                 var rings = get(geomNode, 'LinearRing'),
17767                                     coords = [];
17768                                 for (k = 0; k < rings.length; k++) {
17769                                     coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
17770                                 }
17771                                 geoms.push({
17772                                     type: 'Polygon',
17773                                     coordinates: coords
17774                                 });
17775                             } else if (geotypes[i] == 'Track') {
17776                                 geoms.push({
17777                                     type: 'LineString',
17778                                     coordinates: gxCoords(geomNode)
17779                                 });
17780                             }
17781                         }
17782                     }
17783                 }
17784                 return geoms;
17785             }
17786             function getPlacemark(root) {
17787                 var geoms = getGeometry(root), i, properties = {},
17788                     name = nodeVal(get1(root, 'name')),
17789                     styleUrl = nodeVal(get1(root, 'styleUrl')),
17790                     description = nodeVal(get1(root, 'description')),
17791                     extendedData = get1(root, 'ExtendedData');
17792
17793                 if (!geoms.length) return [];
17794                 if (name) properties.name = name;
17795                 if (styleUrl && styleIndex[styleUrl]) {
17796                     properties.styleUrl = styleUrl;
17797                     properties.styleHash = styleIndex[styleUrl];
17798                 }
17799                 if (description) properties.description = description;
17800                 if (extendedData) {
17801                     var datas = get(extendedData, 'Data'),
17802                         simpleDatas = get(extendedData, 'SimpleData');
17803
17804                     for (i = 0; i < datas.length; i++) {
17805                         properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
17806                     }
17807                     for (i = 0; i < simpleDatas.length; i++) {
17808                         properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
17809                     }
17810                 }
17811                 return [{
17812                     type: 'Feature',
17813                     geometry: (geoms.length === 1) ? geoms[0] : {
17814                         type: 'GeometryCollection',
17815                         geometries: geoms
17816                     },
17817                     properties: properties
17818                 }];
17819             }
17820             return gj;
17821         },
17822         gpx: function(doc, o) {
17823             var i,
17824                 tracks = get(doc, 'trk'),
17825                 routes = get(doc, 'rte'),
17826                 waypoints = get(doc, 'wpt'),
17827                 // a feature collection
17828                 gj = fc();
17829             for (i = 0; i < tracks.length; i++) {
17830                 gj.features.push(getLinestring(tracks[i], 'trkpt'));
17831             }
17832             for (i = 0; i < routes.length; i++) {
17833                 gj.features.push(getLinestring(routes[i], 'rtept'));
17834             }
17835             for (i = 0; i < waypoints.length; i++) {
17836                 gj.features.push(getPoint(waypoints[i]));
17837             }
17838             function getLinestring(node, pointname) {
17839                 var j, pts = get(node, pointname), line = [];
17840                 for (j = 0; j < pts.length; j++) {
17841                     line.push(coordPair(pts[j]));
17842                 }
17843                 return {
17844                     type: 'Feature',
17845                     properties: getProperties(node),
17846                     geometry: {
17847                         type: 'LineString',
17848                         coordinates: line
17849                     }
17850                 };
17851             }
17852             function getPoint(node) {
17853                 var prop = getProperties(node);
17854                 prop.ele = nodeVal(get1(node, 'ele'));
17855                 prop.sym = nodeVal(get1(node, 'sym'));
17856                 return {
17857                     type: 'Feature',
17858                     properties: prop,
17859                     geometry: {
17860                         type: 'Point',
17861                         coordinates: coordPair(node)
17862                     }
17863                 };
17864             }
17865             function getProperties(node) {
17866                 var meta = ['name', 'desc', 'author', 'copyright', 'link',
17867                             'time', 'keywords'],
17868                     prop = {},
17869                     k;
17870                 for (k = 0; k < meta.length; k++) {
17871                     prop[meta[k]] = nodeVal(get1(node, meta[k]));
17872                 }
17873                 return clean(prop);
17874             }
17875             return gj;
17876         }
17877     };
17878     return t;
17879 })();
17880
17881 if (typeof module !== 'undefined') module.exports = toGeoJSON;
17882 /**
17883  * marked - a markdown parser
17884  * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
17885  * https://github.com/chjj/marked
17886  */
17887
17888 ;(function() {
17889
17890 /**
17891  * Block-Level Grammar
17892  */
17893
17894 var block = {
17895   newline: /^\n+/,
17896   code: /^( {4}[^\n]+\n*)+/,
17897   fences: noop,
17898   hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17899   heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17900   nptable: noop,
17901   lheading: /^([^\n]+)\n *(=|-){3,} *\n*/,
17902   blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
17903   list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17904   html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
17905   def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17906   table: noop,
17907   paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17908   text: /^[^\n]+/
17909 };
17910
17911 block.bullet = /(?:[*+-]|\d+\.)/;
17912 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17913 block.item = replace(block.item, 'gm')
17914   (/bull/g, block.bullet)
17915   ();
17916
17917 block.list = replace(block.list)
17918   (/bull/g, block.bullet)
17919   ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)
17920   ();
17921
17922 block._tag = '(?!(?:'
17923   + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17924   + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17925   + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b';
17926
17927 block.html = replace(block.html)
17928   ('comment', /<!--[\s\S]*?-->/)
17929   ('closed', /<(tag)[\s\S]+?<\/\1>/)
17930   ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17931   (/tag/g, block._tag)
17932   ();
17933
17934 block.paragraph = replace(block.paragraph)
17935   ('hr', block.hr)
17936   ('heading', block.heading)
17937   ('lheading', block.lheading)
17938   ('blockquote', block.blockquote)
17939   ('tag', '<' + block._tag)
17940   ('def', block.def)
17941   ();
17942
17943 /**
17944  * Normal Block Grammar
17945  */
17946
17947 block.normal = merge({}, block);
17948
17949 /**
17950  * GFM Block Grammar
17951  */
17952
17953 block.gfm = merge({}, block.normal, {
17954   fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
17955   paragraph: /^/
17956 });
17957
17958 block.gfm.paragraph = replace(block.paragraph)
17959   ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|')
17960   ();
17961
17962 /**
17963  * GFM + Tables Block Grammar
17964  */
17965
17966 block.tables = merge({}, block.gfm, {
17967   nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17968   table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17969 });
17970
17971 /**
17972  * Block Lexer
17973  */
17974
17975 function Lexer(options) {
17976   this.tokens = [];
17977   this.tokens.links = {};
17978   this.options = options || marked.defaults;
17979   this.rules = block.normal;
17980
17981   if (this.options.gfm) {
17982     if (this.options.tables) {
17983       this.rules = block.tables;
17984     } else {
17985       this.rules = block.gfm;
17986     }
17987   }
17988 }
17989
17990 /**
17991  * Expose Block Rules
17992  */
17993
17994 Lexer.rules = block;
17995
17996 /**
17997  * Static Lex Method
17998  */
17999
18000 Lexer.lex = function(src, options) {
18001   var lexer = new Lexer(options);
18002   return lexer.lex(src);
18003 };
18004
18005 /**
18006  * Preprocessing
18007  */
18008
18009 Lexer.prototype.lex = function(src) {
18010   src = src
18011     .replace(/\r\n|\r/g, '\n')
18012     .replace(/\t/g, '    ')
18013     .replace(/\u00a0/g, ' ')
18014     .replace(/\u2424/g, '\n');
18015
18016   return this.token(src, true);
18017 };
18018
18019 /**
18020  * Lexing
18021  */
18022
18023 Lexer.prototype.token = function(src, top) {
18024   var src = src.replace(/^ +$/gm, '')
18025     , next
18026     , loose
18027     , cap
18028     , bull
18029     , b
18030     , item
18031     , space
18032     , i
18033     , l;
18034
18035   while (src) {
18036     // newline
18037     if (cap = this.rules.newline.exec(src)) {
18038       src = src.substring(cap[0].length);
18039       if (cap[0].length > 1) {
18040         this.tokens.push({
18041           type: 'space'
18042         });
18043       }
18044     }
18045
18046     // code
18047     if (cap = this.rules.code.exec(src)) {
18048       src = src.substring(cap[0].length);
18049       cap = cap[0].replace(/^ {4}/gm, '');
18050       this.tokens.push({
18051         type: 'code',
18052         text: !this.options.pedantic
18053           ? cap.replace(/\n+$/, '')
18054           : cap
18055       });
18056       continue;
18057     }
18058
18059     // fences (gfm)
18060     if (cap = this.rules.fences.exec(src)) {
18061       src = src.substring(cap[0].length);
18062       this.tokens.push({
18063         type: 'code',
18064         lang: cap[2],
18065         text: cap[3]
18066       });
18067       continue;
18068     }
18069
18070     // heading
18071     if (cap = this.rules.heading.exec(src)) {
18072       src = src.substring(cap[0].length);
18073       this.tokens.push({
18074         type: 'heading',
18075         depth: cap[1].length,
18076         text: cap[2]
18077       });
18078       continue;
18079     }
18080
18081     // table no leading pipe (gfm)
18082     if (top && (cap = this.rules.nptable.exec(src))) {
18083       src = src.substring(cap[0].length);
18084
18085       item = {
18086         type: 'table',
18087         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18088         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18089         cells: cap[3].replace(/\n$/, '').split('\n')
18090       };
18091
18092       for (i = 0; i < item.align.length; i++) {
18093         if (/^ *-+: *$/.test(item.align[i])) {
18094           item.align[i] = 'right';
18095         } else if (/^ *:-+: *$/.test(item.align[i])) {
18096           item.align[i] = 'center';
18097         } else if (/^ *:-+ *$/.test(item.align[i])) {
18098           item.align[i] = 'left';
18099         } else {
18100           item.align[i] = null;
18101         }
18102       }
18103
18104       for (i = 0; i < item.cells.length; i++) {
18105         item.cells[i] = item.cells[i].split(/ *\| */);
18106       }
18107
18108       this.tokens.push(item);
18109
18110       continue;
18111     }
18112
18113     // lheading
18114     if (cap = this.rules.lheading.exec(src)) {
18115       src = src.substring(cap[0].length);
18116       this.tokens.push({
18117         type: 'heading',
18118         depth: cap[2] === '=' ? 1 : 2,
18119         text: cap[1]
18120       });
18121       continue;
18122     }
18123
18124     // hr
18125     if (cap = this.rules.hr.exec(src)) {
18126       src = src.substring(cap[0].length);
18127       this.tokens.push({
18128         type: 'hr'
18129       });
18130       continue;
18131     }
18132
18133     // blockquote
18134     if (cap = this.rules.blockquote.exec(src)) {
18135       src = src.substring(cap[0].length);
18136
18137       this.tokens.push({
18138         type: 'blockquote_start'
18139       });
18140
18141       cap = cap[0].replace(/^ *> ?/gm, '');
18142
18143       // Pass `top` to keep the current
18144       // "toplevel" state. This is exactly
18145       // how markdown.pl works.
18146       this.token(cap, top);
18147
18148       this.tokens.push({
18149         type: 'blockquote_end'
18150       });
18151
18152       continue;
18153     }
18154
18155     // list
18156     if (cap = this.rules.list.exec(src)) {
18157       src = src.substring(cap[0].length);
18158       bull = cap[2];
18159
18160       this.tokens.push({
18161         type: 'list_start',
18162         ordered: bull.length > 1
18163       });
18164
18165       // Get each top-level item.
18166       cap = cap[0].match(this.rules.item);
18167
18168       next = false;
18169       l = cap.length;
18170       i = 0;
18171
18172       for (; i < l; i++) {
18173         item = cap[i];
18174
18175         // Remove the list item's bullet
18176         // so it is seen as the next token.
18177         space = item.length;
18178         item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18179
18180         // Outdent whatever the
18181         // list item contains. Hacky.
18182         if (~item.indexOf('\n ')) {
18183           space -= item.length;
18184           item = !this.options.pedantic
18185             ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18186             : item.replace(/^ {1,4}/gm, '');
18187         }
18188
18189         // Determine whether the next list item belongs here.
18190         // Backpedal if it does not belong in this list.
18191         if (this.options.smartLists && i !== l - 1) {
18192           b = block.bullet.exec(cap[i+1])[0];
18193           if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18194             src = cap.slice(i + 1).join('\n') + src;
18195             i = l - 1;
18196           }
18197         }
18198
18199         // Determine whether item is loose or not.
18200         // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18201         // for discount behavior.
18202         loose = next || /\n\n(?!\s*$)/.test(item);
18203         if (i !== l - 1) {
18204           next = item[item.length-1] === '\n';
18205           if (!loose) loose = next;
18206         }
18207
18208         this.tokens.push({
18209           type: loose
18210             ? 'loose_item_start'
18211             : 'list_item_start'
18212         });
18213
18214         // Recurse.
18215         this.token(item, false);
18216
18217         this.tokens.push({
18218           type: 'list_item_end'
18219         });
18220       }
18221
18222       this.tokens.push({
18223         type: 'list_end'
18224       });
18225
18226       continue;
18227     }
18228
18229     // html
18230     if (cap = this.rules.html.exec(src)) {
18231       src = src.substring(cap[0].length);
18232       this.tokens.push({
18233         type: this.options.sanitize
18234           ? 'paragraph'
18235           : 'html',
18236         pre: cap[1] === 'pre' || cap[1] === 'script',
18237         text: cap[0]
18238       });
18239       continue;
18240     }
18241
18242     // def
18243     if (top && (cap = this.rules.def.exec(src))) {
18244       src = src.substring(cap[0].length);
18245       this.tokens.links[cap[1].toLowerCase()] = {
18246         href: cap[2],
18247         title: cap[3]
18248       };
18249       continue;
18250     }
18251
18252     // table (gfm)
18253     if (top && (cap = this.rules.table.exec(src))) {
18254       src = src.substring(cap[0].length);
18255
18256       item = {
18257         type: 'table',
18258         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18259         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18260         cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18261       };
18262
18263       for (i = 0; i < item.align.length; i++) {
18264         if (/^ *-+: *$/.test(item.align[i])) {
18265           item.align[i] = 'right';
18266         } else if (/^ *:-+: *$/.test(item.align[i])) {
18267           item.align[i] = 'center';
18268         } else if (/^ *:-+ *$/.test(item.align[i])) {
18269           item.align[i] = 'left';
18270         } else {
18271           item.align[i] = null;
18272         }
18273       }
18274
18275       for (i = 0; i < item.cells.length; i++) {
18276         item.cells[i] = item.cells[i]
18277           .replace(/^ *\| *| *\| *$/g, '')
18278           .split(/ *\| */);
18279       }
18280
18281       this.tokens.push(item);
18282
18283       continue;
18284     }
18285
18286     // top-level paragraph
18287     if (top && (cap = this.rules.paragraph.exec(src))) {
18288       src = src.substring(cap[0].length);
18289       this.tokens.push({
18290         type: 'paragraph',
18291         text: cap[1][cap[1].length-1] === '\n'
18292           ? cap[1].slice(0, -1)
18293           : cap[1]
18294       });
18295       continue;
18296     }
18297
18298     // text
18299     if (cap = this.rules.text.exec(src)) {
18300       // Top-level should never reach here.
18301       src = src.substring(cap[0].length);
18302       this.tokens.push({
18303         type: 'text',
18304         text: cap[0]
18305       });
18306       continue;
18307     }
18308
18309     if (src) {
18310       throw new
18311         Error('Infinite loop on byte: ' + src.charCodeAt(0));
18312     }
18313   }
18314
18315   return this.tokens;
18316 };
18317
18318 /**
18319  * Inline-Level Grammar
18320  */
18321
18322 var inline = {
18323   escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18324   autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18325   url: noop,
18326   tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18327   link: /^!?\[(inside)\]\(href\)/,
18328   reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18329   nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18330   strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18331   em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18332   code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18333   br: /^ {2,}\n(?!\s*$)/,
18334   del: noop,
18335   text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18336 };
18337
18338 inline._inside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
18339 inline._href = /\s*<?([^\s]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18340
18341 inline.link = replace(inline.link)
18342   ('inside', inline._inside)
18343   ('href', inline._href)
18344   ();
18345
18346 inline.reflink = replace(inline.reflink)
18347   ('inside', inline._inside)
18348   ();
18349
18350 /**
18351  * Normal Inline Grammar
18352  */
18353
18354 inline.normal = merge({}, inline);
18355
18356 /**
18357  * Pedantic Inline Grammar
18358  */
18359
18360 inline.pedantic = merge({}, inline.normal, {
18361   strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18362   em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18363 });
18364
18365 /**
18366  * GFM Inline Grammar
18367  */
18368
18369 inline.gfm = merge({}, inline.normal, {
18370   escape: replace(inline.escape)('])', '~|])')(),
18371   url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18372   del: /^~~(?=\S)([\s\S]*?\S)~~/,
18373   text: replace(inline.text)
18374     (']|', '~]|')
18375     ('|', '|https?://|')
18376     ()
18377 });
18378
18379 /**
18380  * GFM + Line Breaks Inline Grammar
18381  */
18382
18383 inline.breaks = merge({}, inline.gfm, {
18384   br: replace(inline.br)('{2,}', '*')(),
18385   text: replace(inline.gfm.text)('{2,}', '*')()
18386 });
18387
18388 /**
18389  * Inline Lexer & Compiler
18390  */
18391
18392 function InlineLexer(links, options) {
18393   this.options = options || marked.defaults;
18394   this.links = links;
18395   this.rules = inline.normal;
18396
18397   if (!this.links) {
18398     throw new
18399       Error('Tokens array requires a `links` property.');
18400   }
18401
18402   if (this.options.gfm) {
18403     if (this.options.breaks) {
18404       this.rules = inline.breaks;
18405     } else {
18406       this.rules = inline.gfm;
18407     }
18408   } else if (this.options.pedantic) {
18409     this.rules = inline.pedantic;
18410   }
18411 }
18412
18413 /**
18414  * Expose Inline Rules
18415  */
18416
18417 InlineLexer.rules = inline;
18418
18419 /**
18420  * Static Lexing/Compiling Method
18421  */
18422
18423 InlineLexer.output = function(src, links, options) {
18424   var inline = new InlineLexer(links, options);
18425   return inline.output(src);
18426 };
18427
18428 /**
18429  * Lexing/Compiling
18430  */
18431
18432 InlineLexer.prototype.output = function(src) {
18433   var out = ''
18434     , link
18435     , text
18436     , href
18437     , cap;
18438
18439   while (src) {
18440     // escape
18441     if (cap = this.rules.escape.exec(src)) {
18442       src = src.substring(cap[0].length);
18443       out += cap[1];
18444       continue;
18445     }
18446
18447     // autolink
18448     if (cap = this.rules.autolink.exec(src)) {
18449       src = src.substring(cap[0].length);
18450       if (cap[2] === '@') {
18451         text = cap[1][6] === ':'
18452           ? this.mangle(cap[1].substring(7))
18453           : this.mangle(cap[1]);
18454         href = this.mangle('mailto:') + text;
18455       } else {
18456         text = escape(cap[1]);
18457         href = text;
18458       }
18459       out += '<a href="'
18460         + href
18461         + '">'
18462         + text
18463         + '</a>';
18464       continue;
18465     }
18466
18467     // url (gfm)
18468     if (cap = this.rules.url.exec(src)) {
18469       src = src.substring(cap[0].length);
18470       text = escape(cap[1]);
18471       href = text;
18472       out += '<a href="'
18473         + href
18474         + '">'
18475         + text
18476         + '</a>';
18477       continue;
18478     }
18479
18480     // tag
18481     if (cap = this.rules.tag.exec(src)) {
18482       src = src.substring(cap[0].length);
18483       out += this.options.sanitize
18484         ? escape(cap[0])
18485         : cap[0];
18486       continue;
18487     }
18488
18489     // link
18490     if (cap = this.rules.link.exec(src)) {
18491       src = src.substring(cap[0].length);
18492       out += this.outputLink(cap, {
18493         href: cap[2],
18494         title: cap[3]
18495       });
18496       continue;
18497     }
18498
18499     // reflink, nolink
18500     if ((cap = this.rules.reflink.exec(src))
18501         || (cap = this.rules.nolink.exec(src))) {
18502       src = src.substring(cap[0].length);
18503       link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18504       link = this.links[link.toLowerCase()];
18505       if (!link || !link.href) {
18506         out += cap[0][0];
18507         src = cap[0].substring(1) + src;
18508         continue;
18509       }
18510       out += this.outputLink(cap, link);
18511       continue;
18512     }
18513
18514     // strong
18515     if (cap = this.rules.strong.exec(src)) {
18516       src = src.substring(cap[0].length);
18517       out += '<strong>'
18518         + this.output(cap[2] || cap[1])
18519         + '</strong>';
18520       continue;
18521     }
18522
18523     // em
18524     if (cap = this.rules.em.exec(src)) {
18525       src = src.substring(cap[0].length);
18526       out += '<em>'
18527         + this.output(cap[2] || cap[1])
18528         + '</em>';
18529       continue;
18530     }
18531
18532     // code
18533     if (cap = this.rules.code.exec(src)) {
18534       src = src.substring(cap[0].length);
18535       out += '<code>'
18536         + escape(cap[2], true)
18537         + '</code>';
18538       continue;
18539     }
18540
18541     // br
18542     if (cap = this.rules.br.exec(src)) {
18543       src = src.substring(cap[0].length);
18544       out += '<br>';
18545       continue;
18546     }
18547
18548     // del (gfm)
18549     if (cap = this.rules.del.exec(src)) {
18550       src = src.substring(cap[0].length);
18551       out += '<del>'
18552         + this.output(cap[1])
18553         + '</del>';
18554       continue;
18555     }
18556
18557     // text
18558     if (cap = this.rules.text.exec(src)) {
18559       src = src.substring(cap[0].length);
18560       out += escape(cap[0]);
18561       continue;
18562     }
18563
18564     if (src) {
18565       throw new
18566         Error('Infinite loop on byte: ' + src.charCodeAt(0));
18567     }
18568   }
18569
18570   return out;
18571 };
18572
18573 /**
18574  * Compile Link
18575  */
18576
18577 InlineLexer.prototype.outputLink = function(cap, link) {
18578   if (cap[0][0] !== '!') {
18579     return '<a href="'
18580       + escape(link.href)
18581       + '"'
18582       + (link.title
18583       ? ' title="'
18584       + escape(link.title)
18585       + '"'
18586       : '')
18587       + '>'
18588       + this.output(cap[1])
18589       + '</a>';
18590   } else {
18591     return '<img src="'
18592       + escape(link.href)
18593       + '" alt="'
18594       + escape(cap[1])
18595       + '"'
18596       + (link.title
18597       ? ' title="'
18598       + escape(link.title)
18599       + '"'
18600       : '')
18601       + '>';
18602   }
18603 };
18604
18605 /**
18606  * Smartypants Transformations
18607  */
18608
18609 InlineLexer.prototype.smartypants = function(text) {
18610   if (!this.options.smartypants) return text;
18611   return text
18612     .replace(/--/g, '—')
18613     .replace(/'([^']*)'/g, '‘$1’')
18614     .replace(/"([^"]*)"/g, '“$1”')
18615     .replace(/\.{3}/g, '…');
18616 };
18617
18618 /**
18619  * Mangle Links
18620  */
18621
18622 InlineLexer.prototype.mangle = function(text) {
18623   var out = ''
18624     , l = text.length
18625     , i = 0
18626     , ch;
18627
18628   for (; i < l; i++) {
18629     ch = text.charCodeAt(i);
18630     if (Math.random() > 0.5) {
18631       ch = 'x' + ch.toString(16);
18632     }
18633     out += '&#' + ch + ';';
18634   }
18635
18636   return out;
18637 };
18638
18639 /**
18640  * Parsing & Compiling
18641  */
18642
18643 function Parser(options) {
18644   this.tokens = [];
18645   this.token = null;
18646   this.options = options || marked.defaults;
18647 }
18648
18649 /**
18650  * Static Parse Method
18651  */
18652
18653 Parser.parse = function(src, options) {
18654   var parser = new Parser(options);
18655   return parser.parse(src);
18656 };
18657
18658 /**
18659  * Parse Loop
18660  */
18661
18662 Parser.prototype.parse = function(src) {
18663   this.inline = new InlineLexer(src.links, this.options);
18664   this.tokens = src.reverse();
18665
18666   var out = '';
18667   while (this.next()) {
18668     out += this.tok();
18669   }
18670
18671   return out;
18672 };
18673
18674 /**
18675  * Next Token
18676  */
18677
18678 Parser.prototype.next = function() {
18679   return this.token = this.tokens.pop();
18680 };
18681
18682 /**
18683  * Preview Next Token
18684  */
18685
18686 Parser.prototype.peek = function() {
18687   return this.tokens[this.tokens.length-1] || 0;
18688 };
18689
18690 /**
18691  * Parse Text Tokens
18692  */
18693
18694 Parser.prototype.parseText = function() {
18695   var body = this.token.text;
18696
18697   while (this.peek().type === 'text') {
18698     body += '\n' + this.next().text;
18699   }
18700
18701   return this.inline.output(body);
18702 };
18703
18704 /**
18705  * Parse Current Token
18706  */
18707
18708 Parser.prototype.tok = function() {
18709   switch (this.token.type) {
18710     case 'space': {
18711       return '';
18712     }
18713     case 'hr': {
18714       return '<hr>\n';
18715     }
18716     case 'heading': {
18717       return '<h'
18718         + this.token.depth
18719         + '>'
18720         + this.inline.output(this.token.text)
18721         + '</h'
18722         + this.token.depth
18723         + '>\n';
18724     }
18725     case 'code': {
18726       if (this.options.highlight) {
18727         var code = this.options.highlight(this.token.text, this.token.lang);
18728         if (code != null && code !== this.token.text) {
18729           this.token.escaped = true;
18730           this.token.text = code;
18731         }
18732       }
18733
18734       if (!this.token.escaped) {
18735         this.token.text = escape(this.token.text, true);
18736       }
18737
18738       return '<pre><code'
18739         + (this.token.lang
18740         ? ' class="'
18741         + this.options.langPrefix
18742         + this.token.lang
18743         + '"'
18744         : '')
18745         + '>'
18746         + this.token.text
18747         + '</code></pre>\n';
18748     }
18749     case 'table': {
18750       var body = ''
18751         , heading
18752         , i
18753         , row
18754         , cell
18755         , j;
18756
18757       // header
18758       body += '<thead>\n<tr>\n';
18759       for (i = 0; i < this.token.header.length; i++) {
18760         heading = this.inline.output(this.token.header[i]);
18761         body += this.token.align[i]
18762           ? '<th align="' + this.token.align[i] + '">' + heading + '</th>\n'
18763           : '<th>' + heading + '</th>\n';
18764       }
18765       body += '</tr>\n</thead>\n';
18766
18767       // body
18768       body += '<tbody>\n'
18769       for (i = 0; i < this.token.cells.length; i++) {
18770         row = this.token.cells[i];
18771         body += '<tr>\n';
18772         for (j = 0; j < row.length; j++) {
18773           cell = this.inline.output(row[j]);
18774           body += this.token.align[j]
18775             ? '<td align="' + this.token.align[j] + '">' + cell + '</td>\n'
18776             : '<td>' + cell + '</td>\n';
18777         }
18778         body += '</tr>\n';
18779       }
18780       body += '</tbody>\n';
18781
18782       return '<table>\n'
18783         + body
18784         + '</table>\n';
18785     }
18786     case 'blockquote_start': {
18787       var body = '';
18788
18789       while (this.next().type !== 'blockquote_end') {
18790         body += this.tok();
18791       }
18792
18793       return '<blockquote>\n'
18794         + body
18795         + '</blockquote>\n';
18796     }
18797     case 'list_start': {
18798       var type = this.token.ordered ? 'ol' : 'ul'
18799         , body = '';
18800
18801       while (this.next().type !== 'list_end') {
18802         body += this.tok();
18803       }
18804
18805       return '<'
18806         + type
18807         + '>\n'
18808         + body
18809         + '</'
18810         + type
18811         + '>\n';
18812     }
18813     case 'list_item_start': {
18814       var body = '';
18815
18816       while (this.next().type !== 'list_item_end') {
18817         body += this.token.type === 'text'
18818           ? this.parseText()
18819           : this.tok();
18820       }
18821
18822       return '<li>'
18823         + body
18824         + '</li>\n';
18825     }
18826     case 'loose_item_start': {
18827       var body = '';
18828
18829       while (this.next().type !== 'list_item_end') {
18830         body += this.tok();
18831       }
18832
18833       return '<li>'
18834         + body
18835         + '</li>\n';
18836     }
18837     case 'html': {
18838       return !this.token.pre && !this.options.pedantic
18839         ? this.inline.output(this.token.text)
18840         : this.token.text;
18841     }
18842     case 'paragraph': {
18843       return '<p>'
18844         + this.inline.output(this.token.text)
18845         + '</p>\n';
18846     }
18847     case 'text': {
18848       return '<p>'
18849         + this.parseText()
18850         + '</p>\n';
18851     }
18852   }
18853 };
18854
18855 /**
18856  * Helpers
18857  */
18858
18859 function escape(html, encode) {
18860   return html
18861     .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18862     .replace(/</g, '&lt;')
18863     .replace(/>/g, '&gt;')
18864     .replace(/"/g, '&quot;')
18865     .replace(/'/g, '&#39;');
18866 }
18867
18868 function replace(regex, opt) {
18869   regex = regex.source;
18870   opt = opt || '';
18871   return function self(name, val) {
18872     if (!name) return new RegExp(regex, opt);
18873     val = val.source || val;
18874     val = val.replace(/(^|[^\[])\^/g, '$1');
18875     regex = regex.replace(name, val);
18876     return self;
18877   };
18878 }
18879
18880 function noop() {}
18881 noop.exec = noop;
18882
18883 function merge(obj) {
18884   var i = 1
18885     , target
18886     , key;
18887
18888   for (; i < arguments.length; i++) {
18889     target = arguments[i];
18890     for (key in target) {
18891       if (Object.prototype.hasOwnProperty.call(target, key)) {
18892         obj[key] = target[key];
18893       }
18894     }
18895   }
18896
18897   return obj;
18898 }
18899
18900 /**
18901  * Marked
18902  */
18903
18904 function marked(src, opt, callback) {
18905   if (callback || typeof opt === 'function') {
18906     if (!callback) {
18907       callback = opt;
18908       opt = null;
18909     }
18910
18911     if (opt) opt = merge({}, marked.defaults, opt);
18912
18913     var tokens = Lexer.lex(tokens, opt)
18914       , highlight = opt.highlight
18915       , pending = 0
18916       , l = tokens.length
18917       , i = 0;
18918
18919     if (!highlight || highlight.length < 3) {
18920       return callback(null, Parser.parse(tokens, opt));
18921     }
18922
18923     var done = function() {
18924       delete opt.highlight;
18925       var out = Parser.parse(tokens, opt);
18926       opt.highlight = highlight;
18927       return callback(null, out);
18928     };
18929
18930     for (; i < l; i++) {
18931       (function(token) {
18932         if (token.type !== 'code') return;
18933         pending++;
18934         return highlight(token.text, token.lang, function(err, code) {
18935           if (code == null || code === token.text) {
18936             return --pending || done();
18937           }
18938           token.text = code;
18939           token.escaped = true;
18940           --pending || done();
18941         });
18942       })(tokens[i]);
18943     }
18944
18945     return;
18946   }
18947   try {
18948     if (opt) opt = merge({}, marked.defaults, opt);
18949     return Parser.parse(Lexer.lex(src, opt), opt);
18950   } catch (e) {
18951     e.message += '\nPlease report this to https://github.com/chjj/marked.';
18952     if ((opt || marked.defaults).silent) {
18953       return '<p>An error occured:</p><pre>'
18954         + escape(e.message + '', true)
18955         + '</pre>';
18956     }
18957     throw e;
18958   }
18959 }
18960
18961 /**
18962  * Options
18963  */
18964
18965 marked.options =
18966 marked.setOptions = function(opt) {
18967   merge(marked.defaults, opt);
18968   return marked;
18969 };
18970
18971 marked.defaults = {
18972   gfm: true,
18973   tables: true,
18974   breaks: false,
18975   pedantic: false,
18976   sanitize: false,
18977   smartLists: false,
18978   silent: false,
18979   highlight: null,
18980   langPrefix: 'lang-'
18981 };
18982
18983 /**
18984  * Expose
18985  */
18986
18987 marked.Parser = Parser;
18988 marked.parser = Parser.parse;
18989
18990 marked.Lexer = Lexer;
18991 marked.lexer = Lexer.lex;
18992
18993 marked.InlineLexer = InlineLexer;
18994 marked.inlineLexer = InlineLexer.output;
18995
18996 marked.parse = marked;
18997
18998 if (typeof exports === 'object') {
18999   module.exports = marked;
19000 } else if (typeof define === 'function' && define.amd) {
19001   define(function() { return marked; });
19002 } else {
19003   this.marked = marked;
19004 }
19005
19006 }).call(function() {
19007   return this || (typeof window !== 'undefined' ? window : global);
19008 }());
19009 (function () {
19010 'use strict';
19011 window.iD = function () {
19012     window.locale.en = iD.data.en;
19013     window.locale.current('en');
19014
19015     var dispatch = d3.dispatch('enter', 'exit'),
19016         context = {};
19017
19018     // https://github.com/openstreetmap/iD/issues/772
19019     // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
19020     var storage;
19021     try { storage = localStorage; } catch (e) {}  // eslint-disable-line no-empty
19022     storage = storage || (function() {
19023         var s = {};
19024         return {
19025             getItem: function(k) { return s[k]; },
19026             setItem: function(k, v) { s[k] = v; },
19027             removeItem: function(k) { delete s[k]; }
19028         };
19029     })();
19030
19031     context.storage = function(k, v) {
19032         try {
19033             if (arguments.length === 1) return storage.getItem(k);
19034             else if (v === null) storage.removeItem(k);
19035             else storage.setItem(k, v);
19036         } catch(e) {
19037             // localstorage quota exceeded
19038             /* eslint-disable no-console */
19039             if (typeof console !== 'undefined') console.error('localStorage quota exceeded');
19040             /* eslint-enable no-console */
19041         }
19042     };
19043
19044
19045     /* Straight accessors. Avoid using these if you can. */
19046     var ui, connection, history;
19047     context.ui = function() { return ui; };
19048     context.connection = function() { return connection; };
19049     context.history = function() { return history; };
19050
19051
19052     /* Connection */
19053     function entitiesLoaded(err, result) {
19054         if (!err) history.merge(result.data, result.extent);
19055     }
19056
19057     context.preauth = function(options) {
19058         connection.switch(options);
19059         return context;
19060     };
19061
19062     context.loadTiles = function(projection, dimensions, callback) {
19063         function done(err, result) {
19064             entitiesLoaded(err, result);
19065             if (callback) callback(err, result);
19066         }
19067         connection.loadTiles(projection, dimensions, done);
19068     };
19069
19070     context.loadEntity = function(id, callback) {
19071         function done(err, result) {
19072             entitiesLoaded(err, result);
19073             if (callback) callback(err, result);
19074         }
19075         connection.loadEntity(id, done);
19076     };
19077
19078     context.zoomToEntity = function(id, zoomTo) {
19079         if (zoomTo !== false) {
19080             this.loadEntity(id, function(err, result) {
19081                 if (err) return;
19082                 var entity = _.find(result.data, function(e) { return e.id === id; });
19083                 if (entity) { map.zoomTo(entity); }
19084             });
19085         }
19086
19087         map.on('drawn.zoomToEntity', function() {
19088             if (!context.hasEntity(id)) return;
19089             map.on('drawn.zoomToEntity', null);
19090             context.on('enter.zoomToEntity', null);
19091             context.enter(iD.modes.Select(context, [id]));
19092         });
19093
19094         context.on('enter.zoomToEntity', function() {
19095             if (mode.id !== 'browse') {
19096                 map.on('drawn.zoomToEntity', null);
19097                 context.on('enter.zoomToEntity', null);
19098             }
19099         });
19100     };
19101
19102     var minEditableZoom = 16;
19103     context.minEditableZoom = function(_) {
19104         if (!arguments.length) return minEditableZoom;
19105         minEditableZoom = _;
19106         connection.tileZoom(_);
19107         return context;
19108     };
19109
19110
19111     /* History */
19112     var inIntro = false;
19113     context.inIntro = function(_) {
19114         if (!arguments.length) return inIntro;
19115         inIntro = _;
19116         return context;
19117     };
19118
19119     context.save = function() {
19120         if (inIntro || (mode && mode.id === 'save') || d3.select('.modal').size()) return;
19121         history.save();
19122         if (history.hasChanges()) return t('save.unsaved_changes');
19123     };
19124
19125     context.flush = function() {
19126         context.debouncedSave.cancel();
19127         connection.flush();
19128         features.reset();
19129         history.reset();
19130         _.each(iD.services, function(service) {
19131             var reset = service().reset;
19132             if (reset) reset(context);
19133         });
19134         return context;
19135     };
19136
19137
19138     /* Graph */
19139     context.hasEntity = function(id) {
19140         return history.graph().hasEntity(id);
19141     };
19142     context.entity = function(id) {
19143         return history.graph().entity(id);
19144     };
19145     context.childNodes = function(way) {
19146         return history.graph().childNodes(way);
19147     };
19148     context.geometry = function(id) {
19149         return context.entity(id).geometry(history.graph());
19150     };
19151
19152
19153     /* Modes */
19154     var mode;
19155     context.mode = function() {
19156         return mode;
19157     };
19158     context.enter = function(newMode) {
19159         if (mode) {
19160             mode.exit();
19161             dispatch.exit(mode);
19162         }
19163
19164         mode = newMode;
19165         mode.enter();
19166         dispatch.enter(mode);
19167     };
19168
19169     context.selectedIDs = function() {
19170         if (mode && mode.selectedIDs) {
19171             return mode.selectedIDs();
19172         } else {
19173             return [];
19174         }
19175     };
19176
19177
19178     /* Behaviors */
19179     context.install = function(behavior) {
19180         context.surface().call(behavior);
19181     };
19182     context.uninstall = function(behavior) {
19183         context.surface().call(behavior.off);
19184     };
19185
19186
19187     /* Copy/Paste */
19188     var copyIDs = [], copyGraph;
19189     context.copyGraph = function() { return copyGraph; };
19190     context.copyIDs = function(_) {
19191         if (!arguments.length) return copyIDs;
19192         copyIDs = _;
19193         copyGraph = history.graph();
19194         return context;
19195     };
19196
19197
19198     /* Background */
19199     var background;
19200     context.background = function() { return background; };
19201
19202
19203     /* Features */
19204     var features;
19205     context.features = function() { return features; };
19206     context.hasHiddenConnections = function(id) {
19207         var graph = history.graph(),
19208             entity = graph.entity(id);
19209         return features.hasHiddenConnections(entity, graph);
19210     };
19211
19212
19213     /* Map */
19214     var map;
19215     context.map = function() { return map; };
19216     context.layers = function() { return map.layers; };
19217     context.surface = function() { return map.surface; };
19218     context.editable = function() { return map.editable(); };
19219
19220     context.surfaceRect = function() {
19221         // Work around a bug in Firefox.
19222         //   http://stackoverflow.com/questions/18153989/
19223         //   https://bugzilla.mozilla.org/show_bug.cgi?id=530985
19224         return context.surface().node().parentNode.getBoundingClientRect();
19225     };
19226
19227
19228     /* Debug */
19229     var debugTile = false, debugCollision = false;
19230     context.debugTile = function(_) {
19231         if (!arguments.length) return debugTile;
19232         debugTile = _;
19233         return context;
19234     };
19235     context.debugCollision = function(_) {
19236         if (!arguments.length) return debugCollision;
19237         debugCollision = _;
19238         return context;
19239     };
19240
19241
19242     /* Presets */
19243     var presets;
19244     context.presets = function(_) {
19245         if (!arguments.length) return presets;
19246         presets.load(_);
19247         iD.areaKeys = presets.areaKeys();
19248         return context;
19249     };
19250
19251
19252     /* Imagery */
19253     context.imagery = function(_) {
19254         background.load(_);
19255         return context;
19256     };
19257
19258
19259     /* Container */
19260     var container, embed;
19261     context.container = function(_) {
19262         if (!arguments.length) return container;
19263         container = _;
19264         container.classed('id-container', true);
19265         return context;
19266     };
19267     context.embed = function(_) {
19268         if (!arguments.length) return embed;
19269         embed = _;
19270         return context;
19271     };
19272
19273
19274     /* Taginfo */
19275     var taginfo;
19276     context.taginfo = function(_) {
19277         if (!arguments.length) return taginfo;
19278         taginfo = _;
19279         return context;
19280     };
19281
19282
19283     /* Assets */
19284     var assetPath = '';
19285     context.assetPath = function(_) {
19286         if (!arguments.length) return assetPath;
19287         assetPath = _;
19288         return context;
19289     };
19290
19291     var assetMap = {};
19292     context.assetMap = function(_) {
19293         if (!arguments.length) return assetMap;
19294         assetMap = _;
19295         return context;
19296     };
19297
19298     context.asset = function(_) {
19299         var filename = assetPath + _;
19300         return assetMap[filename] || filename;
19301     };
19302
19303     context.imagePath = function(_) {
19304         return context.asset('img/' + _);
19305     };
19306
19307     var locale, localePath;
19308     context.locale = function(loc, path) {
19309         locale = loc;
19310         localePath = path;
19311
19312         // Also set iD.detect().locale (unless we detected 'en-us' and openstreetmap wants 'en')..
19313         if (!(loc.toLowerCase() === 'en' && iD.detect().locale.toLowerCase() === 'en-us')) {
19314             iD.detect().locale = loc;
19315         }
19316
19317         return context;
19318     };
19319
19320     context.loadLocale = function(cb) {
19321         if (locale && locale !== 'en' && iD.data.locales.indexOf(locale) !== -1) {
19322             localePath = localePath || context.asset('locales/' + locale + '.json');
19323             d3.json(localePath, function(err, result) {
19324                 window.locale[locale] = result;
19325                 window.locale.current(locale);
19326                 cb();
19327             });
19328         } else {
19329             cb();
19330         }
19331     };
19332
19333
19334     /* Init */
19335
19336     context.projection = iD.geo.RawMercator();
19337
19338     locale = iD.detect().locale;
19339     if (locale && iD.data.locales.indexOf(locale) === -1) {
19340         locale = locale.split('-')[0];
19341     }
19342
19343     history = iD.History(context);
19344     context.graph = history.graph;
19345     context.changes = history.changes;
19346     context.intersects = history.intersects;
19347
19348     // Debounce save, since it's a synchronous localStorage write,
19349     // and history changes can happen frequently (e.g. when dragging).
19350     context.debouncedSave = _.debounce(context.save, 350);
19351     function withDebouncedSave(fn) {
19352         return function() {
19353             var result = fn.apply(history, arguments);
19354             context.debouncedSave();
19355             return result;
19356         };
19357     }
19358
19359     context.perform = withDebouncedSave(history.perform);
19360     context.replace = withDebouncedSave(history.replace);
19361     context.pop = withDebouncedSave(history.pop);
19362     context.overwrite = withDebouncedSave(history.overwrite);
19363     context.undo = withDebouncedSave(history.undo);
19364     context.redo = withDebouncedSave(history.redo);
19365
19366     ui = iD.ui(context);
19367
19368     connection = iD.Connection();
19369
19370     background = iD.Background(context);
19371
19372     features = iD.Features(context);
19373
19374     map = iD.Map(context);
19375     context.mouse = map.mouse;
19376     context.extent = map.extent;
19377     context.pan = map.pan;
19378     context.zoomIn = map.zoomIn;
19379     context.zoomOut = map.zoomOut;
19380     context.zoomInFurther = map.zoomInFurther;
19381     context.zoomOutFurther = map.zoomOutFurther;
19382     context.redrawEnable = map.redrawEnable;
19383
19384     presets = iD.presets();
19385
19386     return d3.rebind(context, dispatch, 'on');
19387 };
19388
19389
19390 iD.version = '1.9.3';
19391
19392 (function() {
19393     var detected = {};
19394
19395     var ua = navigator.userAgent,
19396         m = null;
19397
19398     m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i);   // Edge
19399     if (m !== null) {
19400         detected.browser = m[1];
19401         detected.version = m[2];
19402     }
19403     if (!detected.browser) {
19404         m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i);   // IE11
19405         if (m !== null) {
19406             detected.browser = 'msie';
19407             detected.version = m[1];
19408         }
19409     }
19410     if (!detected.browser) {
19411         m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i);   // Opera 15+
19412         if (m !== null) {
19413             detected.browser = 'Opera';
19414             detected.version = m[2];
19415         }
19416     }
19417     if (!detected.browser) {
19418         m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
19419         if (m !== null) {
19420             detected.browser = m[1];
19421             detected.version = m[2];
19422             m = ua.match(/version\/([\.\d]+)/i);
19423             if (m !== null) detected.version = m[1];
19424         }
19425     }
19426     if (!detected.browser) {
19427         detected.browser = navigator.appName;
19428         detected.version = navigator.appVersion;
19429     }
19430
19431     // keep major.minor version only..
19432     detected.version = detected.version.split(/\W/).slice(0,2).join('.');
19433
19434     if (detected.browser.toLowerCase() === 'msie') {
19435         detected.ie = true;
19436         detected.browser = 'Internet Explorer';
19437         detected.support = parseFloat(detected.version) >= 11;
19438     } else {
19439         detected.ie = false;
19440         detected.support = true;
19441     }
19442
19443     // Added due to incomplete svg style support. See #715
19444     detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 );
19445
19446     detected.locale = (navigator.languages && navigator.languages.length)
19447         ? navigator.languages[0] : (navigator.language || navigator.userLanguage || 'en-US');
19448
19449     detected.filedrop = (window.FileReader && 'ondrop' in window);
19450
19451     function nav(x) {
19452         return navigator.userAgent.indexOf(x) !== -1;
19453     }
19454
19455     if (nav('Win')) {
19456         detected.os = 'win';
19457         detected.platform = 'Windows';
19458     }
19459     else if (nav('Mac')) {
19460         detected.os = 'mac';
19461         detected.platform = 'Macintosh';
19462     }
19463     else if (nav('X11') || nav('Linux')) {
19464         detected.os = 'linux';
19465         detected.platform = 'Linux';
19466     }
19467     else {
19468         detected.os = 'win';
19469         detected.platform = 'Unknown';
19470     }
19471
19472     iD.detect = function() { return detected; };
19473 })();
19474 iD.services = {};
19475 iD.services.mapillary = function() {
19476     var mapillary = {},
19477         dispatch = d3.dispatch('loadedImages', 'loadedSigns'),
19478         apibase = 'https://a.mapillary.com/v2/',
19479         urlImage = 'https://www.mapillary.com/map/im/',
19480         urlThumb = 'https://d1cuyjsrcm0gby.cloudfront.net/',
19481         clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi',
19482         maxResults = 1000,
19483         tileZoom = 14;
19484
19485
19486     function loadSignStyles(context) {
19487         d3.select('head').selectAll('#traffico')
19488             .data([0])
19489             .enter()
19490             .append('link')
19491             .attr('id', 'traffico')
19492             .attr('rel', 'stylesheet')
19493             .attr('href', context.asset('traffico/stylesheets/traffico.css'));
19494     }
19495
19496     function loadSignDefs(context) {
19497         if (!iD.services.mapillary.sign_defs) {
19498             iD.services.mapillary.sign_defs = {};
19499             _.each(['au', 'br', 'ca', 'de', 'us'], function(region) {
19500                 d3.json(context.asset('traffico/string-maps/' + region + '-map.json'), function(err, data) {
19501                     if (err) return;
19502                     if (region === 'de') region = 'eu';
19503                     iD.services.mapillary.sign_defs[region] = data;
19504                 });
19505             });
19506         }
19507     }
19508
19509     function abortRequest(i) {
19510         i.abort();
19511     }
19512
19513     function nearNullIsland(x, y, z) {
19514         if (z >= 7) {
19515             var center = Math.pow(2, z - 1),
19516                 width = Math.pow(2, z - 6),
19517                 min = center - (width / 2),
19518                 max = center + (width / 2) - 1;
19519             return x >= min && x <= max && y >= min && y <= max;
19520         }
19521         return false;
19522     }
19523
19524     function getTiles(projection, dimensions) {
19525         var s = projection.scale() * 2 * Math.PI,
19526             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
19527             ts = 256 * Math.pow(2, z - tileZoom),
19528             origin = [
19529                 s / 2 - projection.translate()[0],
19530                 s / 2 - projection.translate()[1]];
19531
19532         return d3.geo.tile()
19533             .scaleExtent([tileZoom, tileZoom])
19534             .scale(s)
19535             .size(dimensions)
19536             .translate(projection.translate())()
19537             .map(function(tile) {
19538                 var x = tile[0] * ts - origin[0],
19539                     y = tile[1] * ts - origin[1];
19540
19541                 return {
19542                     id: tile.toString(),
19543                     extent: iD.geo.Extent(
19544                         projection.invert([x, y + ts]),
19545                         projection.invert([x + ts, y]))
19546                 };
19547             });
19548     }
19549
19550
19551     function loadTiles(which, url, projection, dimensions) {
19552         var tiles = getTiles(projection, dimensions).filter(function(t) {
19553                 var xyz = t.id.split(',');
19554                 return !nearNullIsland(xyz[0], xyz[1], xyz[2]);
19555             });
19556
19557         _.filter(which.inflight, function(v, k) {
19558             var wanted = _.find(tiles, function(tile) { return k === (tile.id + ',0'); });
19559             if (!wanted) delete which.inflight[k];
19560             return !wanted;
19561         }).map(abortRequest);
19562
19563         tiles.forEach(function(tile) {
19564             loadTilePage(which, url, tile, 0);
19565         });
19566     }
19567
19568     function loadTilePage(which, url, tile, page) {
19569         var cache = iD.services.mapillary.cache[which],
19570             id = tile.id + ',' + String(page),
19571             rect = tile.extent.rectangle();
19572
19573         if (cache.loaded[id] || cache.inflight[id]) return;
19574
19575         cache.inflight[id] = d3.json(url +
19576             iD.util.qsString({
19577                 geojson: 'true',
19578                 limit: maxResults,
19579                 page: page,
19580                 client_id: clientId,
19581                 min_lon: rect[0],
19582                 min_lat: rect[1],
19583                 max_lon: rect[2],
19584                 max_lat: rect[3]
19585             }), function(err, data) {
19586                 cache.loaded[id] = true;
19587                 delete cache.inflight[id];
19588                 if (err || !data.features || !data.features.length) return;
19589
19590                 var features = [],
19591                     feature, loc, d;
19592
19593                 for (var i = 0; i < data.features.length; i++) {
19594                     feature = data.features[i];
19595                     loc = feature.geometry.coordinates;
19596                     d = { key: feature.properties.key, loc: loc };
19597                     if (which === 'images') d.ca = feature.properties.ca;
19598                     if (which === 'signs') d.signs = feature.properties.rects;
19599
19600                     features.push([loc[0], loc[1], loc[0], loc[1], d]);
19601                 }
19602
19603                 cache.rtree.load(features);
19604
19605                 if (which === 'images') dispatch.loadedImages();
19606                 if (which === 'signs') dispatch.loadedSigns();
19607
19608                 if (data.features.length === maxResults) {
19609                     loadTilePage(which, url, tile, ++page);
19610                 }
19611             }
19612         );
19613     }
19614
19615     mapillary.loadImages = function(projection, dimensions) {
19616         var url = apibase + 'search/im/geojson?';
19617         loadTiles('images', url, projection, dimensions);
19618     };
19619
19620     mapillary.loadSigns = function(context, projection, dimensions) {
19621         var url = apibase + 'search/im/geojson/or?';
19622         loadSignStyles(context);
19623         loadSignDefs(context);
19624         loadTiles('signs', url, projection, dimensions);
19625     };
19626
19627
19628     // partition viewport into `psize` x `psize` regions
19629     function partitionViewport(psize, projection, dimensions) {
19630         psize = psize || 16;
19631         var cols = d3.range(0, dimensions[0], psize),
19632             rows = d3.range(0, dimensions[1], psize),
19633             partitions = [];
19634
19635         rows.forEach(function(y) {
19636             cols.forEach(function(x) {
19637                 var min = [x, y + psize],
19638                     max = [x + psize, y];
19639                 partitions.push(
19640                     iD.geo.Extent(projection.invert(min), projection.invert(max)));
19641             });
19642         });
19643
19644         return partitions;
19645     }
19646
19647     // no more than `limit` results per partition.
19648     function searchLimited(psize, limit, projection, dimensions, rtree) {
19649         limit = limit || 3;
19650
19651         var partitions = partitionViewport(psize, projection, dimensions);
19652         return _.flatten(_.compact(_.map(partitions, function(extent) {
19653             return rtree.search(extent.rectangle())
19654                 .slice(0, limit)
19655                 .map(function(d) { return d[4]; });
19656         })));
19657     }
19658
19659     mapillary.images = function(projection, dimensions) {
19660         var psize = 16, limit = 3;
19661         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.images.rtree);
19662     };
19663
19664     mapillary.signs = function(projection, dimensions) {
19665         var psize = 32, limit = 3;
19666         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.signs.rtree);
19667     };
19668
19669     mapillary.signsSupported = function() {
19670         var detected = iD.detect();
19671         return (!(detected.ie || detected.browser.toLowerCase() === 'safari'));
19672     };
19673
19674     mapillary.signHTML = function(d) {
19675         if (!iD.services.mapillary.sign_defs) return;
19676
19677         var detectionPackage = d.signs[0].package,
19678             type = d.signs[0].type,
19679             country = detectionPackage.split('_')[1];
19680         return iD.services.mapillary.sign_defs[country][type];
19681     };
19682
19683     mapillary.showThumbnail = function(imageKey, position) {
19684         if (!imageKey) return;
19685
19686         var positionClass = {
19687             'ar': (position !== 'left'),
19688             'al': (position === 'left')
19689         };
19690
19691         var thumbnail = d3.select('#content').selectAll('.mapillary-image')
19692             .data([0]);
19693
19694         // Enter
19695         var enter = thumbnail.enter().append('div')
19696             .attr('class', 'mapillary-image ar');
19697
19698         enter.append('button')
19699             .on('click', function () {
19700                 mapillary.hideThumbnail();
19701             })
19702             .append('div')
19703             .call(iD.svg.Icon('#icon-close'));
19704
19705         enter.append('img');
19706
19707         enter.append('a')
19708             .attr('class', 'link ar')
19709             .attr('target', '_blank')
19710             .call(iD.svg.Icon('#icon-out-link', 'inline'))
19711             .append('span')
19712             .text(t('mapillary.view_on_mapillary'));
19713
19714         // Update
19715         thumbnail.selectAll('img')
19716             .attr('src', urlThumb + imageKey + '/thumb-320.jpg');
19717
19718         var link = thumbnail.selectAll('a')
19719             .attr('href', urlImage + imageKey);
19720
19721         if (position) {
19722             thumbnail.classed(positionClass);
19723             link.classed(positionClass);
19724         }
19725
19726         thumbnail
19727             .transition()
19728             .duration(200)
19729             .style('opacity', 1);
19730     };
19731
19732     mapillary.hideThumbnail = function() {
19733         if (iD.services.mapillary) {
19734             iD.services.mapillary.thumb = null;
19735         }
19736         d3.select('#content').selectAll('.mapillary-image')
19737             .transition()
19738             .duration(200)
19739             .style('opacity', 0)
19740             .remove();
19741     };
19742
19743     mapillary.selectedThumbnail = function(d) {
19744         if (!iD.services.mapillary) return null;
19745         if (!arguments.length) return iD.services.mapillary.thumb;
19746         iD.services.mapillary.thumb = d;
19747     };
19748
19749     mapillary.reset = function() {
19750         var cache = iD.services.mapillary.cache;
19751
19752         if (cache) {
19753             _.forEach(cache.images.inflight, abortRequest);
19754             _.forEach(cache.signs.inflight, abortRequest);
19755         }
19756
19757         iD.services.mapillary.cache = {
19758             images: { inflight: {}, loaded: {}, rtree: rbush() },
19759             signs:  { inflight: {}, loaded: {}, rtree: rbush() }
19760         };
19761
19762         iD.services.mapillary.thumb = null;
19763
19764         return mapillary;
19765     };
19766
19767
19768     if (!iD.services.mapillary.cache) {
19769         mapillary.reset();
19770     }
19771
19772     return d3.rebind(mapillary, dispatch, 'on');
19773 };
19774 iD.services.nominatim  = function() {
19775     var nominatim = {},
19776         endpoint = 'https://nominatim.openstreetmap.org/reverse?';
19777
19778
19779     nominatim.countryCode = function(location, callback) {
19780         var cache = iD.services.nominatim.cache,
19781             countryCodes = cache.search([location[0], location[1], location[0], location[1]]);
19782
19783         if (countryCodes.length > 0)
19784             return callback(null, countryCodes[0][4]);
19785
19786         d3.json(endpoint +
19787             iD.util.qsString({
19788                 format: 'json',
19789                 addressdetails: 1,
19790                 lat: location[1],
19791                 lon: location[0]
19792             }), function(err, result) {
19793                 if (err)
19794                     return callback(err);
19795                 else if (result && result.error)
19796                     return callback(result.error);
19797
19798                 var extent = iD.geo.Extent(location).padByMeters(1000);
19799
19800                 cache.insert(extent.rectangle().concat(result.address.country_code));
19801
19802                 callback(null, result.address.country_code);
19803             });
19804     };
19805
19806     nominatim.reset = function() {
19807         iD.services.nominatim.cache = rbush();
19808         return nominatim;
19809     };
19810
19811
19812     if (!iD.services.nominatim.cache) {
19813         nominatim.reset();
19814     }
19815
19816     return nominatim;
19817 };
19818 iD.services.taginfo = function() {
19819     var taginfo = {},
19820         endpoint = 'https://taginfo.openstreetmap.org/api/4/',
19821         tag_sorts = {
19822             point: 'count_nodes',
19823             vertex: 'count_nodes',
19824             area: 'count_ways',
19825             line: 'count_ways'
19826         },
19827         tag_filters = {
19828             point: 'nodes',
19829             vertex: 'nodes',
19830             area: 'ways',
19831             line: 'ways'
19832         };
19833
19834
19835     function sets(parameters, n, o) {
19836         if (parameters.geometry && o[parameters.geometry]) {
19837             parameters[n] = o[parameters.geometry];
19838         }
19839         return parameters;
19840     }
19841
19842     function setFilter(parameters) {
19843         return sets(parameters, 'filter', tag_filters);
19844     }
19845
19846     function setSort(parameters) {
19847         return sets(parameters, 'sortname', tag_sorts);
19848     }
19849
19850     function clean(parameters) {
19851         return _.omit(parameters, 'geometry', 'debounce');
19852     }
19853
19854     function popularKeys(parameters) {
19855         var pop_field = 'count_all';
19856         if (parameters.filter) pop_field = 'count_' + parameters.filter;
19857         return function(d) { return parseFloat(d[pop_field]) > 5000 || d.in_wiki; };
19858     }
19859
19860     function popularValues() {
19861         return function(d) { return parseFloat(d.fraction) > 0.01 || d.in_wiki; };
19862     }
19863
19864     function valKey(d) {
19865         return {
19866             value: d.key,
19867             title: d.key
19868         };
19869     }
19870
19871     function valKeyDescription(d) {
19872         return {
19873             value: d.value,
19874             title: d.description || d.value
19875         };
19876     }
19877
19878     // sort keys with ':' lower than keys without ':'
19879     function sortKeys(a, b) {
19880         return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
19881             : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
19882             : 0;
19883     }
19884
19885     var debounced = _.debounce(d3.json, 100, true);
19886
19887     function request(url, debounce, callback) {
19888         var cache = iD.services.taginfo.cache;
19889
19890         if (cache[url]) {
19891             callback(null, cache[url]);
19892         } else if (debounce) {
19893             debounced(url, done);
19894         } else {
19895             d3.json(url, done);
19896         }
19897
19898         function done(err, data) {
19899             if (!err) cache[url] = data;
19900             callback(err, data);
19901         }
19902     }
19903
19904     taginfo.keys = function(parameters, callback) {
19905         var debounce = parameters.debounce;
19906         parameters = clean(setSort(parameters));
19907         request(endpoint + 'keys/all?' +
19908             iD.util.qsString(_.extend({
19909                 rp: 10,
19910                 sortname: 'count_all',
19911                 sortorder: 'desc',
19912                 page: 1
19913             }, parameters)), debounce, function(err, d) {
19914                 if (err) return callback(err);
19915                 callback(null, d.data.filter(popularKeys(parameters)).sort(sortKeys).map(valKey));
19916             });
19917     };
19918
19919     taginfo.values = function(parameters, callback) {
19920         var debounce = parameters.debounce;
19921         parameters = clean(setSort(setFilter(parameters)));
19922         request(endpoint + 'key/values?' +
19923             iD.util.qsString(_.extend({
19924                 rp: 25,
19925                 sortname: 'count_all',
19926                 sortorder: 'desc',
19927                 page: 1
19928             }, parameters)), debounce, function(err, d) {
19929                 if (err) return callback(err);
19930                 callback(null, d.data.filter(popularValues()).map(valKeyDescription), parameters);
19931             });
19932     };
19933
19934     taginfo.docs = function(parameters, callback) {
19935         var debounce = parameters.debounce;
19936         parameters = clean(setSort(parameters));
19937
19938         var path = 'key/wiki_pages?';
19939         if (parameters.value) path = 'tag/wiki_pages?';
19940         else if (parameters.rtype) path = 'relation/wiki_pages?';
19941
19942         request(endpoint + path + iD.util.qsString(parameters), debounce, function(err, d) {
19943             if (err) return callback(err);
19944             callback(null, d.data);
19945         });
19946     };
19947
19948     taginfo.endpoint = function(_) {
19949         if (!arguments.length) return endpoint;
19950         endpoint = _;
19951         return taginfo;
19952     };
19953
19954     taginfo.reset = function() {
19955         iD.services.taginfo.cache = {};
19956         return taginfo;
19957     };
19958
19959
19960     if (!iD.services.taginfo.cache) {
19961         taginfo.reset();
19962     }
19963
19964     return taginfo;
19965 };
19966 iD.services.wikipedia = function() {
19967     var wiki = {},
19968         endpoint = 'https://en.wikipedia.org/w/api.php?';
19969
19970     wiki.search = function(lang, query, callback) {
19971         lang = lang || 'en';
19972         d3.jsonp(endpoint.replace('en', lang) +
19973             iD.util.qsString({
19974                 action: 'query',
19975                 list: 'search',
19976                 srlimit: '10',
19977                 srinfo: 'suggestion',
19978                 format: 'json',
19979                 callback: '{callback}',
19980                 srsearch: query
19981             }), function(data) {
19982                 if (!data.query) return;
19983                 callback(query, data.query.search.map(function(d) {
19984                     return d.title;
19985                 }));
19986             });
19987     };
19988
19989     wiki.suggestions = function(lang, query, callback) {
19990         lang = lang || 'en';
19991         d3.jsonp(endpoint.replace('en', lang) +
19992             iD.util.qsString({
19993                 action: 'opensearch',
19994                 namespace: 0,
19995                 suggest: '',
19996                 format: 'json',
19997                 callback: '{callback}',
19998                 search: query
19999             }), function(d) {
20000                 callback(d[0], d[1]);
20001             });
20002     };
20003
20004     wiki.translations = function(lang, title, callback) {
20005         d3.jsonp(endpoint.replace('en', lang) +
20006             iD.util.qsString({
20007                 action: 'query',
20008                 prop: 'langlinks',
20009                 format: 'json',
20010                 callback: '{callback}',
20011                 lllimit: 500,
20012                 titles: title
20013             }), function(d) {
20014                 var list = d.query.pages[Object.keys(d.query.pages)[0]],
20015                     translations = {};
20016                 if (list && list.langlinks) {
20017                     list.langlinks.forEach(function(d) {
20018                         translations[d.lang] = d['*'];
20019                     });
20020                     callback(translations);
20021                 }
20022             });
20023     };
20024
20025     return wiki;
20026 };
20027 iD.util = {};
20028
20029 iD.util.tagText = function(entity) {
20030     return d3.entries(entity.tags).map(function(e) {
20031         return e.key + '=' + e.value;
20032     }).join(', ');
20033 };
20034
20035 iD.util.entitySelector = function(ids) {
20036     return ids.length ? '.' + ids.join(',.') : 'nothing';
20037 };
20038
20039 iD.util.entityOrMemberSelector = function(ids, graph) {
20040     var s = iD.util.entitySelector(ids);
20041
20042     ids.forEach(function(id) {
20043         var entity = graph.hasEntity(id);
20044         if (entity && entity.type === 'relation') {
20045             entity.members.forEach(function(member) {
20046                 s += ',.' + member.id;
20047             });
20048         }
20049     });
20050
20051     return s;
20052 };
20053
20054 iD.util.displayName = function(entity) {
20055     var localeName = 'name:' + iD.detect().locale.toLowerCase().split('-')[0];
20056     return entity.tags[localeName] || entity.tags.name || entity.tags.ref;
20057 };
20058
20059 iD.util.displayType = function(id) {
20060     return {
20061         n: t('inspector.node'),
20062         w: t('inspector.way'),
20063         r: t('inspector.relation')
20064     }[id.charAt(0)];
20065 };
20066
20067 iD.util.stringQs = function(str) {
20068     return str.split('&').reduce(function(obj, pair){
20069         var parts = pair.split('=');
20070         if (parts.length === 2) {
20071             obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
20072         }
20073         return obj;
20074     }, {});
20075 };
20076
20077 iD.util.qsString = function(obj, noencode) {
20078     function softEncode(s) {
20079       // encode everything except special characters used in certain hash parameters:
20080       // "/" in map states, ":", ",", {" and "}" in background
20081       return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
20082     }
20083     return Object.keys(obj).sort().map(function(key) {
20084         return encodeURIComponent(key) + '=' + (
20085             noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
20086     }).join('&');
20087 };
20088
20089 iD.util.prefixDOMProperty = function(property) {
20090     var prefixes = ['webkit', 'ms', 'moz', 'o'],
20091         i = -1,
20092         n = prefixes.length,
20093         s = document.body;
20094
20095     if (property in s)
20096         return property;
20097
20098     property = property.substr(0, 1).toUpperCase() + property.substr(1);
20099
20100     while (++i < n)
20101         if (prefixes[i] + property in s)
20102             return prefixes[i] + property;
20103
20104     return false;
20105 };
20106
20107 iD.util.prefixCSSProperty = function(property) {
20108     var prefixes = ['webkit', 'ms', 'Moz', 'O'],
20109         i = -1,
20110         n = prefixes.length,
20111         s = document.body.style;
20112
20113     if (property.toLowerCase() in s)
20114         return property.toLowerCase();
20115
20116     while (++i < n)
20117         if (prefixes[i] + property in s)
20118             return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
20119
20120     return false;
20121 };
20122
20123
20124 iD.util.setTransform = function(el, x, y, scale) {
20125     var prop = iD.util.transformProperty = iD.util.transformProperty || iD.util.prefixCSSProperty('Transform'),
20126         translate = iD.detect().opera ?
20127             'translate('   + x + 'px,' + y + 'px)' :
20128             'translate3d(' + x + 'px,' + y + 'px,0)';
20129     return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
20130 };
20131
20132 iD.util.getStyle = function(selector) {
20133     for (var i = 0; i < document.styleSheets.length; i++) {
20134         var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules || [];
20135         for (var k = 0; k < rules.length; k++) {
20136             var selectorText = rules[k].selectorText && rules[k].selectorText.split(', ');
20137             if (_.contains(selectorText, selector)) {
20138                 return rules[k];
20139             }
20140         }
20141     }
20142 };
20143
20144 iD.util.editDistance = function(a, b) {
20145     if (a.length === 0) return b.length;
20146     if (b.length === 0) return a.length;
20147     var matrix = [];
20148     for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
20149     for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
20150     for (i = 1; i <= b.length; i++) {
20151         for (j = 1; j <= a.length; j++) {
20152             if (b.charAt(i-1) === a.charAt(j-1)) {
20153                 matrix[i][j] = matrix[i-1][j-1];
20154             } else {
20155                 matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
20156                     Math.min(matrix[i][j-1] + 1, // insertion
20157                     matrix[i-1][j] + 1)); // deletion
20158             }
20159         }
20160     }
20161     return matrix[b.length][a.length];
20162 };
20163
20164 // a d3.mouse-alike which
20165 // 1. Only works on HTML elements, not SVG
20166 // 2. Does not cause style recalculation
20167 iD.util.fastMouse = function(container) {
20168     var rect = container.getBoundingClientRect(),
20169         rectLeft = rect.left,
20170         rectTop = rect.top,
20171         clientLeft = +container.clientLeft,
20172         clientTop = +container.clientTop;
20173     return function(e) {
20174         return [
20175             e.clientX - rectLeft - clientLeft,
20176             e.clientY - rectTop - clientTop];
20177     };
20178 };
20179
20180 /* eslint-disable no-proto */
20181 iD.util.getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; };
20182 /* eslint-enable no-proto */
20183
20184 iD.util.asyncMap = function(inputs, func, callback) {
20185     var remaining = inputs.length,
20186         results = [],
20187         errors = [];
20188
20189     inputs.forEach(function(d, i) {
20190         func(d, function done(err, data) {
20191             errors[i] = err;
20192             results[i] = data;
20193             remaining--;
20194             if (!remaining) callback(errors, results);
20195         });
20196     });
20197 };
20198
20199 // wraps an index to an interval [0..length-1]
20200 iD.util.wrap = function(index, length) {
20201     if (index < 0)
20202         index += Math.ceil(-index/length)*length;
20203     return index % length;
20204 };
20205 // A per-domain session mutex backed by a cookie and dead man's
20206 // switch. If the session crashes, the mutex will auto-release
20207 // after 5 seconds.
20208
20209 iD.util.SessionMutex = function(name) {
20210     var mutex = {},
20211         intervalID;
20212
20213     function renew() {
20214         var expires = new Date();
20215         expires.setSeconds(expires.getSeconds() + 5);
20216         document.cookie = name + '=1; expires=' + expires.toUTCString();
20217     }
20218
20219     mutex.lock = function() {
20220         if (intervalID) return true;
20221         var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
20222         if (cookie) return false;
20223         renew();
20224         intervalID = window.setInterval(renew, 4000);
20225         return true;
20226     };
20227
20228     mutex.unlock = function() {
20229         if (!intervalID) return;
20230         document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
20231         clearInterval(intervalID);
20232         intervalID = null;
20233     };
20234
20235     mutex.locked = function() {
20236         return !!intervalID;
20237     };
20238
20239     return mutex;
20240 };
20241 iD.util.SuggestNames = function(preset, suggestions) {
20242     preset = preset.id.split('/', 2);
20243     var k = preset[0],
20244         v = preset[1];
20245
20246     return function(value, callback) {
20247         var result = [];
20248         if (value && value.length > 2) {
20249             if (suggestions[k] && suggestions[k][v]) {
20250                 for (var sugg in suggestions[k][v]) {
20251                     var dist = iD.util.editDistance(value, sugg.substring(0, value.length));
20252                     if (dist < 3) {
20253                         result.push({
20254                             title: sugg,
20255                             value: sugg,
20256                             dist: dist
20257                         });
20258                     }
20259                 }
20260             }
20261             result.sort(function(a, b) {
20262                 return a.dist - b.dist;
20263             });
20264         }
20265         result = result.slice(0,3);
20266         callback(result);
20267     };
20268 };
20269 iD.geo = {};
20270
20271 iD.geo.roundCoords = function(c) {
20272     return [Math.floor(c[0]), Math.floor(c[1])];
20273 };
20274
20275 iD.geo.interp = function(p1, p2, t) {
20276     return [p1[0] + (p2[0] - p1[0]) * t,
20277             p1[1] + (p2[1] - p1[1]) * t];
20278 };
20279
20280 // 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product.
20281 // Returns a positive value, if OAB makes a counter-clockwise turn,
20282 // negative for clockwise turn, and zero if the points are collinear.
20283 iD.geo.cross = function(o, a, b) {
20284     return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
20285 };
20286
20287 // http://jsperf.com/id-dist-optimization
20288 iD.geo.euclideanDistance = function(a, b) {
20289     var x = a[0] - b[0], y = a[1] - b[1];
20290     return Math.sqrt((x * x) + (y * y));
20291 };
20292
20293 // using WGS84 polar radius (6356752.314245179 m)
20294 // const = 2 * PI * r / 360
20295 iD.geo.latToMeters = function(dLat) {
20296     return dLat * 110946.257617;
20297 };
20298
20299 // using WGS84 equatorial radius (6378137.0 m)
20300 // const = 2 * PI * r / 360
20301 iD.geo.lonToMeters = function(dLon, atLat) {
20302     return Math.abs(atLat) >= 90 ? 0 :
20303         dLon * 111319.490793 * Math.abs(Math.cos(atLat * (Math.PI/180)));
20304 };
20305
20306 // using WGS84 polar radius (6356752.314245179 m)
20307 // const = 2 * PI * r / 360
20308 iD.geo.metersToLat = function(m) {
20309     return m / 110946.257617;
20310 };
20311
20312 // using WGS84 equatorial radius (6378137.0 m)
20313 // const = 2 * PI * r / 360
20314 iD.geo.metersToLon = function(m, atLat) {
20315     return Math.abs(atLat) >= 90 ? 0 :
20316         m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180)));
20317 };
20318
20319 // Equirectangular approximation of spherical distances on Earth
20320 iD.geo.sphericalDistance = function(a, b) {
20321     var x = iD.geo.lonToMeters(a[0] - b[0], (a[1] + b[1]) / 2),
20322         y = iD.geo.latToMeters(a[1] - b[1]);
20323     return Math.sqrt((x * x) + (y * y));
20324 };
20325
20326 iD.geo.edgeEqual = function(a, b) {
20327     return (a[0] === b[0] && a[1] === b[1]) ||
20328         (a[0] === b[1] && a[1] === b[0]);
20329 };
20330
20331 // Return the counterclockwise angle in the range (-pi, pi)
20332 // between the positive X axis and the line intersecting a and b.
20333 iD.geo.angle = function(a, b, projection) {
20334     a = projection(a.loc);
20335     b = projection(b.loc);
20336     return Math.atan2(b[1] - a[1], b[0] - a[0]);
20337 };
20338
20339 // Choose the edge with the minimal distance from `point` to its orthogonal
20340 // projection onto that edge, if such a projection exists, or the distance to
20341 // the closest vertex on that edge. Returns an object with the `index` of the
20342 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
20343 iD.geo.chooseEdge = function(nodes, point, projection) {
20344     var dist = iD.geo.euclideanDistance,
20345         points = nodes.map(function(n) { return projection(n.loc); }),
20346         min = Infinity,
20347         idx, loc;
20348
20349     function dot(p, q) {
20350         return p[0] * q[0] + p[1] * q[1];
20351     }
20352
20353     for (var i = 0; i < points.length - 1; i++) {
20354         var o = points[i],
20355             s = [points[i + 1][0] - o[0],
20356                  points[i + 1][1] - o[1]],
20357             v = [point[0] - o[0],
20358                  point[1] - o[1]],
20359             proj = dot(v, s) / dot(s, s),
20360             p;
20361
20362         if (proj < 0) {
20363             p = o;
20364         } else if (proj > 1) {
20365             p = points[i + 1];
20366         } else {
20367             p = [o[0] + proj * s[0], o[1] + proj * s[1]];
20368         }
20369
20370         var d = dist(p, point);
20371         if (d < min) {
20372             min = d;
20373             idx = i + 1;
20374             loc = projection.invert(p);
20375         }
20376     }
20377
20378     return {
20379         index: idx,
20380         distance: min,
20381         loc: loc
20382     };
20383 };
20384
20385 // Return the intersection point of 2 line segments.
20386 // From https://github.com/pgkelley4/line-segments-intersect
20387 // This uses the vector cross product approach described below:
20388 //  http://stackoverflow.com/a/565282/786339
20389 iD.geo.lineIntersection = function(a, b) {
20390     function subtractPoints(point1, point2) {
20391         return [point1[0] - point2[0], point1[1] - point2[1]];
20392     }
20393     function crossProduct(point1, point2) {
20394         return point1[0] * point2[1] - point1[1] * point2[0];
20395     }
20396
20397     var p = [a[0][0], a[0][1]],
20398         p2 = [a[1][0], a[1][1]],
20399         q = [b[0][0], b[0][1]],
20400         q2 = [b[1][0], b[1][1]],
20401         r = subtractPoints(p2, p),
20402         s = subtractPoints(q2, q),
20403         uNumerator = crossProduct(subtractPoints(q, p), r),
20404         denominator = crossProduct(r, s);
20405
20406     if (uNumerator && denominator) {
20407         var u = uNumerator / denominator,
20408             t = crossProduct(subtractPoints(q, p), s) / denominator;
20409
20410         if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
20411             return iD.geo.interp(p, p2, t);
20412         }
20413     }
20414
20415     return null;
20416 };
20417
20418 iD.geo.pathIntersections = function(path1, path2) {
20419     var intersections = [];
20420     for (var i = 0; i < path1.length - 1; i++) {
20421         for (var j = 0; j < path2.length - 1; j++) {
20422             var a = [ path1[i], path1[i+1] ],
20423                 b = [ path2[j], path2[j+1] ],
20424                 hit = iD.geo.lineIntersection(a, b);
20425             if (hit) intersections.push(hit);
20426         }
20427     }
20428     return intersections;
20429 };
20430
20431 // Return whether point is contained in polygon.
20432 //
20433 // `point` should be a 2-item array of coordinates.
20434 // `polygon` should be an array of 2-item arrays of coordinates.
20435 //
20436 // From https://github.com/substack/point-in-polygon.
20437 // ray-casting algorithm based on
20438 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
20439 //
20440 iD.geo.pointInPolygon = function(point, polygon) {
20441     var x = point[0],
20442         y = point[1],
20443         inside = false;
20444
20445     for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
20446         var xi = polygon[i][0], yi = polygon[i][1];
20447         var xj = polygon[j][0], yj = polygon[j][1];
20448
20449         var intersect = ((yi > y) !== (yj > y)) &&
20450             (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
20451         if (intersect) inside = !inside;
20452     }
20453
20454     return inside;
20455 };
20456
20457 iD.geo.polygonContainsPolygon = function(outer, inner) {
20458     return _.every(inner, function(point) {
20459         return iD.geo.pointInPolygon(point, outer);
20460     });
20461 };
20462
20463 iD.geo.polygonIntersectsPolygon = function(outer, inner, checkSegments) {
20464     function testSegments(outer, inner) {
20465         for (var i = 0; i < outer.length - 1; i++) {
20466             for (var j = 0; j < inner.length - 1; j++) {
20467                 var a = [ outer[i], outer[i+1] ],
20468                     b = [ inner[j], inner[j+1] ];
20469                 if (iD.geo.lineIntersection(a, b)) return true;
20470             }
20471         }
20472         return false;
20473     }
20474
20475     function testPoints(outer, inner) {
20476         return _.some(inner, function(point) {
20477             return iD.geo.pointInPolygon(point, outer);
20478         });
20479     }
20480
20481    return testPoints(outer, inner) || (!!checkSegments && testSegments(outer, inner));
20482 };
20483
20484 iD.geo.pathLength = function(path) {
20485     var length = 0,
20486         dx, dy;
20487     for (var i = 0; i < path.length - 1; i++) {
20488         dx = path[i][0] - path[i + 1][0];
20489         dy = path[i][1] - path[i + 1][1];
20490         length += Math.sqrt(dx * dx + dy * dy);
20491     }
20492     return length;
20493 };
20494 iD.geo.Extent = function geoExtent(min, max) {
20495     if (!(this instanceof iD.geo.Extent)) return new iD.geo.Extent(min, max);
20496     if (min instanceof iD.geo.Extent) {
20497         return min;
20498     } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
20499         this[0] = min[0];
20500         this[1] = min[1];
20501     } else {
20502         this[0] = min        || [ Infinity,  Infinity];
20503         this[1] = max || min || [-Infinity, -Infinity];
20504     }
20505 };
20506
20507 iD.geo.Extent.prototype = new Array(2);
20508
20509 _.extend(iD.geo.Extent.prototype, {
20510     equals: function (obj) {
20511         return this[0][0] === obj[0][0] &&
20512             this[0][1] === obj[0][1] &&
20513             this[1][0] === obj[1][0] &&
20514             this[1][1] === obj[1][1];
20515     },
20516
20517     extend: function(obj) {
20518         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20519         return iD.geo.Extent([Math.min(obj[0][0], this[0][0]),
20520                               Math.min(obj[0][1], this[0][1])],
20521                              [Math.max(obj[1][0], this[1][0]),
20522                               Math.max(obj[1][1], this[1][1])]);
20523     },
20524
20525     _extend: function(extent) {
20526         this[0][0] = Math.min(extent[0][0], this[0][0]);
20527         this[0][1] = Math.min(extent[0][1], this[0][1]);
20528         this[1][0] = Math.max(extent[1][0], this[1][0]);
20529         this[1][1] = Math.max(extent[1][1], this[1][1]);
20530     },
20531
20532     area: function() {
20533         return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
20534     },
20535
20536     center: function() {
20537         return [(this[0][0] + this[1][0]) / 2,
20538                 (this[0][1] + this[1][1]) / 2];
20539     },
20540
20541     rectangle: function() {
20542         return [this[0][0], this[0][1], this[1][0], this[1][1]];
20543     },
20544
20545     polygon: function() {
20546         return [
20547             [this[0][0], this[0][1]],
20548             [this[0][0], this[1][1]],
20549             [this[1][0], this[1][1]],
20550             [this[1][0], this[0][1]],
20551             [this[0][0], this[0][1]]
20552         ];
20553     },
20554
20555     contains: function(obj) {
20556         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20557         return obj[0][0] >= this[0][0] &&
20558                obj[0][1] >= this[0][1] &&
20559                obj[1][0] <= this[1][0] &&
20560                obj[1][1] <= this[1][1];
20561     },
20562
20563     intersects: function(obj) {
20564         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20565         return obj[0][0] <= this[1][0] &&
20566                obj[0][1] <= this[1][1] &&
20567                obj[1][0] >= this[0][0] &&
20568                obj[1][1] >= this[0][1];
20569     },
20570
20571     intersection: function(obj) {
20572         if (!this.intersects(obj)) return new iD.geo.Extent();
20573         return new iD.geo.Extent([Math.max(obj[0][0], this[0][0]),
20574                                   Math.max(obj[0][1], this[0][1])],
20575                                  [Math.min(obj[1][0], this[1][0]),
20576                                   Math.min(obj[1][1], this[1][1])]);
20577     },
20578
20579     percentContainedIn: function(obj) {
20580         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20581         var a1 = this.intersection(obj).area(),
20582             a2 = this.area();
20583
20584         if (a1 === Infinity || a2 === Infinity || a1 === 0 || a2 === 0) {
20585             return 0;
20586         } else {
20587             return a1 / a2;
20588         }
20589     },
20590
20591     padByMeters: function(meters) {
20592         var dLat = iD.geo.metersToLat(meters),
20593             dLon = iD.geo.metersToLon(meters, this.center()[1]);
20594         return iD.geo.Extent(
20595                 [this[0][0] - dLon, this[0][1] - dLat],
20596                 [this[1][0] + dLon, this[1][1] + dLat]);
20597     },
20598
20599     toParam: function() {
20600         return this.rectangle().join(',');
20601     }
20602
20603 });
20604 iD.geo.Turn = function(turn) {
20605     if (!(this instanceof iD.geo.Turn))
20606         return new iD.geo.Turn(turn);
20607     _.extend(this, turn);
20608 };
20609
20610 iD.geo.Intersection = function(graph, vertexId) {
20611     var vertex = graph.entity(vertexId),
20612         parentWays = graph.parentWays(vertex),
20613         coincident = [],
20614         highways = {};
20615
20616     function addHighway(way, adjacentNodeId) {
20617         if (highways[adjacentNodeId]) {
20618             coincident.push(adjacentNodeId);
20619         } else {
20620             highways[adjacentNodeId] = way;
20621         }
20622     }
20623
20624     // Pre-split ways that would need to be split in
20625     // order to add a restriction. The real split will
20626     // happen when the restriction is added.
20627     parentWays.forEach(function(way) {
20628         if (!way.tags.highway || way.isArea() || way.isDegenerate())
20629             return;
20630
20631         var isFirst = (vertexId === way.first()),
20632             isLast = (vertexId === way.last()),
20633             isAffix = (isFirst || isLast),
20634             isClosingNode = (isFirst && isLast);
20635
20636         if (isAffix && !isClosingNode) {
20637             var index = (isFirst ? 1 : way.nodes.length - 2);
20638             addHighway(way, way.nodes[index]);
20639
20640         } else {
20641             var splitIndex, wayA, wayB, indexA, indexB;
20642             if (isClosingNode) {
20643                 splitIndex = Math.ceil(way.nodes.length / 2);  // split at midpoint
20644                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
20645                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
20646                 indexA = 1;
20647                 indexB = way.nodes.length - 2;
20648             } else {
20649                 splitIndex = _.indexOf(way.nodes, vertex.id, 1);  // split at vertexid
20650                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
20651                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
20652                 indexA = splitIndex - 1;
20653                 indexB = splitIndex + 1;
20654             }
20655             graph = graph.replace(wayA).replace(wayB);
20656             addHighway(wayA, way.nodes[indexA]);
20657             addHighway(wayB, way.nodes[indexB]);
20658         }
20659     });
20660
20661     // remove any ways from this intersection that are coincident
20662     // (i.e. any adjacent node used by more than one intersecting way)
20663     coincident.forEach(function (n) {
20664         delete highways[n];
20665     });
20666
20667
20668     var intersection = {
20669         highways: highways,
20670         ways: _.values(highways),
20671         graph: graph
20672     };
20673
20674     intersection.adjacentNodeId = function(fromWayId) {
20675         return _.find(_.keys(highways), function(k) {
20676             return highways[k].id === fromWayId;
20677         });
20678     };
20679
20680     intersection.turns = function(fromNodeId) {
20681         var start = highways[fromNodeId];
20682         if (!start)
20683             return [];
20684
20685         if (start.first() === vertex.id && start.tags.oneway === 'yes')
20686             return [];
20687         if (start.last() === vertex.id && start.tags.oneway === '-1')
20688             return [];
20689
20690         function withRestriction(turn) {
20691             graph.parentRelations(graph.entity(turn.from.way)).forEach(function(relation) {
20692                 if (relation.tags.type !== 'restriction')
20693                     return;
20694
20695                 var f = relation.memberByRole('from'),
20696                     t = relation.memberByRole('to'),
20697                     v = relation.memberByRole('via');
20698
20699                 if (f && f.id === turn.from.way &&
20700                     v && v.id === turn.via.node &&
20701                     t && t.id === turn.to.way) {
20702                     turn.restriction = relation.id;
20703                 } else if (/^only_/.test(relation.tags.restriction) &&
20704                     f && f.id === turn.from.way &&
20705                     v && v.id === turn.via.node &&
20706                     t && t.id !== turn.to.way) {
20707                     turn.restriction = relation.id;
20708                     turn.indirect_restriction = true;
20709                 }
20710             });
20711
20712             return iD.geo.Turn(turn);
20713         }
20714
20715         var from = {
20716                 node: fromNodeId,
20717                 way: start.id.split(/-(a|b)/)[0]
20718             },
20719             via = { node: vertex.id },
20720             turns = [];
20721
20722         _.each(highways, function(end, adjacentNodeId) {
20723             if (end === start)
20724                 return;
20725
20726             // backward
20727             if (end.first() !== vertex.id && end.tags.oneway !== 'yes') {
20728                 turns.push(withRestriction({
20729                     from: from,
20730                     via: via,
20731                     to: {
20732                         node: adjacentNodeId,
20733                         way: end.id.split(/-(a|b)/)[0]
20734                     }
20735                 }));
20736             }
20737
20738             // forward
20739             if (end.last() !== vertex.id && end.tags.oneway !== '-1') {
20740                 turns.push(withRestriction({
20741                     from: from,
20742                     via: via,
20743                     to: {
20744                         node: adjacentNodeId,
20745                         way: end.id.split(/-(a|b)/)[0]
20746                     }
20747                 }));
20748             }
20749
20750         });
20751
20752         // U-turn
20753         if (start.tags.oneway !== 'yes' && start.tags.oneway !== '-1') {
20754             turns.push(withRestriction({
20755                 from: from,
20756                 via: via,
20757                 to: from,
20758                 u: true
20759             }));
20760         }
20761
20762         return turns;
20763     };
20764
20765     return intersection;
20766 };
20767
20768
20769 iD.geo.inferRestriction = function(graph, from, via, to, projection) {
20770     var fromWay = graph.entity(from.way),
20771         fromNode = graph.entity(from.node),
20772         toWay = graph.entity(to.way),
20773         toNode = graph.entity(to.node),
20774         viaNode = graph.entity(via.node),
20775         fromOneWay = (fromWay.tags.oneway === 'yes' && fromWay.last() === via.node) ||
20776             (fromWay.tags.oneway === '-1' && fromWay.first() === via.node),
20777         toOneWay = (toWay.tags.oneway === 'yes' && toWay.first() === via.node) ||
20778             (toWay.tags.oneway === '-1' && toWay.last() === via.node),
20779         angle = iD.geo.angle(viaNode, fromNode, projection) -
20780                 iD.geo.angle(viaNode, toNode, projection);
20781
20782     angle = angle * 180 / Math.PI;
20783
20784     while (angle < 0)
20785         angle += 360;
20786
20787     if (fromNode === toNode)
20788         return 'no_u_turn';
20789     if ((angle < 23 || angle > 336) && fromOneWay && toOneWay)
20790         return 'no_u_turn';
20791     if (angle < 158)
20792         return 'no_right_turn';
20793     if (angle > 202)
20794         return 'no_left_turn';
20795
20796     return 'no_straight_on';
20797 };
20798 // For fixing up rendering of multipolygons with tags on the outer member.
20799 // https://github.com/openstreetmap/iD/issues/613
20800 iD.geo.isSimpleMultipolygonOuterMember = function(entity, graph) {
20801     if (entity.type !== 'way')
20802         return false;
20803
20804     var parents = graph.parentRelations(entity);
20805     if (parents.length !== 1)
20806         return false;
20807
20808     var parent = parents[0];
20809     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
20810         return false;
20811
20812     var members = parent.members, member;
20813     for (var i = 0; i < members.length; i++) {
20814         member = members[i];
20815         if (member.id === entity.id && member.role && member.role !== 'outer')
20816             return false; // Not outer member
20817         if (member.id !== entity.id && (!member.role || member.role === 'outer'))
20818             return false; // Not a simple multipolygon
20819     }
20820
20821     return parent;
20822 };
20823
20824 iD.geo.simpleMultipolygonOuterMember = function(entity, graph) {
20825     if (entity.type !== 'way')
20826         return false;
20827
20828     var parents = graph.parentRelations(entity);
20829     if (parents.length !== 1)
20830         return false;
20831
20832     var parent = parents[0];
20833     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
20834         return false;
20835
20836     var members = parent.members, member, outerMember;
20837     for (var i = 0; i < members.length; i++) {
20838         member = members[i];
20839         if (!member.role || member.role === 'outer') {
20840             if (outerMember)
20841                 return false; // Not a simple multipolygon
20842             outerMember = member;
20843         }
20844     }
20845
20846     return outerMember && graph.hasEntity(outerMember.id);
20847 };
20848
20849 // Join `array` into sequences of connecting ways.
20850 //
20851 // Segments which share identical start/end nodes will, as much as possible,
20852 // be connected with each other.
20853 //
20854 // The return value is a nested array. Each constituent array contains elements
20855 // of `array` which have been determined to connect. Each consitituent array
20856 // also has a `nodes` property whose value is an ordered array of member nodes,
20857 // with appropriate order reversal and start/end coordinate de-duplication.
20858 //
20859 // Members of `array` must have, at minimum, `type` and `id` properties.
20860 // Thus either an array of `iD.Way`s or a relation member array may be
20861 // used.
20862 //
20863 // If an member has a `tags` property, its tags will be reversed via
20864 // `iD.actions.Reverse` in the output.
20865 //
20866 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
20867 // false) and non-way members are ignored.
20868 //
20869 iD.geo.joinWays = function(array, graph) {
20870     var joined = [], member, current, nodes, first, last, i, how, what;
20871
20872     array = array.filter(function(member) {
20873         return member.type === 'way' && graph.hasEntity(member.id);
20874     });
20875
20876     function resolve(member) {
20877         return graph.childNodes(graph.entity(member.id));
20878     }
20879
20880     function reverse(member) {
20881         return member.tags ? iD.actions.Reverse(member.id, {reverseOneway: true})(graph).entity(member.id) : member;
20882     }
20883
20884     while (array.length) {
20885         member = array.shift();
20886         current = [member];
20887         current.nodes = nodes = resolve(member).slice();
20888         joined.push(current);
20889
20890         while (array.length && _.first(nodes) !== _.last(nodes)) {
20891             first = _.first(nodes);
20892             last  = _.last(nodes);
20893
20894             for (i = 0; i < array.length; i++) {
20895                 member = array[i];
20896                 what = resolve(member);
20897
20898                 if (last === _.first(what)) {
20899                     how  = nodes.push;
20900                     what = what.slice(1);
20901                     break;
20902                 } else if (last === _.last(what)) {
20903                     how  = nodes.push;
20904                     what = what.slice(0, -1).reverse();
20905                     member = reverse(member);
20906                     break;
20907                 } else if (first === _.last(what)) {
20908                     how  = nodes.unshift;
20909                     what = what.slice(0, -1);
20910                     break;
20911                 } else if (first === _.first(what)) {
20912                     how  = nodes.unshift;
20913                     what = what.slice(1).reverse();
20914                     member = reverse(member);
20915                     break;
20916                 } else {
20917                     what = how = null;
20918                 }
20919             }
20920
20921             if (!what)
20922                 break; // No more joinable ways.
20923
20924             how.apply(current, [member]);
20925             how.apply(nodes, what);
20926
20927             array.splice(i, 1);
20928         }
20929     }
20930
20931     return joined;
20932 };
20933 /*
20934     Bypasses features of D3's default projection stream pipeline that are unnecessary:
20935     * Antimeridian clipping
20936     * Spherical rotation
20937     * Resampling
20938 */
20939 iD.geo.RawMercator = function () {
20940     var project = d3.geo.mercator.raw,
20941         k = 512 / Math.PI, // scale
20942         x = 0, y = 0, // translate
20943         clipExtent = [[0, 0], [0, 0]];
20944
20945     function projection(point) {
20946         point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
20947         return [point[0] * k + x, y - point[1] * k];
20948     }
20949
20950     projection.invert = function(point) {
20951         point = project.invert((point[0] - x) / k, (y - point[1]) / k);
20952         return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
20953     };
20954
20955     projection.scale = function(_) {
20956         if (!arguments.length) return k;
20957         k = +_;
20958         return projection;
20959     };
20960
20961     projection.translate = function(_) {
20962         if (!arguments.length) return [x, y];
20963         x = +_[0];
20964         y = +_[1];
20965         return projection;
20966     };
20967
20968     projection.clipExtent = function(_) {
20969         if (!arguments.length) return clipExtent;
20970         clipExtent = _;
20971         return projection;
20972     };
20973
20974     projection.stream = d3.geo.transform({
20975         point: function(x, y) {
20976             x = projection([x, y]);
20977             this.stream.point(x[0], x[1]);
20978         }
20979     }).stream;
20980
20981     return projection;
20982 };
20983 iD.actions = {};
20984 iD.actions.AddEntity = function(way) {
20985     return function(graph) {
20986         return graph.replace(way);
20987     };
20988 };
20989 iD.actions.AddMember = function(relationId, member, memberIndex) {
20990     return function(graph) {
20991         var relation = graph.entity(relationId);
20992
20993         if (isNaN(memberIndex) && member.type === 'way') {
20994             var members = relation.indexedMembers();
20995             members.push(member);
20996
20997             var joined = iD.geo.joinWays(members, graph);
20998             for (var i = 0; i < joined.length; i++) {
20999                 var segment = joined[i];
21000                 for (var j = 0; j < segment.length && segment.length >= 2; j++) {
21001                     if (segment[j] !== member)
21002                         continue;
21003
21004                     if (j === 0) {
21005                         memberIndex = segment[j + 1].index;
21006                     } else if (j === segment.length - 1) {
21007                         memberIndex = segment[j - 1].index + 1;
21008                     } else {
21009                         memberIndex = Math.min(segment[j - 1].index + 1, segment[j + 1].index + 1);
21010                     }
21011                 }
21012             }
21013         }
21014
21015         return graph.replace(relation.addMember(member, memberIndex));
21016     };
21017 };
21018 iD.actions.AddMidpoint = function(midpoint, node) {
21019     return function(graph) {
21020         graph = graph.replace(node.move(midpoint.loc));
21021
21022         var parents = _.intersection(
21023             graph.parentWays(graph.entity(midpoint.edge[0])),
21024             graph.parentWays(graph.entity(midpoint.edge[1])));
21025
21026         parents.forEach(function(way) {
21027             for (var i = 0; i < way.nodes.length - 1; i++) {
21028                 if (iD.geo.edgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
21029                     graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
21030
21031                     // Add only one midpoint on doubled-back segments,
21032                     // turning them into self-intersections.
21033                     return;
21034                 }
21035             }
21036         });
21037
21038         return graph;
21039     };
21040 };
21041 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
21042 iD.actions.AddVertex = function(wayId, nodeId, index) {
21043     return function(graph) {
21044         return graph.replace(graph.entity(wayId).addNode(nodeId, index));
21045     };
21046 };
21047 iD.actions.ChangeMember = function(relationId, member, memberIndex) {
21048     return function(graph) {
21049         return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
21050     };
21051 };
21052 iD.actions.ChangePreset = function(entityId, oldPreset, newPreset) {
21053     return function(graph) {
21054         var entity = graph.entity(entityId),
21055             geometry = entity.geometry(graph),
21056             tags = entity.tags;
21057
21058         if (oldPreset) tags = oldPreset.removeTags(tags, geometry);
21059         if (newPreset) tags = newPreset.applyTags(tags, geometry);
21060
21061         return graph.replace(entity.update({tags: tags}));
21062     };
21063 };
21064 iD.actions.ChangeTags = function(entityId, tags) {
21065     return function(graph) {
21066         var entity = graph.entity(entityId);
21067         return graph.replace(entity.update({tags: tags}));
21068     };
21069 };
21070 iD.actions.Circularize = function(wayId, projection, maxAngle) {
21071     maxAngle = (maxAngle || 20) * Math.PI / 180;
21072
21073     var action = function(graph) {
21074         var way = graph.entity(wayId);
21075
21076         if (!way.isConvex(graph)) {
21077             graph = action.makeConvex(graph);
21078         }
21079
21080         var nodes = _.uniq(graph.childNodes(way)),
21081             keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }),
21082             points = nodes.map(function(n) { return projection(n.loc); }),
21083             keyPoints = keyNodes.map(function(n) { return projection(n.loc); }),
21084             centroid = (points.length === 2) ? iD.geo.interp(points[0], points[1], 0.5) : d3.geom.polygon(points).centroid(),
21085             radius = d3.median(points, function(p) { return iD.geo.euclideanDistance(centroid, p); }),
21086             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
21087             ids;
21088
21089         // we need atleast two key nodes for the algorithm to work
21090         if (!keyNodes.length) {
21091             keyNodes = [nodes[0]];
21092             keyPoints = [points[0]];
21093         }
21094
21095         if (keyNodes.length === 1) {
21096             var index = nodes.indexOf(keyNodes[0]),
21097                 oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
21098
21099             keyNodes.push(nodes[oppositeIndex]);
21100             keyPoints.push(points[oppositeIndex]);
21101         }
21102
21103         // key points and nodes are those connected to the ways,
21104         // they are projected onto the circle, inbetween nodes are moved
21105         // to constant intervals between key nodes, extra inbetween nodes are
21106         // added if necessary.
21107         for (var i = 0; i < keyPoints.length; i++) {
21108             var nextKeyNodeIndex = (i + 1) % keyNodes.length,
21109                 startNode = keyNodes[i],
21110                 endNode = keyNodes[nextKeyNodeIndex],
21111                 startNodeIndex = nodes.indexOf(startNode),
21112                 endNodeIndex = nodes.indexOf(endNode),
21113                 numberNewPoints = -1,
21114                 indexRange = endNodeIndex - startNodeIndex,
21115                 distance, totalAngle, eachAngle, startAngle, endAngle,
21116                 angle, loc, node, j,
21117                 inBetweenNodes = [];
21118
21119             if (indexRange < 0) {
21120                 indexRange += nodes.length;
21121             }
21122
21123             // position this key node
21124             distance = iD.geo.euclideanDistance(centroid, keyPoints[i]);
21125             if (distance === 0) { distance = 1e-4; }
21126             keyPoints[i] = [
21127                 centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
21128                 centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
21129             graph = graph.replace(keyNodes[i].move(projection.invert(keyPoints[i])));
21130
21131             // figure out the between delta angle we want to match to
21132             startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
21133             endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
21134             totalAngle = endAngle - startAngle;
21135
21136             // detects looping around -pi/pi
21137             if (totalAngle * sign > 0) {
21138                 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
21139             }
21140
21141             do {
21142                 numberNewPoints++;
21143                 eachAngle = totalAngle / (indexRange + numberNewPoints);
21144             } while (Math.abs(eachAngle) > maxAngle);
21145
21146             // move existing points
21147             for (j = 1; j < indexRange; j++) {
21148                 angle = startAngle + j * eachAngle;
21149                 loc = projection.invert([
21150                     centroid[0] + Math.cos(angle)*radius,
21151                     centroid[1] + Math.sin(angle)*radius]);
21152
21153                 node = nodes[(j + startNodeIndex) % nodes.length].move(loc);
21154                 graph = graph.replace(node);
21155             }
21156
21157             // add new inbetween nodes if necessary
21158             for (j = 0; j < numberNewPoints; j++) {
21159                 angle = startAngle + (indexRange + j) * eachAngle;
21160                 loc = projection.invert([
21161                     centroid[0] + Math.cos(angle) * radius,
21162                     centroid[1] + Math.sin(angle) * radius]);
21163
21164                 node = iD.Node({loc: loc});
21165                 graph = graph.replace(node);
21166
21167                 nodes.splice(endNodeIndex + j, 0, node);
21168                 inBetweenNodes.push(node.id);
21169             }
21170
21171             // Check for other ways that share these keyNodes..
21172             // If keyNodes are adjacent in both ways,
21173             // we can add inBetween nodes to that shared way too..
21174             if (indexRange === 1 && inBetweenNodes.length) {
21175                 var startIndex1 = way.nodes.lastIndexOf(startNode.id),
21176                     endIndex1 = way.nodes.lastIndexOf(endNode.id),
21177                     wayDirection1 = (endIndex1 - startIndex1);
21178                 if (wayDirection1 < -1) { wayDirection1 = 1; }
21179
21180                 /* eslint-disable no-loop-func */
21181                 _.each(_.without(graph.parentWays(keyNodes[i]), way), function(sharedWay) {
21182                     if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
21183                         var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id),
21184                             endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id),
21185                             wayDirection2 = (endIndex2 - startIndex2),
21186                             insertAt = endIndex2;
21187                         if (wayDirection2 < -1) { wayDirection2 = 1; }
21188
21189                         if (wayDirection1 !== wayDirection2) {
21190                             inBetweenNodes.reverse();
21191                             insertAt = startIndex2;
21192                         }
21193                         for (j = 0; j < inBetweenNodes.length; j++) {
21194                             sharedWay = sharedWay.addNode(inBetweenNodes[j], insertAt + j);
21195                         }
21196                         graph = graph.replace(sharedWay);
21197                     }
21198                 });
21199                 /* eslint-enable no-loop-func */
21200             }
21201
21202         }
21203
21204         // update the way to have all the new nodes
21205         ids = nodes.map(function(n) { return n.id; });
21206         ids.push(ids[0]);
21207
21208         way = way.update({nodes: ids});
21209         graph = graph.replace(way);
21210
21211         return graph;
21212     };
21213
21214     action.makeConvex = function(graph) {
21215         var way = graph.entity(wayId),
21216             nodes = _.uniq(graph.childNodes(way)),
21217             points = nodes.map(function(n) { return projection(n.loc); }),
21218             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
21219             hull = d3.geom.hull(points);
21220
21221         // D3 convex hulls go counterclockwise..
21222         if (sign === -1) {
21223             nodes.reverse();
21224             points.reverse();
21225         }
21226
21227         for (var i = 0; i < hull.length - 1; i++) {
21228             var startIndex = points.indexOf(hull[i]),
21229                 endIndex = points.indexOf(hull[i+1]),
21230                 indexRange = (endIndex - startIndex);
21231
21232             if (indexRange < 0) {
21233                 indexRange += nodes.length;
21234             }
21235
21236             // move interior nodes to the surface of the convex hull..
21237             for (var j = 1; j < indexRange; j++) {
21238                 var point = iD.geo.interp(hull[i], hull[i+1], j / indexRange),
21239                     node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
21240                 graph = graph.replace(node);
21241             }
21242         }
21243         return graph;
21244     };
21245
21246     action.disabled = function(graph) {
21247         if (!graph.entity(wayId).isClosed())
21248             return 'not_closed';
21249     };
21250
21251     return action;
21252 };
21253 // Connect the ways at the given nodes.
21254 //
21255 // The last node will survive. All other nodes will be replaced with
21256 // the surviving node in parent ways, and then removed.
21257 //
21258 // Tags and relation memberships of of non-surviving nodes are merged
21259 // to the survivor.
21260 //
21261 // This is the inverse of `iD.actions.Disconnect`.
21262 //
21263 // Reference:
21264 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
21265 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
21266 //
21267 iD.actions.Connect = function(nodeIds) {
21268     return function(graph) {
21269         var survivor = graph.entity(_.last(nodeIds));
21270
21271         for (var i = 0; i < nodeIds.length - 1; i++) {
21272             var node = graph.entity(nodeIds[i]);
21273
21274             /* eslint-disable no-loop-func */
21275             graph.parentWays(node).forEach(function(parent) {
21276                 if (!parent.areAdjacent(node.id, survivor.id)) {
21277                     graph = graph.replace(parent.replaceNode(node.id, survivor.id));
21278                 }
21279             });
21280
21281             graph.parentRelations(node).forEach(function(parent) {
21282                 graph = graph.replace(parent.replaceMember(node, survivor));
21283             });
21284             /* eslint-enable no-loop-func */
21285
21286             survivor = survivor.mergeTags(node.tags);
21287             graph = iD.actions.DeleteNode(node.id)(graph);
21288         }
21289
21290         graph = graph.replace(survivor);
21291
21292         return graph;
21293     };
21294 };
21295 iD.actions.CopyEntities = function(ids, fromGraph) {
21296     var copies = {};
21297
21298     var action = function(graph) {
21299         ids.forEach(function(id) {
21300             fromGraph.entity(id).copy(fromGraph, copies);
21301         });
21302
21303         for (var id in copies) {
21304             graph = graph.replace(copies[id]);
21305         }
21306
21307         return graph;
21308     };
21309
21310     action.copies = function() {
21311         return copies;
21312     };
21313
21314     return action;
21315 };
21316 iD.actions.DeleteMember = function(relationId, memberIndex) {
21317     return function(graph) {
21318         var relation = graph.entity(relationId)
21319             .removeMember(memberIndex);
21320
21321         graph = graph.replace(relation);
21322
21323         if (relation.isDegenerate())
21324             graph = iD.actions.DeleteRelation(relation.id)(graph);
21325
21326         return graph;
21327     };
21328 };
21329 iD.actions.DeleteMultiple = function(ids) {
21330     var actions = {
21331         way: iD.actions.DeleteWay,
21332         node: iD.actions.DeleteNode,
21333         relation: iD.actions.DeleteRelation
21334     };
21335
21336     var action = function(graph) {
21337         ids.forEach(function(id) {
21338             if (graph.hasEntity(id)) { // It may have been deleted aready.
21339                 graph = actions[graph.entity(id).type](id)(graph);
21340             }
21341         });
21342
21343         return graph;
21344     };
21345
21346     action.disabled = function(graph) {
21347         for (var i = 0; i < ids.length; i++) {
21348             var id = ids[i],
21349                 disabled = actions[graph.entity(id).type](id).disabled(graph);
21350             if (disabled) return disabled;
21351         }
21352     };
21353
21354     return action;
21355 };
21356 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
21357 iD.actions.DeleteNode = function(nodeId) {
21358     var action = function(graph) {
21359         var node = graph.entity(nodeId);
21360
21361         graph.parentWays(node)
21362             .forEach(function(parent) {
21363                 parent = parent.removeNode(nodeId);
21364                 graph = graph.replace(parent);
21365
21366                 if (parent.isDegenerate()) {
21367                     graph = iD.actions.DeleteWay(parent.id)(graph);
21368                 }
21369             });
21370
21371         graph.parentRelations(node)
21372             .forEach(function(parent) {
21373                 parent = parent.removeMembersWithID(nodeId);
21374                 graph = graph.replace(parent);
21375
21376                 if (parent.isDegenerate()) {
21377                     graph = iD.actions.DeleteRelation(parent.id)(graph);
21378                 }
21379             });
21380
21381         return graph.remove(node);
21382     };
21383
21384     action.disabled = function() {
21385         return false;
21386     };
21387
21388     return action;
21389 };
21390 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
21391 iD.actions.DeleteRelation = function(relationId) {
21392     function deleteEntity(entity, graph) {
21393         return !graph.parentWays(entity).length &&
21394             !graph.parentRelations(entity).length &&
21395             !entity.hasInterestingTags();
21396     }
21397
21398     var action = function(graph) {
21399         var relation = graph.entity(relationId);
21400
21401         graph.parentRelations(relation)
21402             .forEach(function(parent) {
21403                 parent = parent.removeMembersWithID(relationId);
21404                 graph = graph.replace(parent);
21405
21406                 if (parent.isDegenerate()) {
21407                     graph = iD.actions.DeleteRelation(parent.id)(graph);
21408                 }
21409             });
21410
21411         _.uniq(_.pluck(relation.members, 'id')).forEach(function(memberId) {
21412             graph = graph.replace(relation.removeMembersWithID(memberId));
21413
21414             var entity = graph.entity(memberId);
21415             if (deleteEntity(entity, graph)) {
21416                 graph = iD.actions.DeleteMultiple([memberId])(graph);
21417             }
21418         });
21419
21420         return graph.remove(relation);
21421     };
21422
21423     action.disabled = function(graph) {
21424         if (!graph.entity(relationId).isComplete(graph))
21425             return 'incomplete_relation';
21426     };
21427
21428     return action;
21429 };
21430 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
21431 iD.actions.DeleteWay = function(wayId) {
21432     function deleteNode(node, graph) {
21433         return !graph.parentWays(node).length &&
21434             !graph.parentRelations(node).length &&
21435             !node.hasInterestingTags();
21436     }
21437
21438     var action = function(graph) {
21439         var way = graph.entity(wayId);
21440
21441         graph.parentRelations(way)
21442             .forEach(function(parent) {
21443                 parent = parent.removeMembersWithID(wayId);
21444                 graph = graph.replace(parent);
21445
21446                 if (parent.isDegenerate()) {
21447                     graph = iD.actions.DeleteRelation(parent.id)(graph);
21448                 }
21449             });
21450
21451         _.uniq(way.nodes).forEach(function(nodeId) {
21452             graph = graph.replace(way.removeNode(nodeId));
21453
21454             var node = graph.entity(nodeId);
21455             if (deleteNode(node, graph)) {
21456                 graph = graph.remove(node);
21457             }
21458         });
21459
21460         return graph.remove(way);
21461     };
21462
21463     action.disabled = function(graph) {
21464         var disabled = false;
21465
21466         graph.parentRelations(graph.entity(wayId)).forEach(function(parent) {
21467             var type = parent.tags.type,
21468                 role = parent.memberById(wayId).role || 'outer';
21469             if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
21470                 disabled = 'part_of_relation';
21471             }
21472         });
21473
21474         return disabled;
21475     };
21476
21477     return action;
21478 };
21479 iD.actions.DeprecateTags = function(entityId) {
21480     return function(graph) {
21481         var entity = graph.entity(entityId),
21482             newtags = _.clone(entity.tags),
21483             change = false,
21484             rule;
21485
21486         // This handles deprecated tags with a single condition
21487         for (var i = 0; i < iD.data.deprecated.length; i++) {
21488
21489             rule = iD.data.deprecated[i];
21490             var match = _.pairs(rule.old)[0],
21491                 replacements = rule.replace ? _.pairs(rule.replace) : null;
21492
21493             if (entity.tags[match[0]] && match[1] === '*') {
21494
21495                 var value = entity.tags[match[0]];
21496                 if (replacements && !newtags[replacements[0][0]]) {
21497                     newtags[replacements[0][0]] = value;
21498                 }
21499                 delete newtags[match[0]];
21500                 change = true;
21501
21502             } else if (entity.tags[match[0]] === match[1]) {
21503                 newtags = _.assign({}, rule.replace || {}, _.omit(newtags, match[0]));
21504                 change = true;
21505             }
21506         }
21507
21508         if (change) {
21509             return graph.replace(entity.update({tags: newtags}));
21510         } else {
21511             return graph;
21512         }
21513     };
21514 };
21515 iD.actions.DiscardTags = function(difference) {
21516     return function(graph) {
21517         function discardTags(entity) {
21518             if (!_.isEmpty(entity.tags)) {
21519                 var tags = {};
21520                 _.each(entity.tags, function(v, k) {
21521                     if (v) tags[k] = v;
21522                 });
21523
21524                 graph = graph.replace(entity.update({
21525                     tags: _.omit(tags, iD.data.discarded)
21526                 }));
21527             }
21528         }
21529
21530         difference.modified().forEach(discardTags);
21531         difference.created().forEach(discardTags);
21532
21533         return graph;
21534     };
21535 };
21536 // Disconect the ways at the given node.
21537 //
21538 // Optionally, disconnect only the given ways.
21539 //
21540 // For testing convenience, accepts an ID to assign to the (first) new node.
21541 // Normally, this will be undefined and the way will automatically
21542 // be assigned a new ID.
21543 //
21544 // This is the inverse of `iD.actions.Connect`.
21545 //
21546 // Reference:
21547 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
21548 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
21549 //
21550 iD.actions.Disconnect = function(nodeId, newNodeId) {
21551     var wayIds;
21552
21553     var action = function(graph) {
21554         var node = graph.entity(nodeId),
21555             connections = action.connections(graph);
21556
21557         connections.forEach(function(connection) {
21558             var way = graph.entity(connection.wayID),
21559                 newNode = iD.Node({id: newNodeId, loc: node.loc, tags: node.tags});
21560
21561             graph = graph.replace(newNode);
21562             if (connection.index === 0 && way.isArea()) {
21563                 // replace shared node with shared node..
21564                 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
21565             } else {
21566                 // replace shared node with multiple new nodes..
21567                 graph = graph.replace(way.updateNode(newNode.id, connection.index));
21568             }
21569         });
21570
21571         return graph;
21572     };
21573
21574     action.connections = function(graph) {
21575         var candidates = [],
21576             keeping = false,
21577             parentWays = graph.parentWays(graph.entity(nodeId));
21578
21579         parentWays.forEach(function(way) {
21580             if (wayIds && wayIds.indexOf(way.id) === -1) {
21581                 keeping = true;
21582                 return;
21583             }
21584             if (way.isArea() && (way.nodes[0] === nodeId)) {
21585                 candidates.push({wayID: way.id, index: 0});
21586             } else {
21587                 way.nodes.forEach(function(waynode, index) {
21588                     if (waynode === nodeId) {
21589                         candidates.push({wayID: way.id, index: index});
21590                     }
21591                 });
21592             }
21593         });
21594
21595         return keeping ? candidates : candidates.slice(1);
21596     };
21597
21598     action.disabled = function(graph) {
21599         var connections = action.connections(graph);
21600         if (connections.length === 0 || (wayIds && wayIds.length !== connections.length))
21601             return 'not_connected';
21602
21603         var parentWays = graph.parentWays(graph.entity(nodeId)),
21604             seenRelationIds = {},
21605             sharedRelation;
21606
21607         parentWays.forEach(function(way) {
21608             if (wayIds && wayIds.indexOf(way.id) === -1)
21609                 return;
21610
21611             var relations = graph.parentRelations(way);
21612             relations.forEach(function(relation) {
21613                 if (relation.id in seenRelationIds) {
21614                     sharedRelation = relation;
21615                 } else {
21616                     seenRelationIds[relation.id] = true;
21617                 }
21618             });
21619         });
21620
21621         if (sharedRelation)
21622             return 'relation';
21623     };
21624
21625     action.limitWays = function(_) {
21626         if (!arguments.length) return wayIds;
21627         wayIds = _;
21628         return action;
21629     };
21630
21631     return action;
21632 };
21633 // Join ways at the end node they share.
21634 //
21635 // This is the inverse of `iD.actions.Split`.
21636 //
21637 // Reference:
21638 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
21639 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
21640 //
21641 iD.actions.Join = function(ids) {
21642
21643     function groupEntitiesByGeometry(graph) {
21644         var entities = ids.map(function(id) { return graph.entity(id); });
21645         return _.extend({line: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
21646     }
21647
21648     var action = function(graph) {
21649         var ways = ids.map(graph.entity, graph),
21650             survivor = ways[0];
21651
21652         // Prefer to keep an existing way.
21653         for (var i = 0; i < ways.length; i++) {
21654             if (!ways[i].isNew()) {
21655                 survivor = ways[i];
21656                 break;
21657             }
21658         }
21659
21660         var joined = iD.geo.joinWays(ways, graph)[0];
21661
21662         survivor = survivor.update({nodes: _.pluck(joined.nodes, 'id')});
21663         graph = graph.replace(survivor);
21664
21665         joined.forEach(function(way) {
21666             if (way.id === survivor.id)
21667                 return;
21668
21669             graph.parentRelations(way).forEach(function(parent) {
21670                 graph = graph.replace(parent.replaceMember(way, survivor));
21671             });
21672
21673             survivor = survivor.mergeTags(way.tags);
21674
21675             graph = graph.replace(survivor);
21676             graph = iD.actions.DeleteWay(way.id)(graph);
21677         });
21678
21679         return graph;
21680     };
21681
21682     action.disabled = function(graph) {
21683         var geometries = groupEntitiesByGeometry(graph);
21684         if (ids.length < 2 || ids.length !== geometries.line.length)
21685             return 'not_eligible';
21686
21687         var joined = iD.geo.joinWays(ids.map(graph.entity, graph), graph);
21688         if (joined.length > 1)
21689             return 'not_adjacent';
21690
21691         var nodeIds = _.pluck(joined[0].nodes, 'id').slice(1, -1),
21692             relation,
21693             tags = {},
21694             conflicting = false;
21695
21696         joined[0].forEach(function(way) {
21697             var parents = graph.parentRelations(way);
21698             parents.forEach(function(parent) {
21699                 if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; }))
21700                     relation = parent;
21701             });
21702
21703             for (var k in way.tags) {
21704                 if (!(k in tags)) {
21705                     tags[k] = way.tags[k];
21706                 } else if (tags[k] && iD.interestingTag(k) && tags[k] !== way.tags[k]) {
21707                     conflicting = true;
21708                 }
21709             }
21710         });
21711
21712         if (relation)
21713             return 'restriction';
21714
21715         if (conflicting)
21716             return 'conflicting_tags';
21717     };
21718
21719     return action;
21720 };
21721 iD.actions.Merge = function(ids) {
21722     function groupEntitiesByGeometry(graph) {
21723         var entities = ids.map(function(id) { return graph.entity(id); });
21724         return _.extend({point: [], area: [], line: [], relation: []},
21725             _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
21726     }
21727
21728     var action = function(graph) {
21729         var geometries = groupEntitiesByGeometry(graph),
21730             target = geometries.area[0] || geometries.line[0],
21731             points = geometries.point;
21732
21733         points.forEach(function(point) {
21734             target = target.mergeTags(point.tags);
21735
21736             graph.parentRelations(point).forEach(function(parent) {
21737                 graph = graph.replace(parent.replaceMember(point, target));
21738             });
21739
21740             graph = graph.remove(point);
21741         });
21742
21743         graph = graph.replace(target);
21744
21745         return graph;
21746     };
21747
21748     action.disabled = function(graph) {
21749         var geometries = groupEntitiesByGeometry(graph);
21750         if (geometries.point.length === 0 ||
21751             (geometries.area.length + geometries.line.length) !== 1 ||
21752             geometries.relation.length !== 0)
21753             return 'not_eligible';
21754     };
21755
21756     return action;
21757 };
21758 iD.actions.MergePolygon = function(ids, newRelationId) {
21759
21760     function groupEntities(graph) {
21761         var entities = ids.map(function (id) { return graph.entity(id); });
21762         return _.extend({
21763                 closedWay: [],
21764                 multipolygon: [],
21765                 other: []
21766             }, _.groupBy(entities, function(entity) {
21767                 if (entity.type === 'way' && entity.isClosed()) {
21768                     return 'closedWay';
21769                 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
21770                     return 'multipolygon';
21771                 } else {
21772                     return 'other';
21773                 }
21774             }));
21775     }
21776
21777     var action = function(graph) {
21778         var entities = groupEntities(graph);
21779
21780         // An array representing all the polygons that are part of the multipolygon.
21781         //
21782         // Each element is itself an array of objects with an id property, and has a
21783         // locs property which is an array of the locations forming the polygon.
21784         var polygons = entities.multipolygon.reduce(function(polygons, m) {
21785             return polygons.concat(iD.geo.joinWays(m.members, graph));
21786         }, []).concat(entities.closedWay.map(function(d) {
21787             var member = [{id: d.id}];
21788             member.nodes = graph.childNodes(d);
21789             return member;
21790         }));
21791
21792         // contained is an array of arrays of boolean values,
21793         // where contained[j][k] is true iff the jth way is
21794         // contained by the kth way.
21795         var contained = polygons.map(function(w, i) {
21796             return polygons.map(function(d, n) {
21797                 if (i === n) return null;
21798                 return iD.geo.polygonContainsPolygon(
21799                     _.pluck(d.nodes, 'loc'),
21800                     _.pluck(w.nodes, 'loc'));
21801             });
21802         });
21803
21804         // Sort all polygons as either outer or inner ways
21805         var members = [],
21806             outer = true;
21807
21808         while (polygons.length) {
21809             extractUncontained(polygons);
21810             polygons = polygons.filter(isContained);
21811             contained = contained.filter(isContained).map(filterContained);
21812         }
21813
21814         function isContained(d, i) {
21815             return _.any(contained[i]);
21816         }
21817
21818         function filterContained(d) {
21819             return d.filter(isContained);
21820         }
21821
21822         function extractUncontained(polygons) {
21823             polygons.forEach(function(d, i) {
21824                 if (!isContained(d, i)) {
21825                     d.forEach(function(member) {
21826                         members.push({
21827                             type: 'way',
21828                             id: member.id,
21829                             role: outer ? 'outer' : 'inner'
21830                         });
21831                     });
21832                 }
21833             });
21834             outer = !outer;
21835         }
21836
21837         // Move all tags to one relation
21838         var relation = entities.multipolygon[0] ||
21839             iD.Relation({ id: newRelationId, tags: { type: 'multipolygon' }});
21840
21841         entities.multipolygon.slice(1).forEach(function(m) {
21842             relation = relation.mergeTags(m.tags);
21843             graph = graph.remove(m);
21844         });
21845
21846         entities.closedWay.forEach(function(way) {
21847             function isThisOuter(m) {
21848                 return m.id === way.id && m.role !== 'inner';
21849             }
21850             if (members.some(isThisOuter)) {
21851                 relation = relation.mergeTags(way.tags);
21852                 graph = graph.replace(way.update({ tags: {} }));
21853             }
21854         });
21855
21856         return graph.replace(relation.update({
21857             members: members,
21858             tags: _.omit(relation.tags, 'area')
21859         }));
21860     };
21861
21862     action.disabled = function(graph) {
21863         var entities = groupEntities(graph);
21864         if (entities.other.length > 0 ||
21865             entities.closedWay.length + entities.multipolygon.length < 2)
21866             return 'not_eligible';
21867         if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); }))
21868             return 'incomplete_relation';
21869     };
21870
21871     return action;
21872 };
21873 iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser) {
21874     var option = 'safe',  // 'safe', 'force_local', 'force_remote'
21875         conflicts = [];
21876
21877     function user(d) {
21878         return _.isFunction(formatUser) ? formatUser(d) : d;
21879     }
21880
21881
21882     function mergeLocation(remote, target) {
21883         function pointEqual(a, b) {
21884             var epsilon = 1e-6;
21885             return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon);
21886         }
21887
21888         if (option === 'force_local' || pointEqual(target.loc, remote.loc)) {
21889             return target;
21890         }
21891         if (option === 'force_remote') {
21892             return target.update({loc: remote.loc});
21893         }
21894
21895         conflicts.push(t('merge_remote_changes.conflict.location', { user: user(remote.user) }));
21896         return target;
21897     }
21898
21899
21900     function mergeNodes(base, remote, target) {
21901         if (option === 'force_local' || _.isEqual(target.nodes, remote.nodes)) {
21902             return target;
21903         }
21904         if (option === 'force_remote') {
21905             return target.update({nodes: remote.nodes});
21906         }
21907
21908         var ccount = conflicts.length,
21909             o = base.nodes || [],
21910             a = target.nodes || [],
21911             b = remote.nodes || [],
21912             nodes = [],
21913             hunks = Diff3.diff3_merge(a, o, b, true);
21914
21915         for (var i = 0; i < hunks.length; i++) {
21916             var hunk = hunks[i];
21917             if (hunk.ok) {
21918                 nodes.push.apply(nodes, hunk.ok);
21919             } else {
21920                 // for all conflicts, we can assume c.a !== c.b
21921                 // because `diff3_merge` called with `true` option to exclude false conflicts..
21922                 var c = hunk.conflict;
21923                 if (_.isEqual(c.o, c.a)) {  // only changed remotely
21924                     nodes.push.apply(nodes, c.b);
21925                 } else if (_.isEqual(c.o, c.b)) {  // only changed locally
21926                     nodes.push.apply(nodes, c.a);
21927                 } else {       // changed both locally and remotely
21928                     conflicts.push(t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) }));
21929                     break;
21930                 }
21931             }
21932         }
21933
21934         return (conflicts.length === ccount) ? target.update({nodes: nodes}) : target;
21935     }
21936
21937
21938     function mergeChildren(targetWay, children, updates, graph) {
21939         function isUsed(node, targetWay) {
21940             var parentWays = _.pluck(graph.parentWays(node), 'id');
21941             return node.hasInterestingTags() ||
21942                 _.without(parentWays, targetWay.id).length > 0 ||
21943                 graph.parentRelations(node).length > 0;
21944         }
21945
21946         var ccount = conflicts.length;
21947
21948         for (var i = 0; i < children.length; i++) {
21949             var id = children[i],
21950                 node = graph.hasEntity(id);
21951
21952             // remove unused childNodes..
21953             if (targetWay.nodes.indexOf(id) === -1) {
21954                 if (node && !isUsed(node, targetWay)) {
21955                     updates.removeIds.push(id);
21956                 }
21957                 continue;
21958             }
21959
21960             // restore used childNodes..
21961             var local = localGraph.hasEntity(id),
21962                 remote = remoteGraph.hasEntity(id),
21963                 target;
21964
21965             if (option === 'force_remote' && remote && remote.visible) {
21966                 updates.replacements.push(remote);
21967
21968             } else if (option === 'force_local' && local) {
21969                 target = iD.Entity(local);
21970                 if (remote) {
21971                     target = target.update({ version: remote.version });
21972                 }
21973                 updates.replacements.push(target);
21974
21975             } else if (option === 'safe' && local && remote && local.version !== remote.version) {
21976                 target = iD.Entity(local, { version: remote.version });
21977                 if (remote.visible) {
21978                     target = mergeLocation(remote, target);
21979                 } else {
21980                     conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
21981                 }
21982
21983                 if (conflicts.length !== ccount) break;
21984                 updates.replacements.push(target);
21985             }
21986         }
21987
21988         return targetWay;
21989     }
21990
21991
21992     function updateChildren(updates, graph) {
21993         for (var i = 0; i < updates.replacements.length; i++) {
21994             graph = graph.replace(updates.replacements[i]);
21995         }
21996         if (updates.removeIds.length) {
21997             graph = iD.actions.DeleteMultiple(updates.removeIds)(graph);
21998         }
21999         return graph;
22000     }
22001
22002
22003     function mergeMembers(remote, target) {
22004         if (option === 'force_local' || _.isEqual(target.members, remote.members)) {
22005             return target;
22006         }
22007         if (option === 'force_remote') {
22008             return target.update({members: remote.members});
22009         }
22010
22011         conflicts.push(t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) }));
22012         return target;
22013     }
22014
22015
22016     function mergeTags(base, remote, target) {
22017         function ignoreKey(k) {
22018             return _.contains(iD.data.discarded, k);
22019         }
22020
22021         if (option === 'force_local' || _.isEqual(target.tags, remote.tags)) {
22022             return target;
22023         }
22024         if (option === 'force_remote') {
22025             return target.update({tags: remote.tags});
22026         }
22027
22028         var ccount = conflicts.length,
22029             o = base.tags || {},
22030             a = target.tags || {},
22031             b = remote.tags || {},
22032             keys = _.reject(_.union(_.keys(o), _.keys(a), _.keys(b)), ignoreKey),
22033             tags = _.clone(a),
22034             changed = false;
22035
22036         for (var i = 0; i < keys.length; i++) {
22037             var k = keys[i];
22038
22039             if (o[k] !== b[k] && a[k] !== b[k]) {    // changed remotely..
22040                 if (o[k] !== a[k]) {      // changed locally..
22041                     conflicts.push(t('merge_remote_changes.conflict.tags',
22042                         { tag: k, local: a[k], remote: b[k], user: user(remote.user) }));
22043
22044                 } else {                  // unchanged locally, accept remote change..
22045                     if (b.hasOwnProperty(k)) {
22046                         tags[k] = b[k];
22047                     } else {
22048                         delete tags[k];
22049                     }
22050                     changed = true;
22051                 }
22052             }
22053         }
22054
22055         return (changed && conflicts.length === ccount) ? target.update({tags: tags}) : target;
22056     }
22057
22058
22059     //  `graph.base()` is the common ancestor of the two graphs.
22060     //  `localGraph` contains user's edits up to saving
22061     //  `remoteGraph` contains remote edits to modified nodes
22062     //  `graph` must be a descendent of `localGraph` and may include
22063     //      some conflict resolution actions performed on it.
22064     //
22065     //                  --- ... --- `localGraph` -- ... -- `graph`
22066     //                 /
22067     //  `graph.base()` --- ... --- `remoteGraph`
22068     //
22069     var action = function(graph) {
22070         var updates = { replacements: [], removeIds: [] },
22071             base = graph.base().entities[id],
22072             local = localGraph.entity(id),
22073             remote = remoteGraph.entity(id),
22074             target = iD.Entity(local, { version: remote.version });
22075
22076         // delete/undelete
22077         if (!remote.visible) {
22078             if (option === 'force_remote') {
22079                 return iD.actions.DeleteMultiple([id])(graph);
22080
22081             } else if (option === 'force_local') {
22082                 if (target.type === 'way') {
22083                     target = mergeChildren(target, _.uniq(local.nodes), updates, graph);
22084                     graph = updateChildren(updates, graph);
22085                 }
22086                 return graph.replace(target);
22087
22088             } else {
22089                 conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
22090                 return graph;  // do nothing
22091             }
22092         }
22093
22094         // merge
22095         if (target.type === 'node') {
22096             target = mergeLocation(remote, target);
22097
22098         } else if (target.type === 'way') {
22099             // pull in any child nodes that may not be present locally..
22100             graph.rebase(remoteGraph.childNodes(remote), [graph], false);
22101             target = mergeNodes(base, remote, target);
22102             target = mergeChildren(target, _.union(local.nodes, remote.nodes), updates, graph);
22103
22104         } else if (target.type === 'relation') {
22105             target = mergeMembers(remote, target);
22106         }
22107
22108         target = mergeTags(base, remote, target);
22109
22110         if (!conflicts.length) {
22111             graph = updateChildren(updates, graph).replace(target);
22112         }
22113
22114         return graph;
22115     };
22116
22117     action.withOption = function(opt) {
22118         option = opt;
22119         return action;
22120     };
22121
22122     action.conflicts = function() {
22123         return conflicts;
22124     };
22125
22126     return action;
22127 };
22128 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
22129 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
22130 iD.actions.Move = function(moveIds, tryDelta, projection, cache) {
22131     var delta = tryDelta;
22132
22133     function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; }
22134     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
22135
22136     function setupCache(graph) {
22137         function canMove(nodeId) {
22138             var parents = _.pluck(graph.parentWays(graph.entity(nodeId)), 'id');
22139             if (parents.length < 3) return true;
22140
22141             // Don't move a vertex where >2 ways meet, unless all parentWays are moving too..
22142             var parentsMoving = _.all(parents, function(id) { return cache.moving[id]; });
22143             if (!parentsMoving) delete cache.moving[nodeId];
22144
22145             return parentsMoving;
22146         }
22147
22148         function cacheEntities(ids) {
22149             _.each(ids, function(id) {
22150                 if (cache.moving[id]) return;
22151                 cache.moving[id] = true;
22152
22153                 var entity = graph.hasEntity(id);
22154                 if (!entity) return;
22155
22156                 if (entity.type === 'node') {
22157                     cache.nodes.push(id);
22158                     cache.startLoc[id] = entity.loc;
22159                 } else if (entity.type === 'way') {
22160                     cache.ways.push(id);
22161                     cacheEntities(entity.nodes);
22162                 } else {
22163                     cacheEntities(_.pluck(entity.members, 'id'));
22164                 }
22165             });
22166         }
22167
22168         function cacheIntersections(ids) {
22169             function isEndpoint(way, id) { return !way.isClosed() && !!way.affix(id); }
22170
22171             _.each(ids, function(id) {
22172                 // consider only intersections with 1 moved and 1 unmoved way.
22173                 _.each(graph.childNodes(graph.entity(id)), function(node) {
22174                     var parents = graph.parentWays(node);
22175                     if (parents.length !== 2) return;
22176
22177                     var moved = graph.entity(id),
22178                         unmoved = _.find(parents, function(way) { return !cache.moving[way.id]; });
22179                     if (!unmoved) return;
22180
22181                     // exclude ways that are overly connected..
22182                     if (_.intersection(moved.nodes, unmoved.nodes).length > 2) return;
22183
22184                     if (moved.isArea() || unmoved.isArea()) return;
22185
22186                     cache.intersection[node.id] = {
22187                         nodeId: node.id,
22188                         movedId: moved.id,
22189                         unmovedId: unmoved.id,
22190                         movedIsEP: isEndpoint(moved, node.id),
22191                         unmovedIsEP: isEndpoint(unmoved, node.id)
22192                     };
22193                 });
22194             });
22195         }
22196
22197
22198         if (!cache) {
22199             cache = {};
22200         }
22201         if (!cache.ok) {
22202             cache.moving = {};
22203             cache.intersection = {};
22204             cache.replacedVertex = {};
22205             cache.startLoc = {};
22206             cache.nodes = [];
22207             cache.ways = [];
22208
22209             cacheEntities(moveIds);
22210             cacheIntersections(cache.ways);
22211             cache.nodes = _.filter(cache.nodes, canMove);
22212
22213             cache.ok = true;
22214         }
22215     }
22216
22217
22218     // Place a vertex where the moved vertex used to be, to preserve way shape..
22219     function replaceMovedVertex(nodeId, wayId, graph, delta) {
22220         var way = graph.entity(wayId),
22221             moved = graph.entity(nodeId),
22222             movedIndex = way.nodes.indexOf(nodeId),
22223             len, prevIndex, nextIndex;
22224
22225         if (way.isClosed()) {
22226             len = way.nodes.length - 1;
22227             prevIndex = (movedIndex + len - 1) % len;
22228             nextIndex = (movedIndex + len + 1) % len;
22229         } else {
22230             len = way.nodes.length;
22231             prevIndex = movedIndex - 1;
22232             nextIndex = movedIndex + 1;
22233         }
22234
22235         var prev = graph.hasEntity(way.nodes[prevIndex]),
22236             next = graph.hasEntity(way.nodes[nextIndex]);
22237
22238         // Don't add orig vertex at endpoint..
22239         if (!prev || !next) return graph;
22240
22241         var key = wayId + '_' + nodeId,
22242             orig = cache.replacedVertex[key];
22243         if (!orig) {
22244             orig = iD.Node();
22245             cache.replacedVertex[key] = orig;
22246             cache.startLoc[orig.id] = cache.startLoc[nodeId];
22247         }
22248
22249         var start, end;
22250         if (delta) {
22251             start = projection(cache.startLoc[nodeId]);
22252             end = projection.invert(vecAdd(start, delta));
22253         } else {
22254             end = cache.startLoc[nodeId];
22255         }
22256         orig = orig.move(end);
22257
22258         var angle = Math.abs(iD.geo.angle(orig, prev, projection) -
22259                 iD.geo.angle(orig, next, projection)) * 180 / Math.PI;
22260
22261         // Don't add orig vertex if it would just make a straight line..
22262         if (angle > 175 && angle < 185) return graph;
22263
22264         // Don't add orig vertex if another point is already nearby (within 10m)
22265         if (iD.geo.sphericalDistance(prev.loc, orig.loc) < 10 ||
22266             iD.geo.sphericalDistance(orig.loc, next.loc) < 10) return graph;
22267
22268         // moving forward or backward along way?
22269         var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection),
22270             p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection),
22271             d1 = iD.geo.pathLength(p1),
22272             d2 = iD.geo.pathLength(p2),
22273             insertAt = (d1 < d2) ? movedIndex : nextIndex;
22274
22275         // moving around closed loop?
22276         if (way.isClosed() && insertAt === 0) insertAt = len;
22277
22278         way = way.addNode(orig.id, insertAt);
22279         return graph.replace(orig).replace(way);
22280     }
22281
22282     // Reorder nodes around intersections that have moved..
22283     function unZorroIntersection(intersection, graph) {
22284         var vertex = graph.entity(intersection.nodeId),
22285             way1 = graph.entity(intersection.movedId),
22286             way2 = graph.entity(intersection.unmovedId),
22287             isEP1 = intersection.movedIsEP,
22288             isEP2 = intersection.unmovedIsEP;
22289
22290         // don't move the vertex if it is the endpoint of both ways.
22291         if (isEP1 && isEP2) return graph;
22292
22293         var nodes1 = _.without(graph.childNodes(way1), vertex),
22294             nodes2 = _.without(graph.childNodes(way2), vertex);
22295
22296         if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
22297         if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
22298
22299         var edge1 = !isEP1 && iD.geo.chooseEdge(nodes1, projection(vertex.loc), projection),
22300             edge2 = !isEP2 && iD.geo.chooseEdge(nodes2, projection(vertex.loc), projection),
22301             loc;
22302
22303         // snap vertex to nearest edge (or some point between them)..
22304         if (!isEP1 && !isEP2) {
22305             var epsilon = 1e-4, maxIter = 10;
22306             for (var i = 0; i < maxIter; i++) {
22307                 loc = iD.geo.interp(edge1.loc, edge2.loc, 0.5);
22308                 edge1 = iD.geo.chooseEdge(nodes1, projection(loc), projection);
22309                 edge2 = iD.geo.chooseEdge(nodes2, projection(loc), projection);
22310                 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
22311             }
22312         } else if (!isEP1) {
22313             loc = edge1.loc;
22314         } else {
22315             loc = edge2.loc;
22316         }
22317
22318         graph = graph.replace(vertex.move(loc));
22319
22320         // if zorro happened, reorder nodes..
22321         if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
22322             way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
22323             graph = graph.replace(way1);
22324         }
22325         if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
22326             way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
22327             graph = graph.replace(way2);
22328         }
22329
22330         return graph;
22331     }
22332
22333
22334     function cleanupIntersections(graph) {
22335         _.each(cache.intersection, function(obj) {
22336             graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, delta);
22337             graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
22338             graph = unZorroIntersection(obj, graph);
22339         });
22340
22341         return graph;
22342     }
22343
22344     // check if moving way endpoint can cross an unmoved way, if so limit delta..
22345     function limitDelta(graph) {
22346         _.each(cache.intersection, function(obj) {
22347             if (!obj.movedIsEP) return;
22348
22349             var node = graph.entity(obj.nodeId),
22350                 start = projection(node.loc),
22351                 end = vecAdd(start, delta),
22352                 movedNodes = graph.childNodes(graph.entity(obj.movedId)),
22353                 movedPath = _.map(_.pluck(movedNodes, 'loc'),
22354                     function(loc) { return vecAdd(projection(loc), delta); }),
22355                 unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId)),
22356                 unmovedPath = _.map(_.pluck(unmovedNodes, 'loc'), projection),
22357                 hits = iD.geo.pathIntersections(movedPath, unmovedPath);
22358
22359             for (var i = 0; i < hits.length; i++) {
22360                 if (_.isEqual(hits[i], end)) continue;
22361                 var edge = iD.geo.chooseEdge(unmovedNodes, end, projection);
22362                 delta = vecSub(projection(edge.loc), start);
22363             }
22364         });
22365     }
22366
22367
22368     var action = function(graph) {
22369         if (delta[0] === 0 && delta[1] === 0) return graph;
22370
22371         setupCache(graph);
22372
22373         if (!_.isEmpty(cache.intersection)) {
22374             limitDelta(graph);
22375         }
22376
22377         _.each(cache.nodes, function(id) {
22378             var node = graph.entity(id),
22379                 start = projection(node.loc),
22380                 end = vecAdd(start, delta);
22381             graph = graph.replace(node.move(projection.invert(end)));
22382         });
22383
22384         if (!_.isEmpty(cache.intersection)) {
22385             graph = cleanupIntersections(graph);
22386         }
22387
22388         return graph;
22389     };
22390
22391     action.disabled = function(graph) {
22392         function incompleteRelation(id) {
22393             var entity = graph.entity(id);
22394             return entity.type === 'relation' && !entity.isComplete(graph);
22395         }
22396
22397         if (_.any(moveIds, incompleteRelation))
22398             return 'incomplete_relation';
22399     };
22400
22401     action.delta = function() {
22402         return delta;
22403     };
22404
22405     return action;
22406 };
22407 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
22408 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
22409 iD.actions.MoveNode = function(nodeId, loc) {
22410     return function(graph) {
22411         return graph.replace(graph.entity(nodeId).move(loc));
22412     };
22413 };
22414 iD.actions.Noop = function() {
22415     return function(graph) {
22416         return graph;
22417     };
22418 };
22419 /*
22420  * Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
22421  */
22422
22423 iD.actions.Orthogonalize = function(wayId, projection) {
22424     var threshold = 12, // degrees within right or straight to alter
22425         lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180),
22426         upperThreshold = Math.cos(threshold * Math.PI / 180);
22427
22428     var action = function(graph) {
22429         var way = graph.entity(wayId),
22430             nodes = graph.childNodes(way),
22431             points = _.uniq(nodes).map(function(n) { return projection(n.loc); }),
22432             corner = {i: 0, dotp: 1},
22433             epsilon = 1e-4,
22434             i, j, score, motions;
22435
22436         if (nodes.length === 4) {
22437             for (i = 0; i < 1000; i++) {
22438                 motions = points.map(calcMotion);
22439                 points[corner.i] = addPoints(points[corner.i],motions[corner.i]);
22440                 score = corner.dotp;
22441                 if (score < epsilon) {
22442                     break;
22443                 }
22444             }
22445
22446             graph = graph.replace(graph.entity(nodes[corner.i].id)
22447                 .move(projection.invert(points[corner.i])));
22448         } else {
22449             var best,
22450                 originalPoints = _.clone(points);
22451             score = Infinity;
22452
22453             for (i = 0; i < 1000; i++) {
22454                 motions = points.map(calcMotion);
22455                 for (j = 0; j < motions.length; j++) {
22456                     points[j] = addPoints(points[j],motions[j]);
22457                 }
22458                 var newScore = squareness(points);
22459                 if (newScore < score) {
22460                     best = _.clone(points);
22461                     score = newScore;
22462                 }
22463                 if (score < epsilon) {
22464                     break;
22465                 }
22466             }
22467
22468             points = best;
22469
22470             for (i = 0; i < points.length; i++) {
22471                 // only move the points that actually moved
22472                 if (originalPoints[i][0] !== points[i][0] || originalPoints[i][1] !== points[i][1]) {
22473                     graph = graph.replace(graph.entity(nodes[i].id)
22474                         .move(projection.invert(points[i])));
22475                 }
22476             }
22477
22478             // remove empty nodes on straight sections
22479             for (i = 0; i < points.length; i++) {
22480                 var node = nodes[i];
22481
22482                 if (graph.parentWays(node).length > 1 ||
22483                     graph.parentRelations(node).length ||
22484                     node.hasInterestingTags()) {
22485
22486                     continue;
22487                 }
22488
22489                 var dotp = normalizedDotProduct(i, points);
22490                 if (dotp < -1 + epsilon) {
22491                     graph = iD.actions.DeleteNode(nodes[i].id)(graph);
22492                 }
22493             }
22494         }
22495
22496         return graph;
22497
22498         function calcMotion(b, i, array) {
22499             var a = array[(i - 1 + array.length) % array.length],
22500                 c = array[(i + 1) % array.length],
22501                 p = subtractPoints(a, b),
22502                 q = subtractPoints(c, b),
22503                 scale, dotp;
22504
22505             scale = 2 * Math.min(iD.geo.euclideanDistance(p, [0, 0]), iD.geo.euclideanDistance(q, [0, 0]));
22506             p = normalizePoint(p, 1.0);
22507             q = normalizePoint(q, 1.0);
22508
22509             dotp = filterDotProduct(p[0] * q[0] + p[1] * q[1]);
22510
22511             // nasty hack to deal with almost-straight segments (angle is closer to 180 than to 90/270).
22512             if (array.length > 3) {
22513                 if (dotp < -0.707106781186547) {
22514                     dotp += 1.0;
22515                 }
22516             } else if (dotp && Math.abs(dotp) < corner.dotp) {
22517                 corner.i = i;
22518                 corner.dotp = Math.abs(dotp);
22519             }
22520
22521             return normalizePoint(addPoints(p, q), 0.1 * dotp * scale);
22522         }
22523     };
22524
22525     function squareness(points) {
22526         return points.reduce(function(sum, val, i, array) {
22527             var dotp = normalizedDotProduct(i, array);
22528
22529             dotp = filterDotProduct(dotp);
22530             return sum + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
22531         }, 0);
22532     }
22533
22534     function normalizedDotProduct(i, points) {
22535         var a = points[(i - 1 + points.length) % points.length],
22536             b = points[i],
22537             c = points[(i + 1) % points.length],
22538             p = subtractPoints(a, b),
22539             q = subtractPoints(c, b);
22540
22541         p = normalizePoint(p, 1.0);
22542         q = normalizePoint(q, 1.0);
22543
22544         return p[0] * q[0] + p[1] * q[1];
22545     }
22546
22547     function subtractPoints(a, b) {
22548         return [a[0] - b[0], a[1] - b[1]];
22549     }
22550
22551     function addPoints(a, b) {
22552         return [a[0] + b[0], a[1] + b[1]];
22553     }
22554
22555     function normalizePoint(point, scale) {
22556         var vector = [0, 0];
22557         var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]);
22558         if (length !== 0) {
22559             vector[0] = point[0] / length;
22560             vector[1] = point[1] / length;
22561         }
22562
22563         vector[0] *= scale;
22564         vector[1] *= scale;
22565
22566         return vector;
22567     }
22568
22569     function filterDotProduct(dotp) {
22570         if (lowerThreshold > Math.abs(dotp) || Math.abs(dotp) > upperThreshold) {
22571             return dotp;
22572         }
22573
22574         return 0;
22575     }
22576
22577     action.disabled = function(graph) {
22578         var way = graph.entity(wayId),
22579             nodes = graph.childNodes(way),
22580             points = _.uniq(nodes).map(function(n) { return projection(n.loc); });
22581
22582         if (squareness(points)) {
22583             return false;
22584         }
22585
22586         return 'not_squarish';
22587     };
22588
22589     return action;
22590 };
22591 // Create a restriction relation for `turn`, which must have the following structure:
22592 //
22593 //     {
22594 //         from: { node: <node ID>, way: <way ID> },
22595 //         via:  { node: <node ID> },
22596 //         to:   { node: <node ID>, way: <way ID> },
22597 //         restriction: <'no_right_turn', 'no_left_turn', etc.>
22598 //     }
22599 //
22600 // This specifies a restriction of type `restriction` when traveling from
22601 // `from.node` in `from.way` toward `to.node` in `to.way` via `via.node`.
22602 // (The action does not check that these entities form a valid intersection.)
22603 //
22604 // If `restriction` is not provided, it is automatically determined by
22605 // iD.geo.inferRestriction.
22606 //
22607 // If necessary, the `from` and `to` ways are split. In these cases, `from.node`
22608 // and `to.node` are used to determine which portion of the split ways become
22609 // members of the restriction.
22610 //
22611 // For testing convenience, accepts an ID to assign to the new relation.
22612 // Normally, this will be undefined and the relation will automatically
22613 // be assigned a new ID.
22614 //
22615 iD.actions.RestrictTurn = function(turn, projection, restrictionId) {
22616     return function(graph) {
22617         var from = graph.entity(turn.from.way),
22618             via  = graph.entity(turn.via.node),
22619             to   = graph.entity(turn.to.way);
22620
22621         function isClosingNode(way, nodeId) {
22622             return nodeId === way.first() && nodeId === way.last();
22623         }
22624
22625         function split(toOrFrom) {
22626             var newID = toOrFrom.newID || iD.Way().id;
22627             graph = iD.actions.Split(via.id, [newID])
22628                 .limitWays([toOrFrom.way])(graph);
22629
22630             var a = graph.entity(newID),
22631                 b = graph.entity(toOrFrom.way);
22632
22633             if (a.nodes.indexOf(toOrFrom.node) !== -1) {
22634                 return [a, b];
22635             } else {
22636                 return [b, a];
22637             }
22638         }
22639
22640         if (!from.affix(via.id) || isClosingNode(from, via.id)) {
22641             if (turn.from.node === turn.to.node) {
22642                 // U-turn
22643                 from = to = split(turn.from)[0];
22644             } else if (turn.from.way === turn.to.way) {
22645                 // Straight-on or circular
22646                 var s = split(turn.from);
22647                 from = s[0];
22648                 to   = s[1];
22649             } else {
22650                 // Other
22651                 from = split(turn.from)[0];
22652             }
22653         }
22654
22655         if (!to.affix(via.id) || isClosingNode(to, via.id)) {
22656             to = split(turn.to)[0];
22657         }
22658
22659         return graph.replace(iD.Relation({
22660             id: restrictionId,
22661             tags: {
22662                 type: 'restriction',
22663                 restriction: turn.restriction ||
22664                     iD.geo.inferRestriction(
22665                         graph,
22666                         turn.from,
22667                         turn.via,
22668                         turn.to,
22669                         projection)
22670             },
22671             members: [
22672                 {id: from.id, type: 'way',  role: 'from'},
22673                 {id: via.id,  type: 'node', role: 'via'},
22674                 {id: to.id,   type: 'way',  role: 'to'}
22675             ]
22676         }));
22677     };
22678 };
22679 /*
22680   Order the nodes of a way in reverse order and reverse any direction dependent tags
22681   other than `oneway`. (We assume that correcting a backwards oneway is the primary
22682   reason for reversing a way.)
22683
22684   The following transforms are performed:
22685
22686     Keys:
22687           *:right=* ⟺ *:left=*
22688         *:forward=* ⟺ *:backward=*
22689        direction=up ⟺ direction=down
22690          incline=up ⟺ incline=down
22691             *=right ⟺ *=left
22692
22693     Relation members:
22694        role=forward ⟺ role=backward
22695          role=north ⟺ role=south
22696           role=east ⟺ role=west
22697
22698    In addition, numeric-valued `incline` tags are negated.
22699
22700    The JOSM implementation was used as a guide, but transformations that were of unclear benefit
22701    or adjusted tags that don't seem to be used in practice were omitted.
22702
22703    References:
22704       http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
22705       http://wiki.openstreetmap.org/wiki/Key:direction#Steps
22706       http://wiki.openstreetmap.org/wiki/Key:incline
22707       http://wiki.openstreetmap.org/wiki/Route#Members
22708       http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
22709  */
22710 iD.actions.Reverse = function(wayId, options) {
22711     var replacements = [
22712             [/:right$/, ':left'], [/:left$/, ':right'],
22713             [/:forward$/, ':backward'], [/:backward$/, ':forward']
22714         ],
22715         numeric = /^([+\-]?)(?=[\d.])/,
22716         roleReversals = {
22717             forward: 'backward',
22718             backward: 'forward',
22719             north: 'south',
22720             south: 'north',
22721             east: 'west',
22722             west: 'east'
22723         };
22724
22725     function reverseKey(key) {
22726         for (var i = 0; i < replacements.length; ++i) {
22727             var replacement = replacements[i];
22728             if (replacement[0].test(key)) {
22729                 return key.replace(replacement[0], replacement[1]);
22730             }
22731         }
22732         return key;
22733     }
22734
22735     function reverseValue(key, value) {
22736         if (key === 'incline' && numeric.test(value)) {
22737             return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
22738         } else if (key === 'incline' || key === 'direction') {
22739             return {up: 'down', down: 'up'}[value] || value;
22740         } else if (options && options.reverseOneway && key === 'oneway') {
22741             return {yes: '-1', '1': '-1', '-1': 'yes'}[value] || value;
22742         } else {
22743             return {left: 'right', right: 'left'}[value] || value;
22744         }
22745     }
22746
22747     return function(graph) {
22748         var way = graph.entity(wayId),
22749             nodes = way.nodes.slice().reverse(),
22750             tags = {}, key, role;
22751
22752         for (key in way.tags) {
22753             tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
22754         }
22755
22756         graph.parentRelations(way).forEach(function(relation) {
22757             relation.members.forEach(function(member, index) {
22758                 if (member.id === way.id && (role = roleReversals[member.role])) {
22759                     relation = relation.updateMember({role: role}, index);
22760                     graph = graph.replace(relation);
22761                 }
22762             });
22763         });
22764
22765         return graph.replace(way.update({nodes: nodes, tags: tags}));
22766     };
22767 };
22768 iD.actions.Revert = function(id) {
22769
22770     var action = function(graph) {
22771         var entity = graph.hasEntity(id),
22772             base = graph.base().entities[id];
22773
22774         if (entity && !base) {    // entity will be removed..
22775             if (entity.type === 'node') {
22776                 graph.parentWays(entity)
22777                     .forEach(function(parent) {
22778                         parent = parent.removeNode(id);
22779                         graph = graph.replace(parent);
22780
22781                         if (parent.isDegenerate()) {
22782                             graph = iD.actions.DeleteWay(parent.id)(graph);
22783                         }
22784                     });
22785             }
22786
22787             graph.parentRelations(entity)
22788                 .forEach(function(parent) {
22789                     parent = parent.removeMembersWithID(id);
22790                     graph = graph.replace(parent);
22791
22792                     if (parent.isDegenerate()) {
22793                         graph = iD.actions.DeleteRelation(parent.id)(graph);
22794                     }
22795                 });
22796         }
22797
22798         return graph.revert(id);
22799     };
22800
22801     return action;
22802 };
22803 iD.actions.RotateWay = function(wayId, pivot, angle, projection) {
22804     return function(graph) {
22805         return graph.update(function(graph) {
22806             var way = graph.entity(wayId);
22807
22808             _.unique(way.nodes).forEach(function(id) {
22809
22810                 var node = graph.entity(id),
22811                     point = projection(node.loc),
22812                     radial = [0,0];
22813
22814                 radial[0] = point[0] - pivot[0];
22815                 radial[1] = point[1] - pivot[1];
22816
22817                 point = [
22818                     radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + pivot[0],
22819                     radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + pivot[1]
22820                 ];
22821
22822                 graph = graph.replace(node.move(projection.invert(point)));
22823
22824             });
22825
22826         });
22827     };
22828 };
22829 // Split a way at the given node.
22830 //
22831 // Optionally, split only the given ways, if multiple ways share
22832 // the given node.
22833 //
22834 // This is the inverse of `iD.actions.Join`.
22835 //
22836 // For testing convenience, accepts an ID to assign to the new way.
22837 // Normally, this will be undefined and the way will automatically
22838 // be assigned a new ID.
22839 //
22840 // Reference:
22841 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
22842 //
22843 iD.actions.Split = function(nodeId, newWayIds) {
22844     var wayIds;
22845
22846     // if the way is closed, we need to search for a partner node
22847     // to split the way at.
22848     //
22849     // The following looks for a node that is both far away from
22850     // the initial node in terms of way segment length and nearby
22851     // in terms of beeline-distance. This assures that areas get
22852     // split on the most "natural" points (independent of the number
22853     // of nodes).
22854     // For example: bone-shaped areas get split across their waist
22855     // line, circles across the diameter.
22856     function splitArea(nodes, idxA, graph) {
22857         var lengths = new Array(nodes.length),
22858             length,
22859             i,
22860             best = 0,
22861             idxB;
22862
22863         function wrap(index) {
22864             return iD.util.wrap(index, nodes.length);
22865         }
22866
22867         function dist(nA, nB) {
22868             return iD.geo.sphericalDistance(graph.entity(nA).loc, graph.entity(nB).loc);
22869         }
22870
22871         // calculate lengths
22872         length = 0;
22873         for (i = wrap(idxA+1); i !== idxA; i = wrap(i+1)) {
22874             length += dist(nodes[i], nodes[wrap(i-1)]);
22875             lengths[i] = length;
22876         }
22877
22878         length = 0;
22879         for (i = wrap(idxA-1); i !== idxA; i = wrap(i-1)) {
22880             length += dist(nodes[i], nodes[wrap(i+1)]);
22881             if (length < lengths[i])
22882                 lengths[i] = length;
22883         }
22884
22885         // determine best opposite node to split
22886         for (i = 0; i < nodes.length; i++) {
22887             var cost = lengths[i] / dist(nodes[idxA], nodes[i]);
22888             if (cost > best) {
22889                 idxB = i;
22890                 best = cost;
22891             }
22892         }
22893
22894         return idxB;
22895     }
22896
22897     function split(graph, wayA, newWayId) {
22898         var wayB = iD.Way({id: newWayId, tags: wayA.tags}),
22899             nodesA,
22900             nodesB,
22901             isArea = wayA.isArea(),
22902             isOuter = iD.geo.isSimpleMultipolygonOuterMember(wayA, graph);
22903
22904         if (wayA.isClosed()) {
22905             var nodes = wayA.nodes.slice(0, -1),
22906                 idxA = _.indexOf(nodes, nodeId),
22907                 idxB = splitArea(nodes, idxA, graph);
22908
22909             if (idxB < idxA) {
22910                 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
22911                 nodesB = nodes.slice(idxB, idxA + 1);
22912             } else {
22913                 nodesA = nodes.slice(idxA, idxB + 1);
22914                 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
22915             }
22916         } else {
22917             var idx = _.indexOf(wayA.nodes, nodeId, 1);
22918             nodesA = wayA.nodes.slice(0, idx + 1);
22919             nodesB = wayA.nodes.slice(idx);
22920         }
22921
22922         wayA = wayA.update({nodes: nodesA});
22923         wayB = wayB.update({nodes: nodesB});
22924
22925         graph = graph.replace(wayA);
22926         graph = graph.replace(wayB);
22927
22928         graph.parentRelations(wayA).forEach(function(relation) {
22929             if (relation.isRestriction()) {
22930                 var via = relation.memberByRole('via');
22931                 if (via && wayB.contains(via.id)) {
22932                     relation = relation.updateMember({id: wayB.id}, relation.memberById(wayA.id).index);
22933                     graph = graph.replace(relation);
22934                 }
22935             } else {
22936                 if (relation === isOuter) {
22937                     graph = graph.replace(relation.mergeTags(wayA.tags));
22938                     graph = graph.replace(wayA.update({tags: {}}));
22939                     graph = graph.replace(wayB.update({tags: {}}));
22940                 }
22941
22942                 var member = {
22943                     id: wayB.id,
22944                     type: 'way',
22945                     role: relation.memberById(wayA.id).role
22946                 };
22947
22948                 graph = iD.actions.AddMember(relation.id, member)(graph);
22949             }
22950         });
22951
22952         if (!isOuter && isArea) {
22953             var multipolygon = iD.Relation({
22954                 tags: _.extend({}, wayA.tags, {type: 'multipolygon'}),
22955                 members: [
22956                     {id: wayA.id, role: 'outer', type: 'way'},
22957                     {id: wayB.id, role: 'outer', type: 'way'}
22958                 ]});
22959
22960             graph = graph.replace(multipolygon);
22961             graph = graph.replace(wayA.update({tags: {}}));
22962             graph = graph.replace(wayB.update({tags: {}}));
22963         }
22964
22965         return graph;
22966     }
22967
22968     var action = function(graph) {
22969         var candidates = action.ways(graph);
22970         for (var i = 0; i < candidates.length; i++) {
22971             graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
22972         }
22973         return graph;
22974     };
22975
22976     action.ways = function(graph) {
22977         var node = graph.entity(nodeId),
22978             parents = graph.parentWays(node),
22979             hasLines = _.any(parents, function(parent) { return parent.geometry(graph) === 'line'; });
22980
22981         return parents.filter(function(parent) {
22982             if (wayIds && wayIds.indexOf(parent.id) === -1)
22983                 return false;
22984
22985             if (!wayIds && hasLines && parent.geometry(graph) !== 'line')
22986                 return false;
22987
22988             if (parent.isClosed()) {
22989                 return true;
22990             }
22991
22992             for (var i = 1; i < parent.nodes.length - 1; i++) {
22993                 if (parent.nodes[i] === nodeId) {
22994                     return true;
22995                 }
22996             }
22997
22998             return false;
22999         });
23000     };
23001
23002     action.disabled = function(graph) {
23003         var candidates = action.ways(graph);
23004         if (candidates.length === 0 || (wayIds && wayIds.length !== candidates.length))
23005             return 'not_eligible';
23006     };
23007
23008     action.limitWays = function(_) {
23009         if (!arguments.length) return wayIds;
23010         wayIds = _;
23011         return action;
23012     };
23013
23014     return action;
23015 };
23016 /*
23017  * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
23018  */
23019
23020 iD.actions.Straighten = function(wayId, projection) {
23021     function positionAlongWay(n, s, e) {
23022         return ((n[0] - s[0]) * (e[0] - s[0]) + (n[1] - s[1]) * (e[1] - s[1]))/
23023                 (Math.pow(e[0] - s[0], 2) + Math.pow(e[1] - s[1], 2));
23024     }
23025
23026     var action = function(graph) {
23027         var way = graph.entity(wayId),
23028             nodes = graph.childNodes(way),
23029             points = nodes.map(function(n) { return projection(n.loc); }),
23030             startPoint = points[0],
23031             endPoint = points[points.length-1],
23032             toDelete = [],
23033             i;
23034
23035         for (i = 1; i < points.length-1; i++) {
23036             var node = nodes[i],
23037                 point = points[i];
23038
23039             if (graph.parentWays(node).length > 1 ||
23040                 graph.parentRelations(node).length ||
23041                 node.hasInterestingTags()) {
23042
23043                 var u = positionAlongWay(point, startPoint, endPoint),
23044                     p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
23045                     p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]);
23046
23047                 graph = graph.replace(graph.entity(node.id)
23048                     .move(projection.invert([p0, p1])));
23049             } else {
23050                 // safe to delete
23051                 if (toDelete.indexOf(node) === -1) {
23052                     toDelete.push(node);
23053                 }
23054             }
23055         }
23056
23057         for (i = 0; i < toDelete.length; i++) {
23058             graph = iD.actions.DeleteNode(toDelete[i].id)(graph);
23059         }
23060
23061         return graph;
23062     };
23063     
23064     action.disabled = function(graph) {
23065         // check way isn't too bendy
23066         var way = graph.entity(wayId),
23067             nodes = graph.childNodes(way),
23068             points = nodes.map(function(n) { return projection(n.loc); }),
23069             startPoint = points[0],
23070             endPoint = points[points.length-1],
23071             threshold = 0.2 * Math.sqrt(Math.pow(startPoint[0] - endPoint[0], 2) + Math.pow(startPoint[1] - endPoint[1], 2)),
23072             i;
23073
23074         for (i = 1; i < points.length-1; i++) {
23075             var point = points[i],
23076                 u = positionAlongWay(point, startPoint, endPoint),
23077                 p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
23078                 p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]),
23079                 dist = Math.sqrt(Math.pow(p0 - point[0], 2) + Math.pow(p1 - point[1], 2));
23080
23081             // to bendy if point is off by 20% of total start/end distance in projected space
23082             if (dist > threshold) {
23083                 return 'too_bendy';
23084             }
23085         }
23086     };
23087
23088     return action;
23089 };
23090 // Remove the effects of `turn.restriction` on `turn`, which must have the
23091 // following structure:
23092 //
23093 //     {
23094 //         from: { node: <node ID>, way: <way ID> },
23095 //         via:  { node: <node ID> },
23096 //         to:   { node: <node ID>, way: <way ID> },
23097 //         restriction: <relation ID>
23098 //     }
23099 //
23100 // In the simple case, `restriction` is a reference to a `no_*` restriction
23101 // on the turn itself. In this case, it is simply deleted.
23102 //
23103 // The more complex case is where `restriction` references an `only_*`
23104 // restriction on a different turn in the same intersection. In that case,
23105 // that restriction is also deleted, but at the same time restrictions on
23106 // the turns other than the first two are created.
23107 //
23108 iD.actions.UnrestrictTurn = function(turn) {
23109     return function(graph) {
23110         return iD.actions.DeleteRelation(turn.restriction)(graph);
23111     };
23112 };
23113 iD.behavior = {};
23114 iD.behavior.AddWay = function(context) {
23115     var event = d3.dispatch('start', 'startFromWay', 'startFromNode'),
23116         draw = iD.behavior.Draw(context);
23117
23118     var addWay = function(surface) {
23119         draw.on('click', event.start)
23120             .on('clickWay', event.startFromWay)
23121             .on('clickNode', event.startFromNode)
23122             .on('cancel', addWay.cancel)
23123             .on('finish', addWay.cancel);
23124
23125         context.map()
23126             .dblclickEnable(false);
23127
23128         surface.call(draw);
23129     };
23130
23131     addWay.off = function(surface) {
23132         surface.call(draw.off);
23133     };
23134
23135     addWay.cancel = function() {
23136         window.setTimeout(function() {
23137             context.map().dblclickEnable(true);
23138         }, 1000);
23139
23140         context.enter(iD.modes.Browse(context));
23141     };
23142
23143     addWay.tail = function(text) {
23144         draw.tail(text);
23145         return addWay;
23146     };
23147
23148     return d3.rebind(addWay, event, 'on');
23149 };
23150 iD.behavior.Breathe = function() {
23151     var duration = 800,
23152         selector = '.selected.shadow, .selected .shadow',
23153         selected = d3.select(null),
23154         classed = '',
23155         params = {},
23156         done;
23157
23158     function reset(selection) {
23159         selection
23160             .style('stroke-opacity', null)
23161             .style('stroke-width', null)
23162             .style('fill-opacity', null)
23163             .style('r', null);
23164     }
23165
23166     function setAnimationParams(transition, fromTo) {
23167         transition
23168             .style('stroke-opacity', function(d) { return params[d.id][fromTo].opacity; })
23169             .style('stroke-width', function(d) { return params[d.id][fromTo].width; })
23170             .style('fill-opacity', function(d) { return params[d.id][fromTo].opacity; })
23171             .style('r', function(d) { return params[d.id][fromTo].width; });
23172     }
23173
23174     function calcAnimationParams(selection) {
23175         selection
23176             .call(reset)
23177             .each(function(d) {
23178                 var s = d3.select(this),
23179                     tag = s.node().tagName,
23180                     p = {'from': {}, 'to': {}},
23181                     opacity, width;
23182
23183                 // determine base opacity and width
23184                 if (tag === 'circle') {
23185                     opacity = parseFloat(s.style('fill-opacity') || 0.5);
23186                     width = parseFloat(s.style('r') || 15.5);
23187                 } else {
23188                     opacity = parseFloat(s.style('stroke-opacity') || 0.7);
23189                     width = parseFloat(s.style('stroke-width') || 10);
23190                 }
23191
23192                 // calculate from/to interpolation params..
23193                 p.tag = tag;
23194                 p.from.opacity = opacity * 0.6;
23195                 p.to.opacity = opacity * 1.25;
23196                 p.from.width = width * 0.9;
23197                 p.to.width = width * (tag === 'circle' ? 1.5 : 1.25);
23198                 params[d.id] = p;
23199             });
23200     }
23201
23202     function run(surface, fromTo) {
23203         var toFrom = (fromTo === 'from' ? 'to': 'from'),
23204             currSelected = surface.selectAll(selector),
23205             currClassed = surface.attr('class'),
23206             n = 0;
23207
23208         if (done || currSelected.empty()) {
23209             selected.call(reset);
23210             return;
23211         }
23212
23213         if (!_.isEqual(currSelected, selected) || currClassed !== classed) {
23214             selected.call(reset);
23215             classed = currClassed;
23216             selected = currSelected.call(calcAnimationParams);
23217         }
23218
23219         selected
23220             .transition()
23221             .call(setAnimationParams, fromTo)
23222             .duration(duration)
23223             .each(function() { ++n; })
23224             .each('end', function() {
23225                 if (!--n) {  // call once
23226                     surface.call(run, toFrom);
23227                 }
23228             });
23229     }
23230
23231     var breathe = function(surface) {
23232         done = false;
23233         d3.timer(function() {
23234             if (done) return true;
23235
23236             var currSelected = surface.selectAll(selector);
23237             if (currSelected.empty()) return false;
23238
23239             surface.call(run, 'from');
23240             return true;
23241         }, 200);
23242     };
23243
23244     breathe.off = function() {
23245         done = true;
23246         d3.timer.flush();
23247         selected
23248             .transition()
23249             .call(reset)
23250             .duration(0);
23251     };
23252
23253     return breathe;
23254 };
23255 iD.behavior.Copy = function(context) {
23256     var keybinding = d3.keybinding('copy');
23257
23258     function groupEntities(ids, graph) {
23259         var entities = ids.map(function (id) { return graph.entity(id); });
23260         return _.extend({relation: [], way: [], node: []},
23261             _.groupBy(entities, function(entity) { return entity.type; }));
23262     }
23263
23264     function getDescendants(id, graph, descendants) {
23265         var entity = graph.entity(id),
23266             i, children;
23267
23268         descendants = descendants || {};
23269
23270         if (entity.type === 'relation') {
23271             children = _.pluck(entity.members, 'id');
23272         } else if (entity.type === 'way') {
23273             children = entity.nodes;
23274         } else {
23275             children = [];
23276         }
23277
23278         for (i = 0; i < children.length; i++) {
23279             if (!descendants[children[i]]) {
23280                 descendants[children[i]] = true;
23281                 descendants = getDescendants(children[i], graph, descendants);
23282             }
23283         }
23284
23285         return descendants;
23286     }
23287
23288     function doCopy() {
23289         d3.event.preventDefault();
23290         if (context.inIntro()) return;
23291
23292         var graph = context.graph(),
23293             selected = groupEntities(context.selectedIDs(), graph),
23294             canCopy = [],
23295             skip = {},
23296             i, entity;
23297
23298         for (i = 0; i < selected.relation.length; i++) {
23299             entity = selected.relation[i];
23300             if (!skip[entity.id] && entity.isComplete(graph)) {
23301                 canCopy.push(entity.id);
23302                 skip = getDescendants(entity.id, graph, skip);
23303             }
23304         }
23305         for (i = 0; i < selected.way.length; i++) {
23306             entity = selected.way[i];
23307             if (!skip[entity.id]) {
23308                 canCopy.push(entity.id);
23309                 skip = getDescendants(entity.id, graph, skip);
23310             }
23311         }
23312         for (i = 0; i < selected.node.length; i++) {
23313             entity = selected.node[i];
23314             if (!skip[entity.id]) {
23315                 canCopy.push(entity.id);
23316             }
23317         }
23318
23319         context.copyIDs(canCopy);
23320     }
23321
23322     function copy() {
23323         keybinding.on(iD.ui.cmd('⌘C'), doCopy);
23324         d3.select(document).call(keybinding);
23325         return copy;
23326     }
23327
23328     copy.off = function() {
23329         d3.select(document).call(keybinding.off);
23330     };
23331
23332     return copy;
23333 };
23334 /*
23335     `iD.behavior.drag` is like `d3.behavior.drag`, with the following differences:
23336
23337     * The `origin` function is expected to return an [x, y] tuple rather than an
23338       {x, y} object.
23339     * The events are `start`, `move`, and `end`.
23340       (https://github.com/mbostock/d3/issues/563)
23341     * The `start` event is not dispatched until the first cursor movement occurs.
23342       (https://github.com/mbostock/d3/pull/368)
23343     * The `move` event has a `point` and `delta` [x, y] tuple properties rather
23344       than `x`, `y`, `dx`, and `dy` properties.
23345     * The `end` event is not dispatched if no movement occurs.
23346     * An `off` function is available that unbinds the drag's internal event handlers.
23347     * Delegation is supported via the `delegate` function.
23348
23349  */
23350 iD.behavior.drag = function() {
23351     function d3_eventCancel() {
23352       d3.event.stopPropagation();
23353       d3.event.preventDefault();
23354     }
23355
23356     var event = d3.dispatch('start', 'move', 'end'),
23357         origin = null,
23358         selector = '',
23359         filter = null,
23360         event_, target, surface;
23361
23362     event.of = function(thiz, argumentz) {
23363       return function(e1) {
23364         var e0 = e1.sourceEvent = d3.event;
23365         e1.target = drag;
23366         d3.event = e1;
23367         try {
23368           event[e1.type].apply(thiz, argumentz);
23369         } finally {
23370           d3.event = e0;
23371         }
23372       };
23373     };
23374
23375     var d3_event_userSelectProperty = iD.util.prefixCSSProperty('UserSelect'),
23376         d3_event_userSelectSuppress = d3_event_userSelectProperty ?
23377             function () {
23378                 var selection = d3.selection(),
23379                     select = selection.style(d3_event_userSelectProperty);
23380                 selection.style(d3_event_userSelectProperty, 'none');
23381                 return function () {
23382                     selection.style(d3_event_userSelectProperty, select);
23383                 };
23384             } :
23385             function (type) {
23386                 var w = d3.select(window).on('selectstart.' + type, d3_eventCancel);
23387                 return function () {
23388                     w.on('selectstart.' + type, null);
23389                 };
23390             };
23391
23392     function mousedown() {
23393         target = this;
23394         event_ = event.of(target, arguments);
23395         var eventTarget = d3.event.target,
23396             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
23397             offset,
23398             origin_ = point(),
23399             started = false,
23400             selectEnable = d3_event_userSelectSuppress(touchId !== null ? 'drag-' + touchId : 'drag');
23401
23402         var w = d3.select(window)
23403             .on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', dragmove)
23404             .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', dragend, true);
23405
23406         if (origin) {
23407             offset = origin.apply(target, arguments);
23408             offset = [offset[0] - origin_[0], offset[1] - origin_[1]];
23409         } else {
23410             offset = [0, 0];
23411         }
23412
23413         if (touchId === null) d3.event.stopPropagation();
23414
23415         function point() {
23416             var p = target.parentNode || surface;
23417             return touchId !== null ? d3.touches(p).filter(function(p) {
23418                 return p.identifier === touchId;
23419             })[0] : d3.mouse(p);
23420         }
23421
23422         function dragmove() {
23423
23424             var p = point(),
23425                 dx = p[0] - origin_[0],
23426                 dy = p[1] - origin_[1];
23427             
23428             if (dx === 0 && dy === 0)
23429                 return;
23430
23431             if (!started) {
23432                 started = true;
23433                 event_({
23434                     type: 'start'
23435                 });
23436             }
23437
23438             origin_ = p;
23439             d3_eventCancel();
23440
23441             event_({
23442                 type: 'move',
23443                 point: [p[0] + offset[0],  p[1] + offset[1]],
23444                 delta: [dx, dy]
23445             });
23446         }
23447
23448         function dragend() {
23449             if (started) {
23450                 event_({
23451                     type: 'end'
23452                 });
23453
23454                 d3_eventCancel();
23455                 if (d3.event.target === eventTarget) w.on('click.drag', click, true);
23456             }
23457
23458             w.on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', null)
23459                 .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', null);
23460             selectEnable();
23461         }
23462
23463         function click() {
23464             d3_eventCancel();
23465             w.on('click.drag', null);
23466         }
23467     }
23468
23469     function drag(selection) {
23470         var matchesSelector = iD.util.prefixDOMProperty('matchesSelector'),
23471             delegate = mousedown;
23472
23473         if (selector) {
23474             delegate = function() {
23475                 var root = this,
23476                     target = d3.event.target;
23477                 for (; target && target !== root; target = target.parentNode) {
23478                     if (target[matchesSelector](selector) &&
23479                             (!filter || filter(target.__data__))) {
23480                         return mousedown.call(target, target.__data__);
23481                     }
23482                 }
23483             };
23484         }
23485
23486         selection.on('mousedown.drag' + selector, delegate)
23487             .on('touchstart.drag' + selector, delegate);
23488     }
23489
23490     drag.off = function(selection) {
23491         selection.on('mousedown.drag' + selector, null)
23492             .on('touchstart.drag' + selector, null);
23493     };
23494
23495     drag.delegate = function(_) {
23496         if (!arguments.length) return selector;
23497         selector = _;
23498         return drag;
23499     };
23500
23501     drag.filter = function(_) {
23502         if (!arguments.length) return origin;
23503         filter = _;
23504         return drag;
23505     };
23506
23507     drag.origin = function (_) {
23508         if (!arguments.length) return origin;
23509         origin = _;
23510         return drag;
23511     };
23512
23513     drag.cancel = function() {
23514         d3.select(window)
23515             .on('mousemove.drag', null)
23516             .on('mouseup.drag', null);
23517         return drag;
23518     };
23519
23520     drag.target = function() {
23521         if (!arguments.length) return target;
23522         target = arguments[0];
23523         event_ = event.of(target, Array.prototype.slice.call(arguments, 1));
23524         return drag;
23525     };
23526
23527     drag.surface = function() {
23528         if (!arguments.length) return surface;
23529         surface = arguments[0];
23530         return drag;
23531     };
23532
23533     return d3.rebind(drag, event, 'on');
23534 };
23535 iD.behavior.Draw = function(context) {
23536     var event = d3.dispatch('move', 'click', 'clickWay',
23537             'clickNode', 'undo', 'cancel', 'finish'),
23538         keybinding = d3.keybinding('draw'),
23539         hover = iD.behavior.Hover(context)
23540             .altDisables(true)
23541             .on('hover', context.ui().sidebar.hover),
23542         tail = iD.behavior.Tail(),
23543         edit = iD.behavior.Edit(context),
23544         closeTolerance = 4,
23545         tolerance = 12;
23546
23547     function datum() {
23548         if (d3.event.altKey) return {};
23549         else return d3.event.target.__data__ || {};
23550     }
23551
23552     function mousedown() {
23553
23554         function point() {
23555             var p = context.container().node();
23556             return touchId !== null ? d3.touches(p).filter(function(p) {
23557                 return p.identifier === touchId;
23558             })[0] : d3.mouse(p);
23559         }
23560
23561         var element = d3.select(this),
23562             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
23563             t1 = +new Date(),
23564             p1 = point();
23565
23566         element.on('mousemove.draw', null);
23567
23568         d3.select(window).on('mouseup.draw', function() {
23569             var t2 = +new Date(),
23570                 p2 = point(),
23571                 dist = iD.geo.euclideanDistance(p1, p2);
23572
23573             element.on('mousemove.draw', mousemove);
23574             d3.select(window).on('mouseup.draw', null);
23575
23576             if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) {
23577                 // Prevent a quick second click
23578                 d3.select(window).on('click.draw-block', function() {
23579                     d3.event.stopPropagation();
23580                 }, true);
23581
23582                 context.map().dblclickEnable(false);
23583
23584                 window.setTimeout(function() {
23585                     context.map().dblclickEnable(true);
23586                     d3.select(window).on('click.draw-block', null);
23587                 }, 500);
23588
23589                 click();
23590             }
23591         });
23592     }
23593
23594     function mousemove() {
23595         event.move(datum());
23596     }
23597
23598     function click() {
23599         var d = datum();
23600         if (d.type === 'way') {
23601             var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection),
23602                 edge = [d.nodes[choice.index - 1], d.nodes[choice.index]];
23603             event.clickWay(choice.loc, edge);
23604
23605         } else if (d.type === 'node') {
23606             event.clickNode(d);
23607
23608         } else {
23609             event.click(context.map().mouseCoordinates());
23610         }
23611     }
23612
23613     function backspace() {
23614         d3.event.preventDefault();
23615         event.undo();
23616     }
23617
23618     function del() {
23619         d3.event.preventDefault();
23620         event.cancel();
23621     }
23622
23623     function ret() {
23624         d3.event.preventDefault();
23625         event.finish();
23626     }
23627
23628     function draw(selection) {
23629         context.install(hover);
23630         context.install(edit);
23631
23632         if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
23633             context.install(tail);
23634         }
23635
23636         keybinding
23637             .on('⌫', backspace)
23638             .on('⌦', del)
23639             .on('⎋', ret)
23640             .on('↩', ret);
23641
23642         selection
23643             .on('mousedown.draw', mousedown)
23644             .on('mousemove.draw', mousemove);
23645
23646         d3.select(document)
23647             .call(keybinding);
23648
23649         return draw;
23650     }
23651
23652     draw.off = function(selection) {
23653         context.ui().sidebar.hover.cancel();
23654         context.uninstall(hover);
23655         context.uninstall(edit);
23656
23657         if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
23658             context.uninstall(tail);
23659             iD.behavior.Draw.usedTails[tail.text()] = true;
23660         }
23661
23662         selection
23663             .on('mousedown.draw', null)
23664             .on('mousemove.draw', null);
23665
23666         d3.select(window)
23667             .on('mouseup.draw', null);
23668
23669         d3.select(document)
23670             .call(keybinding.off);
23671     };
23672
23673     draw.tail = function(_) {
23674         tail.text(_);
23675         return draw;
23676     };
23677
23678     return d3.rebind(draw, event, 'on');
23679 };
23680
23681 iD.behavior.Draw.usedTails = {};
23682 iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
23683     var way = context.entity(wayId),
23684         isArea = context.geometry(wayId) === 'area',
23685         finished = false,
23686         annotation = t((way.isDegenerate() ?
23687             'operations.start.annotation.' :
23688             'operations.continue.annotation.') + context.geometry(wayId)),
23689         draw = iD.behavior.Draw(context);
23690
23691     var startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0,
23692         start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}),
23693         end = iD.Node({loc: context.map().mouseCoordinates()}),
23694         segment = iD.Way({
23695             nodes: typeof index === 'undefined' ? [start.id, end.id] : [end.id, start.id],
23696             tags: _.clone(way.tags)
23697         });
23698
23699     var f = context[way.isDegenerate() ? 'replace' : 'perform'];
23700     if (isArea) {
23701         f(iD.actions.AddEntity(end),
23702             iD.actions.AddVertex(wayId, end.id, index));
23703     } else {
23704         f(iD.actions.AddEntity(start),
23705             iD.actions.AddEntity(end),
23706             iD.actions.AddEntity(segment));
23707     }
23708
23709     function move(datum) {
23710         var loc;
23711
23712         if (datum.type === 'node' && datum.id !== end.id) {
23713             loc = datum.loc;
23714         } else if (datum.type === 'way' && datum.id !== segment.id) {
23715             loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc;
23716         } else {
23717             loc = context.map().mouseCoordinates();
23718         }
23719
23720         context.replace(iD.actions.MoveNode(end.id, loc));
23721     }
23722
23723     function undone() {
23724         finished = true;
23725         context.enter(iD.modes.Browse(context));
23726     }
23727
23728     function setActiveElements() {
23729         var active = isArea ? [wayId, end.id] : [segment.id, start.id, end.id];
23730         context.surface().selectAll(iD.util.entitySelector(active))
23731             .classed('active', true);
23732     }
23733
23734     var drawWay = function(surface) {
23735         draw.on('move', move)
23736             .on('click', drawWay.add)
23737             .on('clickWay', drawWay.addWay)
23738             .on('clickNode', drawWay.addNode)
23739             .on('undo', context.undo)
23740             .on('cancel', drawWay.cancel)
23741             .on('finish', drawWay.finish);
23742
23743         context.map()
23744             .dblclickEnable(false)
23745             .on('drawn.draw', setActiveElements);
23746
23747         setActiveElements();
23748
23749         surface.call(draw);
23750
23751         context.history()
23752             .on('undone.draw', undone);
23753     };
23754
23755     drawWay.off = function(surface) {
23756         if (!finished)
23757             context.pop();
23758
23759         context.map()
23760             .on('drawn.draw', null);
23761
23762         surface.call(draw.off)
23763             .selectAll('.active')
23764             .classed('active', false);
23765
23766         context.history()
23767             .on('undone.draw', null);
23768     };
23769
23770     function ReplaceTemporaryNode(newNode) {
23771         return function(graph) {
23772             if (isArea) {
23773                 return graph
23774                     .replace(way.addNode(newNode.id, index))
23775                     .remove(end);
23776
23777             } else {
23778                 return graph
23779                     .replace(graph.entity(wayId).addNode(newNode.id, index))
23780                     .remove(end)
23781                     .remove(segment)
23782                     .remove(start);
23783             }
23784         };
23785     }
23786
23787     // Accept the current position of the temporary node and continue drawing.
23788     drawWay.add = function(loc) {
23789
23790         // prevent duplicate nodes
23791         var last = context.hasEntity(way.nodes[way.nodes.length - (isArea ? 2 : 1)]);
23792         if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return;
23793
23794         var newNode = iD.Node({loc: loc});
23795
23796         context.replace(
23797             iD.actions.AddEntity(newNode),
23798             ReplaceTemporaryNode(newNode),
23799             annotation);
23800
23801         finished = true;
23802         context.enter(mode);
23803     };
23804
23805     // Connect the way to an existing way.
23806     drawWay.addWay = function(loc, edge) {
23807         var previousEdge = startIndex ?
23808             [way.nodes[startIndex], way.nodes[startIndex - 1]] :
23809             [way.nodes[0], way.nodes[1]];
23810
23811         // Avoid creating duplicate segments
23812         if (!isArea && iD.geo.edgeEqual(edge, previousEdge))
23813             return;
23814
23815         var newNode = iD.Node({ loc: loc });
23816
23817         context.perform(
23818             iD.actions.AddMidpoint({ loc: loc, edge: edge}, newNode),
23819             ReplaceTemporaryNode(newNode),
23820             annotation);
23821
23822         finished = true;
23823         context.enter(mode);
23824     };
23825
23826     // Connect the way to an existing node and continue drawing.
23827     drawWay.addNode = function(node) {
23828
23829         // Avoid creating duplicate segments
23830         if (way.areAdjacent(node.id, way.nodes[way.nodes.length - 1])) return;
23831
23832         context.perform(
23833             ReplaceTemporaryNode(node),
23834             annotation);
23835
23836         finished = true;
23837         context.enter(mode);
23838     };
23839
23840     // Finish the draw operation, removing the temporary node. If the way has enough
23841     // nodes to be valid, it's selected. Otherwise, return to browse mode.
23842     drawWay.finish = function() {
23843         context.pop();
23844         finished = true;
23845
23846         window.setTimeout(function() {
23847             context.map().dblclickEnable(true);
23848         }, 1000);
23849
23850         if (context.hasEntity(wayId)) {
23851             context.enter(
23852                 iD.modes.Select(context, [wayId])
23853                     .suppressMenu(true)
23854                     .newFeature(true));
23855         } else {
23856             context.enter(iD.modes.Browse(context));
23857         }
23858     };
23859
23860     // Cancel the draw operation and return to browse, deleting everything drawn.
23861     drawWay.cancel = function() {
23862         context.perform(
23863             d3.functor(baseGraph),
23864             t('operations.cancel_draw.annotation'));
23865
23866         window.setTimeout(function() {
23867             context.map().dblclickEnable(true);
23868         }, 1000);
23869
23870         finished = true;
23871         context.enter(iD.modes.Browse(context));
23872     };
23873
23874     drawWay.tail = function(text) {
23875         draw.tail(text);
23876         return drawWay;
23877     };
23878
23879     return drawWay;
23880 };
23881 iD.behavior.Edit = function(context) {
23882     function edit() {
23883         context.map()
23884             .minzoom(context.minEditableZoom());
23885     }
23886
23887     edit.off = function() {
23888         context.map()
23889             .minzoom(0);
23890     };
23891
23892     return edit;
23893 };
23894 iD.behavior.Hash = function(context) {
23895     var s0 = null, // cached location.hash
23896         lat = 90 - 1e-8; // allowable latitude range
23897
23898     var parser = function(map, s) {
23899         var q = iD.util.stringQs(s);
23900         var args = (q.map || '').split('/').map(Number);
23901         if (args.length < 3 || args.some(isNaN)) {
23902             return true; // replace bogus hash
23903         } else if (s !== formatter(map).slice(1)) {
23904             map.centerZoom([args[1],
23905                 Math.min(lat, Math.max(-lat, args[2]))], args[0]);
23906         }
23907     };
23908
23909     var formatter = function(map) {
23910         var mode = context.mode(),
23911             center = map.center(),
23912             zoom = map.zoom(),
23913             precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
23914             q = _.omit(iD.util.stringQs(location.hash.substring(1)), 'comment'),
23915             newParams = {};
23916
23917         if (mode && mode.id === 'browse') {
23918             delete q.id;
23919         } else {
23920             var selected = context.selectedIDs().filter(function(id) {
23921                 return !context.entity(id).isNew();
23922             });
23923             if (selected.length) {
23924                 newParams.id = selected.join(',');
23925             }
23926         }
23927
23928         newParams.map = zoom.toFixed(2) +
23929                 '/' + center[0].toFixed(precision) +
23930                 '/' + center[1].toFixed(precision);
23931
23932         return '#' + iD.util.qsString(_.assign(q, newParams), true);
23933     };
23934
23935     function update() {
23936         if (context.inIntro()) return;
23937         var s1 = formatter(context.map());
23938         if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map!
23939     }
23940
23941     var throttledUpdate = _.throttle(update, 500);
23942
23943     function hashchange() {
23944         if (location.hash === s0) return; // ignore spurious hashchange events
23945         if (parser(context.map(), (s0 = location.hash).substring(1))) {
23946             update(); // replace bogus hash
23947         }
23948     }
23949
23950     function hash() {
23951         context.map()
23952             .on('move.hash', throttledUpdate);
23953
23954         context
23955             .on('enter.hash', throttledUpdate);
23956
23957         d3.select(window)
23958             .on('hashchange.hash', hashchange);
23959
23960         if (location.hash) {
23961             var q = iD.util.stringQs(location.hash.substring(1));
23962             if (q.id) context.zoomToEntity(q.id.split(',')[0], !q.map);
23963             if (q.comment) context.storage('comment', q.comment);
23964             hashchange();
23965             if (q.map) hash.hadHash = true;
23966         }
23967     }
23968
23969     hash.off = function() {
23970         throttledUpdate.cancel();
23971
23972         context.map()
23973             .on('move.hash', null);
23974
23975         context
23976             .on('enter.hash', null);
23977
23978         d3.select(window)
23979             .on('hashchange.hash', null);
23980
23981         location.hash = '';
23982     };
23983
23984     return hash;
23985 };
23986 /*
23987    The hover behavior adds the `.hover` class on mouseover to all elements to which
23988    the identical datum is bound, and removes it on mouseout.
23989
23990    The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
23991    representation may consist of several elements scattered throughout the DOM hierarchy.
23992    Only one of these elements can have the :hover pseudo-class, but all of them will
23993    have the .hover class.
23994  */
23995 iD.behavior.Hover = function() {
23996     var dispatch = d3.dispatch('hover'),
23997         selection,
23998         altDisables,
23999         target;
24000
24001     function keydown() {
24002         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
24003             dispatch.hover(null);
24004             selection.selectAll('.hover')
24005                 .classed('hover-suppressed', true)
24006                 .classed('hover', false);
24007         }
24008     }
24009
24010     function keyup() {
24011         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
24012             dispatch.hover(target ? target.id : null);
24013             selection.selectAll('.hover-suppressed')
24014                 .classed('hover-suppressed', false)
24015                 .classed('hover', true);
24016         }
24017     }
24018
24019     var hover = function(__) {
24020         selection = __;
24021
24022         function enter(d) {
24023             if (d === target) return;
24024
24025             target = d;
24026
24027             selection.selectAll('.hover')
24028                 .classed('hover', false);
24029             selection.selectAll('.hover-suppressed')
24030                 .classed('hover-suppressed', false);
24031
24032             if (target instanceof iD.Entity) {
24033                 var selector = '.' + target.id;
24034
24035                 if (target.type === 'relation') {
24036                     target.members.forEach(function(member) {
24037                         selector += ', .' + member.id;
24038                     });
24039                 }
24040
24041                 var suppressed = altDisables && d3.event && d3.event.altKey;
24042
24043                 selection.selectAll(selector)
24044                     .classed(suppressed ? 'hover-suppressed' : 'hover', true);
24045
24046                 dispatch.hover(target.id);
24047             } else {
24048                 dispatch.hover(null);
24049             }
24050         }
24051
24052         var down;
24053
24054         function mouseover() {
24055             if (down) return;
24056             var target = d3.event.target;
24057             enter(target ? target.__data__ : null);
24058         }
24059
24060         function mouseout() {
24061             if (down) return;
24062             var target = d3.event.relatedTarget;
24063             enter(target ? target.__data__ : null);
24064         }
24065
24066         function mousedown() {
24067             down = true;
24068             d3.select(window)
24069                 .on('mouseup.hover', mouseup);
24070         }
24071
24072         function mouseup() {
24073             down = false;
24074         }
24075
24076         selection
24077             .on('mouseover.hover', mouseover)
24078             .on('mouseout.hover', mouseout)
24079             .on('mousedown.hover', mousedown)
24080             .on('mouseup.hover', mouseup);
24081
24082         d3.select(window)
24083             .on('keydown.hover', keydown)
24084             .on('keyup.hover', keyup);
24085     };
24086
24087     hover.off = function(selection) {
24088         selection.selectAll('.hover')
24089             .classed('hover', false);
24090         selection.selectAll('.hover-suppressed')
24091             .classed('hover-suppressed', false);
24092
24093         selection
24094             .on('mouseover.hover', null)
24095             .on('mouseout.hover', null)
24096             .on('mousedown.hover', null)
24097             .on('mouseup.hover', null);
24098
24099         d3.select(window)
24100             .on('keydown.hover', null)
24101             .on('keyup.hover', null)
24102             .on('mouseup.hover', null);
24103     };
24104
24105     hover.altDisables = function(_) {
24106         if (!arguments.length) return altDisables;
24107         altDisables = _;
24108         return hover;
24109     };
24110
24111     return d3.rebind(hover, dispatch, 'on');
24112 };
24113 iD.behavior.Lasso = function(context) {
24114
24115     var behavior = function(selection) {
24116         var lasso;
24117
24118         function mousedown() {
24119             var button = 0;  // left
24120             if (d3.event.button === button && d3.event.shiftKey === true) {
24121                 lasso = null;
24122
24123                 selection
24124                     .on('mousemove.lasso', mousemove)
24125                     .on('mouseup.lasso', mouseup);
24126
24127                 d3.event.stopPropagation();
24128             }
24129         }
24130
24131         function mousemove() {
24132             if (!lasso) {
24133                 lasso = iD.ui.Lasso(context);
24134                 context.surface().call(lasso);
24135             }
24136
24137             lasso.p(context.mouse());
24138         }
24139
24140         function normalize(a, b) {
24141             return [
24142                 [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
24143                 [Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
24144         }
24145
24146         function lassoed() {
24147             if (!lasso) return [];
24148
24149             var graph = context.graph(),
24150                 bounds = lasso.extent().map(context.projection.invert),
24151                 extent = iD.geo.Extent(normalize(bounds[0], bounds[1]));
24152
24153             return _.pluck(context.intersects(extent).filter(function(entity) {
24154                 return entity.type === 'node' &&
24155                     iD.geo.pointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
24156                     !context.features().isHidden(entity, graph, entity.geometry(graph));
24157             }), 'id');
24158         }
24159
24160         function mouseup() {
24161             selection
24162                 .on('mousemove.lasso', null)
24163                 .on('mouseup.lasso', null);
24164
24165             if (!lasso) return;
24166
24167             var ids = lassoed();
24168             lasso.close();
24169
24170             if (ids.length) {
24171                 context.enter(iD.modes.Select(context, ids));
24172             }
24173         }
24174
24175         selection
24176             .on('mousedown.lasso', mousedown);
24177     };
24178
24179     behavior.off = function(selection) {
24180         selection.on('mousedown.lasso', null);
24181     };
24182
24183     return behavior;
24184 };
24185 iD.behavior.Paste = function(context) {
24186     var keybinding = d3.keybinding('paste');
24187
24188     function omitTag(v, k) {
24189         return (
24190             k === 'phone' ||
24191             k === 'fax' ||
24192             k === 'email' ||
24193             k === 'website' ||
24194             k === 'url' ||
24195             k === 'note' ||
24196             k === 'description' ||
24197             k.indexOf('name') !== -1 ||
24198             k.indexOf('wiki') === 0 ||
24199             k.indexOf('addr:') === 0 ||
24200             k.indexOf('contact:') === 0
24201         );
24202     }
24203
24204     function doPaste() {
24205         d3.event.preventDefault();
24206         if (context.inIntro()) return;
24207
24208         var baseGraph = context.graph(),
24209             mouse = context.mouse(),
24210             projection = context.projection,
24211             viewport = iD.geo.Extent(projection.clipExtent()).polygon();
24212
24213         if (!iD.geo.pointInPolygon(mouse, viewport)) return;
24214
24215         var extent = iD.geo.Extent(),
24216             oldIDs = context.copyIDs(),
24217             oldGraph = context.copyGraph(),
24218             newIDs = [];
24219
24220         if (!oldIDs.length) return;
24221
24222         var action = iD.actions.CopyEntities(oldIDs, oldGraph);
24223         context.perform(action);
24224
24225         var copies = action.copies();
24226         for (var id in copies) {
24227             var oldEntity = oldGraph.entity(id),
24228                 newEntity = copies[id];
24229
24230             extent._extend(oldEntity.extent(oldGraph));
24231             newIDs.push(newEntity.id);
24232             context.perform(iD.actions.ChangeTags(newEntity.id, _.omit(newEntity.tags, omitTag)));
24233         }
24234
24235         // Put pasted objects where mouse pointer is..
24236         var center = projection(extent.center()),
24237             delta = [ mouse[0] - center[0], mouse[1] - center[1] ];
24238
24239         context.perform(iD.actions.Move(newIDs, delta, projection));
24240         context.enter(iD.modes.Move(context, newIDs, baseGraph));
24241     }
24242
24243     function paste() {
24244         keybinding.on(iD.ui.cmd('⌘V'), doPaste);
24245         d3.select(document).call(keybinding);
24246         return paste;
24247     }
24248
24249     paste.off = function() {
24250         d3.select(document).call(keybinding.off);
24251     };
24252
24253     return paste;
24254 };
24255 iD.behavior.Select = function(context) {
24256     function keydown() {
24257         if (d3.event && d3.event.shiftKey) {
24258             context.surface()
24259                 .classed('behavior-multiselect', true);
24260         }
24261     }
24262
24263     function keyup() {
24264         if (!d3.event || !d3.event.shiftKey) {
24265             context.surface()
24266                 .classed('behavior-multiselect', false);
24267         }
24268     }
24269
24270     function click() {
24271         var datum = d3.event.target.__data__,
24272             lasso = d3.select('#surface .lasso').node(),
24273             mode = context.mode();
24274
24275         if (!(datum instanceof iD.Entity)) {
24276             if (!d3.event.shiftKey && !lasso && mode.id !== 'browse')
24277                 context.enter(iD.modes.Browse(context));
24278
24279         } else if (!d3.event.shiftKey && !lasso) {
24280             // Avoid re-entering Select mode with same entity.
24281             if (context.selectedIDs().length !== 1 || context.selectedIDs()[0] !== datum.id) {
24282                 context.enter(iD.modes.Select(context, [datum.id]));
24283             } else {
24284                 mode.suppressMenu(false).reselect();
24285             }
24286         } else if (context.selectedIDs().indexOf(datum.id) >= 0) {
24287             var selectedIDs = _.without(context.selectedIDs(), datum.id);
24288             context.enter(selectedIDs.length ?
24289                 iD.modes.Select(context, selectedIDs) :
24290                 iD.modes.Browse(context));
24291
24292         } else {
24293             context.enter(iD.modes.Select(context, context.selectedIDs().concat([datum.id])));
24294         }
24295     }
24296
24297     var behavior = function(selection) {
24298         d3.select(window)
24299             .on('keydown.select', keydown)
24300             .on('keyup.select', keyup);
24301
24302         selection.on('click.select', click);
24303
24304         keydown();
24305     };
24306
24307     behavior.off = function(selection) {
24308         d3.select(window)
24309             .on('keydown.select', null)
24310             .on('keyup.select', null);
24311
24312         selection.on('click.select', null);
24313
24314         keyup();
24315     };
24316
24317     return behavior;
24318 };
24319 iD.behavior.Tail = function() {
24320     var text,
24321         container,
24322         xmargin = 25,
24323         tooltipSize = [0, 0],
24324         selectionSize = [0, 0];
24325
24326     function tail(selection) {
24327         if (!text) return;
24328
24329         d3.select(window)
24330             .on('resize.tail', function() { selectionSize = selection.dimensions(); });
24331
24332         function show() {
24333             container.style('display', 'block');
24334             tooltipSize = container.dimensions();
24335         }
24336
24337         function mousemove() {
24338             if (container.style('display') === 'none') show();
24339             var xoffset = ((d3.event.clientX + tooltipSize[0] + xmargin) > selectionSize[0]) ?
24340                 -tooltipSize[0] - xmargin : xmargin;
24341             container.classed('left', xoffset > 0);
24342             iD.util.setTransform(container, d3.event.clientX + xoffset, d3.event.clientY);
24343         }
24344
24345         function mouseleave() {
24346             if (d3.event.relatedTarget !== container.node()) {
24347                 container.style('display', 'none');
24348             }
24349         }
24350
24351         function mouseenter() {
24352             if (d3.event.relatedTarget !== container.node()) {
24353                 show();
24354             }
24355         }
24356
24357         container = d3.select(document.body)
24358             .append('div')
24359             .style('display', 'none')
24360             .attr('class', 'tail tooltip-inner');
24361
24362         container.append('div')
24363             .text(text);
24364
24365         selection
24366             .on('mousemove.tail', mousemove)
24367             .on('mouseenter.tail', mouseenter)
24368             .on('mouseleave.tail', mouseleave);
24369
24370         container
24371             .on('mousemove.tail', mousemove);
24372
24373         tooltipSize = container.dimensions();
24374         selectionSize = selection.dimensions();
24375     }
24376
24377     tail.off = function(selection) {
24378         if (!text) return;
24379
24380         container
24381             .on('mousemove.tail', null)
24382             .remove();
24383
24384         selection
24385             .on('mousemove.tail', null)
24386             .on('mouseenter.tail', null)
24387             .on('mouseleave.tail', null);
24388
24389         d3.select(window)
24390             .on('resize.tail', null);
24391     };
24392
24393     tail.text = function(_) {
24394         if (!arguments.length) return text;
24395         text = _;
24396         return tail;
24397     };
24398
24399     return tail;
24400 };
24401 iD.modes = {};
24402 iD.modes.AddArea = function(context) {
24403     var mode = {
24404         id: 'add-area',
24405         button: 'area',
24406         title: t('modes.add_area.title'),
24407         description: t('modes.add_area.description'),
24408         key: '3'
24409     };
24410
24411     var behavior = iD.behavior.AddWay(context)
24412             .tail(t('modes.add_area.tail'))
24413             .on('start', start)
24414             .on('startFromWay', startFromWay)
24415             .on('startFromNode', startFromNode),
24416         defaultTags = {area: 'yes'};
24417
24418     function start(loc) {
24419         var graph = context.graph(),
24420             node = iD.Node({loc: loc}),
24421             way = iD.Way({tags: defaultTags});
24422
24423         context.perform(
24424             iD.actions.AddEntity(node),
24425             iD.actions.AddEntity(way),
24426             iD.actions.AddVertex(way.id, node.id),
24427             iD.actions.AddVertex(way.id, node.id));
24428
24429         context.enter(iD.modes.DrawArea(context, way.id, graph));
24430     }
24431
24432     function startFromWay(loc, edge) {
24433         var graph = context.graph(),
24434             node = iD.Node({loc: loc}),
24435             way = iD.Way({tags: defaultTags});
24436
24437         context.perform(
24438             iD.actions.AddEntity(node),
24439             iD.actions.AddEntity(way),
24440             iD.actions.AddVertex(way.id, node.id),
24441             iD.actions.AddVertex(way.id, node.id),
24442             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24443
24444         context.enter(iD.modes.DrawArea(context, way.id, graph));
24445     }
24446
24447     function startFromNode(node) {
24448         var graph = context.graph(),
24449             way = iD.Way({tags: defaultTags});
24450
24451         context.perform(
24452             iD.actions.AddEntity(way),
24453             iD.actions.AddVertex(way.id, node.id),
24454             iD.actions.AddVertex(way.id, node.id));
24455
24456         context.enter(iD.modes.DrawArea(context, way.id, graph));
24457     }
24458
24459     mode.enter = function() {
24460         context.install(behavior);
24461     };
24462
24463     mode.exit = function() {
24464         context.uninstall(behavior);
24465     };
24466
24467     return mode;
24468 };
24469 iD.modes.AddLine = function(context) {
24470     var mode = {
24471         id: 'add-line',
24472         button: 'line',
24473         title: t('modes.add_line.title'),
24474         description: t('modes.add_line.description'),
24475         key: '2'
24476     };
24477
24478     var behavior = iD.behavior.AddWay(context)
24479         .tail(t('modes.add_line.tail'))
24480         .on('start', start)
24481         .on('startFromWay', startFromWay)
24482         .on('startFromNode', startFromNode);
24483
24484     function start(loc) {
24485         var baseGraph = context.graph(),
24486             node = iD.Node({loc: loc}),
24487             way = iD.Way();
24488
24489         context.perform(
24490             iD.actions.AddEntity(node),
24491             iD.actions.AddEntity(way),
24492             iD.actions.AddVertex(way.id, node.id));
24493
24494         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24495     }
24496
24497     function startFromWay(loc, edge) {
24498         var baseGraph = context.graph(),
24499             node = iD.Node({loc: loc}),
24500             way = iD.Way();
24501
24502         context.perform(
24503             iD.actions.AddEntity(node),
24504             iD.actions.AddEntity(way),
24505             iD.actions.AddVertex(way.id, node.id),
24506             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24507
24508         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24509     }
24510
24511     function startFromNode(node) {
24512         var baseGraph = context.graph(),
24513             way = iD.Way();
24514
24515         context.perform(
24516             iD.actions.AddEntity(way),
24517             iD.actions.AddVertex(way.id, node.id));
24518
24519         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24520     }
24521
24522     mode.enter = function() {
24523         context.install(behavior);
24524     };
24525
24526     mode.exit = function() {
24527         context.uninstall(behavior);
24528     };
24529
24530     return mode;
24531 };
24532 iD.modes.AddPoint = function(context) {
24533     var mode = {
24534         id: 'add-point',
24535         button: 'point',
24536         title: t('modes.add_point.title'),
24537         description: t('modes.add_point.description'),
24538         key: '1'
24539     };
24540
24541     var behavior = iD.behavior.Draw(context)
24542         .tail(t('modes.add_point.tail'))
24543         .on('click', add)
24544         .on('clickWay', addWay)
24545         .on('clickNode', addNode)
24546         .on('cancel', cancel)
24547         .on('finish', cancel);
24548
24549     function add(loc) {
24550         var node = iD.Node({loc: loc});
24551
24552         context.perform(
24553             iD.actions.AddEntity(node),
24554             t('operations.add.annotation.point'));
24555
24556         context.enter(
24557             iD.modes.Select(context, [node.id])
24558                 .suppressMenu(true)
24559                 .newFeature(true));
24560     }
24561
24562     function addWay(loc) {
24563         add(loc);
24564     }
24565
24566     function addNode(node) {
24567         add(node.loc);
24568     }
24569
24570     function cancel() {
24571         context.enter(iD.modes.Browse(context));
24572     }
24573
24574     mode.enter = function() {
24575         context.install(behavior);
24576     };
24577
24578     mode.exit = function() {
24579         context.uninstall(behavior);
24580     };
24581
24582     return mode;
24583 };
24584 iD.modes.Browse = function(context) {
24585     var mode = {
24586         button: 'browse',
24587         id: 'browse',
24588         title: t('modes.browse.title'),
24589         description: t('modes.browse.description')
24590     }, sidebar;
24591
24592     var behaviors = [
24593         iD.behavior.Paste(context),
24594         iD.behavior.Hover(context)
24595             .on('hover', context.ui().sidebar.hover),
24596         iD.behavior.Select(context),
24597         iD.behavior.Lasso(context),
24598         iD.modes.DragNode(context).behavior];
24599
24600     mode.enter = function() {
24601         behaviors.forEach(function(behavior) {
24602             context.install(behavior);
24603         });
24604
24605         // Get focus on the body.
24606         if (document.activeElement && document.activeElement.blur) {
24607             document.activeElement.blur();
24608         }
24609
24610         if (sidebar) {
24611             context.ui().sidebar.show(sidebar);
24612         } else {
24613             context.ui().sidebar.select(null);
24614         }
24615     };
24616
24617     mode.exit = function() {
24618         context.ui().sidebar.hover.cancel();
24619         behaviors.forEach(function(behavior) {
24620             context.uninstall(behavior);
24621         });
24622
24623         if (sidebar) {
24624             context.ui().sidebar.hide();
24625         }
24626     };
24627
24628     mode.sidebar = function(_) {
24629         if (!arguments.length) return sidebar;
24630         sidebar = _;
24631         return mode;
24632     };
24633
24634     return mode;
24635 };
24636 iD.modes.DragNode = function(context) {
24637     var mode = {
24638         id: 'drag-node',
24639         button: 'browse'
24640     };
24641
24642     var nudgeInterval,
24643         activeIDs,
24644         wasMidpoint,
24645         cancelled,
24646         selectedIDs = [],
24647         hover = iD.behavior.Hover(context)
24648             .altDisables(true)
24649             .on('hover', context.ui().sidebar.hover),
24650         edit = iD.behavior.Edit(context);
24651
24652     function edge(point, size) {
24653         var pad = [30, 100, 30, 100];
24654         if (point[0] > size[0] - pad[0]) return [-10, 0];
24655         else if (point[0] < pad[2]) return [10, 0];
24656         else if (point[1] > size[1] - pad[1]) return [0, -10];
24657         else if (point[1] < pad[3]) return [0, 10];
24658         return null;
24659     }
24660
24661     function startNudge(nudge) {
24662         if (nudgeInterval) window.clearInterval(nudgeInterval);
24663         nudgeInterval = window.setInterval(function() {
24664             context.pan(nudge);
24665         }, 50);
24666     }
24667
24668     function stopNudge() {
24669         if (nudgeInterval) window.clearInterval(nudgeInterval);
24670         nudgeInterval = null;
24671     }
24672
24673     function moveAnnotation(entity) {
24674         return t('operations.move.annotation.' + entity.geometry(context.graph()));
24675     }
24676
24677     function connectAnnotation(entity) {
24678         return t('operations.connect.annotation.' + entity.geometry(context.graph()));
24679     }
24680
24681     function origin(entity) {
24682         return context.projection(entity.loc);
24683     }
24684
24685     function start(entity) {
24686         cancelled = d3.event.sourceEvent.shiftKey ||
24687             context.features().hasHiddenConnections(entity, context.graph());
24688
24689         if (cancelled) return behavior.cancel();
24690
24691         wasMidpoint = entity.type === 'midpoint';
24692         if (wasMidpoint) {
24693             var midpoint = entity;
24694             entity = iD.Node();
24695             context.perform(iD.actions.AddMidpoint(midpoint, entity));
24696
24697              var vertex = context.surface()
24698                 .selectAll('.' + entity.id);
24699              behavior.target(vertex.node(), entity);
24700
24701         } else {
24702             context.perform(
24703                 iD.actions.Noop());
24704         }
24705
24706         activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
24707         activeIDs.push(entity.id);
24708
24709         context.enter(mode);
24710     }
24711
24712     function datum() {
24713         if (d3.event.sourceEvent.altKey) {
24714             return {};
24715         }
24716
24717         return d3.event.sourceEvent.target.__data__ || {};
24718     }
24719
24720     // via https://gist.github.com/shawnbot/4166283
24721     function childOf(p, c) {
24722         if (p === c) return false;
24723         while (c && c !== p) c = c.parentNode;
24724         return c === p;
24725     }
24726
24727     function move(entity) {
24728         if (cancelled) return;
24729         d3.event.sourceEvent.stopPropagation();
24730
24731         var nudge = childOf(context.container().node(),
24732             d3.event.sourceEvent.toElement) &&
24733             edge(d3.event.point, context.map().dimensions());
24734
24735         if (nudge) startNudge(nudge);
24736         else stopNudge();
24737
24738         var loc = context.projection.invert(d3.event.point);
24739
24740         var d = datum();
24741         if (d.type === 'node' && d.id !== entity.id) {
24742             loc = d.loc;
24743         } else if (d.type === 'way' && !d3.select(d3.event.sourceEvent.target).classed('fill')) {
24744             loc = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection).loc;
24745         }
24746
24747         context.replace(
24748             iD.actions.MoveNode(entity.id, loc),
24749             moveAnnotation(entity));
24750     }
24751
24752     function end(entity) {
24753         if (cancelled) return;
24754
24755         var d = datum();
24756
24757         if (d.type === 'way') {
24758             var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection);
24759             context.replace(
24760                 iD.actions.AddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity),
24761                 connectAnnotation(d));
24762
24763         } else if (d.type === 'node' && d.id !== entity.id) {
24764             context.replace(
24765                 iD.actions.Connect([d.id, entity.id]),
24766                 connectAnnotation(d));
24767
24768         } else if (wasMidpoint) {
24769             context.replace(
24770                 iD.actions.Noop(),
24771                 t('operations.add.annotation.vertex'));
24772
24773         } else {
24774             context.replace(
24775                 iD.actions.Noop(),
24776                 moveAnnotation(entity));
24777         }
24778
24779         var reselection = selectedIDs.filter(function(id) {
24780             return context.graph().hasEntity(id);
24781         });
24782
24783         if (reselection.length) {
24784             context.enter(
24785                 iD.modes.Select(context, reselection)
24786                     .suppressMenu(true));
24787         } else {
24788             context.enter(iD.modes.Browse(context));
24789         }
24790     }
24791
24792     function cancel() {
24793         behavior.cancel();
24794         context.enter(iD.modes.Browse(context));
24795     }
24796
24797     function setActiveElements() {
24798         context.surface().selectAll(iD.util.entitySelector(activeIDs))
24799             .classed('active', true);
24800     }
24801
24802     var behavior = iD.behavior.drag()
24803         .delegate('g.node, g.point, g.midpoint')
24804         .surface(context.surface().node())
24805         .origin(origin)
24806         .on('start', start)
24807         .on('move', move)
24808         .on('end', end);
24809
24810     mode.enter = function() {
24811         context.install(hover);
24812         context.install(edit);
24813
24814         context.history()
24815             .on('undone.drag-node', cancel);
24816
24817         context.map()
24818             .on('drawn.drag-node', setActiveElements);
24819
24820         setActiveElements();
24821     };
24822
24823     mode.exit = function() {
24824         context.ui().sidebar.hover.cancel();
24825         context.uninstall(hover);
24826         context.uninstall(edit);
24827
24828         context.history()
24829             .on('undone.drag-node', null);
24830
24831         context.map()
24832             .on('drawn.drag-node', null);
24833
24834         context.surface()
24835             .selectAll('.active')
24836             .classed('active', false);
24837
24838         stopNudge();
24839     };
24840
24841     mode.selectedIDs = function(_) {
24842         if (!arguments.length) return selectedIDs;
24843         selectedIDs = _;
24844         return mode;
24845     };
24846
24847     mode.behavior = behavior;
24848
24849     return mode;
24850 };
24851 iD.modes.DrawArea = function(context, wayId, baseGraph) {
24852     var mode = {
24853         button: 'area',
24854         id: 'draw-area'
24855     };
24856
24857     var behavior;
24858
24859     mode.enter = function() {
24860         var way = context.entity(wayId),
24861             headId = way.nodes[way.nodes.length - 2],
24862             tailId = way.first();
24863
24864         behavior = iD.behavior.DrawWay(context, wayId, -1, mode, baseGraph)
24865             .tail(t('modes.draw_area.tail'));
24866
24867         var addNode = behavior.addNode;
24868
24869         behavior.addNode = function(node) {
24870             if (node.id === headId || node.id === tailId) {
24871                 behavior.finish();
24872             } else {
24873                 addNode(node);
24874             }
24875         };
24876
24877         context.install(behavior);
24878     };
24879
24880     mode.exit = function() {
24881         context.uninstall(behavior);
24882     };
24883
24884     mode.selectedIDs = function() {
24885         return [wayId];
24886     };
24887
24888     return mode;
24889 };
24890 iD.modes.DrawLine = function(context, wayId, baseGraph, affix) {
24891     var mode = {
24892         button: 'line',
24893         id: 'draw-line'
24894     };
24895
24896     var behavior;
24897
24898     mode.enter = function() {
24899         var way = context.entity(wayId),
24900             index = (affix === 'prefix') ? 0 : undefined,
24901             headId = (affix === 'prefix') ? way.first() : way.last();
24902
24903         behavior = iD.behavior.DrawWay(context, wayId, index, mode, baseGraph)
24904             .tail(t('modes.draw_line.tail'));
24905
24906         var addNode = behavior.addNode;
24907
24908         behavior.addNode = function(node) {
24909             if (node.id === headId) {
24910                 behavior.finish();
24911             } else {
24912                 addNode(node);
24913             }
24914         };
24915
24916         context.install(behavior);
24917     };
24918
24919     mode.exit = function() {
24920         context.uninstall(behavior);
24921     };
24922
24923     mode.selectedIDs = function() {
24924         return [wayId];
24925     };
24926
24927     return mode;
24928 };
24929 iD.modes.Move = function(context, entityIDs, baseGraph) {
24930     var mode = {
24931         id: 'move',
24932         button: 'browse'
24933     };
24934
24935     var keybinding = d3.keybinding('move'),
24936         edit = iD.behavior.Edit(context),
24937         annotation = entityIDs.length === 1 ?
24938             t('operations.move.annotation.' + context.geometry(entityIDs[0])) :
24939             t('operations.move.annotation.multiple'),
24940         cache,
24941         origin,
24942         nudgeInterval;
24943
24944     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
24945
24946     function edge(point, size) {
24947         var pad = [30, 100, 30, 100];
24948         if (point[0] > size[0] - pad[0]) return [-10, 0];
24949         else if (point[0] < pad[2]) return [10, 0];
24950         else if (point[1] > size[1] - pad[1]) return [0, -10];
24951         else if (point[1] < pad[3]) return [0, 10];
24952         return null;
24953     }
24954
24955     function startNudge(nudge) {
24956         if (nudgeInterval) window.clearInterval(nudgeInterval);
24957         nudgeInterval = window.setInterval(function() {
24958             context.pan(nudge);
24959
24960             var currMouse = context.mouse(),
24961                 origMouse = context.projection(origin),
24962                 delta = vecSub(vecSub(currMouse, origMouse), nudge),
24963                 action = iD.actions.Move(entityIDs, delta, context.projection, cache);
24964
24965             context.overwrite(action, annotation);
24966
24967         }, 50);
24968     }
24969
24970     function stopNudge() {
24971         if (nudgeInterval) window.clearInterval(nudgeInterval);
24972         nudgeInterval = null;
24973     }
24974
24975     function move() {
24976         var currMouse = context.mouse(),
24977             origMouse = context.projection(origin),
24978             delta = vecSub(currMouse, origMouse),
24979             action = iD.actions.Move(entityIDs, delta, context.projection, cache);
24980
24981         context.overwrite(action, annotation);
24982
24983         var nudge = edge(currMouse, context.map().dimensions());
24984         if (nudge) startNudge(nudge);
24985         else stopNudge();
24986     }
24987
24988     function finish() {
24989         d3.event.stopPropagation();
24990         context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
24991         stopNudge();
24992     }
24993
24994     function cancel() {
24995         if (baseGraph) {
24996             while (context.graph() !== baseGraph) context.pop();
24997             context.enter(iD.modes.Browse(context));
24998         } else {
24999             context.pop();
25000             context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
25001         }
25002         stopNudge();
25003     }
25004
25005     function undone() {
25006         context.enter(iD.modes.Browse(context));
25007     }
25008
25009     mode.enter = function() {
25010         origin = context.map().mouseCoordinates();
25011         cache = {};
25012
25013         context.install(edit);
25014
25015         context.perform(
25016             iD.actions.Noop(),
25017             annotation);
25018
25019         context.surface()
25020             .on('mousemove.move', move)
25021             .on('click.move', finish);
25022
25023         context.history()
25024             .on('undone.move', undone);
25025
25026         keybinding
25027             .on('⎋', cancel)
25028             .on('↩', finish);
25029
25030         d3.select(document)
25031             .call(keybinding);
25032     };
25033
25034     mode.exit = function() {
25035         stopNudge();
25036
25037         context.uninstall(edit);
25038
25039         context.surface()
25040             .on('mousemove.move', null)
25041             .on('click.move', null);
25042
25043         context.history()
25044             .on('undone.move', null);
25045
25046         keybinding.off();
25047     };
25048
25049     return mode;
25050 };
25051 iD.modes.RotateWay = function(context, wayId) {
25052     var mode = {
25053         id: 'rotate-way',
25054         button: 'browse'
25055     };
25056
25057     var keybinding = d3.keybinding('rotate-way'),
25058         edit = iD.behavior.Edit(context);
25059
25060     mode.enter = function() {
25061         context.install(edit);
25062
25063         var annotation = t('operations.rotate.annotation.' + context.geometry(wayId)),
25064             way = context.graph().entity(wayId),
25065             nodes = _.uniq(context.graph().childNodes(way)),
25066             points = nodes.map(function(n) { return context.projection(n.loc); }),
25067             pivot = d3.geom.polygon(points).centroid(),
25068             angle;
25069
25070         context.perform(
25071             iD.actions.Noop(),
25072             annotation);
25073
25074         function rotate() {
25075
25076             var mousePoint = context.mouse(),
25077                 newAngle = Math.atan2(mousePoint[1] - pivot[1], mousePoint[0] - pivot[0]);
25078
25079             if (typeof angle === 'undefined') angle = newAngle;
25080
25081             context.replace(
25082                 iD.actions.RotateWay(wayId, pivot, newAngle - angle, context.projection),
25083                 annotation);
25084
25085             angle = newAngle;
25086         }
25087
25088         function finish() {
25089             d3.event.stopPropagation();
25090             context.enter(iD.modes.Select(context, [wayId])
25091                 .suppressMenu(true));
25092         }
25093
25094         function cancel() {
25095             context.pop();
25096             context.enter(iD.modes.Select(context, [wayId])
25097                 .suppressMenu(true));
25098         }
25099
25100         function undone() {
25101             context.enter(iD.modes.Browse(context));
25102         }
25103
25104         context.surface()
25105             .on('mousemove.rotate-way', rotate)
25106             .on('click.rotate-way', finish);
25107
25108         context.history()
25109             .on('undone.rotate-way', undone);
25110
25111         keybinding
25112             .on('⎋', cancel)
25113             .on('↩', finish);
25114
25115         d3.select(document)
25116             .call(keybinding);
25117     };
25118
25119     mode.exit = function() {
25120         context.uninstall(edit);
25121
25122         context.surface()
25123             .on('mousemove.rotate-way', null)
25124             .on('click.rotate-way', null);
25125
25126         context.history()
25127             .on('undone.rotate-way', null);
25128
25129         keybinding.off();
25130     };
25131
25132     return mode;
25133 };
25134 iD.modes.Save = function(context) {
25135     var ui = iD.ui.Commit(context)
25136             .on('cancel', cancel)
25137             .on('save', save);
25138
25139     function cancel() {
25140         context.enter(iD.modes.Browse(context));
25141     }
25142
25143     function save(e, tryAgain) {
25144         function withChildNodes(ids, graph) {
25145             return _.uniq(_.reduce(ids, function(result, id) {
25146                 var e = graph.entity(id);
25147                 if (e.type === 'way') {
25148                     try {
25149                         var cn = graph.childNodes(e);
25150                         result.push.apply(result, _.pluck(_.filter(cn, 'version'), 'id'));
25151                     } catch(err) {
25152                         /* eslint-disable no-console */
25153                         if (typeof console !== 'undefined') console.error(err);
25154                         /* eslint-enable no-console */
25155                     }
25156                 }
25157                 return result;
25158             }, _.clone(ids)));
25159         }
25160
25161         var loading = iD.ui.Loading(context).message(t('save.uploading')).blocking(true),
25162             history = context.history(),
25163             origChanges = history.changes(iD.actions.DiscardTags(history.difference())),
25164             localGraph = context.graph(),
25165             remoteGraph = iD.Graph(history.base(), true),
25166             modified = _.filter(history.difference().summary(), {changeType: 'modified'}),
25167             toCheck = _.pluck(_.pluck(modified, 'entity'), 'id'),
25168             toLoad = withChildNodes(toCheck, localGraph),
25169             conflicts = [],
25170             errors = [];
25171
25172         if (!tryAgain) history.perform(iD.actions.Noop());  // checkpoint
25173         context.container().call(loading);
25174
25175         if (toCheck.length) {
25176             context.connection().loadMultiple(toLoad, loaded);
25177         } else {
25178             finalize();
25179         }
25180
25181
25182         // Reload modified entities into an alternate graph and check for conflicts..
25183         function loaded(err, result) {
25184             if (errors.length) return;
25185
25186             if (err) {
25187                 errors.push({
25188                     msg: err.responseText,
25189                     details: [ t('save.status_code', { code: err.status }) ]
25190                 });
25191                 showErrors();
25192
25193             } else {
25194                 var loadMore = [];
25195                 _.each(result.data, function(entity) {
25196                     remoteGraph.replace(entity);
25197                     toLoad = _.without(toLoad, entity.id);
25198
25199                     // Because loadMultiple doesn't download /full like loadEntity,
25200                     // need to also load children that aren't already being checked..
25201                     if (!entity.visible) return;
25202                     if (entity.type === 'way') {
25203                         loadMore.push.apply(loadMore,
25204                             _.difference(entity.nodes, toCheck, toLoad, loadMore));
25205                     } else if (entity.type === 'relation' && entity.isMultipolygon()) {
25206                         loadMore.push.apply(loadMore,
25207                             _.difference(_.pluck(entity.members, 'id'), toCheck, toLoad, loadMore));
25208                     }
25209                 });
25210
25211                 if (loadMore.length) {
25212                     toLoad.push.apply(toLoad, loadMore);
25213                     context.connection().loadMultiple(loadMore, loaded);
25214                 }
25215
25216                 if (!toLoad.length) {
25217                     checkConflicts();
25218                 }
25219             }
25220         }
25221
25222
25223         function checkConflicts() {
25224             function choice(id, text, action) {
25225                 return { id: id, text: text, action: function() { history.replace(action); } };
25226             }
25227             function formatUser(d) {
25228                 return '<a href="' + context.connection().userURL(d) + '" target="_blank">' + d + '</a>';
25229             }
25230             function entityName(entity) {
25231                 return iD.util.displayName(entity) || (iD.util.displayType(entity.id) + ' ' + entity.id);
25232             }
25233
25234             function compareVersions(local, remote) {
25235                 if (local.version !== remote.version) return false;
25236
25237                 if (local.type === 'way') {
25238                     var children = _.union(local.nodes, remote.nodes);
25239
25240                     for (var i = 0; i < children.length; i++) {
25241                         var a = localGraph.hasEntity(children[i]),
25242                             b = remoteGraph.hasEntity(children[i]);
25243
25244                         if (a && b && a.version !== b.version) return false;
25245                     }
25246                 }
25247
25248                 return true;
25249             }
25250
25251             _.each(toCheck, function(id) {
25252                 var local = localGraph.entity(id),
25253                     remote = remoteGraph.entity(id);
25254
25255                 if (compareVersions(local, remote)) return;
25256
25257                 var action = iD.actions.MergeRemoteChanges,
25258                     merge = action(id, localGraph, remoteGraph, formatUser);
25259
25260                 history.replace(merge);
25261
25262                 var mergeConflicts = merge.conflicts();
25263                 if (!mergeConflicts.length) return;  // merged safely
25264
25265                 var forceLocal = action(id, localGraph, remoteGraph).withOption('force_local'),
25266                     forceRemote = action(id, localGraph, remoteGraph).withOption('force_remote'),
25267                     keepMine = t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore')),
25268                     keepTheirs = t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
25269
25270                 conflicts.push({
25271                     id: id,
25272                     name: entityName(local),
25273                     details: mergeConflicts,
25274                     chosen: 1,
25275                     choices: [
25276                         choice(id, keepMine, forceLocal),
25277                         choice(id, keepTheirs, forceRemote)
25278                     ]
25279                 });
25280             });
25281
25282             finalize();
25283         }
25284
25285
25286         function finalize() {
25287             if (conflicts.length) {
25288                 conflicts.sort(function(a,b) { return b.id.localeCompare(a.id); });
25289                 showConflicts();
25290             } else if (errors.length) {
25291                 showErrors();
25292             } else {
25293                 var changes = history.changes(iD.actions.DiscardTags(history.difference()));
25294                 if (changes.modified.length || changes.created.length || changes.deleted.length) {
25295                     context.connection().putChangeset(
25296                         changes,
25297                         e.comment,
25298                         history.imageryUsed(),
25299                         function(err, changeset_id) {
25300                             if (err) {
25301                                 errors.push({
25302                                     msg: err.responseText,
25303                                     details: [ t('save.status_code', { code: err.status }) ]
25304                                 });
25305                                 showErrors();
25306                             } else {
25307                                 history.clearSaved();
25308                                 success(e, changeset_id);
25309                                 // Add delay to allow for postgres replication #1646 #2678
25310                                 window.setTimeout(function() {
25311                                     loading.close();
25312                                     context.flush();
25313                                 }, 2500);
25314                             }
25315                         });
25316                 } else {        // changes were insignificant or reverted by user
25317                     loading.close();
25318                     context.flush();
25319                     cancel();
25320                 }
25321             }
25322         }
25323
25324
25325         function showConflicts() {
25326             var selection = context.container()
25327                 .select('#sidebar')
25328                 .append('div')
25329                 .attr('class','sidebar-component');
25330
25331             loading.close();
25332
25333             selection.call(iD.ui.Conflicts(context)
25334                 .list(conflicts)
25335                 .on('download', function() {
25336                     var data = JXON.stringify(context.connection().osmChangeJXON('CHANGEME', origChanges)),
25337                         win = window.open('data:text/xml,' + encodeURIComponent(data), '_blank');
25338                     win.focus();
25339                 })
25340                 .on('cancel', function() {
25341                     history.pop();
25342                     selection.remove();
25343                 })
25344                 .on('save', function() {
25345                     for (var i = 0; i < conflicts.length; i++) {
25346                         if (conflicts[i].chosen === 1) {  // user chose "keep theirs"
25347                             var entity = context.hasEntity(conflicts[i].id);
25348                             if (entity && entity.type === 'way') {
25349                                 var children = _.uniq(entity.nodes);
25350                                 for (var j = 0; j < children.length; j++) {
25351                                     history.replace(iD.actions.Revert(children[j]));
25352                                 }
25353                             }
25354                             history.replace(iD.actions.Revert(conflicts[i].id));
25355                         }
25356                     }
25357
25358                     selection.remove();
25359                     save(e, true);
25360                 })
25361             );
25362         }
25363
25364
25365         function showErrors() {
25366             var selection = iD.ui.confirm(context.container());
25367
25368             history.pop();
25369             loading.close();
25370
25371             selection
25372                 .select('.modal-section.header')
25373                 .append('h3')
25374                 .text(t('save.error'));
25375
25376             addErrors(selection, errors);
25377             selection.okButton();
25378         }
25379
25380
25381         function addErrors(selection, data) {
25382             var message = selection
25383                 .select('.modal-section.message-text');
25384
25385             var items = message
25386                 .selectAll('.error-container')
25387                 .data(data);
25388
25389             var enter = items.enter()
25390                 .append('div')
25391                 .attr('class', 'error-container');
25392
25393             enter
25394                 .append('a')
25395                 .attr('class', 'error-description')
25396                 .attr('href', '#')
25397                 .classed('hide-toggle', true)
25398                 .text(function(d) { return d.msg || t('save.unknown_error_details'); })
25399                 .on('click', function() {
25400                     var error = d3.select(this),
25401                         detail = d3.select(this.nextElementSibling),
25402                         exp = error.classed('expanded');
25403
25404                     detail.style('display', exp ? 'none' : 'block');
25405                     error.classed('expanded', !exp);
25406
25407                     d3.event.preventDefault();
25408                 });
25409
25410             var details = enter
25411                 .append('div')
25412                 .attr('class', 'error-detail-container')
25413                 .style('display', 'none');
25414
25415             details
25416                 .append('ul')
25417                 .attr('class', 'error-detail-list')
25418                 .selectAll('li')
25419                 .data(function(d) { return d.details || []; })
25420                 .enter()
25421                 .append('li')
25422                 .attr('class', 'error-detail-item')
25423                 .text(function(d) { return d; });
25424
25425             items.exit()
25426                 .remove();
25427         }
25428
25429     }
25430
25431
25432     function success(e, changeset_id) {
25433         context.enter(iD.modes.Browse(context)
25434             .sidebar(iD.ui.Success(context)
25435                 .changeset({
25436                     id: changeset_id,
25437                     comment: e.comment
25438                 })
25439                 .on('cancel', function() {
25440                     context.ui().sidebar.hide();
25441                 })));
25442     }
25443
25444     var mode = {
25445         id: 'save'
25446     };
25447
25448     mode.enter = function() {
25449         context.connection().authenticate(function(err) {
25450             if (err) {
25451                 cancel();
25452             } else {
25453                 context.ui().sidebar.show(ui);
25454             }
25455         });
25456     };
25457
25458     mode.exit = function() {
25459         context.ui().sidebar.hide();
25460     };
25461
25462     return mode;
25463 };
25464 iD.modes.Select = function(context, selectedIDs) {
25465     var mode = {
25466         id: 'select',
25467         button: 'browse'
25468     };
25469
25470     var keybinding = d3.keybinding('select'),
25471         timeout = null,
25472         behaviors = [
25473             iD.behavior.Copy(context),
25474             iD.behavior.Paste(context),
25475             iD.behavior.Breathe(context),
25476             iD.behavior.Hover(context),
25477             iD.behavior.Select(context),
25478             iD.behavior.Lasso(context),
25479             iD.modes.DragNode(context)
25480                 .selectedIDs(selectedIDs)
25481                 .behavior],
25482         inspector,
25483         radialMenu,
25484         newFeature = false,
25485         suppressMenu = false;
25486
25487     var wrap = context.container()
25488         .select('.inspector-wrap');
25489
25490
25491     function singular() {
25492         if (selectedIDs.length === 1) {
25493             return context.hasEntity(selectedIDs[0]);
25494         }
25495     }
25496
25497     function closeMenu() {
25498         if (radialMenu) {
25499             context.surface().call(radialMenu.close);
25500         }
25501     }
25502
25503     function positionMenu() {
25504         if (suppressMenu || !radialMenu) { return; }
25505
25506         var entity = singular();
25507         if (entity && context.geometry(entity.id) === 'relation') {
25508             suppressMenu = true;
25509         } else if (entity && entity.type === 'node') {
25510             radialMenu.center(context.projection(entity.loc));
25511         } else {
25512             var point = context.mouse(),
25513                 viewport = iD.geo.Extent(context.projection.clipExtent()).polygon();
25514             if (iD.geo.pointInPolygon(point, viewport)) {
25515                 radialMenu.center(point);
25516             } else {
25517                 suppressMenu = true;
25518             }
25519         }
25520     }
25521
25522     function showMenu() {
25523         closeMenu();
25524         if (!suppressMenu && radialMenu) {
25525             context.surface().call(radialMenu);
25526         }
25527     }
25528
25529     function toggleMenu() {
25530         if (d3.select('.radial-menu').empty()) {
25531             showMenu();
25532         } else {
25533             closeMenu();
25534         }
25535     }
25536
25537     mode.selectedIDs = function() {
25538         return selectedIDs;
25539     };
25540
25541     mode.reselect = function() {
25542         var surfaceNode = context.surface().node();
25543         if (surfaceNode.focus) { // FF doesn't support it
25544             surfaceNode.focus();
25545         }
25546
25547         positionMenu();
25548         showMenu();
25549     };
25550
25551     mode.newFeature = function(_) {
25552         if (!arguments.length) return newFeature;
25553         newFeature = _;
25554         return mode;
25555     };
25556
25557     mode.suppressMenu = function(_) {
25558         if (!arguments.length) return suppressMenu;
25559         suppressMenu = _;
25560         return mode;
25561     };
25562
25563     mode.enter = function() {
25564         function update() {
25565             closeMenu();
25566             if (_.any(selectedIDs, function(id) { return !context.hasEntity(id); })) {
25567                 // Exit mode if selected entity gets undone
25568                 context.enter(iD.modes.Browse(context));
25569             }
25570         }
25571
25572         function dblclick() {
25573             var target = d3.select(d3.event.target),
25574                 datum = target.datum();
25575
25576             if (datum instanceof iD.Way && !target.classed('fill')) {
25577                 var choice = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection),
25578                     node = iD.Node();
25579
25580                 var prev = datum.nodes[choice.index - 1],
25581                     next = datum.nodes[choice.index];
25582
25583                 context.perform(
25584                     iD.actions.AddMidpoint({loc: choice.loc, edge: [prev, next]}, node),
25585                     t('operations.add.annotation.vertex'));
25586
25587                 d3.event.preventDefault();
25588                 d3.event.stopPropagation();
25589             }
25590         }
25591
25592         function selectElements(drawn) {
25593             var entity = singular();
25594             if (entity && context.geometry(entity.id) === 'relation') {
25595                 suppressMenu = true;
25596                 return;
25597             }
25598
25599             var selection = context.surface()
25600                     .selectAll(iD.util.entityOrMemberSelector(selectedIDs, context.graph()));
25601
25602             if (selection.empty()) {
25603                 if (drawn) {  // Exit mode if selected DOM elements have disappeared..
25604                     context.enter(iD.modes.Browse(context));
25605                 }
25606             } else {
25607                 selection
25608                     .classed('selected', true);
25609             }
25610         }
25611
25612         function esc() {
25613             if (!context.inIntro()) {
25614                 context.enter(iD.modes.Browse(context));
25615             }
25616         }
25617
25618
25619         behaviors.forEach(function(behavior) {
25620             context.install(behavior);
25621         });
25622
25623         var operations = _.without(d3.values(iD.operations), iD.operations.Delete)
25624                 .map(function(o) { return o(selectedIDs, context); })
25625                 .filter(function(o) { return o.available(); });
25626
25627         operations.unshift(iD.operations.Delete(selectedIDs, context));
25628
25629         keybinding
25630             .on('⎋', esc, true)
25631             .on('space', toggleMenu);
25632
25633         operations.forEach(function(operation) {
25634             operation.keys.forEach(function(key) {
25635                 keybinding.on(key, function() {
25636                     if (!(context.inIntro() || operation.disabled())) {
25637                         operation();
25638                     }
25639                 });
25640             });
25641         });
25642
25643         d3.select(document)
25644             .call(keybinding);
25645
25646         radialMenu = iD.ui.RadialMenu(context, operations);
25647
25648         context.ui().sidebar
25649             .select(singular() ? singular().id : null, newFeature);
25650
25651         context.history()
25652             .on('undone.select', update)
25653             .on('redone.select', update);
25654
25655         context.map()
25656             .on('move.select', closeMenu)
25657             .on('drawn.select', selectElements);
25658
25659         selectElements();
25660
25661         var show = d3.event && !suppressMenu;
25662
25663         if (show) {
25664             positionMenu();
25665         }
25666
25667         timeout = window.setTimeout(function() {
25668             if (show) {
25669                 showMenu();
25670             }
25671
25672             context.surface()
25673                 .on('dblclick.select', dblclick);
25674         }, 200);
25675
25676         if (selectedIDs.length > 1) {
25677             var entities = iD.ui.SelectionList(context, selectedIDs);
25678             context.ui().sidebar.show(entities);
25679         }
25680     };
25681
25682     mode.exit = function() {
25683         if (timeout) window.clearTimeout(timeout);
25684
25685         if (inspector) wrap.call(inspector.close);
25686
25687         behaviors.forEach(function(behavior) {
25688             context.uninstall(behavior);
25689         });
25690
25691         keybinding.off();
25692         closeMenu();
25693         radialMenu = undefined;
25694
25695         context.history()
25696             .on('undone.select', null)
25697             .on('redone.select', null);
25698
25699         context.surface()
25700             .on('dblclick.select', null)
25701             .selectAll('.selected')
25702             .classed('selected', false);
25703
25704         context.map().on('drawn.select', null);
25705         context.ui().sidebar.hide();
25706     };
25707
25708     return mode;
25709 };
25710 iD.operations = {};
25711 iD.operations.Circularize = function(selectedIDs, context) {
25712     var entityId = selectedIDs[0],
25713         entity = context.entity(entityId),
25714         extent = entity.extent(context.graph()),
25715         geometry = context.geometry(entityId),
25716         action = iD.actions.Circularize(entityId, context.projection);
25717
25718     var operation = function() {
25719         var annotation = t('operations.circularize.annotation.' + geometry);
25720         context.perform(action, annotation);
25721     };
25722
25723     operation.available = function() {
25724         return selectedIDs.length === 1 &&
25725             entity.type === 'way' &&
25726             _.uniq(entity.nodes).length > 1;
25727     };
25728
25729     operation.disabled = function() {
25730         var reason;
25731         if (extent.percentContainedIn(context.extent()) < 0.8) {
25732             reason = 'too_large';
25733         } else if (context.hasHiddenConnections(entityId)) {
25734             reason = 'connected_to_hidden';
25735         }
25736         return action.disabled(context.graph()) || reason;
25737     };
25738
25739     operation.tooltip = function() {
25740         var disable = operation.disabled();
25741         return disable ?
25742             t('operations.circularize.' + disable) :
25743             t('operations.circularize.description.' + geometry);
25744     };
25745
25746     operation.id = 'circularize';
25747     operation.keys = [t('operations.circularize.key')];
25748     operation.title = t('operations.circularize.title');
25749
25750     return operation;
25751 };
25752 iD.operations.Continue = function(selectedIDs, context) {
25753     var graph = context.graph(),
25754         entities = selectedIDs.map(function(id) { return graph.entity(id); }),
25755         geometries = _.extend({line: [], vertex: []},
25756             _.groupBy(entities, function(entity) { return entity.geometry(graph); })),
25757         vertex = geometries.vertex[0];
25758
25759     function candidateWays() {
25760         return graph.parentWays(vertex).filter(function(parent) {
25761             return parent.geometry(graph) === 'line' &&
25762                 parent.affix(vertex.id) &&
25763                 (geometries.line.length === 0 || geometries.line[0] === parent);
25764         });
25765     }
25766
25767     var operation = function() {
25768         var candidate = candidateWays()[0];
25769         context.enter(iD.modes.DrawLine(
25770             context,
25771             candidate.id,
25772             context.graph(),
25773             candidate.affix(vertex.id)));
25774     };
25775
25776     operation.available = function() {
25777         return geometries.vertex.length === 1 && geometries.line.length <= 1 &&
25778             !context.features().hasHiddenConnections(vertex, context.graph());
25779     };
25780
25781     operation.disabled = function() {
25782         var candidates = candidateWays();
25783         if (candidates.length === 0)
25784             return 'not_eligible';
25785         if (candidates.length > 1)
25786             return 'multiple';
25787     };
25788
25789     operation.tooltip = function() {
25790         var disable = operation.disabled();
25791         return disable ?
25792             t('operations.continue.' + disable) :
25793             t('operations.continue.description');
25794     };
25795
25796     operation.id = 'continue';
25797     operation.keys = [t('operations.continue.key')];
25798     operation.title = t('operations.continue.title');
25799
25800     return operation;
25801 };
25802 iD.operations.Delete = function(selectedIDs, context) {
25803     var action = iD.actions.DeleteMultiple(selectedIDs);
25804
25805     var operation = function() {
25806         var annotation,
25807             nextSelectedID;
25808
25809         if (selectedIDs.length > 1) {
25810             annotation = t('operations.delete.annotation.multiple', {n: selectedIDs.length});
25811
25812         } else {
25813             var id = selectedIDs[0],
25814                 entity = context.entity(id),
25815                 geometry = context.geometry(id),
25816                 parents = context.graph().parentWays(entity),
25817                 parent = parents[0];
25818
25819             annotation = t('operations.delete.annotation.' + geometry);
25820
25821             // Select the next closest node in the way.
25822             if (geometry === 'vertex' && parents.length === 1 && parent.nodes.length > 2) {
25823                 var nodes = parent.nodes,
25824                     i = nodes.indexOf(id);
25825
25826                 if (i === 0) {
25827                     i++;
25828                 } else if (i === nodes.length - 1) {
25829                     i--;
25830                 } else {
25831                     var a = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc),
25832                         b = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
25833                     i = a < b ? i - 1 : i + 1;
25834                 }
25835
25836                 nextSelectedID = nodes[i];
25837             }
25838         }
25839
25840         if (nextSelectedID && context.hasEntity(nextSelectedID)) {
25841             context.enter(iD.modes.Select(context, [nextSelectedID]));
25842         } else {
25843             context.enter(iD.modes.Browse(context));
25844         }
25845
25846         context.perform(
25847             action,
25848             annotation);
25849     };
25850
25851     operation.available = function() {
25852         return true;
25853     };
25854
25855     operation.disabled = function() {
25856         var reason;
25857         if (_.any(selectedIDs, context.hasHiddenConnections)) {
25858             reason = 'connected_to_hidden';
25859         }
25860         return action.disabled(context.graph()) || reason;
25861     };
25862
25863     operation.tooltip = function() {
25864         var disable = operation.disabled();
25865         return disable ?
25866             t('operations.delete.' + disable) :
25867             t('operations.delete.description');
25868     };
25869
25870     operation.id = 'delete';
25871     operation.keys = [iD.ui.cmd('⌘⌫'), iD.ui.cmd('⌘⌦')];
25872     operation.title = t('operations.delete.title');
25873
25874     return operation;
25875 };
25876 iD.operations.Disconnect = function(selectedIDs, context) {
25877     var vertices = _.filter(selectedIDs, function vertex(entityId) {
25878         return context.geometry(entityId) === 'vertex';
25879     });
25880
25881     var entityId = vertices[0],
25882         action = iD.actions.Disconnect(entityId);
25883
25884     if (selectedIDs.length > 1) {
25885         action.limitWays(_.without(selectedIDs, entityId));
25886     }
25887
25888     var operation = function() {
25889         context.perform(action, t('operations.disconnect.annotation'));
25890     };
25891
25892     operation.available = function() {
25893         return vertices.length === 1;
25894     };
25895
25896     operation.disabled = function() {
25897         var reason;
25898         if (_.any(selectedIDs, context.hasHiddenConnections)) {
25899             reason = 'connected_to_hidden';
25900         }
25901         return action.disabled(context.graph()) || reason;
25902     };
25903
25904     operation.tooltip = function() {
25905         var disable = operation.disabled();
25906         return disable ?
25907             t('operations.disconnect.' + disable) :
25908             t('operations.disconnect.description');
25909     };
25910
25911     operation.id = 'disconnect';
25912     operation.keys = [t('operations.disconnect.key')];
25913     operation.title = t('operations.disconnect.title');
25914
25915     return operation;
25916 };
25917 iD.operations.Merge = function(selectedIDs, context) {
25918     var join = iD.actions.Join(selectedIDs),
25919         merge = iD.actions.Merge(selectedIDs),
25920         mergePolygon = iD.actions.MergePolygon(selectedIDs);
25921
25922     var operation = function() {
25923         var annotation = t('operations.merge.annotation', {n: selectedIDs.length}),
25924             action;
25925
25926         if (!join.disabled(context.graph())) {
25927             action = join;
25928         } else if (!merge.disabled(context.graph())) {
25929             action = merge;
25930         } else {
25931             action = mergePolygon;
25932         }
25933
25934         context.perform(action, annotation);
25935         context.enter(iD.modes.Select(context, selectedIDs.filter(function(id) { return context.hasEntity(id); }))
25936             .suppressMenu(true));
25937     };
25938
25939     operation.available = function() {
25940         return selectedIDs.length >= 2;
25941     };
25942
25943     operation.disabled = function() {
25944         return join.disabled(context.graph()) &&
25945             merge.disabled(context.graph()) &&
25946             mergePolygon.disabled(context.graph());
25947     };
25948
25949     operation.tooltip = function() {
25950         var j = join.disabled(context.graph()),
25951             m = merge.disabled(context.graph()),
25952             p = mergePolygon.disabled(context.graph());
25953
25954         if (j === 'restriction' && m && p)
25955             return t('operations.merge.restriction', {relation: context.presets().item('type/restriction').name()});
25956
25957         if (p === 'incomplete_relation' && j && m)
25958             return t('operations.merge.incomplete_relation');
25959
25960         if (j && m && p)
25961             return t('operations.merge.' + j);
25962
25963         return t('operations.merge.description');
25964     };
25965
25966     operation.id = 'merge';
25967     operation.keys = [t('operations.merge.key')];
25968     operation.title = t('operations.merge.title');
25969
25970     return operation;
25971 };
25972 iD.operations.Move = function(selectedIDs, context) {
25973     var extent = selectedIDs.reduce(function(extent, id) {
25974             return extent.extend(context.entity(id).extent(context.graph()));
25975         }, iD.geo.Extent());
25976
25977     var operation = function() {
25978         context.enter(iD.modes.Move(context, selectedIDs));
25979     };
25980
25981     operation.available = function() {
25982         return selectedIDs.length > 1 ||
25983             context.entity(selectedIDs[0]).type !== 'node';
25984     };
25985
25986     operation.disabled = function() {
25987         var reason;
25988         if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
25989             reason = 'too_large';
25990         } else if (_.any(selectedIDs, context.hasHiddenConnections)) {
25991             reason = 'connected_to_hidden';
25992         }
25993         return iD.actions.Move(selectedIDs).disabled(context.graph()) || reason;
25994     };
25995
25996     operation.tooltip = function() {
25997         var disable = operation.disabled();
25998         return disable ?
25999             t('operations.move.' + disable) :
26000             t('operations.move.description');
26001     };
26002
26003     operation.id = 'move';
26004     operation.keys = [t('operations.move.key')];
26005     operation.title = t('operations.move.title');
26006
26007     return operation;
26008 };
26009 iD.operations.Orthogonalize = function(selectedIDs, context) {
26010     var entityId = selectedIDs[0],
26011         entity = context.entity(entityId),
26012         extent = entity.extent(context.graph()),
26013         geometry = context.geometry(entityId),
26014         action = iD.actions.Orthogonalize(entityId, context.projection);
26015
26016     var operation = function() {
26017         var annotation = t('operations.orthogonalize.annotation.' + geometry);
26018         context.perform(action, annotation);
26019     };
26020
26021     operation.available = function() {
26022         return selectedIDs.length === 1 &&
26023             entity.type === 'way' &&
26024             entity.isClosed() &&
26025             _.uniq(entity.nodes).length > 2;
26026     };
26027
26028     operation.disabled = function() {
26029         var reason;
26030         if (extent.percentContainedIn(context.extent()) < 0.8) {
26031             reason = 'too_large';
26032         } else if (context.hasHiddenConnections(entityId)) {
26033             reason = 'connected_to_hidden';
26034         }
26035         return action.disabled(context.graph()) || reason;
26036     };
26037
26038     operation.tooltip = function() {
26039         var disable = operation.disabled();
26040         return disable ?
26041             t('operations.orthogonalize.' + disable) :
26042             t('operations.orthogonalize.description.' + geometry);
26043     };
26044
26045     operation.id = 'orthogonalize';
26046     operation.keys = [t('operations.orthogonalize.key')];
26047     operation.title = t('operations.orthogonalize.title');
26048
26049     return operation;
26050 };
26051 iD.operations.Reverse = function(selectedIDs, context) {
26052     var entityId = selectedIDs[0];
26053
26054     var operation = function() {
26055         context.perform(
26056             iD.actions.Reverse(entityId),
26057             t('operations.reverse.annotation'));
26058     };
26059
26060     operation.available = function() {
26061         return selectedIDs.length === 1 &&
26062             context.geometry(entityId) === 'line';
26063     };
26064
26065     operation.disabled = function() {
26066         return false;
26067     };
26068
26069     operation.tooltip = function() {
26070         return t('operations.reverse.description');
26071     };
26072
26073     operation.id = 'reverse';
26074     operation.keys = [t('operations.reverse.key')];
26075     operation.title = t('operations.reverse.title');
26076
26077     return operation;
26078 };
26079 iD.operations.Rotate = function(selectedIDs, context) {
26080     var entityId = selectedIDs[0],
26081         entity = context.entity(entityId),
26082         extent = entity.extent(context.graph()),
26083         geometry = context.geometry(entityId);
26084
26085     var operation = function() {
26086         context.enter(iD.modes.RotateWay(context, entityId));
26087     };
26088
26089     operation.available = function() {
26090         if (selectedIDs.length !== 1 || entity.type !== 'way')
26091             return false;
26092         if (geometry === 'area')
26093             return true;
26094         if (entity.isClosed() &&
26095             context.graph().parentRelations(entity).some(function(r) { return r.isMultipolygon(); }))
26096             return true;
26097         return false;
26098     };
26099
26100     operation.disabled = function() {
26101         if (extent.percentContainedIn(context.extent()) < 0.8) {
26102             return 'too_large';
26103         } else if (context.hasHiddenConnections(entityId)) {
26104             return 'connected_to_hidden';
26105         } else {
26106             return false;
26107         }
26108     };
26109
26110     operation.tooltip = function() {
26111         var disable = operation.disabled();
26112         return disable ?
26113             t('operations.rotate.' + disable) :
26114             t('operations.rotate.description');
26115     };
26116
26117     operation.id = 'rotate';
26118     operation.keys = [t('operations.rotate.key')];
26119     operation.title = t('operations.rotate.title');
26120
26121     return operation;
26122 };
26123 iD.operations.Split = function(selectedIDs, context) {
26124     var vertices = _.filter(selectedIDs, function vertex(entityId) {
26125         return context.geometry(entityId) === 'vertex';
26126     });
26127
26128     var entityId = vertices[0],
26129         action = iD.actions.Split(entityId);
26130
26131     if (selectedIDs.length > 1) {
26132         action.limitWays(_.without(selectedIDs, entityId));
26133     }
26134
26135     var operation = function() {
26136         var annotation;
26137
26138         var ways = action.ways(context.graph());
26139         if (ways.length === 1) {
26140             annotation = t('operations.split.annotation.' + context.geometry(ways[0].id));
26141         } else {
26142             annotation = t('operations.split.annotation.multiple', {n: ways.length});
26143         }
26144
26145         var difference = context.perform(action, annotation);
26146         context.enter(iD.modes.Select(context, difference.extantIDs()));
26147     };
26148
26149     operation.available = function() {
26150         return vertices.length === 1;
26151     };
26152
26153     operation.disabled = function() {
26154         var reason;
26155         if (_.any(selectedIDs, context.hasHiddenConnections)) {
26156             reason = 'connected_to_hidden';
26157         }
26158         return action.disabled(context.graph()) || reason;
26159     };
26160
26161     operation.tooltip = function() {
26162         var disable = operation.disabled();
26163         if (disable) {
26164             return t('operations.split.' + disable);
26165         }
26166
26167         var ways = action.ways(context.graph());
26168         if (ways.length === 1) {
26169             return t('operations.split.description.' + context.geometry(ways[0].id));
26170         } else {
26171             return t('operations.split.description.multiple');
26172         }
26173     };
26174
26175     operation.id = 'split';
26176     operation.keys = [t('operations.split.key')];
26177     operation.title = t('operations.split.title');
26178
26179     return operation;
26180 };
26181 iD.operations.Straighten = function(selectedIDs, context) {
26182     var entityId = selectedIDs[0],
26183         action = iD.actions.Straighten(entityId, context.projection);
26184
26185     function operation() {
26186         var annotation = t('operations.straighten.annotation');
26187         context.perform(action, annotation);
26188     }
26189
26190     operation.available = function() {
26191         var entity = context.entity(entityId);
26192         return selectedIDs.length === 1 &&
26193             entity.type === 'way' &&
26194             !entity.isClosed() &&
26195             _.uniq(entity.nodes).length > 2;
26196     };
26197
26198     operation.disabled = function() {
26199         var reason;
26200         if (context.hasHiddenConnections(entityId)) {
26201             reason = 'connected_to_hidden';
26202         }
26203         return action.disabled(context.graph()) || reason;
26204     };
26205
26206     operation.tooltip = function() {
26207         var disable = operation.disabled();
26208         return disable ?
26209             t('operations.straighten.' + disable) :
26210             t('operations.straighten.description');
26211     };
26212
26213     operation.id = 'straighten';
26214     operation.keys = [t('operations.straighten.key')];
26215     operation.title = t('operations.straighten.title');
26216
26217     return operation;
26218 };
26219 iD.Connection = function(useHttps) {
26220     if (typeof useHttps !== 'boolean') {
26221       useHttps = window.location.protocol === 'https:';
26222     }
26223
26224     var event = d3.dispatch('authenticating', 'authenticated', 'auth', 'loading', 'loaded'),
26225         protocol = useHttps ? 'https:' : 'http:',
26226         url = protocol + '//www.openstreetmap.org',
26227         connection = {},
26228         inflight = {},
26229         loadedTiles = {},
26230         tileZoom = 16,
26231         oauth = osmAuth({
26232             url: protocol + '//www.openstreetmap.org',
26233             oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
26234             oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
26235             loading: authenticating,
26236             done: authenticated
26237         }),
26238         ndStr = 'nd',
26239         tagStr = 'tag',
26240         memberStr = 'member',
26241         nodeStr = 'node',
26242         wayStr = 'way',
26243         relationStr = 'relation',
26244         userDetails,
26245         off;
26246
26247
26248     connection.changesetURL = function(changesetId) {
26249         return url + '/changeset/' + changesetId;
26250     };
26251
26252     connection.changesetsURL = function(center, zoom) {
26253         var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
26254         return url + '/history#map=' +
26255             Math.floor(zoom) + '/' +
26256             center[1].toFixed(precision) + '/' +
26257             center[0].toFixed(precision);
26258     };
26259
26260     connection.entityURL = function(entity) {
26261         return url + '/' + entity.type + '/' + entity.osmId();
26262     };
26263
26264     connection.userURL = function(username) {
26265         return url + '/user/' + username;
26266     };
26267
26268     connection.loadFromURL = function(url, callback) {
26269         function done(err, dom) {
26270             return callback(err, parse(dom));
26271         }
26272         return d3.xml(url).get(done);
26273     };
26274
26275     connection.loadEntity = function(id, callback) {
26276         var type = iD.Entity.id.type(id),
26277             osmID = iD.Entity.id.toOSM(id);
26278
26279         connection.loadFromURL(
26280             url + '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''),
26281             function(err, entities) {
26282                 if (callback) callback(err, {data: entities});
26283             });
26284     };
26285
26286     connection.loadEntityVersion = function(id, version, callback) {
26287         var type = iD.Entity.id.type(id),
26288             osmID = iD.Entity.id.toOSM(id);
26289
26290         connection.loadFromURL(
26291             url + '/api/0.6/' + type + '/' + osmID + '/' + version,
26292             function(err, entities) {
26293                 if (callback) callback(err, {data: entities});
26294             });
26295     };
26296
26297     connection.loadMultiple = function(ids, callback) {
26298         _.each(_.groupBy(_.uniq(ids), iD.Entity.id.type), function(v, k) {
26299             var type = k + 's',
26300                 osmIDs = _.map(v, iD.Entity.id.toOSM);
26301
26302             _.each(_.chunk(osmIDs, 150), function(arr) {
26303                 connection.loadFromURL(
26304                     url + '/api/0.6/' + type + '?' + type + '=' + arr.join(),
26305                     function(err, entities) {
26306                         if (callback) callback(err, {data: entities});
26307                     });
26308             });
26309         });
26310     };
26311
26312     function authenticating() {
26313         event.authenticating();
26314     }
26315
26316     function authenticated() {
26317         event.authenticated();
26318     }
26319
26320     function getLoc(attrs) {
26321         var lon = attrs.lon && attrs.lon.value,
26322             lat = attrs.lat && attrs.lat.value;
26323         return [parseFloat(lon), parseFloat(lat)];
26324     }
26325
26326     function getNodes(obj) {
26327         var elems = obj.getElementsByTagName(ndStr),
26328             nodes = new Array(elems.length);
26329         for (var i = 0, l = elems.length; i < l; i++) {
26330             nodes[i] = 'n' + elems[i].attributes.ref.value;
26331         }
26332         return nodes;
26333     }
26334
26335     function getTags(obj) {
26336         var elems = obj.getElementsByTagName(tagStr),
26337             tags = {};
26338         for (var i = 0, l = elems.length; i < l; i++) {
26339             var attrs = elems[i].attributes;
26340             tags[attrs.k.value] = attrs.v.value;
26341         }
26342         return tags;
26343     }
26344
26345     function getMembers(obj) {
26346         var elems = obj.getElementsByTagName(memberStr),
26347             members = new Array(elems.length);
26348         for (var i = 0, l = elems.length; i < l; i++) {
26349             var attrs = elems[i].attributes;
26350             members[i] = {
26351                 id: attrs.type.value[0] + attrs.ref.value,
26352                 type: attrs.type.value,
26353                 role: attrs.role.value
26354             };
26355         }
26356         return members;
26357     }
26358
26359     function getVisible(attrs) {
26360         return (!attrs.visible || attrs.visible.value !== 'false');
26361     }
26362
26363     var parsers = {
26364         node: function nodeData(obj) {
26365             var attrs = obj.attributes;
26366             return new iD.Node({
26367                 id: iD.Entity.id.fromOSM(nodeStr, attrs.id.value),
26368                 loc: getLoc(attrs),
26369                 version: attrs.version.value,
26370                 user: attrs.user && attrs.user.value,
26371                 tags: getTags(obj),
26372                 visible: getVisible(attrs)
26373             });
26374         },
26375
26376         way: function wayData(obj) {
26377             var attrs = obj.attributes;
26378             return new iD.Way({
26379                 id: iD.Entity.id.fromOSM(wayStr, attrs.id.value),
26380                 version: attrs.version.value,
26381                 user: attrs.user && attrs.user.value,
26382                 tags: getTags(obj),
26383                 nodes: getNodes(obj),
26384                 visible: getVisible(attrs)
26385             });
26386         },
26387
26388         relation: function relationData(obj) {
26389             var attrs = obj.attributes;
26390             return new iD.Relation({
26391                 id: iD.Entity.id.fromOSM(relationStr, attrs.id.value),
26392                 version: attrs.version.value,
26393                 user: attrs.user && attrs.user.value,
26394                 tags: getTags(obj),
26395                 members: getMembers(obj),
26396                 visible: getVisible(attrs)
26397             });
26398         }
26399     };
26400
26401     function parse(dom) {
26402         if (!dom || !dom.childNodes) return;
26403
26404         var root = dom.childNodes[0],
26405             children = root.childNodes,
26406             entities = [];
26407
26408         for (var i = 0, l = children.length; i < l; i++) {
26409             var child = children[i],
26410                 parser = parsers[child.nodeName];
26411             if (parser) {
26412                 entities.push(parser(child));
26413             }
26414         }
26415
26416         return entities;
26417     }
26418
26419     connection.authenticated = function() {
26420         return oauth.authenticated();
26421     };
26422
26423     // Generate Changeset XML. Returns a string.
26424     connection.changesetJXON = function(tags) {
26425         return {
26426             osm: {
26427                 changeset: {
26428                     tag: _.map(tags, function(value, key) {
26429                         return { '@k': key, '@v': value };
26430                     }),
26431                     '@version': 0.6,
26432                     '@generator': 'iD'
26433                 }
26434             }
26435         };
26436     };
26437
26438     // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
26439     // XML. Returns a string.
26440     connection.osmChangeJXON = function(changeset_id, changes) {
26441         function nest(x, order) {
26442             var groups = {};
26443             for (var i = 0; i < x.length; i++) {
26444                 var tagName = Object.keys(x[i])[0];
26445                 if (!groups[tagName]) groups[tagName] = [];
26446                 groups[tagName].push(x[i][tagName]);
26447             }
26448             var ordered = {};
26449             order.forEach(function(o) {
26450                 if (groups[o]) ordered[o] = groups[o];
26451             });
26452             return ordered;
26453         }
26454
26455         function rep(entity) {
26456             return entity.asJXON(changeset_id);
26457         }
26458
26459         return {
26460             osmChange: {
26461                 '@version': 0.6,
26462                 '@generator': 'iD',
26463                 'create': nest(changes.created.map(rep), ['node', 'way', 'relation']),
26464                 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
26465                 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true})
26466             }
26467         };
26468     };
26469
26470     connection.changesetTags = function(comment, imageryUsed) {
26471         var detected = iD.detect(),
26472             tags = {
26473                 created_by: 'iD ' + iD.version,
26474                 imagery_used: imageryUsed.join(';').substr(0, 255),
26475                 host: (window.location.origin + window.location.pathname).substr(0, 255),
26476                 locale: detected.locale
26477             };
26478
26479         if (comment) {
26480             tags.comment = comment.substr(0, 255);
26481         }
26482
26483         return tags;
26484     };
26485
26486     connection.putChangeset = function(changes, comment, imageryUsed, callback) {
26487         oauth.xhr({
26488                 method: 'PUT',
26489                 path: '/api/0.6/changeset/create',
26490                 options: { header: { 'Content-Type': 'text/xml' } },
26491                 content: JXON.stringify(connection.changesetJXON(connection.changesetTags(comment, imageryUsed)))
26492             }, function(err, changeset_id) {
26493                 if (err) return callback(err);
26494                 oauth.xhr({
26495                     method: 'POST',
26496                     path: '/api/0.6/changeset/' + changeset_id + '/upload',
26497                     options: { header: { 'Content-Type': 'text/xml' } },
26498                     content: JXON.stringify(connection.osmChangeJXON(changeset_id, changes))
26499                 }, function(err) {
26500                     if (err) return callback(err);
26501                     // POST was successful, safe to call the callback.
26502                     // Still attempt to close changeset, but ignore response because #2667
26503                     // Add delay to allow for postgres replication #1646 #2678
26504                     window.setTimeout(function() { callback(null, changeset_id); }, 2500);
26505                     oauth.xhr({
26506                         method: 'PUT',
26507                         path: '/api/0.6/changeset/' + changeset_id + '/close',
26508                         options: { header: { 'Content-Type': 'text/xml' } }
26509                     }, d3.functor(true));
26510                 });
26511             });
26512     };
26513
26514     connection.userDetails = function(callback) {
26515         if (userDetails) {
26516             callback(undefined, userDetails);
26517             return;
26518         }
26519
26520         function done(err, user_details) {
26521             if (err) return callback(err);
26522
26523             var u = user_details.getElementsByTagName('user')[0],
26524                 img = u.getElementsByTagName('img'),
26525                 image_url = '';
26526
26527             if (img && img[0] && img[0].getAttribute('href')) {
26528                 image_url = img[0].getAttribute('href');
26529             }
26530
26531             userDetails = {
26532                 display_name: u.attributes.display_name.value,
26533                 image_url: image_url,
26534                 id: u.attributes.id.value
26535             };
26536
26537             callback(undefined, userDetails);
26538         }
26539
26540         oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
26541     };
26542
26543     connection.userChangesets = function(callback) {
26544         connection.userDetails(function(err, user) {
26545             if (err) return callback(err);
26546
26547             function done(changesets) {
26548                 callback(undefined, Array.prototype.map.call(changesets.getElementsByTagName('changeset'),
26549                     function (changeset) {
26550                         return { tags: getTags(changeset) };
26551                     }));
26552             }
26553
26554             d3.xml(url + '/api/0.6/changesets?user=' + user.id).get()
26555                 .on('load', done)
26556                 .on('error', callback);
26557         });
26558     };
26559
26560     connection.status = function(callback) {
26561         function done(capabilities) {
26562             var apiStatus = capabilities.getElementsByTagName('status');
26563             callback(undefined, apiStatus[0].getAttribute('api'));
26564         }
26565         d3.xml(url + '/api/capabilities').get()
26566             .on('load', done)
26567             .on('error', callback);
26568     };
26569
26570     function abortRequest(i) { i.abort(); }
26571
26572     connection.tileZoom = function(_) {
26573         if (!arguments.length) return tileZoom;
26574         tileZoom = _;
26575         return connection;
26576     };
26577
26578     connection.loadTiles = function(projection, dimensions, callback) {
26579
26580         if (off) return;
26581
26582         var s = projection.scale() * 2 * Math.PI,
26583             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
26584             ts = 256 * Math.pow(2, z - tileZoom),
26585             origin = [
26586                 s / 2 - projection.translate()[0],
26587                 s / 2 - projection.translate()[1]];
26588
26589         var tiles = d3.geo.tile()
26590             .scaleExtent([tileZoom, tileZoom])
26591             .scale(s)
26592             .size(dimensions)
26593             .translate(projection.translate())()
26594             .map(function(tile) {
26595                 var x = tile[0] * ts - origin[0],
26596                     y = tile[1] * ts - origin[1];
26597
26598                 return {
26599                     id: tile.toString(),
26600                     extent: iD.geo.Extent(
26601                         projection.invert([x, y + ts]),
26602                         projection.invert([x + ts, y]))
26603                 };
26604             });
26605
26606         function bboxUrl(tile) {
26607             return url + '/api/0.6/map?bbox=' + tile.extent.toParam();
26608         }
26609
26610         _.filter(inflight, function(v, i) {
26611             var wanted = _.find(tiles, function(tile) {
26612                 return i === tile.id;
26613             });
26614             if (!wanted) delete inflight[i];
26615             return !wanted;
26616         }).map(abortRequest);
26617
26618         tiles.forEach(function(tile) {
26619             var id = tile.id;
26620
26621             if (loadedTiles[id] || inflight[id]) return;
26622
26623             if (_.isEmpty(inflight)) {
26624                 event.loading();
26625             }
26626
26627             inflight[id] = connection.loadFromURL(bboxUrl(tile), function(err, parsed) {
26628                 loadedTiles[id] = true;
26629                 delete inflight[id];
26630
26631                 if (callback) callback(err, _.extend({data: parsed}, tile));
26632
26633                 if (_.isEmpty(inflight)) {
26634                     event.loaded();
26635                 }
26636             });
26637         });
26638     };
26639
26640     connection.switch = function(options) {
26641         url = options.url;
26642         oauth.options(_.extend({
26643             loading: authenticating,
26644             done: authenticated
26645         }, options));
26646         event.auth();
26647         connection.flush();
26648         return connection;
26649     };
26650
26651     connection.toggle = function(_) {
26652         off = !_;
26653         return connection;
26654     };
26655
26656     connection.flush = function() {
26657         userDetails = undefined;
26658         _.forEach(inflight, abortRequest);
26659         loadedTiles = {};
26660         inflight = {};
26661         return connection;
26662     };
26663
26664     connection.loadedTiles = function(_) {
26665         if (!arguments.length) return loadedTiles;
26666         loadedTiles = _;
26667         return connection;
26668     };
26669
26670     connection.logout = function() {
26671         userDetails = undefined;
26672         oauth.logout();
26673         event.auth();
26674         return connection;
26675     };
26676
26677     connection.authenticate = function(callback) {
26678         userDetails = undefined;
26679         function done(err, res) {
26680             event.auth();
26681             if (callback) callback(err, res);
26682         }
26683         return oauth.authenticate(done);
26684     };
26685
26686     return d3.rebind(connection, event, 'on');
26687 };
26688 /*
26689     iD.Difference represents the difference between two graphs.
26690     It knows how to calculate the set of entities that were
26691     created, modified, or deleted, and also contains the logic
26692     for recursively extending a difference to the complete set
26693     of entities that will require a redraw, taking into account
26694     child and parent relationships.
26695  */
26696 iD.Difference = function(base, head) {
26697     var changes = {}, length = 0;
26698
26699     function changed(h, b) {
26700         return h !== b && !_.isEqual(_.omit(h, 'v'), _.omit(b, 'v'));
26701     }
26702
26703     _.each(head.entities, function(h, id) {
26704         var b = base.entities[id];
26705         if (changed(h, b)) {
26706             changes[id] = {base: b, head: h};
26707             length++;
26708         }
26709     });
26710
26711     _.each(base.entities, function(b, id) {
26712         var h = head.entities[id];
26713         if (!changes[id] && changed(h, b)) {
26714             changes[id] = {base: b, head: h};
26715             length++;
26716         }
26717     });
26718
26719     function addParents(parents, result) {
26720         for (var i = 0; i < parents.length; i++) {
26721             var parent = parents[i];
26722
26723             if (parent.id in result)
26724                 continue;
26725
26726             result[parent.id] = parent;
26727             addParents(head.parentRelations(parent), result);
26728         }
26729     }
26730
26731     var difference = {};
26732
26733     difference.length = function() {
26734         return length;
26735     };
26736
26737     difference.changes = function() {
26738         return changes;
26739     };
26740
26741     difference.extantIDs = function() {
26742         var result = [];
26743         _.each(changes, function(change, id) {
26744             if (change.head) result.push(id);
26745         });
26746         return result;
26747     };
26748
26749     difference.modified = function() {
26750         var result = [];
26751         _.each(changes, function(change) {
26752             if (change.base && change.head) result.push(change.head);
26753         });
26754         return result;
26755     };
26756
26757     difference.created = function() {
26758         var result = [];
26759         _.each(changes, function(change) {
26760             if (!change.base && change.head) result.push(change.head);
26761         });
26762         return result;
26763     };
26764
26765     difference.deleted = function() {
26766         var result = [];
26767         _.each(changes, function(change) {
26768             if (change.base && !change.head) result.push(change.base);
26769         });
26770         return result;
26771     };
26772
26773     difference.summary = function() {
26774         var relevant = {};
26775
26776         function addEntity(entity, graph, changeType) {
26777             relevant[entity.id] = {
26778                 entity: entity,
26779                 graph: graph,
26780                 changeType: changeType
26781             };
26782         }
26783
26784         function addParents(entity) {
26785             var parents = head.parentWays(entity);
26786             for (var j = parents.length - 1; j >= 0; j--) {
26787                 var parent = parents[j];
26788                 if (!(parent.id in relevant)) addEntity(parent, head, 'modified');
26789             }
26790         }
26791
26792         _.each(changes, function(change) {
26793             if (change.head && change.head.geometry(head) !== 'vertex') {
26794                 addEntity(change.head, head, change.base ? 'modified' : 'created');
26795
26796             } else if (change.base && change.base.geometry(base) !== 'vertex') {
26797                 addEntity(change.base, base, 'deleted');
26798
26799             } else if (change.base && change.head) { // modified vertex
26800                 var moved    = !_.isEqual(change.base.loc,  change.head.loc),
26801                     retagged = !_.isEqual(change.base.tags, change.head.tags);
26802
26803                 if (moved) {
26804                     addParents(change.head);
26805                 }
26806
26807                 if (retagged || (moved && change.head.hasInterestingTags())) {
26808                     addEntity(change.head, head, 'modified');
26809                 }
26810
26811             } else if (change.head && change.head.hasInterestingTags()) { // created vertex
26812                 addEntity(change.head, head, 'created');
26813
26814             } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
26815                 addEntity(change.base, base, 'deleted');
26816             }
26817         });
26818
26819         return d3.values(relevant);
26820     };
26821
26822     difference.complete = function(extent) {
26823         var result = {}, id, change;
26824
26825         for (id in changes) {
26826             change = changes[id];
26827
26828             var h = change.head,
26829                 b = change.base,
26830                 entity = h || b;
26831
26832             if (extent &&
26833                 (!h || !h.intersects(extent, head)) &&
26834                 (!b || !b.intersects(extent, base)))
26835                 continue;
26836
26837             result[id] = h;
26838
26839             if (entity.type === 'way') {
26840                 var nh = h ? h.nodes : [],
26841                     nb = b ? b.nodes : [],
26842                     diff, i;
26843
26844                 diff = _.difference(nh, nb);
26845                 for (i = 0; i < diff.length; i++) {
26846                     result[diff[i]] = head.hasEntity(diff[i]);
26847                 }
26848
26849                 diff = _.difference(nb, nh);
26850                 for (i = 0; i < diff.length; i++) {
26851                     result[diff[i]] = head.hasEntity(diff[i]);
26852                 }
26853             }
26854
26855             addParents(head.parentWays(entity), result);
26856             addParents(head.parentRelations(entity), result);
26857         }
26858
26859         return result;
26860     };
26861
26862     return difference;
26863 };
26864 iD.Entity = function(attrs) {
26865     // For prototypal inheritance.
26866     if (this instanceof iD.Entity) return;
26867
26868     // Create the appropriate subtype.
26869     if (attrs && attrs.type) {
26870         return iD.Entity[attrs.type].apply(this, arguments);
26871     } else if (attrs && attrs.id) {
26872         return iD.Entity[iD.Entity.id.type(attrs.id)].apply(this, arguments);
26873     }
26874
26875     // Initialize a generic Entity (used only in tests).
26876     return (new iD.Entity()).initialize(arguments);
26877 };
26878
26879 iD.Entity.id = function(type) {
26880     return iD.Entity.id.fromOSM(type, iD.Entity.id.next[type]--);
26881 };
26882
26883 iD.Entity.id.next = {node: -1, way: -1, relation: -1};
26884
26885 iD.Entity.id.fromOSM = function(type, id) {
26886     return type[0] + id;
26887 };
26888
26889 iD.Entity.id.toOSM = function(id) {
26890     return id.slice(1);
26891 };
26892
26893 iD.Entity.id.type = function(id) {
26894     return {'n': 'node', 'w': 'way', 'r': 'relation'}[id[0]];
26895 };
26896
26897 // A function suitable for use as the second argument to d3.selection#data().
26898 iD.Entity.key = function(entity) {
26899     return entity.id + 'v' + (entity.v || 0);
26900 };
26901
26902 iD.Entity.prototype = {
26903     tags: {},
26904
26905     initialize: function(sources) {
26906         for (var i = 0; i < sources.length; ++i) {
26907             var source = sources[i];
26908             for (var prop in source) {
26909                 if (Object.prototype.hasOwnProperty.call(source, prop)) {
26910                     if (source[prop] === undefined) {
26911                         delete this[prop];
26912                     } else {
26913                         this[prop] = source[prop];
26914                     }
26915                 }
26916             }
26917         }
26918
26919         if (!this.id && this.type) {
26920             this.id = iD.Entity.id(this.type);
26921         }
26922         if (!this.hasOwnProperty('visible')) {
26923             this.visible = true;
26924         }
26925
26926         if (iD.debug) {
26927             Object.freeze(this);
26928             Object.freeze(this.tags);
26929
26930             if (this.loc) Object.freeze(this.loc);
26931             if (this.nodes) Object.freeze(this.nodes);
26932             if (this.members) Object.freeze(this.members);
26933         }
26934
26935         return this;
26936     },
26937
26938     copy: function(resolver, copies) {
26939         if (copies[this.id])
26940             return copies[this.id];
26941
26942         var copy = iD.Entity(this, {id: undefined, user: undefined, version: undefined});
26943         copies[this.id] = copy;
26944
26945         return copy;
26946     },
26947
26948     osmId: function() {
26949         return iD.Entity.id.toOSM(this.id);
26950     },
26951
26952     isNew: function() {
26953         return this.osmId() < 0;
26954     },
26955
26956     update: function(attrs) {
26957         return iD.Entity(this, attrs, {v: 1 + (this.v || 0)});
26958     },
26959
26960     mergeTags: function(tags) {
26961         var merged = _.clone(this.tags), changed = false;
26962         for (var k in tags) {
26963             var t1 = merged[k],
26964                 t2 = tags[k];
26965             if (!t1) {
26966                 changed = true;
26967                 merged[k] = t2;
26968             } else if (t1 !== t2) {
26969                 changed = true;
26970                 merged[k] = _.union(t1.split(/;\s*/), t2.split(/;\s*/)).join(';');
26971             }
26972         }
26973         return changed ? this.update({tags: merged}) : this;
26974     },
26975
26976     intersects: function(extent, resolver) {
26977         return this.extent(resolver).intersects(extent);
26978     },
26979
26980     isUsed: function(resolver) {
26981         return _.without(Object.keys(this.tags), 'area').length > 0 ||
26982             resolver.parentRelations(this).length > 0;
26983     },
26984
26985     hasInterestingTags: function() {
26986         return _.keys(this.tags).some(iD.interestingTag);
26987     },
26988
26989     isHighwayIntersection: function() {
26990         return false;
26991     },
26992
26993     deprecatedTags: function() {
26994         var tags = _.pairs(this.tags);
26995         var deprecated = {};
26996
26997         iD.data.deprecated.forEach(function(d) {
26998             var match = _.pairs(d.old)[0];
26999             tags.forEach(function(t) {
27000                 if (t[0] === match[0] &&
27001                     (t[1] === match[1] || match[1] === '*')) {
27002                     deprecated[t[0]] = t[1];
27003                 }
27004             });
27005         });
27006
27007         return deprecated;
27008     }
27009 };
27010 iD.Graph = function(other, mutable) {
27011     if (!(this instanceof iD.Graph)) return new iD.Graph(other, mutable);
27012
27013     if (other instanceof iD.Graph) {
27014         var base = other.base();
27015         this.entities = _.assign(Object.create(base.entities), other.entities);
27016         this._parentWays = _.assign(Object.create(base.parentWays), other._parentWays);
27017         this._parentRels = _.assign(Object.create(base.parentRels), other._parentRels);
27018
27019     } else {
27020         this.entities = Object.create({});
27021         this._parentWays = Object.create({});
27022         this._parentRels = Object.create({});
27023         this.rebase(other || [], [this]);
27024     }
27025
27026     this.transients = {};
27027     this._childNodes = {};
27028     this.frozen = !mutable;
27029 };
27030
27031 iD.Graph.prototype = {
27032     hasEntity: function(id) {
27033         return this.entities[id];
27034     },
27035
27036     entity: function(id) {
27037         var entity = this.entities[id];
27038         if (!entity) {
27039             throw new Error('entity ' + id + ' not found');
27040         }
27041         return entity;
27042     },
27043
27044     transient: function(entity, key, fn) {
27045         var id = entity.id,
27046             transients = this.transients[id] ||
27047             (this.transients[id] = {});
27048
27049         if (transients[key] !== undefined) {
27050             return transients[key];
27051         }
27052
27053         transients[key] = fn.call(entity);
27054
27055         return transients[key];
27056     },
27057
27058     parentWays: function(entity) {
27059         var parents = this._parentWays[entity.id],
27060             result = [];
27061
27062         if (parents) {
27063             for (var i = 0; i < parents.length; i++) {
27064                 result.push(this.entity(parents[i]));
27065             }
27066         }
27067         return result;
27068     },
27069
27070     isPoi: function(entity) {
27071         var parentWays = this._parentWays[entity.id];
27072         return !parentWays || parentWays.length === 0;
27073     },
27074
27075     isShared: function(entity) {
27076         var parentWays = this._parentWays[entity.id];
27077         return parentWays && parentWays.length > 1;
27078     },
27079
27080     parentRelations: function(entity) {
27081         var parents = this._parentRels[entity.id],
27082             result = [];
27083
27084         if (parents) {
27085             for (var i = 0; i < parents.length; i++) {
27086                 result.push(this.entity(parents[i]));
27087             }
27088         }
27089         return result;
27090     },
27091
27092     childNodes: function(entity) {
27093         if (this._childNodes[entity.id]) return this._childNodes[entity.id];
27094         if (!entity.nodes) return [];
27095
27096         var nodes = [];
27097         for (var i = 0; i < entity.nodes.length; i++) {
27098             nodes[i] = this.entity(entity.nodes[i]);
27099         }
27100
27101         if (iD.debug) Object.freeze(nodes);
27102
27103         this._childNodes[entity.id] = nodes;
27104         return this._childNodes[entity.id];
27105     },
27106
27107     base: function() {
27108         return {
27109             'entities': iD.util.getPrototypeOf(this.entities),
27110             'parentWays': iD.util.getPrototypeOf(this._parentWays),
27111             'parentRels': iD.util.getPrototypeOf(this._parentRels)
27112         };
27113     },
27114
27115     // Unlike other graph methods, rebase mutates in place. This is because it
27116     // is used only during the history operation that merges newly downloaded
27117     // data into each state. To external consumers, it should appear as if the
27118     // graph always contained the newly downloaded data.
27119     rebase: function(entities, stack, force) {
27120         var base = this.base(),
27121             i, j, k, id;
27122
27123         for (i = 0; i < entities.length; i++) {
27124             var entity = entities[i];
27125
27126             if (!entity.visible || (!force && base.entities[entity.id]))
27127                 continue;
27128
27129             // Merging data into the base graph
27130             base.entities[entity.id] = entity;
27131             this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
27132
27133             // Restore provisionally-deleted nodes that are discovered to have an extant parent
27134             if (entity.type === 'way') {
27135                 for (j = 0; j < entity.nodes.length; j++) {
27136                     id = entity.nodes[j];
27137                     for (k = 1; k < stack.length; k++) {
27138                         var ents = stack[k].entities;
27139                         if (ents.hasOwnProperty(id) && ents[id] === undefined) {
27140                             delete ents[id];
27141                         }
27142                     }
27143                 }
27144             }
27145         }
27146
27147         for (i = 0; i < stack.length; i++) {
27148             stack[i]._updateRebased();
27149         }
27150     },
27151
27152     _updateRebased: function() {
27153         var base = this.base(),
27154             i, k, child, id, keys;
27155
27156         keys = Object.keys(this._parentWays);
27157         for (i = 0; i < keys.length; i++) {
27158             child = keys[i];
27159             if (base.parentWays[child]) {
27160                 for (k = 0; k < base.parentWays[child].length; k++) {
27161                     id = base.parentWays[child][k];
27162                     if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentWays[child], id)) {
27163                         this._parentWays[child].push(id);
27164                     }
27165                 }
27166             }
27167         }
27168
27169         keys = Object.keys(this._parentRels);
27170         for (i = 0; i < keys.length; i++) {
27171             child = keys[i];
27172             if (base.parentRels[child]) {
27173                 for (k = 0; k < base.parentRels[child].length; k++) {
27174                     id = base.parentRels[child][k];
27175                     if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentRels[child], id)) {
27176                         this._parentRels[child].push(id);
27177                     }
27178                 }
27179             }
27180         }
27181
27182         this.transients = {};
27183
27184         // this._childNodes is not updated, under the assumption that
27185         // ways are always downloaded with their child nodes.
27186     },
27187
27188     // Updates calculated properties (parentWays, parentRels) for the specified change
27189     _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
27190
27191         parentWays = parentWays || this._parentWays;
27192         parentRels = parentRels || this._parentRels;
27193
27194         var type = entity && entity.type || oldentity && oldentity.type,
27195             removed, added, ways, rels, i;
27196
27197
27198         if (type === 'way') {
27199
27200             // Update parentWays
27201             if (oldentity && entity) {
27202                 removed = _.difference(oldentity.nodes, entity.nodes);
27203                 added = _.difference(entity.nodes, oldentity.nodes);
27204             } else if (oldentity) {
27205                 removed = oldentity.nodes;
27206                 added = [];
27207             } else if (entity) {
27208                 removed = [];
27209                 added = entity.nodes;
27210             }
27211             for (i = 0; i < removed.length; i++) {
27212                 parentWays[removed[i]] = _.without(parentWays[removed[i]], oldentity.id);
27213             }
27214             for (i = 0; i < added.length; i++) {
27215                 ways = _.without(parentWays[added[i]], entity.id);
27216                 ways.push(entity.id);
27217                 parentWays[added[i]] = ways;
27218             }
27219
27220         } else if (type === 'relation') {
27221
27222             // Update parentRels
27223             if (oldentity && entity) {
27224                 removed = _.difference(oldentity.members, entity.members);
27225                 added = _.difference(entity.members, oldentity);
27226             } else if (oldentity) {
27227                 removed = oldentity.members;
27228                 added = [];
27229             } else if (entity) {
27230                 removed = [];
27231                 added = entity.members;
27232             }
27233             for (i = 0; i < removed.length; i++) {
27234                 parentRels[removed[i].id] = _.without(parentRels[removed[i].id], oldentity.id);
27235             }
27236             for (i = 0; i < added.length; i++) {
27237                 rels = _.without(parentRels[added[i].id], entity.id);
27238                 rels.push(entity.id);
27239                 parentRels[added[i].id] = rels;
27240             }
27241         }
27242     },
27243
27244     replace: function(entity) {
27245         if (this.entities[entity.id] === entity)
27246             return this;
27247
27248         return this.update(function() {
27249             this._updateCalculated(this.entities[entity.id], entity);
27250             this.entities[entity.id] = entity;
27251         });
27252     },
27253
27254     remove: function(entity) {
27255         return this.update(function() {
27256             this._updateCalculated(entity, undefined);
27257             this.entities[entity.id] = undefined;
27258         });
27259     },
27260
27261     revert: function(id) {
27262         var baseEntity = this.base().entities[id],
27263             headEntity = this.entities[id];
27264
27265         if (headEntity === baseEntity)
27266             return this;
27267
27268         return this.update(function() {
27269             this._updateCalculated(headEntity, baseEntity);
27270             delete this.entities[id];
27271         });
27272     },
27273
27274     update: function() {
27275         var graph = this.frozen ? iD.Graph(this, true) : this;
27276
27277         for (var i = 0; i < arguments.length; i++) {
27278             arguments[i].call(graph, graph);
27279         }
27280
27281         if (this.frozen) graph.frozen = true;
27282
27283         return graph;
27284     },
27285
27286     // Obliterates any existing entities
27287     load: function(entities) {
27288         var base = this.base();
27289         this.entities = Object.create(base.entities);
27290
27291         for (var i in entities) {
27292             this.entities[i] = entities[i];
27293             this._updateCalculated(base.entities[i], this.entities[i]);
27294         }
27295
27296         return this;
27297     }
27298 };
27299 iD.History = function(context) {
27300     var stack, index, tree,
27301         imageryUsed = ['Bing'],
27302         dispatch = d3.dispatch('change', 'undone', 'redone'),
27303         lock = iD.util.SessionMutex('lock');
27304
27305     function perform(actions) {
27306         actions = Array.prototype.slice.call(actions);
27307
27308         var annotation;
27309
27310         if (!_.isFunction(_.last(actions))) {
27311             annotation = actions.pop();
27312         }
27313
27314         var graph = stack[index].graph;
27315         for (var i = 0; i < actions.length; i++) {
27316             graph = actions[i](graph);
27317         }
27318
27319         return {
27320             graph: graph,
27321             annotation: annotation,
27322             imageryUsed: imageryUsed
27323         };
27324     }
27325
27326     function change(previous) {
27327         var difference = iD.Difference(previous, history.graph());
27328         dispatch.change(difference);
27329         return difference;
27330     }
27331
27332     // iD uses namespaced keys so multiple installations do not conflict
27333     function getKey(n) {
27334         return 'iD_' + window.location.origin + '_' + n;
27335     }
27336
27337     var history = {
27338         graph: function() {
27339             return stack[index].graph;
27340         },
27341
27342         base: function() {
27343             return stack[0].graph;
27344         },
27345
27346         merge: function(entities, extent) {
27347             stack[0].graph.rebase(entities, _.pluck(stack, 'graph'), false);
27348             tree.rebase(entities, false);
27349
27350             dispatch.change(undefined, extent);
27351         },
27352
27353         perform: function() {
27354             var previous = stack[index].graph;
27355
27356             stack = stack.slice(0, index + 1);
27357             stack.push(perform(arguments));
27358             index++;
27359
27360             return change(previous);
27361         },
27362
27363         replace: function() {
27364             var previous = stack[index].graph;
27365
27366             // assert(index == stack.length - 1)
27367             stack[index] = perform(arguments);
27368
27369             return change(previous);
27370         },
27371
27372         pop: function() {
27373             var previous = stack[index].graph;
27374
27375             if (index > 0) {
27376                 index--;
27377                 stack.pop();
27378                 return change(previous);
27379             }
27380         },
27381
27382         // Same as calling pop and then perform
27383         overwrite: function() {
27384             var previous = stack[index].graph;
27385
27386             if (index > 0) {
27387                 index--;
27388                 stack.pop();
27389             }
27390             stack = stack.slice(0, index + 1);
27391             stack.push(perform(arguments));
27392             index++;
27393
27394             return change(previous);
27395         },
27396
27397         undo: function() {
27398             var previous = stack[index].graph;
27399
27400             // Pop to the next annotated state.
27401             while (index > 0) {
27402                 index--;
27403                 if (stack[index].annotation) break;
27404             }
27405
27406             dispatch.undone();
27407             return change(previous);
27408         },
27409
27410         redo: function() {
27411             var previous = stack[index].graph;
27412
27413             while (index < stack.length - 1) {
27414                 index++;
27415                 if (stack[index].annotation) break;
27416             }
27417
27418             dispatch.redone();
27419             return change(previous);
27420         },
27421
27422         undoAnnotation: function() {
27423             var i = index;
27424             while (i >= 0) {
27425                 if (stack[i].annotation) return stack[i].annotation;
27426                 i--;
27427             }
27428         },
27429
27430         redoAnnotation: function() {
27431             var i = index + 1;
27432             while (i <= stack.length - 1) {
27433                 if (stack[i].annotation) return stack[i].annotation;
27434                 i++;
27435             }
27436         },
27437
27438         intersects: function(extent) {
27439             return tree.intersects(extent, stack[index].graph);
27440         },
27441
27442         difference: function() {
27443             var base = stack[0].graph,
27444                 head = stack[index].graph;
27445             return iD.Difference(base, head);
27446         },
27447
27448         changes: function(action) {
27449             var base = stack[0].graph,
27450                 head = stack[index].graph;
27451
27452             if (action) {
27453                 head = action(head);
27454             }
27455
27456             var difference = iD.Difference(base, head);
27457
27458             return {
27459                 modified: difference.modified(),
27460                 created: difference.created(),
27461                 deleted: difference.deleted()
27462             };
27463         },
27464
27465         validate: function(changes) {
27466             return _(iD.validations)
27467                 .map(function(fn) { return fn()(changes, stack[index].graph); })
27468                 .flatten()
27469                 .value();
27470         },
27471
27472         hasChanges: function() {
27473             return this.difference().length() > 0;
27474         },
27475
27476         imageryUsed: function(sources) {
27477             if (sources) {
27478                 imageryUsed = sources;
27479                 return history;
27480             } else {
27481                 return _(stack.slice(1, index + 1))
27482                     .pluck('imageryUsed')
27483                     .flatten()
27484                     .unique()
27485                     .without(undefined, 'Custom')
27486                     .value();
27487             }
27488         },
27489
27490         reset: function() {
27491             stack = [{graph: iD.Graph()}];
27492             index = 0;
27493             tree = iD.Tree(stack[0].graph);
27494             dispatch.change();
27495             return history;
27496         },
27497
27498         toJSON: function() {
27499             if (!this.hasChanges()) return;
27500
27501             var allEntities = {},
27502                 baseEntities = {},
27503                 base = stack[0];
27504
27505             var s = stack.map(function(i) {
27506                 var modified = [], deleted = [];
27507
27508                 _.forEach(i.graph.entities, function(entity, id) {
27509                     if (entity) {
27510                         var key = iD.Entity.key(entity);
27511                         allEntities[key] = entity;
27512                         modified.push(key);
27513                     } else {
27514                         deleted.push(id);
27515                     }
27516
27517                     // make sure that the originals of changed or deleted entities get merged
27518                     // into the base of the stack after restoring the data from JSON.
27519                     if (id in base.graph.entities) {
27520                         baseEntities[id] = base.graph.entities[id];
27521                     }
27522                     // get originals of parent entities too
27523                     _.forEach(base.graph._parentWays[id], function(parentId) {
27524                         if (parentId in base.graph.entities) {
27525                             baseEntities[parentId] = base.graph.entities[parentId];
27526                         }
27527                     });
27528                 });
27529
27530                 var x = {};
27531
27532                 if (modified.length) x.modified = modified;
27533                 if (deleted.length) x.deleted = deleted;
27534                 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
27535                 if (i.annotation) x.annotation = i.annotation;
27536
27537                 return x;
27538             });
27539
27540             return JSON.stringify({
27541                 version: 3,
27542                 entities: _.values(allEntities),
27543                 baseEntities: _.values(baseEntities),
27544                 stack: s,
27545                 nextIDs: iD.Entity.id.next,
27546                 index: index
27547             });
27548         },
27549
27550         fromJSON: function(json, loadChildNodes) {
27551             var h = JSON.parse(json),
27552                 loadComplete = true;
27553
27554             iD.Entity.id.next = h.nextIDs;
27555             index = h.index;
27556
27557             if (h.version === 2 || h.version === 3) {
27558                 var allEntities = {};
27559
27560                 h.entities.forEach(function(entity) {
27561                     allEntities[iD.Entity.key(entity)] = iD.Entity(entity);
27562                 });
27563
27564                 if (h.version === 3) {
27565                     // This merges originals for changed entities into the base of
27566                     // the stack even if the current stack doesn't have them (for
27567                     // example when iD has been restarted in a different region)
27568                     var baseEntities = h.baseEntities.map(function(d) { return iD.Entity(d); });
27569                     stack[0].graph.rebase(baseEntities, _.pluck(stack, 'graph'), true);
27570                     tree.rebase(baseEntities, true);
27571
27572                     // When we restore a modified way, we also need to fetch any missing
27573                     // childnodes that would normally have been downloaded with it.. #2142
27574                     if (loadChildNodes) {
27575                         var missing =  _(baseEntities)
27576                                 .filter('type', 'way')
27577                                 .pluck('nodes')
27578                                 .flatten()
27579                                 .uniq()
27580                                 .reject(function(n) { return stack[0].graph.hasEntity(n); })
27581                                 .value();
27582
27583                         if (!_.isEmpty(missing)) {
27584                             loadComplete = false;
27585                             context.redrawEnable(false);
27586
27587                             var loading = iD.ui.Loading(context).blocking(true);
27588                             context.container().call(loading);
27589
27590                             var childNodesLoaded = function(err, result) {
27591                                 if (!err) {
27592                                     var visible = _.groupBy(result.data, 'visible');
27593                                     if (!_.isEmpty(visible.true)) {
27594                                         missing = _.difference(missing, _.pluck(visible.true, 'id'));
27595                                         stack[0].graph.rebase(visible.true, _.pluck(stack, 'graph'), true);
27596                                         tree.rebase(visible.true, true);
27597                                     }
27598
27599                                     // fetch older versions of nodes that were deleted..
27600                                     _.each(visible.false, function(entity) {
27601                                         context.connection()
27602                                             .loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
27603                                     });
27604                                 }
27605
27606                                 if (err || _.isEmpty(missing)) {
27607                                     loading.close();
27608                                     context.redrawEnable(true);
27609                                     dispatch.change();
27610                                 }
27611                             };
27612
27613                             context.connection().loadMultiple(missing, childNodesLoaded);
27614                         }
27615                     }
27616                 }
27617
27618                 stack = h.stack.map(function(d) {
27619                     var entities = {}, entity;
27620
27621                     if (d.modified) {
27622                         d.modified.forEach(function(key) {
27623                             entity = allEntities[key];
27624                             entities[entity.id] = entity;
27625                         });
27626                     }
27627
27628                     if (d.deleted) {
27629                         d.deleted.forEach(function(id) {
27630                             entities[id] = undefined;
27631                         });
27632                     }
27633
27634                     return {
27635                         graph: iD.Graph(stack[0].graph).load(entities),
27636                         annotation: d.annotation,
27637                         imageryUsed: d.imageryUsed
27638                     };
27639                 });
27640
27641             } else { // original version
27642                 stack = h.stack.map(function(d) {
27643                     var entities = {};
27644
27645                     for (var i in d.entities) {
27646                         var entity = d.entities[i];
27647                         entities[i] = entity === 'undefined' ? undefined : iD.Entity(entity);
27648                     }
27649
27650                     d.graph = iD.Graph(stack[0].graph).load(entities);
27651                     return d;
27652                 });
27653             }
27654
27655             if (loadComplete) {
27656                 dispatch.change();
27657             }
27658
27659             return history;
27660         },
27661
27662         save: function() {
27663             if (lock.locked()) context.storage(getKey('saved_history'), history.toJSON() || null);
27664             return history;
27665         },
27666
27667         clearSaved: function() {
27668             context.debouncedSave.cancel();
27669             if (lock.locked()) context.storage(getKey('saved_history'), null);
27670             return history;
27671         },
27672
27673         lock: function() {
27674             return lock.lock();
27675         },
27676
27677         unlock: function() {
27678             lock.unlock();
27679         },
27680
27681         // is iD not open in another window and it detects that
27682         // there's a history stored in localStorage that's recoverable?
27683         restorableChanges: function() {
27684             return lock.locked() && !!context.storage(getKey('saved_history'));
27685         },
27686
27687         // load history from a version stored in localStorage
27688         restore: function() {
27689             if (!lock.locked()) return;
27690
27691             var json = context.storage(getKey('saved_history'));
27692             if (json) history.fromJSON(json, true);
27693         },
27694
27695         _getKey: getKey
27696
27697     };
27698
27699     history.reset();
27700
27701     return d3.rebind(history, dispatch, 'on');
27702 };
27703 iD.Node = iD.Entity.node = function iD_Node() {
27704     if (!(this instanceof iD_Node)) {
27705         return (new iD_Node()).initialize(arguments);
27706     } else if (arguments.length) {
27707         this.initialize(arguments);
27708     }
27709 };
27710
27711 iD.Node.prototype = Object.create(iD.Entity.prototype);
27712
27713 _.extend(iD.Node.prototype, {
27714     type: 'node',
27715
27716     extent: function() {
27717         return new iD.geo.Extent(this.loc);
27718     },
27719
27720     geometry: function(graph) {
27721         return graph.transient(this, 'geometry', function() {
27722             return graph.isPoi(this) ? 'point' : 'vertex';
27723         });
27724     },
27725
27726     move: function(loc) {
27727         return this.update({loc: loc});
27728     },
27729
27730     isIntersection: function(resolver) {
27731         return resolver.transient(this, 'isIntersection', function() {
27732             return resolver.parentWays(this).filter(function(parent) {
27733                 return (parent.tags.highway ||
27734                     parent.tags.waterway ||
27735                     parent.tags.railway ||
27736                     parent.tags.aeroway) &&
27737                     parent.geometry(resolver) === 'line';
27738             }).length > 1;
27739         });
27740     },
27741
27742     isHighwayIntersection: function(resolver) {
27743         return resolver.transient(this, 'isHighwayIntersection', function() {
27744             return resolver.parentWays(this).filter(function(parent) {
27745                 return parent.tags.highway && parent.geometry(resolver) === 'line';
27746             }).length > 1;
27747         });
27748     },
27749
27750     asJXON: function(changeset_id) {
27751         var r = {
27752             node: {
27753                 '@id': this.osmId(),
27754                 '@lon': this.loc[0],
27755                 '@lat': this.loc[1],
27756                 '@version': (this.version || 0),
27757                 tag: _.map(this.tags, function(v, k) {
27758                     return { keyAttributes: { k: k, v: v } };
27759                 })
27760             }
27761         };
27762         if (changeset_id) r.node['@changeset'] = changeset_id;
27763         return r;
27764     },
27765
27766     asGeoJSON: function() {
27767         return {
27768             type: 'Point',
27769             coordinates: this.loc
27770         };
27771     }
27772 });
27773 iD.Relation = iD.Entity.relation = function iD_Relation() {
27774     if (!(this instanceof iD_Relation)) {
27775         return (new iD_Relation()).initialize(arguments);
27776     } else if (arguments.length) {
27777         this.initialize(arguments);
27778     }
27779 };
27780
27781 iD.Relation.prototype = Object.create(iD.Entity.prototype);
27782
27783 iD.Relation.creationOrder = function(a, b) {
27784     var aId = parseInt(iD.Entity.id.toOSM(a.id), 10);
27785     var bId = parseInt(iD.Entity.id.toOSM(b.id), 10);
27786
27787     if (aId < 0 || bId < 0) return aId - bId;
27788     return bId - aId;
27789 };
27790
27791 _.extend(iD.Relation.prototype, {
27792     type: 'relation',
27793     members: [],
27794
27795     copy: function(resolver, copies) {
27796         if (copies[this.id])
27797             return copies[this.id];
27798
27799         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
27800
27801         var members = this.members.map(function(member) {
27802             return _.extend({}, member, {id: resolver.entity(member.id).copy(resolver, copies).id});
27803         });
27804
27805         copy = copy.update({members: members});
27806         copies[this.id] = copy;
27807
27808         return copy;
27809     },
27810
27811     extent: function(resolver, memo) {
27812         return resolver.transient(this, 'extent', function() {
27813             if (memo && memo[this.id]) return iD.geo.Extent();
27814             memo = memo || {};
27815             memo[this.id] = true;
27816
27817             var extent = iD.geo.Extent();
27818             for (var i = 0; i < this.members.length; i++) {
27819                 var member = resolver.hasEntity(this.members[i].id);
27820                 if (member) {
27821                     extent._extend(member.extent(resolver, memo));
27822                 }
27823             }
27824             return extent;
27825         });
27826     },
27827
27828     geometry: function(graph) {
27829         return graph.transient(this, 'geometry', function() {
27830             return this.isMultipolygon() ? 'area' : 'relation';
27831         });
27832     },
27833
27834     isDegenerate: function() {
27835         return this.members.length === 0;
27836     },
27837
27838     // Return an array of members, each extended with an 'index' property whose value
27839     // is the member index.
27840     indexedMembers: function() {
27841         var result = new Array(this.members.length);
27842         for (var i = 0; i < this.members.length; i++) {
27843             result[i] = _.extend({}, this.members[i], {index: i});
27844         }
27845         return result;
27846     },
27847
27848     // Return the first member with the given role. A copy of the member object
27849     // is returned, extended with an 'index' property whose value is the member index.
27850     memberByRole: function(role) {
27851         for (var i = 0; i < this.members.length; i++) {
27852             if (this.members[i].role === role) {
27853                 return _.extend({}, this.members[i], {index: i});
27854             }
27855         }
27856     },
27857
27858     // Return the first member with the given id. A copy of the member object
27859     // is returned, extended with an 'index' property whose value is the member index.
27860     memberById: function(id) {
27861         for (var i = 0; i < this.members.length; i++) {
27862             if (this.members[i].id === id) {
27863                 return _.extend({}, this.members[i], {index: i});
27864             }
27865         }
27866     },
27867
27868     // Return the first member with the given id and role. A copy of the member object
27869     // is returned, extended with an 'index' property whose value is the member index.
27870     memberByIdAndRole: function(id, role) {
27871         for (var i = 0; i < this.members.length; i++) {
27872             if (this.members[i].id === id && this.members[i].role === role) {
27873                 return _.extend({}, this.members[i], {index: i});
27874             }
27875         }
27876     },
27877
27878     addMember: function(member, index) {
27879         var members = this.members.slice();
27880         members.splice(index === undefined ? members.length : index, 0, member);
27881         return this.update({members: members});
27882     },
27883
27884     updateMember: function(member, index) {
27885         var members = this.members.slice();
27886         members.splice(index, 1, _.extend({}, members[index], member));
27887         return this.update({members: members});
27888     },
27889
27890     removeMember: function(index) {
27891         var members = this.members.slice();
27892         members.splice(index, 1);
27893         return this.update({members: members});
27894     },
27895
27896     removeMembersWithID: function(id) {
27897         var members = _.reject(this.members, function(m) { return m.id === id; });
27898         return this.update({members: members});
27899     },
27900
27901     // Wherever a member appears with id `needle.id`, replace it with a member
27902     // with id `replacement.id`, type `replacement.type`, and the original role,
27903     // unless a member already exists with that id and role. Return an updated
27904     // relation.
27905     replaceMember: function(needle, replacement) {
27906         if (!this.memberById(needle.id))
27907             return this;
27908
27909         var members = [];
27910
27911         for (var i = 0; i < this.members.length; i++) {
27912             var member = this.members[i];
27913             if (member.id !== needle.id) {
27914                 members.push(member);
27915             } else if (!this.memberByIdAndRole(replacement.id, member.role)) {
27916                 members.push({id: replacement.id, type: replacement.type, role: member.role});
27917             }
27918         }
27919
27920         return this.update({members: members});
27921     },
27922
27923     asJXON: function(changeset_id) {
27924         var r = {
27925             relation: {
27926                 '@id': this.osmId(),
27927                 '@version': this.version || 0,
27928                 member: _.map(this.members, function(member) {
27929                     return { keyAttributes: { type: member.type, role: member.role, ref: iD.Entity.id.toOSM(member.id) } };
27930                 }),
27931                 tag: _.map(this.tags, function(v, k) {
27932                     return { keyAttributes: { k: k, v: v } };
27933                 })
27934             }
27935         };
27936         if (changeset_id) r.relation['@changeset'] = changeset_id;
27937         return r;
27938     },
27939
27940     asGeoJSON: function(resolver) {
27941         return resolver.transient(this, 'GeoJSON', function () {
27942             if (this.isMultipolygon()) {
27943                 return {
27944                     type: 'MultiPolygon',
27945                     coordinates: this.multipolygon(resolver)
27946                 };
27947             } else {
27948                 return {
27949                     type: 'FeatureCollection',
27950                     properties: this.tags,
27951                     features: this.members.map(function (member) {
27952                         return _.extend({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
27953                     })
27954                 };
27955             }
27956         });
27957     },
27958
27959     area: function(resolver) {
27960         return resolver.transient(this, 'area', function() {
27961             return d3.geo.area(this.asGeoJSON(resolver));
27962         });
27963     },
27964
27965     isMultipolygon: function() {
27966         return this.tags.type === 'multipolygon';
27967     },
27968
27969     isComplete: function(resolver) {
27970         for (var i = 0; i < this.members.length; i++) {
27971             if (!resolver.hasEntity(this.members[i].id)) {
27972                 return false;
27973             }
27974         }
27975         return true;
27976     },
27977
27978     isRestriction: function() {
27979         return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
27980     },
27981
27982     // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
27983     // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
27984     //
27985     // This corresponds to the structure needed for rendering a multipolygon path using a
27986     // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
27987     //
27988     // In the case of invalid geometries, this function will still return a result which
27989     // includes the nodes of all way members, but some Nds may be unclosed and some inner
27990     // rings not matched with the intended outer ring.
27991     //
27992     multipolygon: function(resolver) {
27993         var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
27994             inners = this.members.filter(function(m) { return 'inner' === m.role; });
27995
27996         outers = iD.geo.joinWays(outers, resolver);
27997         inners = iD.geo.joinWays(inners, resolver);
27998
27999         outers = outers.map(function(outer) { return _.pluck(outer.nodes, 'loc'); });
28000         inners = inners.map(function(inner) { return _.pluck(inner.nodes, 'loc'); });
28001
28002         var result = outers.map(function(o) {
28003             // Heuristic for detecting counterclockwise winding order. Assumes
28004             // that OpenStreetMap polygons are not hemisphere-spanning.
28005             return [d3.geo.area({type: 'Polygon', coordinates: [o]}) > 2 * Math.PI ? o.reverse() : o];
28006         });
28007
28008         function findOuter(inner) {
28009             var o, outer;
28010
28011             for (o = 0; o < outers.length; o++) {
28012                 outer = outers[o];
28013                 if (iD.geo.polygonContainsPolygon(outer, inner))
28014                     return o;
28015             }
28016
28017             for (o = 0; o < outers.length; o++) {
28018                 outer = outers[o];
28019                 if (iD.geo.polygonIntersectsPolygon(outer, inner))
28020                     return o;
28021             }
28022         }
28023
28024         for (var i = 0; i < inners.length; i++) {
28025             var inner = inners[i];
28026
28027             if (d3.geo.area({type: 'Polygon', coordinates: [inner]}) < 2 * Math.PI) {
28028                 inner = inner.reverse();
28029             }
28030
28031             var o = findOuter(inners[i]);
28032             if (o !== undefined)
28033                 result[o].push(inners[i]);
28034             else
28035                 result.push([inners[i]]); // Invalid geometry
28036         }
28037
28038         return result;
28039     }
28040 });
28041 iD.oneWayTags = {
28042     'aerialway': {
28043         'chair_lift': true,
28044         'mixed_lift': true,
28045         't-bar': true,
28046         'j-bar': true,
28047         'platter': true,
28048         'rope_tow': true,
28049         'magic_carpet': true,
28050         'yes': true
28051     },
28052     'highway': {
28053         'motorway': true,
28054         'motorway_link': true
28055     },
28056     'junction': {
28057         'roundabout': true
28058     },
28059     'man_made': {
28060         'piste:halfpipe': true
28061     },
28062     'piste:type': {
28063         'downhill': true,
28064         'sled': true,
28065         'yes': true
28066     },
28067     'waterway': {
28068         'river': true,
28069         'stream': true
28070     }
28071 };
28072
28073 iD.pavedTags = {
28074     'surface': {
28075         'paved': true,
28076         'asphalt': true,
28077         'concrete': true
28078     },
28079     'tracktype': {
28080         'grade1': true
28081     }
28082 };
28083
28084 iD.interestingTag = function (key) {
28085     return key !== 'attribution' &&
28086         key !== 'created_by' &&
28087         key !== 'source' &&
28088         key !== 'odbl' &&
28089         key.indexOf('tiger:') !== 0;
28090
28091 };
28092 iD.Tree = function(head) {
28093     var rtree = rbush(),
28094         rectangles = {};
28095
28096     function entityRectangle(entity) {
28097         var rect = entity.extent(head).rectangle();
28098         rect.id = entity.id;
28099         rectangles[entity.id] = rect;
28100         return rect;
28101     }
28102
28103     function updateParents(entity, insertions, memo) {
28104         head.parentWays(entity).forEach(function(way) {
28105             if (rectangles[way.id]) {
28106                 rtree.remove(rectangles[way.id]);
28107                 insertions[way.id] = way;
28108             }
28109             updateParents(way, insertions, memo);
28110         });
28111
28112         head.parentRelations(entity).forEach(function(relation) {
28113             if (memo[entity.id]) return;
28114             memo[entity.id] = true;
28115             if (rectangles[relation.id]) {
28116                 rtree.remove(rectangles[relation.id]);
28117                 insertions[relation.id] = relation;
28118             }
28119             updateParents(relation, insertions, memo);
28120         });
28121     }
28122
28123     var tree = {};
28124
28125     tree.rebase = function(entities, force) {
28126         var insertions = {};
28127
28128         for (var i = 0; i < entities.length; i++) {
28129             var entity = entities[i];
28130
28131             if (!entity.visible)
28132                 continue;
28133
28134             if (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id]) {
28135                 if (!force) {
28136                     continue;
28137                 } else if (rectangles[entity.id]) {
28138                     rtree.remove(rectangles[entity.id]);
28139                 }
28140             }
28141
28142             insertions[entity.id] = entity;
28143             updateParents(entity, insertions, {});
28144         }
28145
28146         rtree.load(_.map(insertions, entityRectangle));
28147
28148         return tree;
28149     };
28150
28151     tree.intersects = function(extent, graph) {
28152         if (graph !== head) {
28153             var diff = iD.Difference(head, graph),
28154                 insertions = {};
28155
28156             head = graph;
28157
28158             diff.deleted().forEach(function(entity) {
28159                 rtree.remove(rectangles[entity.id]);
28160                 delete rectangles[entity.id];
28161             });
28162
28163             diff.modified().forEach(function(entity) {
28164                 rtree.remove(rectangles[entity.id]);
28165                 insertions[entity.id] = entity;
28166                 updateParents(entity, insertions, {});
28167             });
28168
28169             diff.created().forEach(function(entity) {
28170                 insertions[entity.id] = entity;
28171             });
28172
28173             rtree.load(_.map(insertions, entityRectangle));
28174         }
28175
28176         return rtree.search(extent.rectangle()).map(function(rect) {
28177             return head.entity(rect.id);
28178         });
28179     };
28180
28181     return tree;
28182 };
28183 iD.Way = iD.Entity.way = function iD_Way() {
28184     if (!(this instanceof iD_Way)) {
28185         return (new iD_Way()).initialize(arguments);
28186     } else if (arguments.length) {
28187         this.initialize(arguments);
28188     }
28189 };
28190
28191 iD.Way.prototype = Object.create(iD.Entity.prototype);
28192
28193 _.extend(iD.Way.prototype, {
28194     type: 'way',
28195     nodes: [],
28196
28197     copy: function(resolver, copies) {
28198         if (copies[this.id])
28199             return copies[this.id];
28200
28201         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
28202
28203         var nodes = this.nodes.map(function(id) {
28204             return resolver.entity(id).copy(resolver, copies).id;
28205         });
28206
28207         copy = copy.update({nodes: nodes});
28208         copies[this.id] = copy;
28209
28210         return copy;
28211     },
28212
28213     extent: function(resolver) {
28214         return resolver.transient(this, 'extent', function() {
28215             var extent = iD.geo.Extent();
28216             for (var i = 0; i < this.nodes.length; i++) {
28217                 var node = resolver.hasEntity(this.nodes[i]);
28218                 if (node) {
28219                     extent._extend(node.extent());
28220                 }
28221             }
28222             return extent;
28223         });
28224     },
28225
28226     first: function() {
28227         return this.nodes[0];
28228     },
28229
28230     last: function() {
28231         return this.nodes[this.nodes.length - 1];
28232     },
28233
28234     contains: function(node) {
28235         return this.nodes.indexOf(node) >= 0;
28236     },
28237
28238     affix: function(node) {
28239         if (this.nodes[0] === node) return 'prefix';
28240         if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
28241     },
28242
28243     layer: function() {
28244         // explicit layer tag, clamp between -10, 10..
28245         if (this.tags.layer !== undefined) {
28246             return Math.max(-10, Math.min(+(this.tags.layer), 10));
28247         }
28248
28249         // implied layer tag..
28250         if (this.tags.location === 'overground') return 1;
28251         if (this.tags.location === 'underground') return -1;
28252         if (this.tags.location === 'underwater') return -10;
28253
28254         if (this.tags.power === 'line') return 10;
28255         if (this.tags.power === 'minor_line') return 10;
28256         if (this.tags.aerialway) return 10;
28257         if (this.tags.bridge) return 1;
28258         if (this.tags.cutting) return -1;
28259         if (this.tags.tunnel) return -1;
28260         if (this.tags.waterway) return -1;
28261         if (this.tags.man_made === 'pipeline') return -10;
28262         if (this.tags.boundary) return -10;
28263         return 0;
28264     },
28265
28266     isOneWay: function() {
28267         // explicit oneway tag..
28268         if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; }
28269         if (['no', '0'].indexOf(this.tags.oneway) !== -1) { return false; }
28270
28271         // implied oneway tag..
28272         for (var key in this.tags) {
28273             if (key in iD.oneWayTags && (this.tags[key] in iD.oneWayTags[key]))
28274                 return true;
28275         }
28276         return false;
28277     },
28278
28279     isClosed: function() {
28280         return this.nodes.length > 0 && this.first() === this.last();
28281     },
28282
28283     isConvex: function(resolver) {
28284         if (!this.isClosed() || this.isDegenerate()) return null;
28285
28286         var nodes = _.uniq(resolver.childNodes(this)),
28287             coords = _.pluck(nodes, 'loc'),
28288             curr = 0, prev = 0;
28289
28290         for (var i = 0; i < coords.length; i++) {
28291             var o = coords[(i+1) % coords.length],
28292                 a = coords[i],
28293                 b = coords[(i+2) % coords.length],
28294                 res = iD.geo.cross(o, a, b);
28295
28296             curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
28297             if (curr === 0) {
28298                 continue;
28299             } else if (prev && curr !== prev) {
28300                 return false;
28301             }
28302             prev = curr;
28303         }
28304         return true;
28305     },
28306
28307     isArea: function() {
28308         if (this.tags.area === 'yes')
28309             return true;
28310         if (!this.isClosed() || this.tags.area === 'no')
28311             return false;
28312         for (var key in this.tags)
28313             if (key in iD.areaKeys && !(this.tags[key] in iD.areaKeys[key]))
28314                 return true;
28315         return false;
28316     },
28317
28318     isDegenerate: function() {
28319         return _.uniq(this.nodes).length < (this.isArea() ? 3 : 2);
28320     },
28321
28322     areAdjacent: function(n1, n2) {
28323         for (var i = 0; i < this.nodes.length; i++) {
28324             if (this.nodes[i] === n1) {
28325                 if (this.nodes[i - 1] === n2) return true;
28326                 if (this.nodes[i + 1] === n2) return true;
28327             }
28328         }
28329         return false;
28330     },
28331
28332     geometry: function(graph) {
28333         return graph.transient(this, 'geometry', function() {
28334             return this.isArea() ? 'area' : 'line';
28335         });
28336     },
28337
28338     addNode: function(id, index) {
28339         var nodes = this.nodes.slice();
28340         nodes.splice(index === undefined ? nodes.length : index, 0, id);
28341         return this.update({nodes: nodes});
28342     },
28343
28344     updateNode: function(id, index) {
28345         var nodes = this.nodes.slice();
28346         nodes.splice(index, 1, id);
28347         return this.update({nodes: nodes});
28348     },
28349
28350     replaceNode: function(needle, replacement) {
28351         if (this.nodes.indexOf(needle) < 0)
28352             return this;
28353
28354         var nodes = this.nodes.slice();
28355         for (var i = 0; i < nodes.length; i++) {
28356             if (nodes[i] === needle) {
28357                 nodes[i] = replacement;
28358             }
28359         }
28360         return this.update({nodes: nodes});
28361     },
28362
28363     removeNode: function(id) {
28364         var nodes = [];
28365
28366         for (var i = 0; i < this.nodes.length; i++) {
28367             var node = this.nodes[i];
28368             if (node !== id && nodes[nodes.length - 1] !== node) {
28369                 nodes.push(node);
28370             }
28371         }
28372
28373         // Preserve circularity
28374         if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] !== nodes[0]) {
28375             nodes.push(nodes[0]);
28376         }
28377
28378         return this.update({nodes: nodes});
28379     },
28380
28381     asJXON: function(changeset_id) {
28382         var r = {
28383             way: {
28384                 '@id': this.osmId(),
28385                 '@version': this.version || 0,
28386                 nd: _.map(this.nodes, function(id) {
28387                     return { keyAttributes: { ref: iD.Entity.id.toOSM(id) } };
28388                 }),
28389                 tag: _.map(this.tags, function(v, k) {
28390                     return { keyAttributes: { k: k, v: v } };
28391                 })
28392             }
28393         };
28394         if (changeset_id) r.way['@changeset'] = changeset_id;
28395         return r;
28396     },
28397
28398     asGeoJSON: function(resolver) {
28399         return resolver.transient(this, 'GeoJSON', function() {
28400             var coordinates = _.pluck(resolver.childNodes(this), 'loc');
28401             if (this.isArea() && this.isClosed()) {
28402                 return {
28403                     type: 'Polygon',
28404                     coordinates: [coordinates]
28405                 };
28406             } else {
28407                 return {
28408                     type: 'LineString',
28409                     coordinates: coordinates
28410                 };
28411             }
28412         });
28413     },
28414
28415     area: function(resolver) {
28416         return resolver.transient(this, 'area', function() {
28417             var nodes = resolver.childNodes(this);
28418
28419             var json = {
28420                 type: 'Polygon',
28421                 coordinates: [_.pluck(nodes, 'loc')]
28422             };
28423
28424             if (!this.isClosed() && nodes.length) {
28425                 json.coordinates[0].push(nodes[0].loc);
28426             }
28427
28428             var area = d3.geo.area(json);
28429
28430             // Heuristic for detecting counterclockwise winding order. Assumes
28431             // that OpenStreetMap polygons are not hemisphere-spanning.
28432             if (area > 2 * Math.PI) {
28433                 json.coordinates[0] = json.coordinates[0].reverse();
28434                 area = d3.geo.area(json);
28435             }
28436
28437             return isNaN(area) ? 0 : area;
28438         });
28439     }
28440 });
28441 iD.Background = function(context) {
28442     var dispatch = d3.dispatch('change'),
28443         baseLayer = iD.TileLayer().projection(context.projection),
28444         overlayLayers = [],
28445         backgroundSources;
28446
28447
28448     function findSource(id) {
28449         return _.find(backgroundSources, function(d) {
28450             return d.id && d.id === id;
28451         });
28452     }
28453
28454
28455     function background(selection) {
28456         var base = selection.selectAll('.layer-background')
28457             .data([0]);
28458
28459         base.enter()
28460             .insert('div', '.layer-data')
28461             .attr('class', 'layer layer-background');
28462
28463         base.call(baseLayer);
28464
28465         var overlays = selection.selectAll('.layer-overlay')
28466             .data(overlayLayers, function(d) { return d.source().name(); });
28467
28468         overlays.enter()
28469             .insert('div', '.layer-data')
28470             .attr('class', 'layer layer-overlay');
28471
28472         overlays.each(function(layer) {
28473             d3.select(this).call(layer);
28474         });
28475
28476         overlays.exit()
28477             .remove();
28478     }
28479
28480
28481     background.updateImagery = function() {
28482         var b = background.baseLayerSource(),
28483             o = overlayLayers.map(function (d) { return d.source().id; }).join(','),
28484             q = iD.util.stringQs(location.hash.substring(1));
28485
28486         var id = b.id;
28487         if (id === 'custom') {
28488             id = 'custom:' + b.template;
28489         }
28490
28491         if (id) {
28492             q.background = id;
28493         } else {
28494             delete q.background;
28495         }
28496
28497         if (o) {
28498             q.overlays = o;
28499         } else {
28500             delete q.overlays;
28501         }
28502
28503         location.replace('#' + iD.util.qsString(q, true));
28504
28505         var imageryUsed = [b.imageryUsed()];
28506
28507         overlayLayers.forEach(function (d) {
28508             var source = d.source();
28509             if (!source.isLocatorOverlay()) {
28510                 imageryUsed.push(source.imageryUsed());
28511             }
28512         });
28513
28514         var gpx = context.layers().layer('gpx');
28515         if (gpx && gpx.enabled() && gpx.hasGpx()) {
28516             imageryUsed.push('Local GPX');
28517         }
28518
28519         context.history().imageryUsed(imageryUsed);
28520     };
28521
28522     background.sources = function(extent) {
28523         return backgroundSources.filter(function(source) {
28524             return source.intersects(extent);
28525         });
28526     };
28527
28528     background.dimensions = function(_) {
28529         baseLayer.dimensions(_);
28530
28531         overlayLayers.forEach(function(layer) {
28532             layer.dimensions(_);
28533         });
28534     };
28535
28536     background.baseLayerSource = function(d) {
28537         if (!arguments.length) return baseLayer.source();
28538
28539         baseLayer.source(d);
28540         dispatch.change();
28541         background.updateImagery();
28542
28543         return background;
28544     };
28545
28546     background.bing = function() {
28547         background.baseLayerSource(findSource('Bing'));
28548     };
28549
28550     background.showsLayer = function(d) {
28551         return d === baseLayer.source() ||
28552             (d.id === 'custom' && baseLayer.source().id === 'custom') ||
28553             overlayLayers.some(function(l) { return l.source() === d; });
28554     };
28555
28556     background.overlayLayerSources = function() {
28557         return overlayLayers.map(function (l) { return l.source(); });
28558     };
28559
28560     background.toggleOverlayLayer = function(d) {
28561         var layer;
28562
28563         for (var i = 0; i < overlayLayers.length; i++) {
28564             layer = overlayLayers[i];
28565             if (layer.source() === d) {
28566                 overlayLayers.splice(i, 1);
28567                 dispatch.change();
28568                 background.updateImagery();
28569                 return;
28570             }
28571         }
28572
28573         layer = iD.TileLayer()
28574             .source(d)
28575             .projection(context.projection)
28576             .dimensions(baseLayer.dimensions());
28577
28578         overlayLayers.push(layer);
28579         dispatch.change();
28580         background.updateImagery();
28581     };
28582
28583     background.nudge = function(d, zoom) {
28584         baseLayer.source().nudge(d, zoom);
28585         dispatch.change();
28586         return background;
28587     };
28588
28589     background.offset = function(d) {
28590         if (!arguments.length) return baseLayer.source().offset();
28591         baseLayer.source().offset(d);
28592         dispatch.change();
28593         return background;
28594     };
28595
28596     background.load = function(imagery) {
28597         function parseMap(qmap) {
28598             if (!qmap) return false;
28599             var args = qmap.split('/').map(Number);
28600             if (args.length < 3 || args.some(isNaN)) return false;
28601             return iD.geo.Extent([args[1], args[2]]);
28602         }
28603
28604         var q = iD.util.stringQs(location.hash.substring(1)),
28605             chosen = q.background || q.layer,
28606             extent = parseMap(q.map),
28607             best;
28608
28609         backgroundSources = imagery.map(function(source) {
28610             if (source.type === 'bing') {
28611                 return iD.BackgroundSource.Bing(source, dispatch);
28612             } else {
28613                 return iD.BackgroundSource(source);
28614             }
28615         });
28616
28617         backgroundSources.unshift(iD.BackgroundSource.None());
28618
28619         if (!chosen && extent) {
28620             best = _.find(this.sources(extent), function(s) { return s.best(); });
28621         }
28622
28623         if (chosen && chosen.indexOf('custom:') === 0) {
28624             background.baseLayerSource(iD.BackgroundSource.Custom(chosen.replace(/^custom:/, '')));
28625         } else {
28626             background.baseLayerSource(findSource(chosen) || best || findSource('Bing') || backgroundSources[1] || backgroundSources[0]);
28627         }
28628
28629         var locator = _.find(backgroundSources, function(d) {
28630             return d.overlay && d.default;
28631         });
28632
28633         if (locator) {
28634             background.toggleOverlayLayer(locator);
28635         }
28636
28637         var overlays = (q.overlays || '').split(',');
28638         overlays.forEach(function(overlay) {
28639             overlay = findSource(overlay);
28640             if (overlay) background.toggleOverlayLayer(overlay);
28641         });
28642
28643         if (q.gpx) {
28644             var gpx = context.layers().layer('gpx');
28645             if (gpx) { gpx.url(q.gpx); }
28646         }
28647     };
28648
28649     return d3.rebind(background, dispatch, 'on');
28650 };
28651 iD.BackgroundSource = function(data) {
28652     var source = _.clone(data),
28653         offset = [0, 0],
28654         name = source.name,
28655         best = !!source.best;
28656
28657     source.scaleExtent = data.scaleExtent || [0, 20];
28658     source.overzoom = data.overzoom !== false;
28659
28660     source.offset = function(_) {
28661         if (!arguments.length) return offset;
28662         offset = _;
28663         return source;
28664     };
28665
28666     source.nudge = function(_, zoomlevel) {
28667         offset[0] += _[0] / Math.pow(2, zoomlevel);
28668         offset[1] += _[1] / Math.pow(2, zoomlevel);
28669         return source;
28670     };
28671
28672     source.name = function() {
28673         return name;
28674     };
28675
28676     source.best = function() {
28677         return best;
28678     };
28679
28680     source.imageryUsed = function() {
28681         return source.id || name;
28682     };
28683
28684     source.url = function(coord) {
28685         return data.template
28686             .replace('{x}', coord[0])
28687             .replace('{y}', coord[1])
28688             // TMS-flipped y coordinate
28689             .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
28690             .replace(/\{z(oom)?\}/, coord[2])
28691             .replace(/\{switch:([^}]+)\}/, function(s, r) {
28692                 var subdomains = r.split(',');
28693                 return subdomains[(coord[0] + coord[1]) % subdomains.length];
28694             })
28695             .replace('{u}', function() {
28696                 var u = '';
28697                 for (var zoom = coord[2]; zoom > 0; zoom--) {
28698                     var b = 0;
28699                     var mask = 1 << (zoom - 1);
28700                     if ((coord[0] & mask) !== 0) b++;
28701                     if ((coord[1] & mask) !== 0) b += 2;
28702                     u += b.toString();
28703                 }
28704                 return u;
28705             });
28706     };
28707
28708     source.intersects = function(extent) {
28709         extent = extent.polygon();
28710         return !data.polygon || data.polygon.some(function(polygon) {
28711             return iD.geo.polygonIntersectsPolygon(polygon, extent, true);
28712         });
28713     };
28714
28715     source.validZoom = function(z) {
28716         return source.scaleExtent[0] <= z &&
28717             (source.overzoom || source.scaleExtent[1] > z);
28718     };
28719
28720     source.isLocatorOverlay = function() {
28721         return name === 'Locator Overlay';
28722     };
28723
28724     source.copyrightNotices = function() {};
28725
28726     return source;
28727 };
28728
28729 iD.BackgroundSource.Bing = function(data, dispatch) {
28730     // http://msdn.microsoft.com/en-us/library/ff701716.aspx
28731     // http://msdn.microsoft.com/en-us/library/ff701701.aspx
28732
28733     data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
28734
28735     var bing = iD.BackgroundSource(data),
28736         key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU', // Same as P2 and JOSM
28737         url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' +
28738             key + '&jsonp={callback}',
28739         providers = [];
28740
28741     d3.jsonp(url, function(json) {
28742         providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
28743             return {
28744                 attribution: provider.attribution,
28745                 areas: provider.coverageAreas.map(function(area) {
28746                     return {
28747                         zoom: [area.zoomMin, area.zoomMax],
28748                         extent: iD.geo.Extent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
28749                     };
28750                 })
28751             };
28752         });
28753         dispatch.change();
28754     });
28755
28756     bing.copyrightNotices = function(zoom, extent) {
28757         zoom = Math.min(zoom, 21);
28758         return providers.filter(function(provider) {
28759             return _.any(provider.areas, function(area) {
28760                 return extent.intersects(area.extent) &&
28761                     area.zoom[0] <= zoom &&
28762                     area.zoom[1] >= zoom;
28763             });
28764         }).map(function(provider) {
28765             return provider.attribution;
28766         }).join(', ');
28767     };
28768
28769     bing.logo = 'bing_maps.png';
28770     bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
28771
28772     return bing;
28773 };
28774
28775 iD.BackgroundSource.None = function() {
28776     var source = iD.BackgroundSource({id: 'none', template: ''});
28777
28778     source.name = function() {
28779         return t('background.none');
28780     };
28781
28782     source.imageryUsed = function() {
28783         return 'None';
28784     };
28785
28786     return source;
28787 };
28788
28789 iD.BackgroundSource.Custom = function(template) {
28790     var source = iD.BackgroundSource({id: 'custom', template: template});
28791
28792     source.name = function() {
28793         return t('background.custom');
28794     };
28795
28796     source.imageryUsed = function() {
28797         return 'Custom (' + template + ')';
28798     };
28799
28800     return source;
28801 };
28802 iD.Features = function(context) {
28803     var traffic_roads = {
28804         'motorway': true,
28805         'motorway_link': true,
28806         'trunk': true,
28807         'trunk_link': true,
28808         'primary': true,
28809         'primary_link': true,
28810         'secondary': true,
28811         'secondary_link': true,
28812         'tertiary': true,
28813         'tertiary_link': true,
28814         'residential': true,
28815         'unclassified': true,
28816         'living_street': true
28817     };
28818
28819     var service_roads = {
28820         'service': true,
28821         'road': true,
28822         'track': true
28823     };
28824
28825     var paths = {
28826         'path': true,
28827         'footway': true,
28828         'cycleway': true,
28829         'bridleway': true,
28830         'steps': true,
28831         'pedestrian': true,
28832         'corridor': true
28833     };
28834
28835     var past_futures = {
28836         'proposed': true,
28837         'construction': true,
28838         'abandoned': true,
28839         'dismantled': true,
28840         'disused': true,
28841         'razed': true,
28842         'demolished': true,
28843         'obliterated': true
28844     };
28845
28846     var dispatch = d3.dispatch('change', 'redraw'),
28847         _cullFactor = 1,
28848         _cache = {},
28849         _features = {},
28850         _stats = {},
28851         _keys = [],
28852         _hidden = [];
28853
28854     function update() {
28855         _hidden = features.hidden();
28856         dispatch.change();
28857         dispatch.redraw();
28858     }
28859
28860     function defineFeature(k, filter, max) {
28861         _keys.push(k);
28862         _features[k] = {
28863             filter: filter,
28864             enabled: true,   // whether the user wants it enabled..
28865             count: 0,
28866             currentMax: (max || Infinity),
28867             defaultMax: (max || Infinity),
28868             enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
28869             disable: function() { this.enabled = false; this.currentMax = 0; },
28870             hidden: function() { return !context.editable() || this.count > this.currentMax * _cullFactor; },
28871             autoHidden: function() { return this.hidden() && this.currentMax > 0; }
28872         };
28873     }
28874
28875
28876     defineFeature('points', function isPoint(entity, resolver, geometry) {
28877         return geometry === 'point';
28878     }, 200);
28879
28880     defineFeature('traffic_roads', function isTrafficRoad(entity) {
28881         return traffic_roads[entity.tags.highway];
28882     });
28883
28884     defineFeature('service_roads', function isServiceRoad(entity) {
28885         return service_roads[entity.tags.highway];
28886     });
28887
28888     defineFeature('paths', function isPath(entity) {
28889         return paths[entity.tags.highway];
28890     });
28891
28892     defineFeature('buildings', function isBuilding(entity) {
28893         return (
28894             !!entity.tags['building:part'] ||
28895             (!!entity.tags.building && entity.tags.building !== 'no') ||
28896             entity.tags.amenity === 'shelter' ||
28897             entity.tags.parking === 'multi-storey' ||
28898             entity.tags.parking === 'sheds' ||
28899             entity.tags.parking === 'carports' ||
28900             entity.tags.parking === 'garage_boxes'
28901         );
28902     }, 250);
28903
28904     defineFeature('landuse', function isLanduse(entity, resolver, geometry) {
28905         return geometry === 'area' &&
28906             !_features.buildings.filter(entity) &&
28907             !_features.water.filter(entity);
28908     });
28909
28910     defineFeature('boundaries', function isBoundary(entity) {
28911         return !!entity.tags.boundary;
28912     });
28913
28914     defineFeature('water', function isWater(entity) {
28915         return (
28916             !!entity.tags.waterway ||
28917             entity.tags.natural === 'water' ||
28918             entity.tags.natural === 'coastline' ||
28919             entity.tags.natural === 'bay' ||
28920             entity.tags.landuse === 'pond' ||
28921             entity.tags.landuse === 'basin' ||
28922             entity.tags.landuse === 'reservoir' ||
28923             entity.tags.landuse === 'salt_pond'
28924         );
28925     });
28926
28927     defineFeature('rail', function isRail(entity) {
28928         return (
28929             !!entity.tags.railway ||
28930             entity.tags.landuse === 'railway'
28931         ) && !(
28932             traffic_roads[entity.tags.highway] ||
28933             service_roads[entity.tags.highway] ||
28934             paths[entity.tags.highway]
28935         );
28936     });
28937
28938     defineFeature('power', function isPower(entity) {
28939         return !!entity.tags.power;
28940     });
28941
28942     // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
28943     defineFeature('past_future', function isPastFuture(entity) {
28944         if (
28945             traffic_roads[entity.tags.highway] ||
28946             service_roads[entity.tags.highway] ||
28947             paths[entity.tags.highway]
28948         ) { return false; }
28949
28950         var strings = Object.keys(entity.tags);
28951
28952         for (var i = 0; i < strings.length; i++) {
28953             var s = strings[i];
28954             if (past_futures[s] || past_futures[entity.tags[s]]) { return true; }
28955         }
28956         return false;
28957     });
28958
28959     // Lines or areas that don't match another feature filter.
28960     // IMPORTANT: The 'others' feature must be the last one defined,
28961     //   so that code in getMatches can skip this test if `hasMatch = true`
28962     defineFeature('others', function isOther(entity, resolver, geometry) {
28963         return (geometry === 'line' || geometry === 'area');
28964     });
28965
28966
28967     function features() {}
28968
28969     features.features = function() {
28970         return _features;
28971     };
28972
28973     features.keys = function() {
28974         return _keys;
28975     };
28976
28977     features.enabled = function(k) {
28978         if (!arguments.length) {
28979             return _.filter(_keys, function(k) { return _features[k].enabled; });
28980         }
28981         return _features[k] && _features[k].enabled;
28982     };
28983
28984     features.disabled = function(k) {
28985         if (!arguments.length) {
28986             return _.reject(_keys, function(k) { return _features[k].enabled; });
28987         }
28988         return _features[k] && !_features[k].enabled;
28989     };
28990
28991     features.hidden = function(k) {
28992         if (!arguments.length) {
28993             return _.filter(_keys, function(k) { return _features[k].hidden(); });
28994         }
28995         return _features[k] && _features[k].hidden();
28996     };
28997
28998     features.autoHidden = function(k) {
28999         if (!arguments.length) {
29000             return _.filter(_keys, function(k) { return _features[k].autoHidden(); });
29001         }
29002         return _features[k] && _features[k].autoHidden();
29003     };
29004
29005     features.enable = function(k) {
29006         if (_features[k] && !_features[k].enabled) {
29007             _features[k].enable();
29008             update();
29009         }
29010     };
29011
29012     features.disable = function(k) {
29013         if (_features[k] && _features[k].enabled) {
29014             _features[k].disable();
29015             update();
29016         }
29017     };
29018
29019     features.toggle = function(k) {
29020         if (_features[k]) {
29021             (function(f) { return f.enabled ? f.disable() : f.enable(); }(_features[k]));
29022             update();
29023         }
29024     };
29025
29026     features.resetStats = function() {
29027         _.each(_features, function(f) { f.count = 0; });
29028         dispatch.change();
29029     };
29030
29031     features.gatherStats = function(d, resolver, dimensions) {
29032         var needsRedraw = false,
29033             type = _.groupBy(d, function(ent) { return ent.type; }),
29034             entities = [].concat(type.relation || [], type.way || [], type.node || []),
29035             currHidden, geometry, matches;
29036
29037         _.each(_features, function(f) { f.count = 0; });
29038
29039         // adjust the threshold for point/building culling based on viewport size..
29040         // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
29041         _cullFactor = dimensions[0] * dimensions[1] / 1000000;
29042
29043         for (var i = 0; i < entities.length; i++) {
29044             geometry = entities[i].geometry(resolver);
29045             if (!(geometry === 'vertex' || geometry === 'relation')) {
29046                 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
29047                 for (var j = 0; j < matches.length; j++) {
29048                     _features[matches[j]].count++;
29049                 }
29050             }
29051         }
29052
29053         currHidden = features.hidden();
29054         if (currHidden !== _hidden) {
29055             _hidden = currHidden;
29056             needsRedraw = true;
29057             dispatch.change();
29058         }
29059
29060         return needsRedraw;
29061     };
29062
29063     features.stats = function() {
29064         _.each(_keys, function(k) { _stats[k] = _features[k].count; });
29065         return _stats;
29066     };
29067
29068     features.clear = function(d) {
29069         for (var i = 0; i < d.length; i++) {
29070             features.clearEntity(d[i]);
29071         }
29072     };
29073
29074     features.clearEntity = function(entity) {
29075         delete _cache[iD.Entity.key(entity)];
29076     };
29077
29078     features.reset = function() {
29079         _cache = {};
29080     };
29081
29082     features.getMatches = function(entity, resolver, geometry) {
29083         if (geometry === 'vertex' || geometry === 'relation') return {};
29084
29085         var ent = iD.Entity.key(entity);
29086         if (!_cache[ent]) {
29087             _cache[ent] = {};
29088         }
29089
29090         if (!_cache[ent].matches) {
29091             var matches = {},
29092                 hasMatch = false;
29093
29094             for (var i = 0; i < _keys.length; i++) {
29095                 if (_keys[i] === 'others') {
29096                     if (hasMatch) continue;
29097
29098                     // Multipolygon members:
29099                     // If an entity...
29100                     //   1. is a way that hasn't matched other "interesting" feature rules,
29101                     //   2. and it belongs to a single parent multipolygon relation
29102                     // ...then match whatever feature rules the parent multipolygon has matched.
29103                     // see #2548, #2887
29104                     //
29105                     // IMPORTANT:
29106                     // For this to work, getMatches must be called on relations before ways.
29107                     //
29108                     if (entity.type === 'way') {
29109                         var parents = features.getParents(entity, resolver, geometry);
29110                         if (parents.length === 1 && parents[0].isMultipolygon()) {
29111                             var pkey = iD.Entity.key(parents[0]);
29112                             if (_cache[pkey] && _cache[pkey].matches) {
29113                                 matches = _.clone(_cache[pkey].matches);
29114                                 continue;
29115                             }
29116                         }
29117                     }
29118                 }
29119
29120                 if (_features[_keys[i]].filter(entity, resolver, geometry)) {
29121                     matches[_keys[i]] = hasMatch = true;
29122                 }
29123             }
29124             _cache[ent].matches = matches;
29125         }
29126
29127         return _cache[ent].matches;
29128     };
29129
29130     features.getParents = function(entity, resolver, geometry) {
29131         if (geometry === 'point') return [];
29132
29133         var ent = iD.Entity.key(entity);
29134         if (!_cache[ent]) {
29135             _cache[ent] = {};
29136         }
29137
29138         if (!_cache[ent].parents) {
29139             var parents = [];
29140             if (geometry === 'vertex') {
29141                 parents = resolver.parentWays(entity);
29142             } else {   // 'line', 'area', 'relation'
29143                 parents = resolver.parentRelations(entity);
29144             }
29145             _cache[ent].parents = parents;
29146         }
29147         return _cache[ent].parents;
29148     };
29149
29150     features.isHiddenFeature = function(entity, resolver, geometry) {
29151         if (!_hidden.length) return false;
29152         if (!entity.version) return false;
29153
29154         var matches = features.getMatches(entity, resolver, geometry);
29155
29156         for (var i = 0; i < _hidden.length; i++) {
29157             if (matches[_hidden[i]]) return true;
29158         }
29159         return false;
29160     };
29161
29162     features.isHiddenChild = function(entity, resolver, geometry) {
29163         if (!_hidden.length) return false;
29164         if (!entity.version || geometry === 'point') return false;
29165
29166         var parents = features.getParents(entity, resolver, geometry);
29167         if (!parents.length) return false;
29168
29169         for (var i = 0; i < parents.length; i++) {
29170             if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
29171                 return false;
29172             }
29173         }
29174         return true;
29175     };
29176
29177     features.hasHiddenConnections = function(entity, resolver) {
29178         if (!_hidden.length) return false;
29179         var childNodes, connections;
29180
29181         if (entity.type === 'midpoint') {
29182             childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
29183             connections = [];
29184         } else {
29185             childNodes = entity.nodes ? resolver.childNodes(entity) : [];
29186             connections = features.getParents(entity, resolver, entity.geometry(resolver));
29187         }
29188
29189         // gather ways connected to child nodes..
29190         connections = _.reduce(childNodes, function(result, e) {
29191             return resolver.isShared(e) ? _.union(result, resolver.parentWays(e)) : result;
29192         }, connections);
29193
29194         return connections.length ? _.any(connections, function(e) {
29195             return features.isHidden(e, resolver, e.geometry(resolver));
29196         }) : false;
29197     };
29198
29199     features.isHidden = function(entity, resolver, geometry) {
29200         if (!_hidden.length) return false;
29201         if (!entity.version) return false;
29202
29203         var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
29204         return fn(entity, resolver, geometry);
29205     };
29206
29207     features.filter = function(d, resolver) {
29208         if (!_hidden.length) return d;
29209
29210         var result = [];
29211         for (var i = 0; i < d.length; i++) {
29212             var entity = d[i];
29213             if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
29214                 result.push(entity);
29215             }
29216         }
29217         return result;
29218     };
29219
29220     return d3.rebind(features, dispatch, 'on');
29221 };
29222 iD.Map = function(context) {
29223     var dimensions = [1, 1],
29224         dispatch = d3.dispatch('move', 'drawn'),
29225         projection = context.projection,
29226         zoom = d3.behavior.zoom()
29227             .translate(projection.translate())
29228             .scale(projection.scale() * 2 * Math.PI)
29229             .scaleExtent([1024, 256 * Math.pow(2, 24)])
29230             .on('zoom', zoomPan),
29231         dblclickEnabled = true,
29232         redrawEnabled = true,
29233         transformStart,
29234         transformed = false,
29235         minzoom = 0,
29236         drawLayers = iD.svg.Layers(projection, context),
29237         drawPoints = iD.svg.Points(projection, context),
29238         drawVertices = iD.svg.Vertices(projection, context),
29239         drawLines = iD.svg.Lines(projection),
29240         drawAreas = iD.svg.Areas(projection),
29241         drawMidpoints = iD.svg.Midpoints(projection, context),
29242         drawLabels = iD.svg.Labels(projection, context),
29243         supersurface,
29244         wrapper,
29245         surface,
29246         mouse,
29247         mousemove;
29248
29249     function map(selection) {
29250         context.history()
29251             .on('change.map', redraw);
29252         context.background()
29253             .on('change.map', redraw);
29254         context.features()
29255             .on('redraw.map', redraw);
29256         drawLayers
29257             .on('change.map', function() {
29258                 context.background().updateImagery();
29259                 redraw();
29260             });
29261
29262         selection
29263             .on('dblclick.map', dblClick)
29264             .call(zoom);
29265
29266         supersurface = selection.append('div')
29267             .attr('id', 'supersurface')
29268             .call(iD.util.setTransform, 0, 0);
29269
29270         // Need a wrapper div because Opera can't cope with an absolutely positioned
29271         // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
29272         wrapper = supersurface
29273             .append('div')
29274             .attr('class', 'layer layer-data');
29275
29276         map.surface = surface = wrapper
29277             .call(drawLayers)
29278             .selectAll('.surface')
29279             .attr('id', 'surface');
29280
29281         surface
29282             .on('mousedown.zoom', function() {
29283                 if (d3.event.button === 2) {
29284                     d3.event.stopPropagation();
29285                 }
29286             }, true)
29287             .on('mouseup.zoom', function() {
29288                 if (resetTransform()) redraw();
29289             })
29290             .on('mousemove.map', function() {
29291                 mousemove = d3.event;
29292             })
29293             .on('mouseover.vertices', function() {
29294                 if (map.editable() && !transformed) {
29295                     var hover = d3.event.target.__data__;
29296                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29297                     dispatch.drawn({full: false});
29298                 }
29299             })
29300             .on('mouseout.vertices', function() {
29301                 if (map.editable() && !transformed) {
29302                     var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__;
29303                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29304                     dispatch.drawn({full: false});
29305                 }
29306             });
29307
29308
29309         supersurface
29310             .call(context.background());
29311
29312
29313         context.on('enter.map', function() {
29314             if (map.editable() && !transformed) {
29315                 var all = context.intersects(map.extent()),
29316                     filter = d3.functor(true),
29317                     graph = context.graph();
29318
29319                 all = context.features().filter(all, graph);
29320                 surface
29321                     .call(drawVertices, graph, all, filter, map.extent(), map.zoom())
29322                     .call(drawMidpoints, graph, all, filter, map.trimmedExtent());
29323                 dispatch.drawn({full: false});
29324             }
29325         });
29326
29327         map.dimensions(selection.dimensions());
29328
29329         drawLabels.supersurface(supersurface);
29330     }
29331
29332     function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }
29333
29334     function drawVector(difference, extent) {
29335         var graph = context.graph(),
29336             features = context.features(),
29337             all = context.intersects(map.extent()),
29338             data, filter;
29339
29340         if (difference) {
29341             var complete = difference.complete(map.extent());
29342             data = _.compact(_.values(complete));
29343             filter = function(d) { return d.id in complete; };
29344             features.clear(data);
29345
29346         } else {
29347             // force a full redraw if gatherStats detects that a feature
29348             // should be auto-hidden (e.g. points or buildings)..
29349             if (features.gatherStats(all, graph, dimensions)) {
29350                 extent = undefined;
29351             }
29352
29353             if (extent) {
29354                 data = context.intersects(map.extent().intersection(extent));
29355                 var set = d3.set(_.pluck(data, 'id'));
29356                 filter = function(d) { return set.has(d.id); };
29357
29358             } else {
29359                 data = all;
29360                 filter = d3.functor(true);
29361             }
29362         }
29363
29364         data = features.filter(data, graph);
29365
29366         surface
29367             .call(drawVertices, graph, data, filter, map.extent(), map.zoom())
29368             .call(drawLines, graph, data, filter)
29369             .call(drawAreas, graph, data, filter)
29370             .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
29371             .call(drawLabels, graph, data, filter, dimensions, !difference && !extent)
29372             .call(drawPoints, graph, data, filter);
29373
29374         dispatch.drawn({full: true});
29375     }
29376
29377     function editOff() {
29378         context.features().resetStats();
29379         surface.selectAll('.layer-osm *').remove();
29380         dispatch.drawn({full: true});
29381     }
29382
29383     function dblClick() {
29384         if (!dblclickEnabled) {
29385             d3.event.preventDefault();
29386             d3.event.stopImmediatePropagation();
29387         }
29388     }
29389
29390     function zoomPan() {
29391         if (Math.log(d3.event.scale) / Math.LN2 - 8 < minzoom) {
29392             surface.interrupt();
29393             iD.ui.flash(context.container())
29394                 .select('.content')
29395                 .text(t('cannot_zoom'));
29396             setZoom(context.minEditableZoom(), true);
29397             queueRedraw();
29398             dispatch.move(map);
29399             return;
29400         }
29401
29402         projection
29403             .translate(d3.event.translate)
29404             .scale(d3.event.scale / (2 * Math.PI));
29405
29406         var scale = d3.event.scale / transformStart[0],
29407             tX = (d3.event.translate[0] / scale - transformStart[1][0]) * scale,
29408             tY = (d3.event.translate[1] / scale - transformStart[1][1]) * scale;
29409
29410         transformed = true;
29411         iD.util.setTransform(supersurface, tX, tY, scale);
29412         queueRedraw();
29413
29414         dispatch.move(map);
29415     }
29416
29417     function resetTransform() {
29418         if (!transformed) return false;
29419
29420         surface.selectAll('.radial-menu').interrupt().remove();
29421         iD.util.setTransform(supersurface, 0, 0);
29422         transformed = false;
29423         return true;
29424     }
29425
29426     function redraw(difference, extent) {
29427         if (!surface || !redrawEnabled) return;
29428
29429         clearTimeout(timeoutId);
29430
29431         // If we are in the middle of a zoom/pan, we can't do differenced redraws.
29432         // It would result in artifacts where differenced entities are redrawn with
29433         // one transform and unchanged entities with another.
29434         if (resetTransform()) {
29435             difference = extent = undefined;
29436         }
29437
29438         var zoom = String(~~map.zoom());
29439         if (surface.attr('data-zoom') !== zoom) {
29440             surface.attr('data-zoom', zoom)
29441                 .classed('low-zoom', zoom <= 16);
29442         }
29443
29444         if (!difference) {
29445             supersurface.call(context.background());
29446         }
29447
29448         // OSM
29449         if (map.editable()) {
29450             context.loadTiles(projection, dimensions);
29451             drawVector(difference, extent);
29452         } else {
29453             editOff();
29454         }
29455
29456         wrapper
29457             .call(drawLayers);
29458
29459         transformStart = [
29460             projection.scale() * 2 * Math.PI,
29461             projection.translate().slice()];
29462
29463         return map;
29464     }
29465
29466     var timeoutId;
29467     function queueRedraw() {
29468         clearTimeout(timeoutId);
29469         timeoutId = setTimeout(function() { redraw(); }, 300);
29470     }
29471
29472     function pointLocation(p) {
29473         var translate = projection.translate(),
29474             scale = projection.scale() * 2 * Math.PI;
29475         return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
29476     }
29477
29478     function locationPoint(l) {
29479         var translate = projection.translate(),
29480             scale = projection.scale() * 2 * Math.PI;
29481         return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
29482     }
29483
29484     map.mouse = function() {
29485         var e = mousemove || d3.event, s;
29486         while ((s = e.sourceEvent)) e = s;
29487         return mouse(e);
29488     };
29489
29490     map.mouseCoordinates = function() {
29491         return projection.invert(map.mouse());
29492     };
29493
29494     map.dblclickEnable = function(_) {
29495         if (!arguments.length) return dblclickEnabled;
29496         dblclickEnabled = _;
29497         return map;
29498     };
29499
29500     map.redrawEnable = function(_) {
29501         if (!arguments.length) return redrawEnabled;
29502         redrawEnabled = _;
29503         return map;
29504     };
29505
29506     function interpolateZoom(_) {
29507         var k = projection.scale(),
29508             t = projection.translate();
29509
29510         surface.node().__chart__ = {
29511             x: t[0],
29512             y: t[1],
29513             k: k * 2 * Math.PI
29514         };
29515
29516         setZoom(_);
29517         projection.scale(k).translate(t);  // undo setZoom projection changes
29518
29519         zoom.event(surface.transition());
29520     }
29521
29522     function setZoom(_, force) {
29523         if (_ === map.zoom() && !force)
29524             return false;
29525         var scale = 256 * Math.pow(2, _),
29526             center = pxCenter(),
29527             l = pointLocation(center);
29528         scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
29529         projection.scale(scale / (2 * Math.PI));
29530         zoom.scale(scale);
29531         var t = projection.translate();
29532         l = locationPoint(l);
29533         t[0] += center[0] - l[0];
29534         t[1] += center[1] - l[1];
29535         projection.translate(t);
29536         zoom.translate(projection.translate());
29537         return true;
29538     }
29539
29540     function setCenter(_) {
29541         var c = map.center();
29542         if (_[0] === c[0] && _[1] === c[1])
29543             return false;
29544         var t = projection.translate(),
29545             pxC = pxCenter(),
29546             ll = projection(_);
29547         projection.translate([
29548             t[0] - ll[0] + pxC[0],
29549             t[1] - ll[1] + pxC[1]]);
29550         zoom.translate(projection.translate());
29551         return true;
29552     }
29553
29554     map.pan = function(d) {
29555         var t = projection.translate();
29556         t[0] += d[0];
29557         t[1] += d[1];
29558         projection.translate(t);
29559         zoom.translate(projection.translate());
29560         dispatch.move(map);
29561         return redraw();
29562     };
29563
29564     map.dimensions = function(_) {
29565         if (!arguments.length) return dimensions;
29566         var center = map.center();
29567         dimensions = _;
29568         drawLayers.dimensions(dimensions);
29569         context.background().dimensions(dimensions);
29570         projection.clipExtent([[0, 0], dimensions]);
29571         mouse = iD.util.fastMouse(supersurface.node());
29572         setCenter(center);
29573         return redraw();
29574     };
29575
29576     function zoomIn(integer) {
29577       interpolateZoom(~~map.zoom() + integer);
29578     }
29579
29580     function zoomOut(integer) {
29581       interpolateZoom(~~map.zoom() - integer);
29582     }
29583
29584     map.zoomIn = function() { zoomIn(1); };
29585     map.zoomInFurther = function() { zoomIn(4); };
29586
29587     map.zoomOut = function() { zoomOut(1); };
29588     map.zoomOutFurther = function() { zoomOut(4); };
29589
29590     map.center = function(loc) {
29591         if (!arguments.length) {
29592             return projection.invert(pxCenter());
29593         }
29594
29595         if (setCenter(loc)) {
29596             dispatch.move(map);
29597         }
29598
29599         return redraw();
29600     };
29601
29602     map.zoom = function(z) {
29603         if (!arguments.length) {
29604             return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
29605         }
29606
29607         if (z < minzoom) {
29608             surface.interrupt();
29609             iD.ui.flash(context.container())
29610                 .select('.content')
29611                 .text(t('cannot_zoom'));
29612             z = context.minEditableZoom();
29613         }
29614
29615         if (setZoom(z)) {
29616             dispatch.move(map);
29617         }
29618
29619         return redraw();
29620     };
29621
29622     map.zoomTo = function(entity, zoomLimits) {
29623         var extent = entity.extent(context.graph());
29624         if (!isFinite(extent.area())) return;
29625
29626         var zoom = map.trimmedExtentZoom(extent);
29627         zoomLimits = zoomLimits || [context.minEditableZoom(), 20];
29628         map.centerZoom(extent.center(), Math.min(Math.max(zoom, zoomLimits[0]), zoomLimits[1]));
29629     };
29630
29631     map.centerZoom = function(loc, z) {
29632         var centered = setCenter(loc),
29633             zoomed   = setZoom(z);
29634
29635         if (centered || zoomed) {
29636             dispatch.move(map);
29637         }
29638
29639         return redraw();
29640     };
29641
29642     map.centerEase = function(loc) {
29643         var from = map.center().slice(),
29644             t = 0,
29645             stop;
29646
29647         surface.one('mousedown.ease', function() {
29648             stop = true;
29649         });
29650
29651         d3.timer(function() {
29652             if (stop) return true;
29653             map.center(iD.geo.interp(from, loc, (t += 1) / 10));
29654             return t === 10;
29655         }, 20);
29656         return map;
29657     };
29658
29659     map.extent = function(_) {
29660         if (!arguments.length) {
29661             return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
29662                                  projection.invert([dimensions[0], 0]));
29663         } else {
29664             var extent = iD.geo.Extent(_);
29665             map.centerZoom(extent.center(), map.extentZoom(extent));
29666         }
29667     };
29668
29669     map.trimmedExtent = function(_) {
29670         if (!arguments.length) {
29671             var headerY = 60, footerY = 30, pad = 10;
29672             return new iD.geo.Extent(projection.invert([pad, dimensions[1] - footerY - pad]),
29673                     projection.invert([dimensions[0] - pad, headerY + pad]));
29674         } else {
29675             var extent = iD.geo.Extent(_);
29676             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
29677         }
29678     };
29679
29680     function calcZoom(extent, dim) {
29681         var tl = projection([extent[0][0], extent[1][1]]),
29682             br = projection([extent[1][0], extent[0][1]]);
29683
29684         // Calculate maximum zoom that fits extent
29685         var hFactor = (br[0] - tl[0]) / dim[0],
29686             vFactor = (br[1] - tl[1]) / dim[1],
29687             hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
29688             vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
29689             newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
29690
29691         return newZoom;
29692     }
29693
29694     map.extentZoom = function(_) {
29695         return calcZoom(iD.geo.Extent(_), dimensions);
29696     };
29697
29698     map.trimmedExtentZoom = function(_) {
29699         var trimY = 120, trimX = 40,
29700             trimmed = [dimensions[0] - trimX, dimensions[1] - trimY];
29701         return calcZoom(iD.geo.Extent(_), trimmed);
29702     };
29703
29704     map.editable = function() {
29705         return map.zoom() >= context.minEditableZoom();
29706     };
29707
29708     map.minzoom = function(_) {
29709         if (!arguments.length) return minzoom;
29710         minzoom = _;
29711         return map;
29712     };
29713
29714     map.layers = drawLayers;
29715
29716     return d3.rebind(map, dispatch, 'on');
29717 };
29718 iD.TileLayer = function() {
29719     var tileSize = 256,
29720         tile = d3.geo.tile(),
29721         projection,
29722         cache = {},
29723         tileOrigin,
29724         z,
29725         transformProp = iD.util.prefixCSSProperty('Transform'),
29726         source = d3.functor('');
29727
29728
29729     // blacklist overlay tiles around Null Island..
29730     function nearNullIsland(x, y, z) {
29731         if (z >= 7) {
29732             var center = Math.pow(2, z - 1),
29733                 width = Math.pow(2, z - 6),
29734                 min = center - (width / 2),
29735                 max = center + (width / 2) - 1;
29736             return x >= min && x <= max && y >= min && y <= max;
29737         }
29738         return false;
29739     }
29740
29741     function tileSizeAtZoom(d, z) {
29742         var epsilon = 0.002;
29743         return ((tileSize * Math.pow(2, z - d[2])) / tileSize) + epsilon;
29744     }
29745
29746     function atZoom(t, distance) {
29747         var power = Math.pow(2, distance);
29748         return [
29749             Math.floor(t[0] * power),
29750             Math.floor(t[1] * power),
29751             t[2] + distance];
29752     }
29753
29754     function lookUp(d) {
29755         for (var up = -1; up > -d[2]; up--) {
29756             var tile = atZoom(d, up);
29757             if (cache[source.url(tile)] !== false) {
29758                 return tile;
29759             }
29760         }
29761     }
29762
29763     function uniqueBy(a, n) {
29764         var o = [], seen = {};
29765         for (var i = 0; i < a.length; i++) {
29766             if (seen[a[i][n]] === undefined) {
29767                 o.push(a[i]);
29768                 seen[a[i][n]] = true;
29769             }
29770         }
29771         return o;
29772     }
29773
29774     function addSource(d) {
29775         d.push(source.url(d));
29776         return d;
29777     }
29778
29779     // Update tiles based on current state of `projection`.
29780     function background(selection) {
29781         tile.scale(projection.scale() * 2 * Math.PI)
29782             .translate(projection.translate());
29783
29784         tileOrigin = [
29785             projection.scale() * Math.PI - projection.translate()[0],
29786             projection.scale() * Math.PI - projection.translate()[1]];
29787
29788         z = Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.log(2) - 8, 0);
29789
29790         render(selection);
29791     }
29792
29793     // Derive the tiles onscreen, remove those offscreen and position them.
29794     // Important that this part not depend on `projection` because it's
29795     // rentered when tiles load/error (see #644).
29796     function render(selection) {
29797         var requests = [];
29798
29799         if (source.validZoom(z)) {
29800             tile().forEach(function(d) {
29801                 addSource(d);
29802                 if (d[3] === '') return;
29803                 if (typeof d[3] !== 'string') return; // Workaround for chrome crash https://github.com/openstreetmap/iD/issues/2295
29804                 requests.push(d);
29805                 if (cache[d[3]] === false && lookUp(d)) {
29806                     requests.push(addSource(lookUp(d)));
29807                 }
29808             });
29809
29810             requests = uniqueBy(requests, 3).filter(function(r) {
29811                 if (!!source.overlay && nearNullIsland(r[0], r[1], r[2])) {
29812                     return false;
29813                 }
29814                 // don't re-request tiles which have failed in the past
29815                 return cache[r[3]] !== false;
29816             });
29817         }
29818
29819         var pixelOffset = [
29820             source.offset()[0] * Math.pow(2, z),
29821             source.offset()[1] * Math.pow(2, z)
29822         ];
29823
29824         function load(d) {
29825             cache[d[3]] = true;
29826             d3.select(this)
29827                 .on('error', null)
29828                 .on('load', null)
29829                 .classed('tile-loaded', true);
29830             render(selection);
29831         }
29832
29833         function error(d) {
29834             cache[d[3]] = false;
29835             d3.select(this)
29836                 .on('error', null)
29837                 .on('load', null)
29838                 .remove();
29839             render(selection);
29840         }
29841
29842         function imageTransform(d) {
29843             var _ts = tileSize * Math.pow(2, z - d[2]);
29844             var scale = tileSizeAtZoom(d, z);
29845             return 'translate(' +
29846                 ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0]) + 'px,' +
29847                 ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1]) + 'px)' +
29848                 'scale(' + scale + ',' + scale + ')';
29849         }
29850
29851         var image = selection
29852             .selectAll('img')
29853             .data(requests, function(d) { return d[3]; });
29854
29855         image.exit()
29856             .style(transformProp, imageTransform)
29857             .classed('tile-removing', true)
29858             .each(function() {
29859                 var tile = d3.select(this);
29860                 window.setTimeout(function() {
29861                     if (tile.classed('tile-removing')) {
29862                         tile.remove();
29863                     }
29864                 }, 300);
29865             });
29866
29867         image.enter().append('img')
29868             .attr('class', 'tile')
29869             .attr('src', function(d) { return d[3]; })
29870             .on('error', error)
29871             .on('load', load);
29872
29873         image
29874             .style(transformProp, imageTransform)
29875             .classed('tile-removing', false);
29876     }
29877
29878     background.projection = function(_) {
29879         if (!arguments.length) return projection;
29880         projection = _;
29881         return background;
29882     };
29883
29884     background.dimensions = function(_) {
29885         if (!arguments.length) return tile.size();
29886         tile.size(_);
29887         return background;
29888     };
29889
29890     background.source = function(_) {
29891         if (!arguments.length) return source;
29892         source = _;
29893         cache = {};
29894         tile.scaleExtent(source.scaleExtent);
29895         return background;
29896     };
29897
29898     return background;
29899 };
29900 iD.svg = {
29901     PointTransform: function(projection) {
29902         return function(entity) {
29903             // http://jsperf.com/short-array-join
29904             var pt = projection(entity.loc);
29905             return 'translate(' + pt[0] + ',' + pt[1] + ')';
29906         };
29907     },
29908
29909     Path: function(projection, graph, polygon) {
29910         var cache = {},
29911             clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
29912             project = projection.stream,
29913             path = d3.geo.path()
29914                 .projection({stream: function(output) { return polygon ? project(output) : project(clip(output)); }});
29915
29916         return function(entity) {
29917             if (entity.id in cache) {
29918                 return cache[entity.id];
29919             } else {
29920                 return cache[entity.id] = path(entity.asGeoJSON(graph));
29921             }
29922         };
29923     },
29924
29925     OneWaySegments: function(projection, graph, dt) {
29926         return function(entity) {
29927             var a,
29928                 b,
29929                 i = 0,
29930                 offset = dt,
29931                 segments = [],
29932                 clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
29933                 coordinates = graph.childNodes(entity).map(function(n) {
29934                     return n.loc;
29935                 });
29936
29937             if (entity.tags.oneway === '-1') coordinates.reverse();
29938
29939             d3.geo.stream({
29940                 type: 'LineString',
29941                 coordinates: coordinates
29942             }, projection.stream(clip({
29943                 lineStart: function() {},
29944                 lineEnd: function() {
29945                     a = null;
29946                 },
29947                 point: function(x, y) {
29948                     b = [x, y];
29949
29950                     if (a) {
29951                         var span = iD.geo.euclideanDistance(a, b) - offset;
29952
29953                         if (span >= 0) {
29954                             var angle = Math.atan2(b[1] - a[1], b[0] - a[0]),
29955                                 dx = dt * Math.cos(angle),
29956                                 dy = dt * Math.sin(angle),
29957                                 p = [a[0] + offset * Math.cos(angle),
29958                                      a[1] + offset * Math.sin(angle)];
29959
29960                             var segment = 'M' + a[0] + ',' + a[1] +
29961                                           'L' + p[0] + ',' + p[1];
29962
29963                             for (span -= dt; span >= 0; span -= dt) {
29964                                 p[0] += dx;
29965                                 p[1] += dy;
29966                                 segment += 'L' + p[0] + ',' + p[1];
29967                             }
29968
29969                             segment += 'L' + b[0] + ',' + b[1];
29970                             segments.push({id: entity.id, index: i, d: segment});
29971                         }
29972
29973                         offset = -span;
29974                         i++;
29975                     }
29976
29977                     a = b;
29978                 }
29979             })));
29980
29981             return segments;
29982         };
29983     },
29984
29985     RelationMemberTags: function(graph) {
29986         return function(entity) {
29987             var tags = entity.tags;
29988             graph.parentRelations(entity).forEach(function(relation) {
29989                 var type = relation.tags.type;
29990                 if (type === 'multipolygon' || type === 'boundary') {
29991                     tags = _.extend({}, relation.tags, tags);
29992                 }
29993             });
29994             return tags;
29995         };
29996     }
29997 };
29998 iD.svg.Areas = function(projection) {
29999     // Patterns only work in Firefox when set directly on element.
30000     // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
30001     var patterns = {
30002         wetland: 'wetland',
30003         beach: 'beach',
30004         scrub: 'scrub',
30005         construction: 'construction',
30006         military: 'construction',
30007         cemetery: 'cemetery',
30008         grave_yard: 'cemetery',
30009         meadow: 'meadow',
30010         farm: 'farmland',
30011         farmland: 'farmland',
30012         orchard: 'orchard'
30013     };
30014
30015     var patternKeys = ['landuse', 'natural', 'amenity'];
30016
30017     function setPattern(d) {
30018         for (var i = 0; i < patternKeys.length; i++) {
30019             if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) {
30020                 this.style.fill = this.style.stroke = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")';
30021                 return;
30022             }
30023         }
30024         this.style.fill = this.style.stroke = '';
30025     }
30026
30027     return function drawAreas(surface, graph, entities, filter) {
30028         var path = iD.svg.Path(projection, graph, true),
30029             areas = {},
30030             multipolygon;
30031
30032         for (var i = 0; i < entities.length; i++) {
30033             var entity = entities[i];
30034             if (entity.geometry(graph) !== 'area') continue;
30035
30036             multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph);
30037             if (multipolygon) {
30038                 areas[multipolygon.id] = {
30039                     entity: multipolygon.mergeTags(entity.tags),
30040                     area: Math.abs(entity.area(graph))
30041                 };
30042             } else if (!areas[entity.id]) {
30043                 areas[entity.id] = {
30044                     entity: entity,
30045                     area: Math.abs(entity.area(graph))
30046                 };
30047             }
30048         }
30049
30050         areas = d3.values(areas).filter(function hasPath(a) { return path(a.entity); });
30051         areas.sort(function areaSort(a, b) { return b.area - a.area; });
30052         areas = _.pluck(areas, 'entity');
30053
30054         var strokes = areas.filter(function(area) {
30055             return area.type === 'way';
30056         });
30057
30058         var data = {
30059             clip: areas,
30060             shadow: strokes,
30061             stroke: strokes,
30062             fill: areas
30063         };
30064
30065         var clipPaths = surface.selectAll('defs').selectAll('.clipPath')
30066            .filter(filter)
30067            .data(data.clip, iD.Entity.key);
30068
30069         clipPaths.enter()
30070            .append('clipPath')
30071            .attr('class', 'clipPath')
30072            .attr('id', function(entity) { return entity.id + '-clippath'; })
30073            .append('path');
30074
30075         clipPaths.selectAll('path')
30076            .attr('d', path);
30077
30078         clipPaths.exit()
30079            .remove();
30080
30081         var areagroup = surface
30082             .selectAll('.layer-areas')
30083             .selectAll('g.areagroup')
30084             .data(['fill', 'shadow', 'stroke']);
30085
30086         areagroup.enter()
30087             .append('g')
30088             .attr('class', function(d) { return 'layer areagroup area-' + d; });
30089
30090         var paths = areagroup
30091             .selectAll('path')
30092             .filter(filter)
30093             .data(function(layer) { return data[layer]; }, iD.Entity.key);
30094
30095         // Remove exiting areas first, so they aren't included in the `fills`
30096         // array used for sorting below (https://github.com/openstreetmap/iD/issues/1903).
30097         paths.exit()
30098             .remove();
30099
30100         var fills = surface.selectAll('.area-fill path.area')[0];
30101
30102         var bisect = d3.bisector(function(node) {
30103             return -node.__data__.area(graph);
30104         }).left;
30105
30106         function sortedByArea(entity) {
30107             if (this.__data__ === 'fill') {
30108                 return fills[bisect(fills, -entity.area(graph))];
30109             }
30110         }
30111
30112         paths.enter()
30113             .insert('path', sortedByArea)
30114             .each(function(entity) {
30115                 var layer = this.parentNode.__data__;
30116
30117                 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
30118
30119                 if (layer === 'fill') {
30120                     this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)');
30121                     setPattern.apply(this, arguments);
30122                 }
30123             })
30124             .call(iD.svg.TagClasses());
30125
30126         paths
30127             .attr('d', path);
30128     };
30129 };
30130 /*
30131     A standalone SVG element that contains only a `defs` sub-element. To be
30132     used once globally, since defs IDs must be unique within a document.
30133 */
30134 iD.svg.Defs = function(context) {
30135
30136     function SVGSpriteDefinition(id, href) {
30137         return function(defs) {
30138             d3.xml(href, 'image/svg+xml', function(err, svg) {
30139                 if (err) return;
30140                 defs.node().appendChild(
30141                     d3.select(svg.documentElement).attr('id', id).node()
30142                 );
30143             });
30144         };
30145     }
30146
30147     return function drawDefs(selection) {
30148         var defs = selection.append('defs');
30149
30150         // marker
30151         defs.append('marker')
30152             .attr({
30153                 id: 'oneway-marker',
30154                 viewBox: '0 0 10 10',
30155                 refY: 2.5,
30156                 refX: 5,
30157                 markerWidth: 2,
30158                 markerHeight: 2,
30159                 markerUnits: 'strokeWidth',
30160                 orient: 'auto'
30161             })
30162             .append('path')
30163             .attr('class', 'oneway')
30164             .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')
30165             .attr('stroke', 'none')
30166             .attr('fill', '#000')
30167             .attr('opacity', '0.5');
30168
30169         // patterns
30170         var patterns = defs.selectAll('pattern')
30171             .data([
30172                 // pattern name, pattern image name
30173                 ['wetland', 'wetland'],
30174                 ['construction', 'construction'],
30175                 ['cemetery', 'cemetery'],
30176                 ['orchard', 'orchard'],
30177                 ['farmland', 'farmland'],
30178                 ['beach', 'dots'],
30179                 ['scrub', 'dots'],
30180                 ['meadow', 'dots']
30181             ])
30182             .enter()
30183             .append('pattern')
30184             .attr({
30185                 id: function (d) {
30186                     return 'pattern-' + d[0];
30187                 },
30188                 width: 32,
30189                 height: 32,
30190                 patternUnits: 'userSpaceOnUse'
30191             });
30192
30193         patterns.append('rect')
30194             .attr({
30195                 x: 0,
30196                 y: 0,
30197                 width: 32,
30198                 height: 32,
30199                 'class': function (d) {
30200                     return 'pattern-color-' + d[0];
30201                 }
30202             });
30203
30204         patterns.append('image')
30205             .attr({
30206                 x: 0,
30207                 y: 0,
30208                 width: 32,
30209                 height: 32
30210             })
30211             .attr('xlink:href', function (d) {
30212                 return context.imagePath('pattern/' + d[1] + '.png');
30213             });
30214
30215         // clip paths
30216         defs.selectAll()
30217             .data([12, 18, 20, 32, 45])
30218             .enter().append('clipPath')
30219             .attr('id', function (d) {
30220                 return 'clip-square-' + d;
30221             })
30222             .append('rect')
30223             .attr('x', 0)
30224             .attr('y', 0)
30225             .attr('width', function (d) {
30226                 return d;
30227             })
30228             .attr('height', function (d) {
30229                 return d;
30230             });
30231
30232         defs.call(SVGSpriteDefinition(
30233             'iD-sprite',
30234             context.imagePath('iD-sprite.svg')));
30235
30236         defs.call(SVGSpriteDefinition(
30237             'maki-sprite',
30238             context.imagePath('maki-sprite.svg')));
30239     };
30240 };
30241 iD.svg.Gpx = function(projection, context, dispatch) {
30242     var showLabels = true,
30243         layer;
30244
30245     function init() {
30246         if (iD.svg.Gpx.initialized) return;  // run once
30247
30248         iD.svg.Gpx.geojson = {};
30249         iD.svg.Gpx.enabled = true;
30250
30251         function over() {
30252             d3.event.stopPropagation();
30253             d3.event.preventDefault();
30254             d3.event.dataTransfer.dropEffect = 'copy';
30255         }
30256
30257         d3.select('body')
30258             .attr('dropzone', 'copy')
30259             .on('drop.localgpx', function() {
30260                 d3.event.stopPropagation();
30261                 d3.event.preventDefault();
30262                 if (!iD.detect().filedrop) return;
30263                 drawGpx.files(d3.event.dataTransfer.files);
30264             })
30265             .on('dragenter.localgpx', over)
30266             .on('dragexit.localgpx', over)
30267             .on('dragover.localgpx', over);
30268
30269         iD.svg.Gpx.initialized = true;
30270     }
30271
30272
30273     function drawGpx(surface) {
30274         var geojson = iD.svg.Gpx.geojson,
30275             enabled = iD.svg.Gpx.enabled;
30276
30277         layer = surface.selectAll('.layer-gpx')
30278             .data(enabled ? [0] : []);
30279
30280         layer.enter()
30281             .append('g')
30282             .attr('class', 'layer-gpx');
30283
30284         layer.exit()
30285             .remove();
30286
30287
30288         var paths = layer
30289             .selectAll('path')
30290             .data([geojson]);
30291
30292         paths.enter()
30293             .append('path')
30294             .attr('class', 'gpx');
30295
30296         paths.exit()
30297             .remove();
30298
30299         var path = d3.geo.path()
30300             .projection(projection);
30301
30302         paths
30303             .attr('d', path);
30304
30305
30306         var labels = layer.selectAll('text')
30307             .data(showLabels && geojson.features ? geojson.features : []);
30308
30309         labels.enter()
30310             .append('text')
30311             .attr('class', 'gpx');
30312
30313         labels.exit()
30314             .remove();
30315
30316         labels
30317             .text(function(d) {
30318                 return d.properties.desc || d.properties.name;
30319             })
30320             .attr('x', function(d) {
30321                 var centroid = path.centroid(d);
30322                 return centroid[0] + 7;
30323             })
30324             .attr('y', function(d) {
30325                 var centroid = path.centroid(d);
30326                 return centroid[1];
30327             });
30328
30329     }
30330
30331     function toDom(x) {
30332         return (new DOMParser()).parseFromString(x, 'text/xml');
30333     }
30334
30335     drawGpx.showLabels = function(_) {
30336         if (!arguments.length) return showLabels;
30337         showLabels = _;
30338         return this;
30339     };
30340
30341     drawGpx.enabled = function(_) {
30342         if (!arguments.length) return iD.svg.Gpx.enabled;
30343         iD.svg.Gpx.enabled = _;
30344         dispatch.change();
30345         return this;
30346     };
30347
30348     drawGpx.hasGpx = function() {
30349         var geojson = iD.svg.Gpx.geojson;
30350         return (!(_.isEmpty(geojson) || _.isEmpty(geojson.features)));
30351     };
30352
30353     drawGpx.geojson = function(gj) {
30354         if (!arguments.length) return iD.svg.Gpx.geojson;
30355         if (_.isEmpty(gj) || _.isEmpty(gj.features)) return this;
30356         iD.svg.Gpx.geojson = gj;
30357         dispatch.change();
30358         return this;
30359     };
30360
30361     drawGpx.url = function(url) {
30362         d3.text(url, function(err, data) {
30363             if (!err) {
30364                 drawGpx.geojson(toGeoJSON.gpx(toDom(data)));
30365             }
30366         });
30367         return this;
30368     };
30369
30370     drawGpx.files = function(fileList) {
30371         if (!fileList.length) return this;
30372         var f = fileList[0],
30373             reader = new FileReader();
30374
30375         reader.onload = function(e) {
30376             drawGpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom();
30377         };
30378
30379         reader.readAsText(f);
30380         return this;
30381     };
30382
30383     drawGpx.fitZoom = function() {
30384         if (!this.hasGpx()) return this;
30385         var geojson = iD.svg.Gpx.geojson;
30386
30387         var map = context.map(),
30388             viewport = map.trimmedExtent().polygon(),
30389             coords = _.reduce(geojson.features, function(coords, feature) {
30390                 var c = feature.geometry.coordinates;
30391                 return _.union(coords, feature.geometry.type === 'Point' ? [c] : c);
30392             }, []);
30393
30394         if (!iD.geo.polygonIntersectsPolygon(viewport, coords, true)) {
30395             var extent = iD.geo.Extent(d3.geo.bounds(geojson));
30396             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
30397         }
30398
30399         return this;
30400     };
30401
30402     init();
30403     return drawGpx;
30404 };
30405 iD.svg.Icon = function(name, svgklass, useklass) {
30406     return function drawIcon(selection) {
30407         selection.selectAll('svg')
30408             .data([0])
30409             .enter()
30410             .append('svg')
30411             .attr('class', 'icon ' + (svgklass || ''))
30412             .append('use')
30413             .attr('xlink:href', name)
30414             .attr('class', useklass);
30415     };
30416 };
30417 iD.svg.Labels = function(projection, context) {
30418     var path = d3.geo.path().projection(projection);
30419
30420     // Replace with dict and iterate over entities tags instead?
30421     var label_stack = [
30422         ['line', 'aeroway'],
30423         ['line', 'highway'],
30424         ['line', 'railway'],
30425         ['line', 'waterway'],
30426         ['area', 'aeroway'],
30427         ['area', 'amenity'],
30428         ['area', 'building'],
30429         ['area', 'historic'],
30430         ['area', 'leisure'],
30431         ['area', 'man_made'],
30432         ['area', 'natural'],
30433         ['area', 'shop'],
30434         ['area', 'tourism'],
30435         ['point', 'aeroway'],
30436         ['point', 'amenity'],
30437         ['point', 'building'],
30438         ['point', 'historic'],
30439         ['point', 'leisure'],
30440         ['point', 'man_made'],
30441         ['point', 'natural'],
30442         ['point', 'shop'],
30443         ['point', 'tourism'],
30444         ['line', 'name'],
30445         ['area', 'name'],
30446         ['point', 'name']
30447     ];
30448
30449     var default_size = 12;
30450
30451     var font_sizes = label_stack.map(function(d) {
30452         var style = iD.util.getStyle('text.' + d[0] + '.tag-' + d[1]),
30453             m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30454         if (m) return parseInt(m[1], 10);
30455
30456         style = iD.util.getStyle('text.' + d[0]);
30457         m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30458         if (m) return parseInt(m[1], 10);
30459
30460         return default_size;
30461     });
30462
30463     var iconSize = 18;
30464
30465     var pointOffsets = [
30466         [15, -11, 'start'], // right
30467         [10, -11, 'start'], // unused right now
30468         [-15, -11, 'end']
30469     ];
30470
30471     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25,
30472         75, 20, 80, 15, 95, 10, 90, 5, 95];
30473
30474
30475     var noIcons = ['building', 'landuse', 'natural'];
30476     function blacklisted(preset) {
30477         return _.any(noIcons, function(s) {
30478             return preset.id.indexOf(s) >= 0;
30479         });
30480     }
30481
30482     function get(array, prop) {
30483         return function(d, i) { return array[i][prop]; };
30484     }
30485
30486     var textWidthCache = {};
30487
30488     function textWidth(text, size, elem) {
30489         var c = textWidthCache[size];
30490         if (!c) c = textWidthCache[size] = {};
30491
30492         if (c[text]) {
30493             return c[text];
30494
30495         } else if (elem) {
30496             c[text] = elem.getComputedTextLength();
30497             return c[text];
30498
30499         } else {
30500             var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
30501             if (str === null) {
30502                 return size / 3 * 2 * text.length;
30503             } else {
30504                 return size / 3 * (2 * text.length + str.length);
30505             }
30506         }
30507     }
30508
30509     function drawLineLabels(group, entities, filter, classes, labels) {
30510         var texts = group.selectAll('text.' + classes)
30511             .filter(filter)
30512             .data(entities, iD.Entity.key);
30513
30514         texts.enter()
30515             .append('text')
30516             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
30517             .append('textPath')
30518             .attr('class', 'textpath');
30519
30520
30521         texts.selectAll('.textpath')
30522             .filter(filter)
30523             .data(entities, iD.Entity.key)
30524             .attr({
30525                 'startOffset': '50%',
30526                 'xlink:href': function(d) { return '#labelpath-' + d.id; }
30527             })
30528             .text(iD.util.displayName);
30529
30530         texts.exit().remove();
30531     }
30532
30533     function drawLinePaths(group, entities, filter, classes, labels) {
30534         var halos = group.selectAll('path')
30535             .filter(filter)
30536             .data(entities, iD.Entity.key);
30537
30538         halos.enter()
30539             .append('path')
30540             .style('stroke-width', get(labels, 'font-size'))
30541             .attr('id', function(d) { return 'labelpath-' + d.id; })
30542             .attr('class', classes);
30543
30544         halos.attr('d', get(labels, 'lineString'));
30545
30546         halos.exit().remove();
30547     }
30548
30549     function drawPointLabels(group, entities, filter, classes, labels) {
30550         var texts = group.selectAll('text.' + classes)
30551             .filter(filter)
30552             .data(entities, iD.Entity.key);
30553
30554         texts.enter()
30555             .append('text')
30556             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; });
30557
30558         texts.attr('x', get(labels, 'x'))
30559             .attr('y', get(labels, 'y'))
30560             .style('text-anchor', get(labels, 'textAnchor'))
30561             .text(iD.util.displayName)
30562             .each(function(d, i) { textWidth(iD.util.displayName(d), labels[i].height, this); });
30563
30564         texts.exit().remove();
30565         return texts;
30566     }
30567
30568     function drawAreaLabels(group, entities, filter, classes, labels) {
30569         entities = entities.filter(hasText);
30570         labels = labels.filter(hasText);
30571         return drawPointLabels(group, entities, filter, classes, labels);
30572
30573         function hasText(d, i) {
30574             return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
30575         }
30576     }
30577
30578     function drawAreaIcons(group, entities, filter, classes, labels) {
30579         var icons = group.selectAll('use')
30580             .filter(filter)
30581             .data(entities, iD.Entity.key);
30582
30583         icons.enter()
30584             .append('use')
30585             .attr('class', 'icon areaicon')
30586             .attr('width', '18px')
30587             .attr('height', '18px');
30588
30589         icons.attr('transform', get(labels, 'transform'))
30590             .attr('xlink:href', function(d) {
30591                 var icon = context.presets().match(d, context.graph()).icon;
30592                 return '#' + icon + (icon === 'hairdresser' ? '-24': '-18');    // workaround: maki hairdresser-18 broken?
30593             });
30594
30595
30596         icons.exit().remove();
30597     }
30598
30599     function reverse(p) {
30600         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
30601         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
30602     }
30603
30604     function lineString(nodes) {
30605         return 'M' + nodes.join('L');
30606     }
30607
30608     function subpath(nodes, from, to) {
30609         function segmentLength(i) {
30610             var dx = nodes[i][0] - nodes[i + 1][0];
30611             var dy = nodes[i][1] - nodes[i + 1][1];
30612             return Math.sqrt(dx * dx + dy * dy);
30613         }
30614
30615         var sofar = 0,
30616             start, end, i0, i1;
30617         for (var i = 0; i < nodes.length - 1; i++) {
30618             var current = segmentLength(i);
30619             var portion;
30620             if (!start && sofar + current >= from) {
30621                 portion = (from - sofar) / current;
30622                 start = [
30623                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30624                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30625                 ];
30626                 i0 = i + 1;
30627             }
30628             if (!end && sofar + current >= to) {
30629                 portion = (to - sofar) / current;
30630                 end = [
30631                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30632                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30633                 ];
30634                 i1 = i + 1;
30635             }
30636             sofar += current;
30637
30638         }
30639         var ret = nodes.slice(i0, i1);
30640         ret.unshift(start);
30641         ret.push(end);
30642         return ret;
30643
30644     }
30645
30646     function hideOnMouseover() {
30647         var layers = d3.select(this)
30648             .selectAll('.layer-label, .layer-halo');
30649
30650         layers.selectAll('.proximate')
30651             .classed('proximate', false);
30652
30653         var mouse = context.mouse(),
30654             pad = 50,
30655             rect = [mouse[0] - pad, mouse[1] - pad, mouse[0] + pad, mouse[1] + pad],
30656             ids = _.pluck(rtree.search(rect), 'id');
30657
30658         if (!ids.length) return;
30659         layers.selectAll('.' + ids.join(', .'))
30660             .classed('proximate', true);
30661     }
30662
30663     var rtree = rbush(),
30664         rectangles = {};
30665
30666     function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
30667         var hidePoints = !surface.selectAll('.node.point').node();
30668
30669         var labelable = [], i, k, entity;
30670         for (i = 0; i < label_stack.length; i++) labelable.push([]);
30671
30672         if (fullRedraw) {
30673             rtree.clear();
30674             rectangles = {};
30675         } else {
30676             for (i = 0; i < entities.length; i++) {
30677                 rtree.remove(rectangles[entities[i].id]);
30678             }
30679         }
30680
30681         // Split entities into groups specified by label_stack
30682         for (i = 0; i < entities.length; i++) {
30683             entity = entities[i];
30684             var geometry = entity.geometry(graph);
30685
30686             if (geometry === 'vertex')
30687                 continue;
30688             if (hidePoints && geometry === 'point')
30689                 continue;
30690
30691             var preset = geometry === 'area' && context.presets().match(entity, graph),
30692                 icon = preset && !blacklisted(preset) && preset.icon;
30693
30694             if (!icon && !iD.util.displayName(entity))
30695                 continue;
30696
30697             for (k = 0; k < label_stack.length; k++) {
30698                 if (geometry === label_stack[k][0] && entity.tags[label_stack[k][1]]) {
30699                     labelable[k].push(entity);
30700                     break;
30701                 }
30702             }
30703         }
30704
30705         var positions = {
30706             point: [],
30707             line: [],
30708             area: []
30709         };
30710
30711         var labelled = {
30712             point: [],
30713             line: [],
30714             area: []
30715         };
30716
30717         // Try and find a valid label for labellable entities
30718         for (k = 0; k < labelable.length; k++) {
30719             var font_size = font_sizes[k];
30720             for (i = 0; i < labelable[k].length; i++) {
30721                 entity = labelable[k][i];
30722                 var name = iD.util.displayName(entity),
30723                     width = name && textWidth(name, font_size),
30724                     p;
30725                 if (entity.geometry(graph) === 'point') {
30726                     p = getPointLabel(entity, width, font_size);
30727                 } else if (entity.geometry(graph) === 'line') {
30728                     p = getLineLabel(entity, width, font_size);
30729                 } else if (entity.geometry(graph) === 'area') {
30730                     p = getAreaLabel(entity, width, font_size);
30731                 }
30732                 if (p) {
30733                     p.classes = entity.geometry(graph) + ' tag-' + label_stack[k][1];
30734                     positions[entity.geometry(graph)].push(p);
30735                     labelled[entity.geometry(graph)].push(entity);
30736                 }
30737             }
30738         }
30739
30740         function getPointLabel(entity, width, height) {
30741             var coord = projection(entity.loc),
30742                 m = 5,  // margin
30743                 offset = pointOffsets[0],
30744                 p = {
30745                     height: height,
30746                     width: width,
30747                     x: coord[0] + offset[0],
30748                     y: coord[1] + offset[1],
30749                     textAnchor: offset[2]
30750                 };
30751             var rect = [p.x - m, p.y - m, p.x + width + m, p.y + height + m];
30752             if (tryInsert(rect, entity.id)) return p;
30753         }
30754
30755
30756         function getLineLabel(entity, width, height) {
30757             var nodes = _.pluck(graph.childNodes(entity), 'loc').map(projection),
30758                 length = iD.geo.pathLength(nodes);
30759             if (length < width + 20) return;
30760
30761             for (var i = 0; i < lineOffsets.length; i++) {
30762                 var offset = lineOffsets[i],
30763                     middle = offset / 100 * length,
30764                     start = middle - width/2;
30765                 if (start < 0 || start + width > length) continue;
30766                 var sub = subpath(nodes, start, start + width),
30767                     rev = reverse(sub),
30768                     rect = [
30769                         Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
30770                         Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
30771                         Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
30772                         Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
30773                     ];
30774                 if (rev) sub = sub.reverse();
30775                 if (tryInsert(rect, entity.id)) return {
30776                     'font-size': height + 2,
30777                     lineString: lineString(sub),
30778                     startOffset: offset + '%'
30779                 };
30780             }
30781         }
30782
30783         function getAreaLabel(entity, width, height) {
30784             var centroid = path.centroid(entity.asGeoJSON(graph, true)),
30785                 extent = entity.extent(graph),
30786                 entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
30787                 rect;
30788
30789             if (isNaN(centroid[0]) || entitywidth < 20) return;
30790
30791             var iconX = centroid[0] - (iconSize/2),
30792                 iconY = centroid[1] - (iconSize/2),
30793                 textOffset = iconSize + 5;
30794
30795             var p = {
30796                 transform: 'translate(' + iconX + ',' + iconY + ')'
30797             };
30798
30799             if (width && entitywidth >= width + 20) {
30800                 p.x = centroid[0];
30801                 p.y = centroid[1] + textOffset;
30802                 p.textAnchor = 'middle';
30803                 p.height = height;
30804                 rect = [p.x - width/2, p.y, p.x + width/2, p.y + height + textOffset];
30805             } else {
30806                 rect = [iconX, iconY, iconX + iconSize, iconY + iconSize];
30807             }
30808
30809             if (tryInsert(rect, entity.id)) return p;
30810
30811         }
30812
30813         function tryInsert(rect, id) {
30814             // Check that label is visible
30815             if (rect[0] < 0 || rect[1] < 0 || rect[2] > dimensions[0] ||
30816                 rect[3] > dimensions[1]) return false;
30817             var v = rtree.search(rect).length === 0;
30818             if (v) {
30819                 rect.id = id;
30820                 rtree.insert(rect);
30821                 rectangles[id] = rect;
30822             }
30823             return v;
30824         }
30825
30826         var label = surface.selectAll('.layer-label'),
30827             halo = surface.selectAll('.layer-halo');
30828
30829         // points
30830         drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
30831         drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
30832
30833         // lines
30834         drawLinePaths(halo, labelled.line, filter, '', positions.line);
30835         drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
30836         drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
30837
30838         // areas
30839         drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
30840         drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
30841         drawAreaIcons(label, labelled.area, filter, 'arealabel-icon', positions.area);
30842
30843         // debug
30844         var showDebug = context.debugCollision();
30845         var debug = label.selectAll('.layer-label-debug')
30846             .data(showDebug ? [true] : []);
30847
30848         debug.enter()
30849             .append('g')
30850             .attr('class', 'layer-label-debug');
30851
30852         debug.exit()
30853             .remove();
30854
30855         if (showDebug) {
30856             var gj = rtree.all().map(function(d) {
30857                 return { type: 'Polygon', coordinates: [[
30858                     [d[0], d[1]],
30859                     [d[2], d[1]],
30860                     [d[2], d[3]],
30861                     [d[0], d[3]],
30862                     [d[0], d[1]]
30863                 ]]};
30864             });
30865
30866             var debugboxes = debug.selectAll('.bbox').data(gj);
30867
30868             debugboxes.enter()
30869                 .append('path')
30870                 .attr('class', 'bbox');
30871
30872             debugboxes.exit()
30873                 .remove();
30874
30875             debugboxes
30876                 .attr('d', d3.geo.path().projection(null));
30877         }
30878     }
30879
30880     drawLabels.supersurface = function(supersurface) {
30881         supersurface
30882             .on('mousemove.hidelabels', hideOnMouseover)
30883             .on('mousedown.hidelabels', function () {
30884                 supersurface.on('mousemove.hidelabels', null);
30885             })
30886             .on('mouseup.hidelabels', function () {
30887                 supersurface.on('mousemove.hidelabels', hideOnMouseover);
30888             });
30889     };
30890
30891     return drawLabels;
30892 };
30893 iD.svg.Layers = function(projection, context) {
30894     var dispatch = d3.dispatch('change'),
30895         svg = d3.select(null),
30896         layers = [
30897             { id: 'osm', layer: iD.svg.Osm(projection, context, dispatch) },
30898             { id: 'gpx', layer: iD.svg.Gpx(projection, context, dispatch) },
30899             { id: 'mapillary-images', layer: iD.svg.MapillaryImages(projection, context, dispatch) },
30900             { id: 'mapillary-signs',  layer: iD.svg.MapillarySigns(projection, context, dispatch) }
30901         ];
30902
30903
30904     function drawLayers(selection) {
30905         svg = selection.selectAll('.surface')
30906             .data([0]);
30907
30908         svg.enter()
30909             .append('svg')
30910             .attr('class', 'surface')
30911             .append('defs');
30912
30913         var groups = svg.selectAll('.data-layer')
30914             .data(layers);
30915
30916         groups.enter()
30917             .append('g')
30918             .attr('class', function(d) { return 'data-layer data-layer-' + d.id; });
30919
30920         groups
30921             .each(function(d) { d3.select(this).call(d.layer); });
30922
30923         groups.exit()
30924             .remove();
30925     }
30926
30927     drawLayers.all = function() {
30928         return layers;
30929     };
30930
30931     drawLayers.layer = function(id) {
30932         var obj = _.find(layers, 'id', id);
30933         return obj && obj.layer;
30934     };
30935
30936     drawLayers.only = function(what) {
30937         var arr = [].concat(what);
30938         drawLayers.remove(_.difference(_.pluck(layers, 'id'), arr));
30939         return this;
30940     };
30941
30942     drawLayers.remove = function(what) {
30943         var arr = [].concat(what);
30944         arr.forEach(function(id) {
30945             layers = _.reject(layers, 'id', id);
30946         });
30947         dispatch.change();
30948         return this;
30949     };
30950
30951     drawLayers.add = function(what) {
30952         var arr = [].concat(what);
30953         arr.forEach(function(obj) {
30954             if ('id' in obj && 'layer' in obj) {
30955                 layers.push(obj);
30956             }
30957         });
30958         dispatch.change();
30959         return this;
30960     };
30961
30962     drawLayers.dimensions = function(_) {
30963         if (!arguments.length) return svg.dimensions();
30964         svg.dimensions(_);
30965         layers.forEach(function(obj) {
30966             if (obj.layer.dimensions) {
30967                 obj.layer.dimensions(_);
30968             }
30969         });
30970         return this;
30971     };
30972
30973
30974     return d3.rebind(drawLayers, dispatch, 'on');
30975 };
30976 iD.svg.Lines = function(projection) {
30977
30978     var highway_stack = {
30979         motorway: 0,
30980         motorway_link: 1,
30981         trunk: 2,
30982         trunk_link: 3,
30983         primary: 4,
30984         primary_link: 5,
30985         secondary: 6,
30986         tertiary: 7,
30987         unclassified: 8,
30988         residential: 9,
30989         service: 10,
30990         footway: 11
30991     };
30992
30993     function waystack(a, b) {
30994         var as = 0, bs = 0;
30995
30996         if (a.tags.highway) { as -= highway_stack[a.tags.highway]; }
30997         if (b.tags.highway) { bs -= highway_stack[b.tags.highway]; }
30998         return as - bs;
30999     }
31000
31001     return function drawLines(surface, graph, entities, filter) {
31002         var ways = [], pathdata = {}, onewaydata = {},
31003             getPath = iD.svg.Path(projection, graph);
31004
31005         for (var i = 0; i < entities.length; i++) {
31006             var entity = entities[i],
31007                 outer = iD.geo.simpleMultipolygonOuterMember(entity, graph);
31008             if (outer) {
31009                 ways.push(entity.mergeTags(outer.tags));
31010             } else if (entity.geometry(graph) === 'line') {
31011                 ways.push(entity);
31012             }
31013         }
31014
31015         ways = ways.filter(getPath);
31016
31017         pathdata = _.groupBy(ways, function(way) { return way.layer(); });
31018
31019         _.forOwn(pathdata, function(v, k) {
31020             onewaydata[k] = _(v)
31021                 .filter(function(d) { return d.isOneWay(); })
31022                 .map(iD.svg.OneWaySegments(projection, graph, 35))
31023                 .flatten()
31024                 .valueOf();
31025         });
31026
31027         var layergroup = surface
31028             .selectAll('.layer-lines')
31029             .selectAll('g.layergroup')
31030             .data(d3.range(-10, 11));
31031
31032         layergroup.enter()
31033             .append('g')
31034             .attr('class', function(d) { return 'layer layergroup layer' + String(d); });
31035
31036
31037         var linegroup = layergroup
31038             .selectAll('g.linegroup')
31039             .data(['shadow', 'casing', 'stroke']);
31040
31041         linegroup.enter()
31042             .append('g')
31043             .attr('class', function(d) { return 'layer linegroup line-' + d; });
31044
31045
31046         var lines = linegroup
31047             .selectAll('path')
31048             .filter(filter)
31049             .data(
31050                 function() { return pathdata[this.parentNode.parentNode.__data__] || []; },
31051                 iD.Entity.key
31052             );
31053
31054         // Optimization: call simple TagClasses only on enter selection. This
31055         // works because iD.Entity.key is defined to include the entity v attribute.
31056         lines.enter()
31057             .append('path')
31058             .attr('class', function(d) { return 'way line ' + this.parentNode.__data__ + ' ' + d.id; })
31059             .call(iD.svg.TagClasses());
31060
31061         lines
31062             .sort(waystack)
31063             .attr('d', getPath)
31064             .call(iD.svg.TagClasses().tags(iD.svg.RelationMemberTags(graph)));
31065
31066         lines.exit()
31067             .remove();
31068
31069
31070         var onewaygroup = layergroup
31071             .selectAll('g.onewaygroup')
31072             .data(['oneway']);
31073
31074         onewaygroup.enter()
31075             .append('g')
31076             .attr('class', 'layer onewaygroup');
31077
31078
31079         var oneways = onewaygroup
31080             .selectAll('path')
31081             .filter(filter)
31082             .data(
31083                 function() { return onewaydata[this.parentNode.parentNode.__data__] || []; },
31084                 function(d) { return [d.id, d.index]; }
31085             );
31086
31087         oneways.enter()
31088             .append('path')
31089             .attr('class', 'oneway')
31090             .attr('marker-mid', 'url(#oneway-marker)');
31091
31092         oneways
31093             .attr('d', function(d) { return d.d; });
31094
31095         if (iD.detect().ie) {
31096             oneways.each(function() { this.parentNode.insertBefore(this, this); });
31097         }
31098
31099         oneways.exit()
31100             .remove();
31101
31102     };
31103 };
31104 iD.svg.MapillaryImages = function(projection, context, dispatch) {
31105     var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
31106         minZoom = 12,
31107         layer = d3.select(null),
31108         _mapillary;
31109
31110
31111     function init() {
31112         if (iD.svg.MapillaryImages.initialized) return;  // run once
31113         iD.svg.MapillaryImages.enabled = false;
31114         iD.svg.MapillaryImages.initialized = true;
31115     }
31116
31117     function getMapillary() {
31118         if (iD.services.mapillary && !_mapillary) {
31119             _mapillary = iD.services.mapillary().on('loadedImages', debouncedRedraw);
31120         } else if (!iD.services.mapillary && _mapillary) {
31121             _mapillary = null;
31122         }
31123         return _mapillary;
31124     }
31125
31126     function showThumbnail(image) {
31127         var mapillary = getMapillary();
31128         if (!mapillary) return;
31129
31130         var thumb = mapillary.selectedThumbnail(),
31131             posX = projection(image.loc)[0],
31132             width = layer.dimensions()[0],
31133             position = (posX < width / 2) ? 'right' : 'left';
31134
31135         if (thumb) {
31136             d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31137                 .classed('selected', function(d) { return d.key === thumb.key; });
31138         }
31139
31140         mapillary.showThumbnail(image.key, position);
31141     }
31142
31143     function hideThumbnail() {
31144         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31145             .classed('selected', false);
31146
31147         var mapillary = getMapillary();
31148         if (mapillary) {
31149             mapillary.hideThumbnail();
31150         }
31151     }
31152
31153     function showLayer() {
31154         editOn();
31155         layer
31156             .style('opacity', 0)
31157             .transition()
31158             .duration(500)
31159             .style('opacity', 1)
31160             .each('end', debouncedRedraw);
31161     }
31162
31163     function hideLayer() {
31164         debouncedRedraw.cancel();
31165         hideThumbnail();
31166         layer
31167             .transition()
31168             .duration(500)
31169             .style('opacity', 0)
31170             .each('end', editOff);
31171     }
31172
31173     function editOn() {
31174         layer.style('display', 'block');
31175     }
31176
31177     function editOff() {
31178         layer.selectAll('.viewfield-group').remove();
31179         layer.style('display', 'none');
31180     }
31181
31182     function transform(d) {
31183         var t = iD.svg.PointTransform(projection)(d);
31184         if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
31185         return t;
31186     }
31187
31188     function update() {
31189         var mapillary = getMapillary(),
31190             data = (mapillary ? mapillary.images(projection, layer.dimensions()) : []);
31191
31192         var markers = layer.selectAll('.viewfield-group')
31193             .data(data, function(d) { return d.key; });
31194
31195         // Enter
31196         var enter = markers.enter()
31197             .append('g')
31198             .attr('class', 'viewfield-group');
31199
31200         enter.append('path')
31201             .attr('class', 'viewfield')
31202             .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
31203             .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');
31204
31205         enter.append('circle')
31206             .attr('dx', '0')
31207             .attr('dy', '0')
31208             .attr('r', '6');
31209
31210         // Exit
31211         markers.exit()
31212             .remove();
31213
31214         // Update
31215         markers
31216             .attr('transform', transform);
31217     }
31218
31219     function drawImages(selection) {
31220         var enabled = iD.svg.MapillaryImages.enabled,
31221             mapillary = getMapillary();
31222
31223         layer = selection.selectAll('.layer-mapillary-images')
31224             .data(mapillary ? [0] : []);
31225
31226         layer.enter()
31227             .append('g')
31228             .attr('class', 'layer-mapillary-images')
31229             .style('display', enabled ? 'block' : 'none')
31230             .on('click', function() {   // deselect/select
31231                 var mapillary = getMapillary();
31232                 if (!mapillary) return;
31233                 var d = d3.event.target.__data__,
31234                     thumb = mapillary.selectedThumbnail();
31235                 if (thumb && thumb.key === d.key) {
31236                     hideThumbnail();
31237                 } else {
31238                     mapillary.selectedThumbnail(d);
31239                     context.map().centerEase(d.loc);
31240                     showThumbnail(d);
31241                 }
31242             })
31243             .on('mouseover', function() {
31244                 var mapillary = getMapillary();
31245                 if (!mapillary) return;
31246                 showThumbnail(d3.event.target.__data__);
31247             })
31248             .on('mouseout', function() {
31249                 var mapillary = getMapillary();
31250                 if (!mapillary) return;
31251                 var thumb = mapillary.selectedThumbnail();
31252                 if (thumb) {
31253                     showThumbnail(thumb);
31254                 } else {
31255                     hideThumbnail();
31256                 }
31257             });
31258
31259         layer.exit()
31260             .remove();
31261
31262         if (enabled) {
31263             if (mapillary && ~~context.map().zoom() >= minZoom) {
31264                 editOn();
31265                 update();
31266                 mapillary.loadImages(projection, layer.dimensions());
31267             } else {
31268                 editOff();
31269             }
31270         }
31271     }
31272
31273     drawImages.enabled = function(_) {
31274         if (!arguments.length) return iD.svg.MapillaryImages.enabled;
31275         iD.svg.MapillaryImages.enabled = _;
31276         if (iD.svg.MapillaryImages.enabled) {
31277             showLayer();
31278         } else {
31279             hideLayer();
31280         }
31281         dispatch.change();
31282         return this;
31283     };
31284
31285     drawImages.supported = function() {
31286         return !!getMapillary();
31287     };
31288
31289     drawImages.dimensions = function(_) {
31290         if (!arguments.length) return layer.dimensions();
31291         layer.dimensions(_);
31292         return this;
31293     };
31294
31295     init();
31296     return drawImages;
31297 };
31298 iD.svg.MapillarySigns = function(projection, context, dispatch) {
31299     var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
31300         minZoom = 12,
31301         layer = d3.select(null),
31302         _mapillary;
31303
31304
31305     function init() {
31306         if (iD.svg.MapillarySigns.initialized) return;  // run once
31307         iD.svg.MapillarySigns.enabled = false;
31308         iD.svg.MapillarySigns.initialized = true;
31309     }
31310
31311     function getMapillary() {
31312         if (iD.services.mapillary && !_mapillary) {
31313             _mapillary = iD.services.mapillary().on('loadedSigns', debouncedRedraw);
31314         } else if (!iD.services.mapillary && _mapillary) {
31315             _mapillary = null;
31316         }
31317         return _mapillary;
31318     }
31319
31320     function showThumbnail(image) {
31321         var mapillary = getMapillary();
31322         if (!mapillary) return;
31323
31324         var thumb = mapillary.selectedThumbnail(),
31325             posX = projection(image.loc)[0],
31326             width = layer.dimensions()[0],
31327             position = (posX < width / 2) ? 'right' : 'left';
31328
31329         if (thumb) {
31330             d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31331                 .classed('selected', function(d) { return d.key === thumb.key; });
31332         }
31333
31334         mapillary.showThumbnail(image.key, position);
31335     }
31336
31337     function hideThumbnail() {
31338         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31339             .classed('selected', false);
31340
31341         var mapillary = getMapillary();
31342         if (mapillary) {
31343             mapillary.hideThumbnail();
31344         }
31345     }
31346
31347     function showLayer() {
31348         editOn();
31349         debouncedRedraw();
31350     }
31351
31352     function hideLayer() {
31353         debouncedRedraw.cancel();
31354         hideThumbnail();
31355         editOff();
31356     }
31357
31358     function editOn() {
31359         layer.style('display', 'block');
31360     }
31361
31362     function editOff() {
31363         layer.selectAll('.icon-sign').remove();
31364         layer.style('display', 'none');
31365     }
31366
31367     function update() {
31368         var mapillary = getMapillary(),
31369             data = (mapillary ? mapillary.signs(projection, layer.dimensions()) : []);
31370
31371         var signs = layer.selectAll('.icon-sign')
31372             .data(data, function(d) { return d.key; });
31373
31374         // Enter
31375         var enter = signs.enter()
31376             .append('foreignObject')
31377             .attr('class', 'icon-sign')
31378             .attr('width', '32px')      // for Firefox
31379             .attr('height', '32px');    // for Firefox
31380
31381         enter
31382             .append('xhtml:body')
31383             .html(mapillary.signHTML);
31384
31385         enter
31386             .on('click', function(d) {   // deselect/select
31387                 var mapillary = getMapillary();
31388                 if (!mapillary) return;
31389                 var thumb = mapillary.selectedThumbnail();
31390                 if (thumb && thumb.key === d.key) {
31391                     hideThumbnail();
31392                 } else {
31393                     mapillary.selectedThumbnail(d);
31394                     context.map().centerEase(d.loc);
31395                     showThumbnail(d);
31396                 }
31397             })
31398             .on('mouseover', showThumbnail)
31399             .on('mouseout', function() {
31400                 var mapillary = getMapillary();
31401                 if (!mapillary) return;
31402                 var thumb = mapillary.selectedThumbnail();
31403                 if (thumb) {
31404                     showThumbnail(thumb);
31405                 } else {
31406                     hideThumbnail();
31407                 }
31408             });
31409
31410         // Exit
31411         signs.exit()
31412             .remove();
31413
31414         // Update
31415         signs
31416             .attr('transform', iD.svg.PointTransform(projection));
31417     }
31418
31419     function drawSigns(selection) {
31420         var enabled = iD.svg.MapillarySigns.enabled,
31421             mapillary = getMapillary();
31422
31423         layer = selection.selectAll('.layer-mapillary-signs')
31424             .data(mapillary ? [0] : []);
31425
31426         layer.enter()
31427             .append('g')
31428             .attr('class', 'layer-mapillary-signs')
31429             .style('display', enabled ? 'block' : 'none')
31430             .attr('transform', 'translate(-16, -16)');  // center signs on loc
31431
31432         layer.exit()
31433             .remove();
31434
31435         if (enabled) {
31436             if (mapillary && ~~context.map().zoom() >= minZoom) {
31437                 editOn();
31438                 update();
31439                 mapillary.loadSigns(context, projection, layer.dimensions());
31440             } else {
31441                 editOff();
31442             }
31443         }
31444     }
31445
31446     drawSigns.enabled = function(_) {
31447         if (!arguments.length) return iD.svg.MapillarySigns.enabled;
31448         iD.svg.MapillarySigns.enabled = _;
31449         if (iD.svg.MapillarySigns.enabled) {
31450             showLayer();
31451         } else {
31452             hideLayer();
31453         }
31454         dispatch.change();
31455         return this;
31456     };
31457
31458     drawSigns.supported = function() {
31459         var mapillary = getMapillary();
31460         return (mapillary && mapillary.signsSupported());
31461     };
31462
31463     drawSigns.dimensions = function(_) {
31464         if (!arguments.length) return layer.dimensions();
31465         layer.dimensions(_);
31466         return this;
31467     };
31468
31469     init();
31470     return drawSigns;
31471 };
31472 iD.svg.Midpoints = function(projection, context) {
31473     return function drawMidpoints(surface, graph, entities, filter, extent) {
31474         var poly = extent.polygon(),
31475             midpoints = {};
31476
31477         for (var i = 0; i < entities.length; i++) {
31478             var entity = entities[i];
31479
31480             if (entity.type !== 'way')
31481                 continue;
31482             if (!filter(entity))
31483                 continue;
31484             if (context.selectedIDs().indexOf(entity.id) < 0)
31485                 continue;
31486
31487             var nodes = graph.childNodes(entity);
31488             for (var j = 0; j < nodes.length - 1; j++) {
31489
31490                 var a = nodes[j],
31491                     b = nodes[j + 1],
31492                     id = [a.id, b.id].sort().join('-');
31493
31494                 if (midpoints[id]) {
31495                     midpoints[id].parents.push(entity);
31496                 } else {
31497                     if (iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
31498                         var point = iD.geo.interp(a.loc, b.loc, 0.5),
31499                             loc = null;
31500
31501                         if (extent.intersects(point)) {
31502                             loc = point;
31503                         } else {
31504                             for (var k = 0; k < 4; k++) {
31505                                 point = iD.geo.lineIntersection([a.loc, b.loc], [poly[k], poly[k+1]]);
31506                                 if (point &&
31507                                     iD.geo.euclideanDistance(projection(a.loc), projection(point)) > 20 &&
31508                                     iD.geo.euclideanDistance(projection(b.loc), projection(point)) > 20)
31509                                 {
31510                                     loc = point;
31511                                     break;
31512                                 }
31513                             }
31514                         }
31515
31516                         if (loc) {
31517                             midpoints[id] = {
31518                                 type: 'midpoint',
31519                                 id: id,
31520                                 loc: loc,
31521                                 edge: [a.id, b.id],
31522                                 parents: [entity]
31523                             };
31524                         }
31525                     }
31526                 }
31527             }
31528         }
31529
31530         function midpointFilter(d) {
31531             if (midpoints[d.id])
31532                 return true;
31533
31534             for (var i = 0; i < d.parents.length; i++)
31535                 if (filter(d.parents[i]))
31536                     return true;
31537
31538             return false;
31539         }
31540
31541         var groups = surface.selectAll('.layer-hit').selectAll('g.midpoint')
31542             .filter(midpointFilter)
31543             .data(_.values(midpoints), function(d) { return d.id; });
31544
31545         var enter = groups.enter()
31546             .insert('g', ':first-child')
31547             .attr('class', 'midpoint');
31548
31549         enter.append('polygon')
31550             .attr('points', '-6,8 10,0 -6,-8')
31551             .attr('class', 'shadow');
31552
31553         enter.append('polygon')
31554             .attr('points', '-3,4 5,0 -3,-4')
31555             .attr('class', 'fill');
31556
31557         groups
31558             .attr('transform', function(d) {
31559                 var translate = iD.svg.PointTransform(projection),
31560                     a = context.entity(d.edge[0]),
31561                     b = context.entity(d.edge[1]),
31562                     angle = Math.round(iD.geo.angle(a, b, projection) * (180 / Math.PI));
31563                 return translate(d) + ' rotate(' + angle + ')';
31564             })
31565             .call(iD.svg.TagClasses().tags(
31566                 function(d) { return d.parents[0].tags; }
31567             ));
31568
31569         // Propagate data bindings.
31570         groups.select('polygon.shadow');
31571         groups.select('polygon.fill');
31572
31573         groups.exit()
31574             .remove();
31575     };
31576 };
31577 iD.svg.Osm = function() {
31578     return function drawOsm(selection) {
31579         var layers = selection.selectAll('.layer-osm')
31580             .data(['areas', 'lines', 'hit', 'halo', 'label']);
31581
31582         layers.enter().append('g')
31583             .attr('class', function(d) { return 'layer-osm layer-' + d; });
31584     };
31585 };
31586 iD.svg.Points = function(projection, context) {
31587     function markerPath(selection, klass) {
31588         selection
31589             .attr('class', klass)
31590             .attr('transform', 'translate(-8, -23)')
31591             .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');
31592     }
31593
31594     function sortY(a, b) {
31595         return b.loc[1] - a.loc[1];
31596     }
31597
31598     return function drawPoints(surface, graph, entities, filter) {
31599         var wireframe = surface.classed('fill-wireframe'),
31600             points = wireframe ? [] : _.filter(entities, function(e) {
31601                 return e.geometry(graph) === 'point';
31602             });
31603
31604         points.sort(sortY);
31605
31606         var groups = surface.selectAll('.layer-hit').selectAll('g.point')
31607             .filter(filter)
31608             .data(points, iD.Entity.key);
31609
31610         var group = groups.enter()
31611             .append('g')
31612             .attr('class', function(d) { return 'node point ' + d.id; })
31613             .order();
31614
31615         group.append('path')
31616             .call(markerPath, 'shadow');
31617
31618         group.append('path')
31619             .call(markerPath, 'stroke');
31620
31621         group.append('use')
31622             .attr('transform', 'translate(-6, -20)')
31623             .attr('class', 'icon')
31624             .attr('width', '12px')
31625             .attr('height', '12px');
31626
31627         groups.attr('transform', iD.svg.PointTransform(projection))
31628             .call(iD.svg.TagClasses());
31629
31630         // Selecting the following implicitly
31631         // sets the data (point entity) on the element
31632         groups.select('.shadow');
31633         groups.select('.stroke');
31634         groups.select('.icon')
31635             .attr('xlink:href', function(entity) {
31636                 var preset = context.presets().match(entity, graph);
31637                 return preset.icon ? '#' + preset.icon + '-12' : '';
31638             });
31639
31640         groups.exit()
31641             .remove();
31642     };
31643 };
31644 iD.svg.TagClasses = function() {
31645     var primaries = [
31646             'building', 'highway', 'railway', 'waterway', 'aeroway',
31647             'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse',
31648             'leisure', 'place'
31649         ],
31650         statuses = [
31651             'proposed', 'construction', 'disused', 'abandoned', 'dismantled',
31652             'razed', 'demolished', 'obliterated'
31653         ],
31654         secondaries = [
31655             'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
31656             'surface', 'tracktype', 'crossing'
31657         ],
31658         tagClassRe = /^tag-/,
31659         tags = function(entity) { return entity.tags; };
31660
31661
31662     var tagClasses = function(selection) {
31663         selection.each(function tagClassesEach(entity) {
31664             var value = this.className,
31665                 classes, primary, status;
31666
31667             if (value.baseVal !== undefined) value = value.baseVal;
31668
31669             classes = value.trim().split(/\s+/).filter(function(name) {
31670                 return name.length && !tagClassRe.test(name);
31671             }).join(' ');
31672
31673             var t = tags(entity), i, k, v;
31674
31675             // pick at most one primary classification tag..
31676             for (i = 0; i < primaries.length; i++) {
31677                 k = primaries[i];
31678                 v = t[k];
31679                 if (!v || v === 'no') continue;
31680
31681                 primary = k;
31682                 if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
31683                     status = v;
31684                     classes += ' tag-' + k;
31685                 } else {
31686                     classes += ' tag-' + k + ' tag-' + k + '-' + v;
31687                 }
31688
31689                 break;
31690             }
31691
31692             // add at most one status tag, only if relates to primary tag..
31693             if (!status) {
31694                 for (i = 0; i < statuses.length; i++) {
31695                     k = statuses[i];
31696                     v = t[k];
31697                     if (!v || v === 'no') continue;
31698
31699                     if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
31700                         status = k;
31701                     }
31702                     else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
31703                         status = k;
31704                     } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
31705                         status = k;
31706                         primary = v;
31707                         classes += ' tag-' + v;
31708                     }  // else ignore e.g.  `highway=path + abandoned=railway`
31709
31710                     if (status) break;
31711                 }
31712             }
31713
31714             if (status) {
31715                 classes += ' tag-status tag-status-' + status;
31716             }
31717
31718             // add any secondary (structure) tags
31719             for (i = 0; i < secondaries.length; i++) {
31720                 k = secondaries[i];
31721                 v = t[k];
31722                 if (!v || v === 'no') continue;
31723                 classes += ' tag-' + k + ' tag-' + k + '-' + v;
31724             }
31725
31726             // For highways, look for surface tagging..
31727             if (primary === 'highway') {
31728                 var paved = (t.highway !== 'track');
31729                 for (k in t) {
31730                     v = t[k];
31731                     if (k in iD.pavedTags) {
31732                         paved = !!iD.pavedTags[k][v];
31733                         break;
31734                     }
31735                 }
31736                 if (!paved) {
31737                     classes += ' tag-unpaved';
31738                 }
31739             }
31740
31741             classes = classes.trim();
31742
31743             if (classes !== value) {
31744                 d3.select(this).attr('class', classes);
31745             }
31746         });
31747     };
31748
31749     tagClasses.tags = function(_) {
31750         if (!arguments.length) return tags;
31751         tags = _;
31752         return tagClasses;
31753     };
31754
31755     return tagClasses;
31756 };
31757 iD.svg.Turns = function(projection) {
31758     return function drawTurns(surface, graph, turns) {
31759         function key(turn) {
31760             return [turn.from.node + turn.via.node + turn.to.node].join('-');
31761         }
31762
31763         function icon(turn) {
31764             var u = turn.u ? '-u' : '';
31765             if (!turn.restriction)
31766                 return '#turn-yes' + u;
31767             var restriction = graph.entity(turn.restriction).tags.restriction;
31768             return '#turn-' +
31769                 (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u;
31770         }
31771
31772         var groups = surface.selectAll('.layer-hit').selectAll('g.turn')
31773             .data(turns, key);
31774
31775         // Enter
31776         var enter = groups.enter().append('g')
31777             .attr('class', 'turn');
31778
31779         var nEnter = enter.filter(function (turn) { return !turn.u; });
31780
31781         nEnter.append('rect')
31782             .attr('transform', 'translate(-22, -12)')
31783             .attr('width', '44')
31784             .attr('height', '24');
31785
31786         nEnter.append('use')
31787             .attr('transform', 'translate(-22, -12)')
31788             .attr('width', '44')
31789             .attr('height', '24');
31790
31791
31792         var uEnter = enter.filter(function (turn) { return turn.u; });
31793
31794         uEnter.append('circle')
31795             .attr('r', '16');
31796
31797         uEnter.append('use')
31798             .attr('transform', 'translate(-16, -16)')
31799             .attr('width', '32')
31800             .attr('height', '32');
31801
31802
31803         // Update
31804         groups
31805             .attr('transform', function (turn) {
31806                 var v = graph.entity(turn.via.node),
31807                     t = graph.entity(turn.to.node),
31808                     a = iD.geo.angle(v, t, projection),
31809                     p = projection(v.loc),
31810                     r = turn.u ? 0 : 60;
31811
31812                 return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' +
31813                     'rotate(' + a * 180 / Math.PI + ')';
31814             });
31815
31816         groups.select('use')
31817             .attr('xlink:href', icon);
31818
31819         groups.select('rect');
31820         groups.select('circle');
31821
31822
31823         // Exit
31824         groups.exit()
31825             .remove();
31826
31827         return this;
31828     };
31829 };
31830 iD.svg.Vertices = function(projection, context) {
31831     var radiuses = {
31832         //       z16-, z17, z18+, tagged
31833         shadow: [6,    7.5,   7.5,  11.5],
31834         stroke: [2.5,  3.5,   3.5,  7],
31835         fill:   [1,    1.5,   1.5,  1.5]
31836     };
31837
31838     var hover;
31839
31840     function siblingAndChildVertices(ids, graph, extent) {
31841         var vertices = {};
31842
31843         function addChildVertices(entity) {
31844             if (!context.features().isHiddenFeature(entity, graph, entity.geometry(graph))) {
31845                 var i;
31846                 if (entity.type === 'way') {
31847                     for (i = 0; i < entity.nodes.length; i++) {
31848                         addChildVertices(graph.entity(entity.nodes[i]));
31849                     }
31850                 } else if (entity.type === 'relation') {
31851                     for (i = 0; i < entity.members.length; i++) {
31852                         var member = context.hasEntity(entity.members[i].id);
31853                         if (member) {
31854                             addChildVertices(member);
31855                         }
31856                     }
31857                 } else if (entity.intersects(extent, graph)) {
31858                     vertices[entity.id] = entity;
31859                 }
31860             }
31861         }
31862
31863         ids.forEach(function(id) {
31864             var entity = context.hasEntity(id);
31865             if (entity && entity.type === 'node') {
31866                 vertices[entity.id] = entity;
31867                 context.graph().parentWays(entity).forEach(function(entity) {
31868                     addChildVertices(entity);
31869                 });
31870             } else if (entity) {
31871                 addChildVertices(entity);
31872             }
31873         });
31874
31875         return vertices;
31876     }
31877
31878     function draw(selection, vertices, klass, graph, zoom) {
31879         var icons = {},
31880             z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
31881
31882         var groups = selection
31883             .data(vertices, iD.Entity.key);
31884
31885         function icon(entity) {
31886             if (entity.id in icons) return icons[entity.id];
31887             icons[entity.id] =
31888                 entity.hasInterestingTags() &&
31889                 context.presets().match(entity, graph).icon;
31890             return icons[entity.id];
31891         }
31892
31893         function setClass(klass) {
31894             return function(entity) {
31895                 this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id);
31896             };
31897         }
31898
31899         function setAttributes(selection) {
31900             ['shadow','stroke','fill'].forEach(function(klass) {
31901                 var rads = radiuses[klass];
31902                 selection.selectAll('.' + klass)
31903                     .each(function(entity) {
31904                         var i = z && icon(entity),
31905                             c = i ? 0.5 : 0,
31906                             r = rads[i ? 3 : z];
31907                         this.setAttribute('cx', c);
31908                         this.setAttribute('cy', -c);
31909                         this.setAttribute('r', r);
31910                         if (i && klass === 'fill') {
31911                             this.setAttribute('visibility', 'hidden');
31912                         } else {
31913                             this.removeAttribute('visibility');
31914                         }
31915                     });
31916             });
31917
31918             selection.selectAll('use')
31919                 .each(function() {
31920                     if (z) {
31921                         this.removeAttribute('visibility');
31922                     } else {
31923                         this.setAttribute('visibility', 'hidden');
31924                     }
31925                 });
31926         }
31927
31928         var enter = groups.enter()
31929             .append('g')
31930             .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; });
31931
31932         enter.append('circle')
31933             .each(setClass('shadow'));
31934
31935         enter.append('circle')
31936             .each(setClass('stroke'));
31937
31938         // Vertices with icons get a `use`.
31939         enter.filter(function(d) { return icon(d); })
31940             .append('use')
31941             .attr('transform', 'translate(-6, -6)')
31942             .attr('xlink:href', function(d) { return '#' + icon(d) + '-12'; })
31943             .attr('width', '12px')
31944             .attr('height', '12px')
31945             .each(setClass('icon'));
31946
31947         // Vertices with tags get a fill.
31948         enter.filter(function(d) { return d.hasInterestingTags(); })
31949             .append('circle')
31950             .each(setClass('fill'));
31951
31952         groups
31953             .attr('transform', iD.svg.PointTransform(projection))
31954             .classed('shared', function(entity) { return graph.isShared(entity); })
31955             .call(setAttributes);
31956
31957         groups.exit()
31958             .remove();
31959     }
31960
31961     function drawVertices(surface, graph, entities, filter, extent, zoom) {
31962         var selected = siblingAndChildVertices(context.selectedIDs(), graph, extent),
31963             wireframe = surface.classed('fill-wireframe'),
31964             vertices = [];
31965
31966         for (var i = 0; i < entities.length; i++) {
31967             var entity = entities[i],
31968                 geometry = entity.geometry(graph);
31969
31970             if (wireframe && geometry === 'point') {
31971                 vertices.push(entity);
31972                 continue;
31973             }
31974
31975             if (geometry !== 'vertex')
31976                 continue;
31977
31978             if (entity.id in selected ||
31979                 entity.hasInterestingTags() ||
31980                 entity.isIntersection(graph)) {
31981                 vertices.push(entity);
31982             }
31983         }
31984
31985         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-persistent')
31986             .filter(filter)
31987             .call(draw, vertices, 'vertex-persistent', graph, zoom);
31988
31989         drawHover(surface, graph, extent, zoom);
31990     }
31991
31992     function drawHover(surface, graph, extent, zoom) {
31993         var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent) : {};
31994
31995         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-hover')
31996             .call(draw, d3.values(hovered), 'vertex-hover', graph, zoom);
31997     }
31998
31999     drawVertices.drawHover = function(surface, graph, target, extent, zoom) {
32000         if (target === hover) return;
32001         hover = target;
32002         drawHover(surface, graph, extent, zoom);
32003     };
32004
32005     return drawVertices;
32006 };
32007 iD.ui = function(context) {
32008     function render(container) {
32009         var map = context.map();
32010
32011         if (iD.detect().opera) container.classed('opera', true);
32012
32013         var hash = iD.behavior.Hash(context);
32014
32015         hash();
32016
32017         if (!hash.hadHash) {
32018             map.centerZoom([0, 0], 2);
32019         }
32020
32021         container.append('svg')
32022             .attr('id', 'defs')
32023             .call(iD.svg.Defs(context));
32024
32025         container.append('div')
32026             .attr('id', 'sidebar')
32027             .attr('class', 'col4')
32028             .call(ui.sidebar);
32029
32030         var content = container.append('div')
32031             .attr('id', 'content');
32032
32033         var bar = content.append('div')
32034             .attr('id', 'bar')
32035             .attr('class', 'fillD');
32036
32037         content.append('div')
32038             .attr('id', 'map')
32039             .call(map);
32040
32041         content
32042             .call(iD.ui.MapInMap(context));
32043
32044         content.append('div')
32045             .call(iD.ui.Info(context));
32046
32047         bar.append('div')
32048             .attr('class', 'spacer col4');
32049
32050         var limiter = bar.append('div')
32051             .attr('class', 'limiter');
32052
32053         limiter.append('div')
32054             .attr('class', 'button-wrap joined col3')
32055             .call(iD.ui.Modes(context), limiter);
32056
32057         limiter.append('div')
32058             .attr('class', 'button-wrap joined col1')
32059             .call(iD.ui.UndoRedo(context));
32060
32061         limiter.append('div')
32062             .attr('class', 'button-wrap col1')
32063             .call(iD.ui.Save(context));
32064
32065         bar.append('div')
32066             .attr('class', 'full-screen')
32067             .call(iD.ui.FullScreen(context));
32068
32069         bar.append('div')
32070             .attr('class', 'spinner')
32071             .call(iD.ui.Spinner(context));
32072
32073         var controls = bar.append('div')
32074             .attr('class', 'map-controls');
32075
32076         controls.append('div')
32077             .attr('class', 'map-control zoombuttons')
32078             .call(iD.ui.Zoom(context));
32079
32080         controls.append('div')
32081             .attr('class', 'map-control geolocate-control')
32082             .call(iD.ui.Geolocate(context));
32083
32084         controls.append('div')
32085             .attr('class', 'map-control background-control')
32086             .call(iD.ui.Background(context));
32087
32088         controls.append('div')
32089             .attr('class', 'map-control map-data-control')
32090             .call(iD.ui.MapData(context));
32091
32092         controls.append('div')
32093             .attr('class', 'map-control help-control')
32094             .call(iD.ui.Help(context));
32095
32096         var about = content.append('div')
32097             .attr('id', 'about');
32098
32099         about.append('div')
32100             .attr('id', 'attrib')
32101             .call(iD.ui.Attribution(context));
32102
32103         var footer = about.append('div')
32104             .attr('id', 'footer')
32105             .attr('class', 'fillD');
32106
32107         footer.append('div')
32108             .attr('class', 'api-status')
32109             .call(iD.ui.Status(context));
32110
32111         footer.append('div')
32112             .attr('id', 'scale-block')
32113             .call(iD.ui.Scale(context));
32114
32115         var aboutList = footer.append('div')
32116             .attr('id', 'info-block')
32117             .append('ul')
32118             .attr('id', 'about-list');
32119
32120         if (!context.embed()) {
32121             aboutList.call(iD.ui.Account(context));
32122         }
32123
32124         aboutList.append('li')
32125             .append('a')
32126             .attr('target', '_blank')
32127             .attr('tabindex', -1)
32128             .attr('href', 'https://github.com/openstreetmap/iD')
32129             .text(iD.version);
32130
32131         var issueLinks = aboutList.append('li');
32132
32133         issueLinks.append('a')
32134             .attr('target', '_blank')
32135             .attr('tabindex', -1)
32136             .attr('href', 'https://github.com/openstreetmap/iD/issues')
32137             .call(iD.svg.Icon('#icon-bug', 'light'))
32138             .call(bootstrap.tooltip()
32139                 .title(t('report_a_bug'))
32140                 .placement('top')
32141             );
32142
32143         issueLinks.append('a')
32144             .attr('target', '_blank')
32145             .attr('tabindex', -1)
32146             .attr('href', 'https://github.com/openstreetmap/iD/blob/master/CONTRIBUTING.md#translating')
32147             .call(iD.svg.Icon('#icon-translate', 'light'))
32148             .call(bootstrap.tooltip()
32149                 .title(t('help_translate'))
32150                 .placement('top')
32151             );
32152
32153         aboutList.append('li')
32154             .attr('class', 'feature-warning')
32155             .attr('tabindex', -1)
32156             .call(iD.ui.FeatureInfo(context));
32157
32158         aboutList.append('li')
32159             .attr('class', 'user-list')
32160             .attr('tabindex', -1)
32161             .call(iD.ui.Contributors(context));
32162
32163         window.onbeforeunload = function() {
32164             return context.save();
32165         };
32166
32167         window.onunload = function() {
32168             context.history().unlock();
32169         };
32170
32171         var mapDimensions = map.dimensions();
32172
32173         d3.select(window).on('resize.editor', function() {
32174             mapDimensions = content.dimensions(null);
32175             map.dimensions(mapDimensions);
32176         });
32177
32178         function pan(d) {
32179             return function() {
32180                 d3.event.preventDefault();
32181                 if (!context.inIntro()) context.pan(d);
32182             };
32183         }
32184
32185         // pan amount
32186         var pa = 10;
32187
32188         var keybinding = d3.keybinding('main')
32189             .on('⌫', function() { d3.event.preventDefault(); })
32190             .on('←', pan([pa, 0]))
32191             .on('↑', pan([0, pa]))
32192             .on('→', pan([-pa, 0]))
32193             .on('↓', pan([0, -pa]))
32194             .on('⇧←', pan([mapDimensions[0], 0]))
32195             .on('⇧↑', pan([0, mapDimensions[1]]))
32196             .on('⇧→', pan([-mapDimensions[0], 0]))
32197             .on('⇧↓', pan([0, -mapDimensions[1]]))
32198             .on(iD.ui.cmd('⌘←'), pan([mapDimensions[0], 0]))
32199             .on(iD.ui.cmd('⌘↑'), pan([0, mapDimensions[1]]))
32200             .on(iD.ui.cmd('⌘→'), pan([-mapDimensions[0], 0]))
32201             .on(iD.ui.cmd('⌘↓'), pan([0, -mapDimensions[1]]));
32202
32203         d3.select(document)
32204             .call(keybinding);
32205
32206         context.enter(iD.modes.Browse(context));
32207
32208         context.container()
32209             .call(iD.ui.Splash(context))
32210             .call(iD.ui.Restore(context));
32211
32212         var authenticating = iD.ui.Loading(context)
32213             .message(t('loading_auth'));
32214
32215         context.connection()
32216             .on('authenticating.ui', function() {
32217                 context.container()
32218                     .call(authenticating);
32219             })
32220             .on('authenticated.ui', function() {
32221                 authenticating.close();
32222             });
32223     }
32224
32225     function ui(container) {
32226         context.container(container);
32227         context.loadLocale(function() {
32228             render(container);
32229         });
32230     }
32231
32232     ui.sidebar = iD.ui.Sidebar(context);
32233
32234     return ui;
32235 };
32236
32237 iD.ui.tooltipHtml = function(text, key) {
32238     var s = '<span>' + text + '</span>';
32239     if (key) {
32240         s += '<div class="keyhint-wrap">' +
32241             '<span> ' + (t('tooltip_keyhint')) + ' </span>' +
32242             '<span class="keyhint"> ' + key + '</span></div>';
32243     }
32244     return s;
32245 };
32246 iD.ui.Account = function(context) {
32247     var connection = context.connection();
32248
32249     function update(selection) {
32250         if (!connection.authenticated()) {
32251             selection.selectAll('#userLink, #logoutLink')
32252                 .classed('hide', true);
32253             return;
32254         }
32255
32256         connection.userDetails(function(err, details) {
32257             var userLink = selection.select('#userLink'),
32258                 logoutLink = selection.select('#logoutLink');
32259
32260             userLink.html('');
32261             logoutLink.html('');
32262
32263             if (err) return;
32264
32265             selection.selectAll('#userLink, #logoutLink')
32266                 .classed('hide', false);
32267
32268             // Link
32269             userLink.append('a')
32270                 .attr('href', connection.userURL(details.display_name))
32271                 .attr('target', '_blank');
32272
32273             // Add thumbnail or dont
32274             if (details.image_url) {
32275                 userLink.append('img')
32276                     .attr('class', 'icon pre-text user-icon')
32277                     .attr('src', details.image_url);
32278             } else {
32279                 userLink
32280                     .call(iD.svg.Icon('#icon-avatar', 'pre-text light'));
32281             }
32282
32283             // Add user name
32284             userLink.append('span')
32285                 .attr('class', 'label')
32286                 .text(details.display_name);
32287
32288             logoutLink.append('a')
32289                 .attr('class', 'logout')
32290                 .attr('href', '#')
32291                 .text(t('logout'))
32292                 .on('click.logout', function() {
32293                     d3.event.preventDefault();
32294                     connection.logout();
32295                 });
32296         });
32297     }
32298
32299     return function(selection) {
32300         selection.append('li')
32301             .attr('id', 'logoutLink')
32302             .classed('hide', true);
32303
32304         selection.append('li')
32305             .attr('id', 'userLink')
32306             .classed('hide', true);
32307
32308         connection.on('auth.account', function() { update(selection); });
32309         update(selection);
32310     };
32311 };
32312 iD.ui.Attribution = function(context) {
32313     var selection;
32314
32315     function attribution(data, klass) {
32316         var div = selection.selectAll('.' + klass)
32317             .data([0]);
32318
32319         div.enter()
32320             .append('div')
32321             .attr('class', klass);
32322
32323         var background = div.selectAll('.attribution')
32324             .data(data, function(d) { return d.name(); });
32325
32326         background.enter()
32327             .append('span')
32328             .attr('class', 'attribution')
32329             .each(function(d) {
32330                 if (d.terms_html) {
32331                     d3.select(this)
32332                         .html(d.terms_html);
32333                     return;
32334                 }
32335
32336                 var source = d.terms_text || d.id || d.name();
32337
32338                 if (d.logo) {
32339                     source = '<img class="source-image" src="' + context.imagePath(d.logo) + '">';
32340                 }
32341
32342                 if (d.terms_url) {
32343                     d3.select(this)
32344                         .append('a')
32345                         .attr('href', d.terms_url)
32346                         .attr('target', '_blank')
32347                         .html(source);
32348                 } else {
32349                     d3.select(this)
32350                         .text(source);
32351                 }
32352             });
32353
32354         background.exit()
32355             .remove();
32356
32357         var copyright = background.selectAll('.copyright-notice')
32358             .data(function(d) {
32359                 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
32360                 return notice ? [notice] : [];
32361             });
32362
32363         copyright.enter()
32364             .append('span')
32365             .attr('class', 'copyright-notice');
32366
32367         copyright.text(String);
32368
32369         copyright.exit()
32370             .remove();
32371     }
32372
32373     function update() {
32374         attribution([context.background().baseLayerSource()], 'base-layer-attribution');
32375         attribution(context.background().overlayLayerSources().filter(function (s) {
32376             return s.validZoom(context.map().zoom());
32377         }), 'overlay-layer-attribution');
32378     }
32379
32380     return function(select) {
32381         selection = select;
32382
32383         context.background()
32384             .on('change.attribution', update);
32385
32386         context.map()
32387             .on('move.attribution', _.throttle(update, 400, {leading: false}));
32388
32389         update();
32390     };
32391 };
32392 iD.ui.Background = function(context) {
32393     var key = 'B',
32394         opacities = [1, 0.75, 0.5, 0.25],
32395         directions = [
32396             ['right', [0.5, 0]],
32397             ['top', [0, -0.5]],
32398             ['left', [-0.5, 0]],
32399             ['bottom', [0, 0.5]]],
32400         opacityDefault = (context.storage('background-opacity') !== null) ?
32401             (+context.storage('background-opacity')) : 1.0,
32402         customTemplate = context.storage('background-custom-template') || '';
32403
32404     // Can be 0 from <1.3.0 use or due to issue #1923.
32405     if (opacityDefault === 0) opacityDefault = 1.0;
32406
32407     function background(selection) {
32408
32409         function sortSources(a, b) {
32410             return a.best() ? -1
32411                 : b.best() ? 1
32412                 : a.id === 'none' ? 1
32413                 : b.id === 'none' ? -1
32414                 : d3.ascending(a, b);
32415         }
32416
32417         function setOpacity(d) {
32418             var bg = context.container().selectAll('.layer-background')
32419                 .transition()
32420                 .style('opacity', d)
32421                 .attr('data-opacity', d);
32422
32423             if (!iD.detect().opera) {
32424                 iD.util.setTransform(bg, 0, 0);
32425             }
32426
32427             opacityList.selectAll('li')
32428                 .classed('active', function(_) { return _ === d; });
32429
32430             context.storage('background-opacity', d);
32431         }
32432
32433         function selectLayer() {
32434             function active(d) {
32435                 return context.background().showsLayer(d);
32436             }
32437
32438             content.selectAll('.layer, .custom_layer')
32439                 .classed('active', active)
32440                 .selectAll('input')
32441                 .property('checked', active);
32442         }
32443
32444         function clickSetSource(d) {
32445             d3.event.preventDefault();
32446             context.background().baseLayerSource(d);
32447             selectLayer();
32448         }
32449
32450         function editCustom() {
32451             d3.event.preventDefault();
32452             var template = window.prompt(t('background.custom_prompt'), customTemplate);
32453             if (!template ||
32454                 template.indexOf('google.com') !== -1 ||
32455                 template.indexOf('googleapis.com') !== -1 ||
32456                 template.indexOf('google.ru') !== -1) {
32457                 selectLayer();
32458                 return;
32459             }
32460             setCustom(template);
32461         }
32462
32463         function setCustom(template) {
32464             context.background().baseLayerSource(iD.BackgroundSource.Custom(template));
32465             selectLayer();
32466             context.storage('background-custom-template', template);
32467         }
32468
32469         function clickSetOverlay(d) {
32470             d3.event.preventDefault();
32471             context.background().toggleOverlayLayer(d);
32472             selectLayer();
32473         }
32474
32475         function drawList(layerList, type, change, filter) {
32476             var sources = context.background()
32477                 .sources(context.map().extent())
32478                 .filter(filter);
32479
32480             var layerLinks = layerList.selectAll('li.layer')
32481                 .data(sources, function(d) { return d.name(); })
32482                 .sort(sortSources);
32483
32484             var enter = layerLinks.enter()
32485                 .insert('li', '.custom_layer')
32486                 .attr('class', 'layer')
32487                 .classed('best', function(d) { return d.best(); });
32488
32489             // only set tooltips for layers with tooltips
32490             enter.filter(function(d) { return d.description; })
32491                 .call(bootstrap.tooltip()
32492                     .title(function(d) { return d.description; })
32493                     .placement('top'));
32494
32495             enter.filter(function(d) { return d.best(); })
32496                 .append('div')
32497                 .attr('class', 'best')
32498                 .call(bootstrap.tooltip()
32499                     .title(t('background.best_imagery'))
32500                     .placement('left'))
32501                 .append('span')
32502                 .html('&#9733;');
32503
32504             var label = enter.append('label');
32505
32506             label.append('input')
32507                 .attr('type', type)
32508                 .attr('name', 'layers')
32509                 .on('change', change);
32510
32511             label.append('span')
32512                 .text(function(d) { return d.name(); });
32513
32514             layerLinks.exit()
32515                 .remove();
32516
32517             layerList.style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
32518         }
32519
32520         function update() {
32521             backgroundList.call(drawList, 'radio', clickSetSource, function(d) { return !d.overlay; });
32522             overlayList.call(drawList, 'checkbox', clickSetOverlay, function(d) { return d.overlay; });
32523
32524             selectLayer();
32525
32526             var source = context.background().baseLayerSource();
32527             if (source.id === 'custom') {
32528                 customTemplate = source.template;
32529             }
32530
32531             updateOffsetVal();
32532         }
32533
32534         function offsetToMeters(offset) {
32535             var equatRadius = 6356752.314245179,
32536                 polarRadius = 6378137.0,
32537                 tileSize = 256;
32538
32539             return [
32540                 offset[0] * 2 * Math.PI * equatRadius / tileSize,
32541                 -offset[1] * 2 * Math.PI * polarRadius / tileSize
32542             ];
32543         }
32544
32545         function metersToOffset(meters) {
32546             var equatRadius = 6356752.314245179,
32547                 polarRadius = 6378137.0,
32548                 tileSize = 256;
32549
32550             return [
32551                 meters[0] * tileSize / (2 * Math.PI * equatRadius),
32552                 -meters[1] * tileSize / (2 * Math.PI * polarRadius)
32553             ];
32554         }
32555
32556         function updateOffsetVal() {
32557             var meters = offsetToMeters(context.background().offset()),
32558                 x = +meters[0].toFixed(2),
32559                 y = +meters[1].toFixed(2);
32560
32561             d3.selectAll('.nudge-inner-rect')
32562                 .select('input')
32563                 .classed('error', false)
32564                 .property('value', x + ', ' + y);
32565
32566             d3.selectAll('.nudge-reset')
32567                 .classed('disabled', function() {
32568                     return (x === 0 && y === 0);
32569                 });
32570         }
32571
32572         function resetOffset() {
32573             context.background().offset([0, 0]);
32574             updateOffsetVal();
32575         }
32576
32577         function nudge(d) {
32578             context.background().nudge(d, context.map().zoom());
32579             updateOffsetVal();
32580         }
32581
32582         function buttonOffset(d) {
32583             var timeout = window.setTimeout(function() {
32584                     interval = window.setInterval(nudge.bind(null, d), 100);
32585                 }, 500),
32586                 interval;
32587
32588             d3.select(window).on('mouseup', function() {
32589                 window.clearInterval(interval);
32590                 window.clearTimeout(timeout);
32591                 d3.select(window).on('mouseup', null);
32592             });
32593
32594             nudge(d);
32595         }
32596
32597         function inputOffset() {
32598             var input = d3.select(this);
32599             var d = input.node().value;
32600
32601             if (d === '') return resetOffset();
32602
32603             d = d.replace(/;/g, ',').split(',').map(function(n) {
32604                 // if n is NaN, it will always get mapped to false.
32605                 return !isNaN(n) && n;
32606             });
32607
32608             if (d.length !== 2 || !d[0] || !d[1]) {
32609                 input.classed('error', true);
32610                 return;
32611             }
32612
32613             context.background().offset(metersToOffset(d));
32614             updateOffsetVal();
32615         }
32616
32617         function dragOffset() {
32618             var origin = [d3.event.clientX, d3.event.clientY];
32619
32620             d3.select(window)
32621                 .on('mousemove.offset', function() {
32622                     var latest = [d3.event.clientX, d3.event.clientY];
32623                     var d = [
32624                         -(origin[0] - latest[0]) / 4,
32625                         -(origin[1] - latest[1]) / 4
32626                     ];
32627
32628                     origin = latest;
32629                     nudge(d);
32630                 })
32631                 .on('mouseup.offset', function() {
32632                     d3.select(window)
32633                         .on('mousemove.offset', null)
32634                         .on('mouseup.offset', null);
32635                 });
32636
32637             d3.event.preventDefault();
32638         }
32639
32640         function hide() {
32641             setVisible(false);
32642         }
32643
32644         function toggle() {
32645             if (d3.event) d3.event.preventDefault();
32646             tooltip.hide(button);
32647             setVisible(!button.classed('active'));
32648         }
32649
32650         function setVisible(show) {
32651             if (show !== shown) {
32652                 button.classed('active', show);
32653                 shown = show;
32654
32655                 if (show) {
32656                     selection.on('mousedown.background-inside', function() {
32657                         return d3.event.stopPropagation();
32658                     });
32659                     content.style('display', 'block')
32660                         .style('right', '-300px')
32661                         .transition()
32662                         .duration(200)
32663                         .style('right', '0px');
32664                 } else {
32665                     content.style('display', 'block')
32666                         .style('right', '0px')
32667                         .transition()
32668                         .duration(200)
32669                         .style('right', '-300px')
32670                         .each('end', function() {
32671                             d3.select(this).style('display', 'none');
32672                         });
32673                     selection.on('mousedown.background-inside', null);
32674                 }
32675             }
32676         }
32677
32678
32679         var content = selection.append('div')
32680                 .attr('class', 'fillL map-overlay col3 content hide'),
32681             tooltip = bootstrap.tooltip()
32682                 .placement('left')
32683                 .html(true)
32684                 .title(iD.ui.tooltipHtml(t('background.description'), key)),
32685             button = selection.append('button')
32686                 .attr('tabindex', -1)
32687                 .on('click', toggle)
32688                 .call(iD.svg.Icon('#icon-layers', 'light'))
32689                 .call(tooltip),
32690             shown = false;
32691
32692
32693         /* opacity switcher */
32694
32695         var opa = content.append('div')
32696                 .attr('class', 'opacity-options-wrapper');
32697
32698         opa.append('h4')
32699             .text(t('background.title'));
32700
32701         var opacityList = opa.append('ul')
32702             .attr('class', 'opacity-options');
32703
32704         opacityList.selectAll('div.opacity')
32705             .data(opacities)
32706             .enter()
32707             .append('li')
32708             .attr('data-original-title', function(d) {
32709                 return t('background.percent_brightness', { opacity: (d * 100) });
32710             })
32711             .on('click.set-opacity', setOpacity)
32712             .html('<div class="select-box"></div>')
32713             .call(bootstrap.tooltip()
32714                 .placement('left'))
32715             .append('div')
32716             .attr('class', 'opacity')
32717             .style('opacity', function(d) { return 1.25 - d; });
32718
32719
32720         /* background switcher */
32721
32722         var backgroundList = content.append('ul')
32723             .attr('class', 'layer-list');
32724
32725         var custom = backgroundList.append('li')
32726             .attr('class', 'custom_layer')
32727             .datum(iD.BackgroundSource.Custom());
32728
32729         custom.append('button')
32730             .attr('class', 'layer-browse')
32731             .call(bootstrap.tooltip()
32732                 .title(t('background.custom_button'))
32733                 .placement('left'))
32734             .on('click', editCustom)
32735             .call(iD.svg.Icon('#icon-search'));
32736
32737         var label = custom.append('label');
32738
32739         label.append('input')
32740             .attr('type', 'radio')
32741             .attr('name', 'layers')
32742             .on('change', function () {
32743                 if (customTemplate) {
32744                     setCustom(customTemplate);
32745                 } else {
32746                     editCustom();
32747                 }
32748             });
32749
32750         label.append('span')
32751             .text(t('background.custom'));
32752
32753         content.append('div')
32754             .attr('class', 'imagery-faq')
32755             .append('a')
32756             .attr('target', '_blank')
32757             .attr('tabindex', -1)
32758             .call(iD.svg.Icon('#icon-out-link', 'inline'))
32759             .attr('href', 'https://github.com/openstreetmap/iD/blob/master/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
32760             .append('span')
32761             .text(t('background.imagery_source_faq'));
32762
32763         var overlayList = content.append('ul')
32764             .attr('class', 'layer-list');
32765
32766         var controls = content.append('div')
32767             .attr('class', 'controls-list');
32768
32769
32770         /* minimap toggle */
32771
32772         var minimapLabel = controls
32773             .append('label')
32774             .call(bootstrap.tooltip()
32775                 .html(true)
32776                 .title(iD.ui.tooltipHtml(t('background.minimap.tooltip'), '/'))
32777                 .placement('top')
32778             );
32779
32780         minimapLabel.classed('minimap-toggle', true)
32781             .append('input')
32782             .attr('type', 'checkbox')
32783             .on('change', function() {
32784                 iD.ui.MapInMap.toggle();
32785                 d3.event.preventDefault();
32786             });
32787
32788         minimapLabel.append('span')
32789             .text(t('background.minimap.description'));
32790
32791
32792         /* imagery offset controls */
32793
32794         var adjustments = content.append('div')
32795             .attr('class', 'adjustments');
32796
32797         adjustments.append('a')
32798             .text(t('background.fix_misalignment'))
32799             .attr('href', '#')
32800             .classed('hide-toggle', true)
32801             .classed('expanded', false)
32802             .on('click', function() {
32803                 var exp = d3.select(this).classed('expanded');
32804                 nudgeContainer.style('display', exp ? 'none' : 'block');
32805                 d3.select(this).classed('expanded', !exp);
32806                 d3.event.preventDefault();
32807             });
32808
32809         var nudgeContainer = adjustments.append('div')
32810             .attr('class', 'nudge-container cf')
32811             .style('display', 'none');
32812
32813         nudgeContainer.append('div')
32814             .attr('class', 'nudge-instructions')
32815             .text(t('background.offset'));
32816
32817         var nudgeRect = nudgeContainer.append('div')
32818             .attr('class', 'nudge-outer-rect')
32819             .on('mousedown', dragOffset);
32820
32821         nudgeRect.append('div')
32822             .attr('class', 'nudge-inner-rect')
32823             .append('input')
32824             .on('change', inputOffset)
32825             .on('mousedown', function() {
32826                 d3.event.stopPropagation();
32827             });
32828
32829         nudgeContainer.append('div')
32830             .selectAll('button')
32831             .data(directions).enter()
32832             .append('button')
32833             .attr('class', function(d) { return d[0] + ' nudge'; })
32834             .on('mousedown', function(d) {
32835                 buttonOffset(d[1]);
32836             });
32837
32838         nudgeContainer.append('button')
32839             .attr('title', t('background.reset'))
32840             .attr('class', 'nudge-reset disabled')
32841             .on('click', resetOffset)
32842             .call(iD.svg.Icon('#icon-undo'));
32843
32844         context.map()
32845             .on('move.background-update', _.debounce(update, 1000));
32846
32847         context.background()
32848             .on('change.background-update', update);
32849
32850
32851         update();
32852         setOpacity(opacityDefault);
32853
32854         var keybinding = d3.keybinding('background')
32855             .on(key, toggle)
32856             .on('F', hide)
32857             .on('H', hide);
32858
32859         d3.select(document)
32860             .call(keybinding);
32861
32862         context.surface().on('mousedown.background-outside', hide);
32863         context.container().on('mousedown.background-outside', hide);
32864     }
32865
32866     return background;
32867 };
32868 // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
32869 // For example, ⌘Z -> Ctrl+Z
32870 iD.ui.cmd = function(code) {
32871     if (iD.detect().os === 'mac') {
32872         return code;
32873     }
32874
32875     if (iD.detect().os === 'win') {
32876         if (code === '⌘⇧Z') return 'Ctrl+Y';
32877     }
32878
32879     var result = '',
32880         replacements = {
32881             '⌘': 'Ctrl',
32882             '⇧': 'Shift',
32883             '⌥': 'Alt',
32884             '⌫': 'Backspace',
32885             '⌦': 'Delete'
32886         };
32887
32888     for (var i = 0; i < code.length; i++) {
32889         if (code[i] in replacements) {
32890             result += replacements[code[i]] + '+';
32891         } else {
32892             result += code[i];
32893         }
32894     }
32895
32896     return result;
32897 };
32898 iD.ui.Commit = function(context) {
32899     var dispatch = d3.dispatch('cancel', 'save');
32900
32901     function commit(selection) {
32902         var changes = context.history().changes(),
32903             summary = context.history().difference().summary();
32904
32905         function zoomToEntity(change) {
32906             var entity = change.entity;
32907             if (change.changeType !== 'deleted' &&
32908                 context.graph().entity(entity.id).geometry(context.graph()) !== 'vertex') {
32909                 context.map().zoomTo(entity);
32910                 context.surface().selectAll(
32911                     iD.util.entityOrMemberSelector([entity.id], context.graph()))
32912                     .classed('hover', true);
32913             }
32914         }
32915
32916         var header = selection.append('div')
32917             .attr('class', 'header fillL');
32918
32919         header.append('h3')
32920             .text(t('commit.title'));
32921
32922         var body = selection.append('div')
32923             .attr('class', 'body');
32924
32925
32926         // Comment Section
32927         var commentSection = body.append('div')
32928             .attr('class', 'modal-section form-field commit-form');
32929
32930         commentSection.append('label')
32931             .attr('class', 'form-label')
32932             .text(t('commit.message_label'));
32933
32934         var commentField = commentSection.append('textarea')
32935             .attr('placeholder', t('commit.description_placeholder'))
32936             .attr('maxlength', 255)
32937             .property('value', context.storage('comment') || '')
32938             .on('input.save', enableDisableSaveButton)
32939             .on('change.save', enableDisableSaveButton)
32940             .on('input.save', detectForClippy)
32941             .on('change.save', detectForClippy)
32942             .on('blur.save', function() {
32943                 context.storage('comment', this.value);
32944             });
32945
32946         function enableDisableSaveButton() {
32947             d3.selectAll('.save-section .save-button')
32948                 .attr('disabled', (this.value.length ? null : true));
32949         }
32950
32951         function detectForClippy() {
32952             var googleWarning = clippyArea
32953                .html('')
32954                .selectAll('a')
32955                .data(this.value.match(/google/i) ? [true] : []);
32956
32957             googleWarning.exit().remove();
32958
32959             googleWarning.enter()
32960                .append('a')
32961                .attr('target', '_blank')
32962                .attr('tabindex', -1)
32963                .call(iD.svg.Icon('#icon-alert', 'inline'))
32964                .attr('href', t('commit.google_warning_link'))
32965                .append('span')
32966                .text(t('commit.google_warning'));
32967         }
32968
32969         commentField.node().select();
32970
32971         context.connection().userChangesets(function (err, changesets) {
32972             if (err) return;
32973
32974             var comments = [];
32975
32976             for (var i = 0; i < changesets.length; i++) {
32977                 if (changesets[i].tags.comment) {
32978                     comments.push({
32979                         title: changesets[i].tags.comment,
32980                         value: changesets[i].tags.comment
32981                     });
32982                 }
32983             }
32984
32985             commentField.call(d3.combobox().caseSensitive(true).data(comments));
32986         });
32987
32988         var clippyArea = commentSection.append('div')
32989             .attr('class', 'clippy-area');
32990
32991
32992         var changeSetInfo = commentSection.append('div')
32993             .attr('class', 'changeset-info');
32994
32995         changeSetInfo.append('a')
32996             .attr('target', '_blank')
32997             .attr('tabindex', -1)
32998             .call(iD.svg.Icon('#icon-out-link', 'inline'))
32999             .attr('href', t('commit.about_changeset_comments_link'))
33000             .append('span')
33001             .text(t('commit.about_changeset_comments'));
33002
33003         // Warnings
33004         var warnings = body.selectAll('div.warning-section')
33005             .data([context.history().validate(changes)])
33006             .enter()
33007             .append('div')
33008             .attr('class', 'modal-section warning-section fillL2')
33009             .style('display', function(d) { return _.isEmpty(d) ? 'none' : null; })
33010             .style('background', '#ffb');
33011
33012         warnings.append('h3')
33013             .text(t('commit.warnings'));
33014
33015         var warningLi = warnings.append('ul')
33016             .attr('class', 'changeset-list')
33017             .selectAll('li')
33018             .data(function(d) { return d; })
33019             .enter()
33020             .append('li')
33021             .style()
33022             .on('mouseover', mouseover)
33023             .on('mouseout', mouseout)
33024             .on('click', warningClick);
33025
33026         warningLi
33027             .call(iD.svg.Icon('#icon-alert', 'pre-text'));
33028
33029         warningLi
33030             .append('strong').text(function(d) {
33031                 return d.message;
33032             });
33033
33034         warningLi.filter(function(d) { return d.tooltip; })
33035             .call(bootstrap.tooltip()
33036                 .title(function(d) { return d.tooltip; })
33037                 .placement('top')
33038             );
33039
33040
33041         // Upload Explanation
33042         var saveSection = body.append('div')
33043             .attr('class','modal-section save-section fillL cf');
33044
33045         var prose = saveSection.append('p')
33046             .attr('class', 'commit-info')
33047             .html(t('commit.upload_explanation'));
33048
33049         context.connection().userDetails(function(err, user) {
33050             if (err) return;
33051
33052             var userLink = d3.select(document.createElement('div'));
33053
33054             if (user.image_url) {
33055                 userLink.append('img')
33056                     .attr('src', user.image_url)
33057                     .attr('class', 'icon pre-text user-icon');
33058             }
33059
33060             userLink.append('a')
33061                 .attr('class','user-info')
33062                 .text(user.display_name)
33063                 .attr('href', context.connection().userURL(user.display_name))
33064                 .attr('tabindex', -1)
33065                 .attr('target', '_blank');
33066
33067             prose.html(t('commit.upload_explanation_with_user', {user: userLink.html()}));
33068         });
33069
33070
33071         // Buttons
33072         var buttonSection = saveSection.append('div')
33073             .attr('class','buttons fillL cf');
33074
33075         var cancelButton = buttonSection.append('button')
33076             .attr('class', 'secondary-action col5 button cancel-button')
33077             .on('click.cancel', function() { dispatch.cancel(); });
33078
33079         cancelButton.append('span')
33080             .attr('class', 'label')
33081             .text(t('commit.cancel'));
33082
33083         var saveButton = buttonSection.append('button')
33084             .attr('class', 'action col5 button save-button')
33085             .attr('disabled', function() {
33086                 var n = d3.select('.commit-form textarea').node();
33087                 return (n && n.value.length) ? null : true;
33088             })
33089             .on('click.save', function() {
33090                 dispatch.save({
33091                     comment: commentField.node().value
33092                 });
33093             });
33094
33095         saveButton.append('span')
33096             .attr('class', 'label')
33097             .text(t('commit.save'));
33098
33099
33100         // Changes
33101         var changeSection = body.selectAll('div.commit-section')
33102             .data([0])
33103             .enter()
33104             .append('div')
33105             .attr('class', 'commit-section modal-section fillL2');
33106
33107         changeSection.append('h3')
33108             .text(t('commit.changes', {count: summary.length}));
33109
33110         var li = changeSection.append('ul')
33111             .attr('class', 'changeset-list')
33112             .selectAll('li')
33113             .data(summary)
33114             .enter()
33115             .append('li')
33116             .on('mouseover', mouseover)
33117             .on('mouseout', mouseout)
33118             .on('click', zoomToEntity);
33119
33120         li.each(function(d) {
33121             d3.select(this)
33122                 .call(iD.svg.Icon('#icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
33123         });
33124
33125         li.append('span')
33126             .attr('class', 'change-type')
33127             .text(function(d) {
33128                 return t('commit.' + d.changeType) + ' ';
33129             });
33130
33131         li.append('strong')
33132             .attr('class', 'entity-type')
33133             .text(function(d) {
33134                 return context.presets().match(d.entity, d.graph).name();
33135             });
33136
33137         li.append('span')
33138             .attr('class', 'entity-name')
33139             .text(function(d) {
33140                 var name = iD.util.displayName(d.entity) || '',
33141                     string = '';
33142                 if (name !== '') string += ':';
33143                 return string += ' ' + name;
33144             });
33145
33146         li.style('opacity', 0)
33147             .transition()
33148             .style('opacity', 1);
33149
33150
33151         function mouseover(d) {
33152             if (d.entity) {
33153                 context.surface().selectAll(
33154                     iD.util.entityOrMemberSelector([d.entity.id], context.graph())
33155                 ).classed('hover', true);
33156             }
33157         }
33158
33159         function mouseout() {
33160             context.surface().selectAll('.hover')
33161                 .classed('hover', false);
33162         }
33163
33164         function warningClick(d) {
33165             if (d.entity) {
33166                 context.map().zoomTo(d.entity);
33167                 context.enter(
33168                     iD.modes.Select(context, [d.entity.id])
33169                         .suppressMenu(true));
33170             }
33171         }
33172
33173         // Call the enableDisableSaveButton and detectForClippy methods
33174         // off the bat, in case a changeset comment is recovered from
33175         // localStorage
33176         commentField.trigger('input');
33177     }
33178
33179     return d3.rebind(commit, dispatch, 'on');
33180 };
33181 iD.ui.confirm = function(selection) {
33182     var modal = iD.ui.modal(selection);
33183
33184     modal.select('.modal')
33185         .classed('modal-alert', true);
33186
33187     var section = modal.select('.content');
33188
33189     section.append('div')
33190         .attr('class', 'modal-section header');
33191
33192     section.append('div')
33193         .attr('class', 'modal-section message-text');
33194
33195     var buttons = section.append('div')
33196         .attr('class', 'modal-section buttons cf');
33197
33198     modal.okButton = function() {
33199         buttons
33200             .append('button')
33201             .attr('class', 'action col4')
33202             .on('click.confirm', function() {
33203                 modal.remove();
33204             })
33205             .text(t('confirm.okay'));
33206
33207         return modal;
33208     };
33209
33210     return modal;
33211 };
33212 iD.ui.Conflicts = function(context) {
33213     var dispatch = d3.dispatch('download', 'cancel', 'save'),
33214         list;
33215
33216     function conflicts(selection) {
33217         var header = selection
33218             .append('div')
33219             .attr('class', 'header fillL');
33220
33221         header
33222             .append('button')
33223             .attr('class', 'fr')
33224             .on('click', function() { dispatch.cancel(); })
33225             .call(iD.svg.Icon('#icon-close'));
33226
33227         header
33228             .append('h3')
33229             .text(t('save.conflict.header'));
33230
33231         var body = selection
33232             .append('div')
33233             .attr('class', 'body fillL');
33234
33235         body
33236             .append('div')
33237             .attr('class', 'conflicts-help')
33238             .text(t('save.conflict.help'))
33239             .append('a')
33240             .attr('class', 'conflicts-download')
33241             .text(t('save.conflict.download_changes'))
33242             .on('click.download', function() { dispatch.download(); });
33243
33244         body
33245             .append('div')
33246             .attr('class', 'conflict-container fillL3')
33247             .call(showConflict, 0);
33248
33249         body
33250             .append('div')
33251             .attr('class', 'conflicts-done')
33252             .attr('opacity', 0)
33253             .style('display', 'none')
33254             .text(t('save.conflict.done'));
33255
33256         var buttons = body
33257             .append('div')
33258             .attr('class','buttons col12 joined conflicts-buttons');
33259
33260         buttons
33261             .append('button')
33262             .attr('disabled', list.length > 1)
33263             .attr('class', 'action conflicts-button col6')
33264             .text(t('save.title'))
33265             .on('click.try_again', function() { dispatch.save(); });
33266
33267         buttons
33268             .append('button')
33269             .attr('class', 'secondary-action conflicts-button col6')
33270             .text(t('confirm.cancel'))
33271             .on('click.cancel', function() { dispatch.cancel(); });
33272     }
33273
33274
33275     function showConflict(selection, index) {
33276         if (index < 0 || index >= list.length) return;
33277
33278         var parent = d3.select(selection.node().parentNode);
33279
33280         // enable save button if this is the last conflict being reviewed..
33281         if (index === list.length - 1) {
33282             window.setTimeout(function() {
33283                 parent.select('.conflicts-button')
33284                     .attr('disabled', null);
33285
33286                 parent.select('.conflicts-done')
33287                     .transition()
33288                     .attr('opacity', 1)
33289                     .style('display', 'block');
33290             }, 250);
33291         }
33292
33293         var item = selection
33294             .selectAll('.conflict')
33295             .data([list[index]]);
33296
33297         var enter = item.enter()
33298             .append('div')
33299             .attr('class', 'conflict');
33300
33301         enter
33302             .append('h4')
33303             .attr('class', 'conflict-count')
33304             .text(t('save.conflict.count', { num: index + 1, total: list.length }));
33305
33306         enter
33307             .append('a')
33308             .attr('class', 'conflict-description')
33309             .attr('href', '#')
33310             .text(function(d) { return d.name; })
33311             .on('click', function(d) {
33312                 zoomToEntity(d.id);
33313                 d3.event.preventDefault();
33314             });
33315
33316         var details = enter
33317             .append('div')
33318             .attr('class', 'conflict-detail-container');
33319
33320         details
33321             .append('ul')
33322             .attr('class', 'conflict-detail-list')
33323             .selectAll('li')
33324             .data(function(d) { return d.details || []; })
33325             .enter()
33326             .append('li')
33327             .attr('class', 'conflict-detail-item')
33328             .html(function(d) { return d; });
33329
33330         details
33331             .append('div')
33332             .attr('class', 'conflict-choices')
33333             .call(addChoices);
33334
33335         details
33336             .append('div')
33337             .attr('class', 'conflict-nav-buttons joined cf')
33338             .selectAll('button')
33339             .data(['previous', 'next'])
33340             .enter()
33341             .append('button')
33342             .text(function(d) { return t('save.conflict.' + d); })
33343             .attr('class', 'conflict-nav-button action col6')
33344             .attr('disabled', function(d, i) {
33345                 return (i === 0 && index === 0) ||
33346                     (i === 1 && index === list.length - 1) || null;
33347             })
33348             .on('click', function(d, i) {
33349                 var container = parent.select('.conflict-container'),
33350                 sign = (i === 0 ? -1 : 1);
33351
33352                 container
33353                     .selectAll('.conflict')
33354                     .remove();
33355
33356                 container
33357                     .call(showConflict, index + sign);
33358
33359                 d3.event.preventDefault();
33360             });
33361
33362         item.exit()
33363             .remove();
33364
33365     }
33366
33367     function addChoices(selection) {
33368         var choices = selection
33369             .append('ul')
33370             .attr('class', 'layer-list')
33371             .selectAll('li')
33372             .data(function(d) { return d.choices || []; });
33373
33374         var enter = choices.enter()
33375             .append('li')
33376             .attr('class', 'layer');
33377
33378         var label = enter
33379             .append('label');
33380
33381         label
33382             .append('input')
33383             .attr('type', 'radio')
33384             .attr('name', function(d) { return d.id; })
33385             .on('change', function(d, i) {
33386                 var ul = this.parentNode.parentNode.parentNode;
33387                 ul.__data__.chosen = i;
33388                 choose(ul, d);
33389             });
33390
33391         label
33392             .append('span')
33393             .text(function(d) { return d.text; });
33394
33395         choices
33396             .each(function(d, i) {
33397                 var ul = this.parentNode;
33398                 if (ul.__data__.chosen === i) choose(ul, d);
33399             });
33400     }
33401
33402     function choose(ul, datum) {
33403         if (d3.event) d3.event.preventDefault();
33404
33405         d3.select(ul)
33406             .selectAll('li')
33407             .classed('active', function(d) { return d === datum; })
33408             .selectAll('input')
33409             .property('checked', function(d) { return d === datum; });
33410
33411         var extent = iD.geo.Extent(),
33412             entity;
33413
33414         entity = context.graph().hasEntity(datum.id);
33415         if (entity) extent._extend(entity.extent(context.graph()));
33416
33417         datum.action();
33418
33419         entity = context.graph().hasEntity(datum.id);
33420         if (entity) extent._extend(entity.extent(context.graph()));
33421
33422         zoomToEntity(datum.id, extent);
33423     }
33424
33425     function zoomToEntity(id, extent) {
33426         context.surface().selectAll('.hover')
33427             .classed('hover', false);
33428
33429         var entity = context.graph().hasEntity(id);
33430         if (entity) {
33431             if (extent) {
33432                 context.map().trimmedExtent(extent);
33433             } else {
33434                 context.map().zoomTo(entity);
33435             }
33436             context.surface().selectAll(
33437                 iD.util.entityOrMemberSelector([entity.id], context.graph()))
33438                 .classed('hover', true);
33439         }
33440     }
33441
33442
33443     // The conflict list should be an array of objects like:
33444     // {
33445     //     id: id,
33446     //     name: entityName(local),
33447     //     details: merge.conflicts(),
33448     //     chosen: 1,
33449     //     choices: [
33450     //         choice(id, keepMine, forceLocal),
33451     //         choice(id, keepTheirs, forceRemote)
33452     //     ]
33453     // }
33454     conflicts.list = function(_) {
33455         if (!arguments.length) return list;
33456         list = _;
33457         return conflicts;
33458     };
33459
33460     return d3.rebind(conflicts, dispatch, 'on');
33461 };
33462 iD.ui.Contributors = function(context) {
33463     var debouncedUpdate = _.debounce(function() { update(); }, 1000),
33464         limit = 4,
33465         hidden = false,
33466         wrap = d3.select(null);
33467
33468     function update() {
33469         var users = {},
33470             entities = context.intersects(context.map().extent());
33471
33472         entities.forEach(function(entity) {
33473             if (entity && entity.user) users[entity.user] = true;
33474         });
33475
33476         var u = Object.keys(users),
33477             subset = u.slice(0, u.length > limit ? limit - 1 : limit);
33478
33479         wrap.html('')
33480             .call(iD.svg.Icon('#icon-nearby', 'pre-text light'));
33481
33482         var userList = d3.select(document.createElement('span'));
33483
33484         userList.selectAll()
33485             .data(subset)
33486             .enter()
33487             .append('a')
33488             .attr('class', 'user-link')
33489             .attr('href', function(d) { return context.connection().userURL(d); })
33490             .attr('target', '_blank')
33491             .attr('tabindex', -1)
33492             .text(String);
33493
33494         if (u.length > limit) {
33495             var count = d3.select(document.createElement('span'));
33496
33497             count.append('a')
33498                 .attr('target', '_blank')
33499                 .attr('tabindex', -1)
33500                 .attr('href', function() {
33501                     return context.connection().changesetsURL(context.map().center(), context.map().zoom());
33502                 })
33503                 .text(u.length - limit + 1);
33504
33505             wrap.append('span')
33506                 .html(t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
33507
33508         } else {
33509             wrap.append('span')
33510                 .html(t('contributors.list', { users: userList.html() }));
33511         }
33512
33513         if (!u.length) {
33514             hidden = true;
33515             wrap
33516                 .transition()
33517                 .style('opacity', 0);
33518
33519         } else if (hidden) {
33520             wrap
33521                 .transition()
33522                 .style('opacity', 1);
33523         }
33524     }
33525
33526     return function(selection) {
33527         wrap = selection;
33528         update();
33529
33530         context.connection().on('loaded.contributors', debouncedUpdate);
33531         context.map().on('move.contributors', debouncedUpdate);
33532     };
33533 };
33534 iD.ui.Disclosure = function() {
33535     var dispatch = d3.dispatch('toggled'),
33536         title,
33537         expanded = false,
33538         content = function () {};
33539
33540     var disclosure = function(selection) {
33541         var $link = selection.selectAll('.hide-toggle')
33542             .data([0]);
33543
33544         $link.enter().append('a')
33545             .attr('href', '#')
33546             .attr('class', 'hide-toggle');
33547
33548         $link.text(title)
33549             .on('click', toggle)
33550             .classed('expanded', expanded);
33551
33552         var $body = selection.selectAll('div')
33553             .data([0]);
33554
33555         $body.enter().append('div');
33556
33557         $body.classed('hide', !expanded)
33558             .call(content);
33559
33560         function toggle() {
33561             expanded = !expanded;
33562             $link.classed('expanded', expanded);
33563             $body.call(iD.ui.Toggle(expanded));
33564             dispatch.toggled(expanded);
33565         }
33566     };
33567
33568     disclosure.title = function(_) {
33569         if (!arguments.length) return title;
33570         title = _;
33571         return disclosure;
33572     };
33573
33574     disclosure.expanded = function(_) {
33575         if (!arguments.length) return expanded;
33576         expanded = _;
33577         return disclosure;
33578     };
33579
33580     disclosure.content = function(_) {
33581         if (!arguments.length) return content;
33582         content = _;
33583         return disclosure;
33584     };
33585
33586     return d3.rebind(disclosure, dispatch, 'on');
33587 };
33588 iD.ui.EntityEditor = function(context) {
33589     var dispatch = d3.dispatch('choose'),
33590         state = 'select',
33591         coalesceChanges = false,
33592         modified = false,
33593         base,
33594         id,
33595         preset,
33596         reference;
33597
33598     var presetEditor = iD.ui.preset(context)
33599         .on('change', changeTags);
33600     var rawTagEditor = iD.ui.RawTagEditor(context)
33601         .on('change', changeTags);
33602
33603     function entityEditor(selection) {
33604         var entity = context.entity(id),
33605             tags = _.clone(entity.tags);
33606
33607         var $header = selection.selectAll('.header')
33608             .data([0]);
33609
33610         // Enter
33611         var $enter = $header.enter().append('div')
33612             .attr('class', 'header fillL cf');
33613
33614         $enter.append('button')
33615             .attr('class', 'fl preset-reset preset-choose')
33616             .append('span')
33617             .html('&#9668;');
33618
33619         $enter.append('button')
33620             .attr('class', 'fr preset-close')
33621             .call(iD.svg.Icon(modified ? '#icon-apply' : '#icon-close'));
33622
33623         $enter.append('h3');
33624
33625         // Update
33626         $header.select('h3')
33627             .text(t('inspector.edit'));
33628
33629         $header.select('.preset-close')
33630             .on('click', function() {
33631                 context.enter(iD.modes.Browse(context));
33632             });
33633
33634         var $body = selection.selectAll('.inspector-body')
33635             .data([0]);
33636
33637         // Enter
33638         $enter = $body.enter().append('div')
33639             .attr('class', 'inspector-body');
33640
33641         $enter.append('div')
33642             .attr('class', 'preset-list-item inspector-inner')
33643             .append('div')
33644             .attr('class', 'preset-list-button-wrap')
33645             .append('button')
33646             .attr('class', 'preset-list-button preset-reset')
33647             .call(bootstrap.tooltip()
33648                 .title(t('inspector.back_tooltip'))
33649                 .placement('bottom'))
33650             .append('div')
33651             .attr('class', 'label');
33652
33653         $body.select('.preset-list-button-wrap')
33654             .call(reference.button);
33655
33656         $body.select('.preset-list-item')
33657             .call(reference.body);
33658
33659         $enter.append('div')
33660             .attr('class', 'inspector-border inspector-preset');
33661
33662         $enter.append('div')
33663             .attr('class', 'inspector-border raw-tag-editor inspector-inner');
33664
33665         $enter.append('div')
33666             .attr('class', 'inspector-border raw-member-editor inspector-inner');
33667
33668         $enter.append('div')
33669             .attr('class', 'raw-membership-editor inspector-inner');
33670
33671         selection.selectAll('.preset-reset')
33672             .on('click', function() {
33673                 dispatch.choose(preset);
33674             });
33675
33676         // Update
33677         $body.select('.preset-list-item button')
33678             .call(iD.ui.PresetIcon()
33679                 .geometry(context.geometry(id))
33680                 .preset(preset));
33681
33682         $body.select('.preset-list-item .label')
33683             .text(preset.name());
33684
33685         $body.select('.inspector-preset')
33686             .call(presetEditor
33687                 .preset(preset)
33688                 .entityID(id)
33689                 .tags(tags)
33690                 .state(state));
33691
33692         $body.select('.raw-tag-editor')
33693             .call(rawTagEditor
33694                 .preset(preset)
33695                 .entityID(id)
33696                 .tags(tags)
33697                 .state(state));
33698
33699         if (entity.type === 'relation') {
33700             $body.select('.raw-member-editor')
33701                 .style('display', 'block')
33702                 .call(iD.ui.RawMemberEditor(context)
33703                     .entityID(id));
33704         } else {
33705             $body.select('.raw-member-editor')
33706                 .style('display', 'none');
33707         }
33708
33709         $body.select('.raw-membership-editor')
33710             .call(iD.ui.RawMembershipEditor(context)
33711                 .entityID(id));
33712
33713         function historyChanged() {
33714             if (state === 'hide') return;
33715
33716             var entity = context.hasEntity(id),
33717                 graph = context.graph();
33718             if (!entity) return;
33719
33720             entityEditor.preset(context.presets().match(entity, graph));
33721             entityEditor.modified(base !== graph);
33722             entityEditor(selection);
33723         }
33724
33725         context.history()
33726             .on('change.entity-editor', historyChanged);
33727     }
33728
33729     function clean(o) {
33730
33731         function cleanVal(k, v) {
33732             function keepSpaces(k) {
33733                 var whitelist = ['opening_hours', 'service_times', 'collection_times',
33734                     'operating_times', 'smoking_hours', 'happy_hours'];
33735                 return _.any(whitelist, function(s) { return k.indexOf(s) !== -1; });
33736             }
33737
33738             var blacklist = ['description', 'note', 'fixme'];
33739             if (_.any(blacklist, function(s) { return k.indexOf(s) !== -1; })) return v;
33740
33741             var cleaned = v.split(';')
33742                 .map(function(s) { return s.trim(); })
33743                 .join(keepSpaces(k) ? '; ' : ';');
33744
33745             // The code below is not intended to validate websites and emails.
33746             // It is only intended to prevent obvious copy-paste errors. (#2323)
33747
33748             // clean website- and email-like tags
33749             if (k.indexOf('website') !== -1 ||
33750                 k.indexOf('email') !== -1 ||
33751                 cleaned.indexOf('http') === 0) {
33752                 cleaned = cleaned
33753                     .replace(/[\u200B-\u200F\uFEFF]/g, '');  // strip LRM and other zero width chars
33754
33755             }
33756
33757             return cleaned;
33758         }
33759
33760         var out = {}, k, v;
33761         for (k in o) {
33762             if (k && (v = o[k]) !== undefined) {
33763                 out[k] = cleanVal(k, v);
33764             }
33765         }
33766         return out;
33767     }
33768
33769     // Tag changes that fire on input can all get coalesced into a single
33770     // history operation when the user leaves the field.  #2342
33771     function changeTags(changed, onInput) {
33772         var entity = context.entity(id),
33773             annotation = t('operations.change_tags.annotation'),
33774             tags = _.extend({}, entity.tags, changed);
33775
33776         if (!onInput) {
33777             tags = clean(tags);
33778         }
33779         if (!_.isEqual(entity.tags, tags)) {
33780             if (coalesceChanges) {
33781                 context.overwrite(iD.actions.ChangeTags(id, tags), annotation);
33782             } else {
33783                 context.perform(iD.actions.ChangeTags(id, tags), annotation);
33784                 coalesceChanges = !!onInput;
33785             }
33786         }
33787     }
33788
33789     entityEditor.modified = function(_) {
33790         if (!arguments.length) return modified;
33791         modified = _;
33792         d3.selectAll('button.preset-close use')
33793             .attr('xlink:href', (modified ? '#icon-apply' : '#icon-close'));
33794     };
33795
33796     entityEditor.state = function(_) {
33797         if (!arguments.length) return state;
33798         state = _;
33799         return entityEditor;
33800     };
33801
33802     entityEditor.entityID = function(_) {
33803         if (!arguments.length) return id;
33804         id = _;
33805         base = context.graph();
33806         entityEditor.preset(context.presets().match(context.entity(id), base));
33807         entityEditor.modified(false);
33808         coalesceChanges = false;
33809         return entityEditor;
33810     };
33811
33812     entityEditor.preset = function(_) {
33813         if (!arguments.length) return preset;
33814         if (_ !== preset) {
33815             preset = _;
33816             reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context)
33817                 .showing(false);
33818         }
33819         return entityEditor;
33820     };
33821
33822     return d3.rebind(entityEditor, dispatch, 'on');
33823 };
33824 iD.ui.FeatureInfo = function(context) {
33825     function update(selection) {
33826         var features = context.features(),
33827             stats = features.stats(),
33828             count = 0,
33829             hiddenList = _.compact(_.map(features.hidden(), function(k) {
33830                 if (stats[k]) {
33831                     count += stats[k];
33832                     return String(stats[k]) + ' ' + t('feature.' + k + '.description');
33833                 }
33834             }));
33835
33836         selection.html('');
33837
33838         if (hiddenList.length) {
33839             var tooltip = bootstrap.tooltip()
33840                     .placement('top')
33841                     .html(true)
33842                     .title(function() {
33843                         return iD.ui.tooltipHtml(hiddenList.join('<br/>'));
33844                     });
33845
33846             var warning = selection.append('a')
33847                 .attr('href', '#')
33848                 .attr('tabindex', -1)
33849                 .html(t('feature_info.hidden_warning', { count: count }))
33850                 .call(tooltip)
33851                 .on('click', function() {
33852                     tooltip.hide(warning);
33853                     // open map data panel?
33854                     d3.event.preventDefault();
33855                 });
33856         }
33857
33858         selection
33859             .classed('hide', !hiddenList.length);
33860     }
33861
33862     return function(selection) {
33863         update(selection);
33864
33865         context.features().on('change.feature_info', function() {
33866             update(selection);
33867         });
33868     };
33869 };
33870 iD.ui.FeatureList = function(context) {
33871     var geocodeResults;
33872
33873     function featureList(selection) {
33874         var header = selection.append('div')
33875             .attr('class', 'header fillL cf');
33876
33877         header.append('h3')
33878             .text(t('inspector.feature_list'));
33879
33880         function keypress() {
33881             var q = search.property('value'),
33882                 items = list.selectAll('.feature-list-item');
33883             if (d3.event.keyCode === 13 && q.length && items.size()) {
33884                 click(items.datum());
33885             }
33886         }
33887
33888         function inputevent() {
33889             geocodeResults = undefined;
33890             drawList();
33891         }
33892
33893         var searchWrap = selection.append('div')
33894             .attr('class', 'search-header');
33895
33896         var search = searchWrap.append('input')
33897             .attr('placeholder', t('inspector.search'))
33898             .attr('type', 'search')
33899             .on('keypress', keypress)
33900             .on('input', inputevent);
33901
33902         searchWrap
33903             .call(iD.svg.Icon('#icon-search', 'pre-text'));
33904
33905         var listWrap = selection.append('div')
33906             .attr('class', 'inspector-body');
33907
33908         var list = listWrap.append('div')
33909             .attr('class', 'feature-list cf');
33910
33911         context
33912             .on('exit.feature-list', clearSearch);
33913         context.map()
33914             .on('drawn.feature-list', mapDrawn);
33915
33916         function clearSearch() {
33917             search.property('value', '');
33918             drawList();
33919         }
33920
33921         function mapDrawn(e) {
33922             if (e.full) {
33923                 drawList();
33924             }
33925         }
33926
33927         function features() {
33928             var entities = {},
33929                 result = [],
33930                 graph = context.graph(),
33931                 q = search.property('value').toLowerCase();
33932
33933             if (!q) return result;
33934
33935             var idMatch = q.match(/^([nwr])([0-9]+)$/);
33936
33937             if (idMatch) {
33938                 result.push({
33939                     id: idMatch[0],
33940                     geometry: idMatch[1] === 'n' ? 'point' : idMatch[1] === 'w' ? 'line' : 'relation',
33941                     type: idMatch[1] === 'n' ? t('inspector.node') : idMatch[1] === 'w' ? t('inspector.way') : t('inspector.relation'),
33942                     name: idMatch[2]
33943                 });
33944             }
33945
33946             var locationMatch = sexagesimal.pair(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
33947
33948             if (locationMatch) {
33949                 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
33950                 result.push({
33951                     id: -1,
33952                     geometry: 'point',
33953                     type: t('inspector.location'),
33954                     name: loc[0].toFixed(6) + ', ' + loc[1].toFixed(6),
33955                     location: loc
33956                 });
33957             }
33958
33959             function addEntity(entity) {
33960                 if (entity.id in entities || result.length > 200)
33961                     return;
33962
33963                 entities[entity.id] = true;
33964
33965                 var name = iD.util.displayName(entity) || '';
33966                 if (name.toLowerCase().indexOf(q) >= 0) {
33967                     result.push({
33968                         id: entity.id,
33969                         entity: entity,
33970                         geometry: context.geometry(entity.id),
33971                         type: context.presets().match(entity, graph).name(),
33972                         name: name
33973                     });
33974                 }
33975
33976                 graph.parentRelations(entity).forEach(function(parent) {
33977                     addEntity(parent);
33978                 });
33979             }
33980
33981             var visible = context.surface().selectAll('.point, .line, .area')[0];
33982             for (var i = 0; i < visible.length && result.length <= 200; i++) {
33983                 addEntity(visible[i].__data__);
33984             }
33985
33986             (geocodeResults || []).forEach(function(d) {
33987                 // https://github.com/openstreetmap/iD/issues/1890
33988                 if (d.osm_type && d.osm_id) {
33989                     result.push({
33990                         id: iD.Entity.id.fromOSM(d.osm_type, d.osm_id),
33991                         geometry: d.osm_type === 'relation' ? 'relation' : d.osm_type === 'way' ? 'line' : 'point',
33992                         type: d.type !== 'yes' ? (d.type.charAt(0).toUpperCase() + d.type.slice(1)).replace('_', ' ')
33993                                                : (d.class.charAt(0).toUpperCase() + d.class.slice(1)).replace('_', ' '),
33994                         name: d.display_name,
33995                         extent: new iD.geo.Extent(
33996                             [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
33997                             [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
33998                     });
33999                 }
34000             });
34001
34002             return result;
34003         }
34004
34005         function drawList() {
34006             var value = search.property('value'),
34007                 results = features();
34008
34009             list.classed('filtered', value.length);
34010
34011             var noResultsWorldwide = geocodeResults && geocodeResults.length === 0;
34012
34013             var resultsIndicator = list.selectAll('.no-results-item')
34014                 .data([0])
34015                 .enter().append('button')
34016                 .property('disabled', true)
34017                 .attr('class', 'no-results-item')
34018                 .call(iD.svg.Icon('#icon-alert', 'pre-text'));
34019
34020             resultsIndicator.append('span')
34021                 .attr('class', 'entity-name');
34022
34023             list.selectAll('.no-results-item .entity-name')
34024                 .text(noResultsWorldwide ? t('geocoder.no_results_worldwide') : t('geocoder.no_results_visible'));
34025
34026             list.selectAll('.geocode-item')
34027                 .data([0])
34028                 .enter().append('button')
34029                 .attr('class', 'geocode-item')
34030                 .on('click', geocode)
34031                 .append('div')
34032                 .attr('class', 'label')
34033                 .append('span')
34034                 .attr('class', 'entity-name')
34035                 .text(t('geocoder.search'));
34036
34037             list.selectAll('.no-results-item')
34038                 .style('display', (value.length && !results.length) ? 'block' : 'none');
34039
34040             list.selectAll('.geocode-item')
34041                 .style('display', (value && geocodeResults === undefined) ? 'block' : 'none');
34042
34043             list.selectAll('.feature-list-item')
34044                 .data([-1])
34045                 .remove();
34046
34047             var items = list.selectAll('.feature-list-item')
34048                 .data(results, function(d) { return d.id; });
34049
34050             var enter = items.enter()
34051                 .insert('button', '.geocode-item')
34052                 .attr('class', 'feature-list-item')
34053                 .on('mouseover', mouseover)
34054                 .on('mouseout', mouseout)
34055                 .on('click', click);
34056
34057             var label = enter
34058                 .append('div')
34059                 .attr('class', 'label');
34060
34061             label.each(function(d) {
34062                 d3.select(this)
34063                     .call(iD.svg.Icon('#icon-' + d.geometry, 'pre-text'));
34064             });
34065
34066             label.append('span')
34067                 .attr('class', 'entity-type')
34068                 .text(function(d) { return d.type; });
34069
34070             label.append('span')
34071                 .attr('class', 'entity-name')
34072                 .text(function(d) { return d.name; });
34073
34074             enter.style('opacity', 0)
34075                 .transition()
34076                 .style('opacity', 1);
34077
34078             items.order();
34079
34080             items.exit()
34081                 .remove();
34082         }
34083
34084         function mouseover(d) {
34085             if (d.id === -1) return;
34086
34087             context.surface().selectAll(iD.util.entityOrMemberSelector([d.id], context.graph()))
34088                 .classed('hover', true);
34089         }
34090
34091         function mouseout() {
34092             context.surface().selectAll('.hover')
34093                 .classed('hover', false);
34094         }
34095
34096         function click(d) {
34097             d3.event.preventDefault();
34098             if (d.location) {
34099                 context.map().centerZoom([d.location[1], d.location[0]], 20);
34100             }
34101             else if (d.entity) {
34102                 if (d.entity.type === 'node') {
34103                     context.map().center(d.entity.loc);
34104                 } else if (d.entity.type === 'way') {
34105                     var center = context.projection(context.map().center()),
34106                         edge = iD.geo.chooseEdge(context.childNodes(d.entity), center, context.projection);
34107                     context.map().center(edge.loc);
34108                 }
34109                 context.enter(iD.modes.Select(context, [d.entity.id]).suppressMenu(true));
34110             } else {
34111                 context.zoomToEntity(d.id);
34112             }
34113         }
34114
34115         function geocode() {
34116             var searchVal = encodeURIComponent(search.property('value'));
34117             d3.json('https://nominatim.openstreetmap.org/search/' + searchVal + '?limit=10&format=json', function(err, resp) {
34118                 geocodeResults = resp || [];
34119                 drawList();
34120             });
34121         }
34122     }
34123
34124     return featureList;
34125 };
34126 iD.ui.flash = function(selection) {
34127     var modal = iD.ui.modal(selection);
34128
34129     modal.select('.modal').classed('modal-flash', true);
34130
34131     modal.select('.content')
34132         .classed('modal-section', true)
34133         .append('div')
34134         .attr('class', 'description');
34135
34136     modal.on('click.flash', function() { modal.remove(); });
34137
34138     setTimeout(function() {
34139         modal.remove();
34140         return true;
34141     }, 1500);
34142
34143     return modal;
34144 };
34145 iD.ui.FullScreen = function(context) {
34146     var element = context.container().node(),
34147         keybinding = d3.keybinding('full-screen');
34148         // button;
34149
34150     function getFullScreenFn() {
34151         if (element.requestFullscreen) {
34152             return element.requestFullscreen;
34153         } else if (element.msRequestFullscreen) {
34154             return  element.msRequestFullscreen;
34155         } else if (element.mozRequestFullScreen) {
34156             return  element.mozRequestFullScreen;
34157         } else if (element.webkitRequestFullscreen) {
34158             return element.webkitRequestFullscreen;
34159         }
34160     }
34161
34162     function getExitFullScreenFn() {
34163         if (document.exitFullscreen) {
34164             return document.exitFullscreen;
34165         } else if (document.msExitFullscreen) {
34166             return  document.msExitFullscreen;
34167         } else if (document.mozCancelFullScreen) {
34168             return  document.mozCancelFullScreen;
34169         } else if (document.webkitExitFullscreen) {
34170             return document.webkitExitFullscreen;
34171         }
34172     }
34173
34174     function isFullScreen() {
34175         return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement ||
34176             document.msFullscreenElement;
34177     }
34178
34179     function isSupported() {
34180         return !!getFullScreenFn();
34181     }
34182
34183     function fullScreen() {
34184         d3.event.preventDefault();
34185         if (!isFullScreen()) {
34186             // button.classed('active', true);
34187             getFullScreenFn().apply(element);
34188         } else {
34189             // button.classed('active', false);
34190             getExitFullScreenFn().apply(document);
34191         }
34192     }
34193
34194     return function() { // selection) {
34195         if (!isSupported())
34196             return;
34197
34198         // var tooltip = bootstrap.tooltip()
34199         //     .placement('left');
34200
34201         // button = selection.append('button')
34202         //     .attr('title', t('full_screen'))
34203         //     .attr('tabindex', -1)
34204         //     .on('click', fullScreen)
34205         //     .call(tooltip);
34206
34207         // button.append('span')
34208         //     .attr('class', 'icon full-screen');
34209
34210         keybinding
34211             .on('f11', fullScreen)
34212             .on(iD.ui.cmd('⌘⇧F'), fullScreen);
34213
34214         d3.select(document)
34215             .call(keybinding);
34216     };
34217 };
34218 iD.ui.Geolocate = function(context) {
34219     var geoOptions = { enableHighAccuracy: false, timeout: 6000 /* 6sec */ },
34220         locating = iD.ui.Loading(context).message(t('geolocate.locating')).blocking(true),
34221         timeoutId;
34222
34223     function click() {
34224         context.enter(iD.modes.Browse(context));
34225         context.container().call(locating);
34226         navigator.geolocation.getCurrentPosition(success, error, geoOptions);
34227
34228         // This timeout ensures that we still call finish() even if
34229         // the user declines to share their location in Firefox
34230         timeoutId = setTimeout(finish, 10000 /* 10sec */ );
34231     }
34232
34233     function success(position) {
34234         var map = context.map(),
34235             extent = iD.geo.Extent([position.coords.longitude, position.coords.latitude])
34236                 .padByMeters(position.coords.accuracy);
34237
34238         map.centerZoom(extent.center(), Math.min(20, map.extentZoom(extent)));
34239         finish();
34240     }
34241
34242     function error() {
34243         finish();
34244     }
34245
34246     function finish() {
34247         locating.close();  // unblock ui
34248         if (timeoutId) { clearTimeout(timeoutId); }
34249         timeoutId = undefined;
34250     }
34251
34252     return function(selection) {
34253         if (!navigator.geolocation) return;
34254
34255         selection.append('button')
34256             .attr('tabindex', -1)
34257             .attr('title', t('geolocate.title'))
34258             .on('click', click)
34259             .call(iD.svg.Icon('#icon-geolocate', 'light'))
34260             .call(bootstrap.tooltip()
34261                 .placement('left'));
34262     };
34263 };
34264 iD.ui.Help = function(context) {
34265     var key = 'H';
34266
34267     var docKeys = [
34268         'help.help',
34269         'help.editing_saving',
34270         'help.roads',
34271         'help.gps',
34272         'help.imagery',
34273         'help.addresses',
34274         'help.inspector',
34275         'help.buildings',
34276         'help.relations'];
34277
34278     var docs = docKeys.map(function(key) {
34279         var text = t(key);
34280         return {
34281             title: text.split('\n')[0].replace('#', '').trim(),
34282             html: marked(text.split('\n').slice(1).join('\n'))
34283         };
34284     });
34285
34286     function help(selection) {
34287
34288         function hide() {
34289             setVisible(false);
34290         }
34291
34292         function toggle() {
34293             if (d3.event) d3.event.preventDefault();
34294             tooltip.hide(button);
34295             setVisible(!button.classed('active'));
34296         }
34297
34298         function setVisible(show) {
34299             if (show !== shown) {
34300                 button.classed('active', show);
34301                 shown = show;
34302
34303                 if (show) {
34304                     selection.on('mousedown.help-inside', function() {
34305                         return d3.event.stopPropagation();
34306                     });
34307                     pane.style('display', 'block')
34308                         .style('right', '-500px')
34309                         .transition()
34310                         .duration(200)
34311                         .style('right', '0px');
34312                 } else {
34313                     pane.style('right', '0px')
34314                         .transition()
34315                         .duration(200)
34316                         .style('right', '-500px')
34317                         .each('end', function() {
34318                             d3.select(this).style('display', 'none');
34319                         });
34320                     selection.on('mousedown.help-inside', null);
34321                 }
34322             }
34323         }
34324
34325         function clickHelp(d, i) {
34326             pane.property('scrollTop', 0);
34327             doctitle.html(d.title);
34328             body.html(d.html);
34329             body.selectAll('a')
34330                 .attr('target', '_blank');
34331             menuItems.classed('selected', function(m) {
34332                 return m.title === d.title;
34333             });
34334
34335             nav.html('');
34336
34337             if (i > 0) {
34338                 var prevLink = nav.append('a')
34339                     .attr('class', 'previous')
34340                     .on('click', function() {
34341                         clickHelp(docs[i - 1], i - 1);
34342                     });
34343                 prevLink.append('span').html('&#9668; ' + docs[i - 1].title);
34344             }
34345             if (i < docs.length - 1) {
34346                 var nextLink = nav.append('a')
34347                     .attr('class', 'next')
34348                     .on('click', function() {
34349                         clickHelp(docs[i + 1], i + 1);
34350                     });
34351                 nextLink.append('span').html(docs[i + 1].title + ' &#9658;');
34352             }
34353         }
34354
34355         function clickWalkthrough() {
34356             d3.select(document.body).call(iD.ui.intro(context));
34357             setVisible(false);
34358         }
34359
34360
34361         var pane = selection.append('div')
34362                 .attr('class', 'help-wrap map-overlay fillL col5 content hide'),
34363             tooltip = bootstrap.tooltip()
34364                 .placement('left')
34365                 .html(true)
34366                 .title(iD.ui.tooltipHtml(t('help.title'), key)),
34367             button = selection.append('button')
34368                 .attr('tabindex', -1)
34369                 .on('click', toggle)
34370                 .call(iD.svg.Icon('#icon-help', 'light'))
34371                 .call(tooltip),
34372             shown = false;
34373
34374
34375         var toc = pane.append('ul')
34376             .attr('class', 'toc');
34377
34378         var menuItems = toc.selectAll('li')
34379             .data(docs)
34380             .enter()
34381             .append('li')
34382             .append('a')
34383             .html(function(d) { return d.title; })
34384             .on('click', clickHelp);
34385
34386         toc.append('li')
34387             .attr('class','walkthrough')
34388             .append('a')
34389             .text(t('splash.walkthrough'))
34390             .on('click', clickWalkthrough);
34391
34392         var content = pane.append('div')
34393             .attr('class', 'left-content');
34394
34395         var doctitle = content.append('h2')
34396             .text(t('help.title'));
34397
34398         var body = content.append('div')
34399             .attr('class', 'body');
34400
34401         var nav = content.append('div')
34402             .attr('class', 'nav');
34403
34404         clickHelp(docs[0], 0);
34405
34406         var keybinding = d3.keybinding('help')
34407             .on(key, toggle)
34408             .on('B', hide)
34409             .on('F', hide);
34410
34411         d3.select(document)
34412             .call(keybinding);
34413
34414         context.surface().on('mousedown.help-outside', hide);
34415         context.container().on('mousedown.help-outside', hide);
34416     }
34417
34418     return help;
34419 };
34420 iD.ui.Info = function(context) {
34421     var key = iD.ui.cmd('⌘I'),
34422         imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
34423         hidden = true;
34424
34425     function info(selection) {
34426         function radiansToMeters(r) {
34427             // using WGS84 authalic radius (6371007.1809 m)
34428             return r * 6371007.1809;
34429         }
34430
34431         function steradiansToSqmeters(r) {
34432             // http://gis.stackexchange.com/a/124857/40446
34433             return r / 12.56637 * 510065621724000;
34434         }
34435
34436         function toLineString(feature) {
34437             if (feature.type === 'LineString') return feature;
34438
34439             var result = { type: 'LineString', coordinates: [] };
34440             if (feature.type === 'Polygon') {
34441                 result.coordinates = feature.coordinates[0];
34442             } else if (feature.type === 'MultiPolygon') {
34443                 result.coordinates = feature.coordinates[0][0];
34444             }
34445
34446             return result;
34447         }
34448
34449         function displayLength(m) {
34450             var d = m * (imperial ? 3.28084 : 1),
34451                 p, unit;
34452
34453             if (imperial) {
34454                 if (d >= 5280) {
34455                     d /= 5280;
34456                     unit = 'mi';
34457                 } else {
34458                     unit = 'ft';
34459                 }
34460             } else {
34461                 if (d >= 1000) {
34462                     d /= 1000;
34463                     unit = 'km';
34464                 } else {
34465                     unit = 'm';
34466                 }
34467             }
34468
34469             // drop unnecessary precision
34470             p = d > 1000 ? 0 : d > 100 ? 1 : 2;
34471
34472             return String(d.toFixed(p)) + ' ' + unit;
34473         }
34474
34475         function displayArea(m2) {
34476             var d = m2 * (imperial ? 10.7639111056 : 1),
34477                 d1, d2, p1, p2, unit1, unit2;
34478
34479             if (imperial) {
34480                 if (d >= 6969600) {     // > 0.25mi² show mi²
34481                     d1 = d / 27878400;
34482                     unit1 = 'mi²';
34483                 } else {
34484                     d1 = d;
34485                     unit1 = 'ft²';
34486                 }
34487
34488                 if (d > 4356 && d < 43560000) {   // 0.1 - 1000 acres
34489                     d2 = d / 43560;
34490                     unit2 = 'ac';
34491                 }
34492
34493             } else {
34494                 if (d >= 250000) {    // > 0.25km² show km²
34495                     d1 = d / 1000000;
34496                     unit1 = 'km²';
34497                 } else {
34498                     d1 = d;
34499                     unit1 = 'm²';
34500                 }
34501
34502                 if (d > 1000 && d < 10000000) {   // 0.1 - 1000 hectares
34503                     d2 = d / 10000;
34504                     unit2 = 'ha';
34505                 }
34506             }
34507
34508             // drop unnecessary precision
34509             p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2;
34510             p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2;
34511
34512             return String(d1.toFixed(p1)) + ' ' + unit1 +
34513                 (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : '');
34514         }
34515
34516
34517         function redraw() {
34518             if (hidden) return;
34519
34520             var resolver = context.graph(),
34521                 selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }),
34522                 singular = selected.length === 1 ? selected[0] : null,
34523                 extent = iD.geo.Extent(),
34524                 entity;
34525
34526             wrap.html('');
34527             wrap.append('h4')
34528                 .attr('class', 'infobox-heading fillD')
34529                 .text(singular || t('infobox.selected', { n: selected.length }));
34530
34531             if (!selected.length) return;
34532
34533             var center;
34534             for (var i = 0; i < selected.length; i++) {
34535                 entity = context.entity(selected[i]);
34536                 extent._extend(entity.extent(resolver));
34537             }
34538             center = extent.center();
34539
34540
34541             var list = wrap.append('ul');
34542
34543             // multiple wrap, just display extent center..
34544             if (!singular) {
34545                 list.append('li')
34546                     .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
34547                 return;
34548             }
34549
34550             // single wrap, display details..
34551             if (!entity) return;
34552             var geometry = entity.geometry(resolver);
34553
34554             if (geometry === 'line' || geometry === 'area') {
34555                 var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()),
34556                     feature = entity.asGeoJSON(resolver),
34557                     length = radiansToMeters(d3.geo.length(toLineString(feature))),
34558                     lengthLabel = t('infobox.' + (closed ? 'perimeter' : 'length')),
34559                     centroid = d3.geo.centroid(feature);
34560
34561                 list.append('li')
34562                     .text(t('infobox.geometry') + ': ' +
34563                         (closed ? t('infobox.closed') + ' ' : '') + t('geometry.' + geometry) );
34564
34565                 if (closed) {
34566                     var area = steradiansToSqmeters(entity.area(resolver));
34567                     list.append('li')
34568                         .text(t('infobox.area') + ': ' + displayArea(area));
34569                 }
34570
34571                 list.append('li')
34572                     .text(lengthLabel + ': ' + displayLength(length));
34573
34574                 list.append('li')
34575                     .text(t('infobox.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5));
34576
34577
34578                 var toggle  = imperial ? 'imperial' : 'metric';
34579                 wrap.append('a')
34580                     .text(t('infobox.' + toggle))
34581                     .attr('href', '#')
34582                     .attr('class', 'button')
34583                     .on('click', function() {
34584                         d3.event.preventDefault();
34585                         imperial = !imperial;
34586                         redraw();
34587                     });
34588
34589             } else {
34590                 var centerLabel = t('infobox.' + (entity.type === 'node' ? 'location' : 'center'));
34591
34592                 list.append('li')
34593                     .text(t('infobox.geometry') + ': ' + t('geometry.' + geometry));
34594
34595                 list.append('li')
34596                     .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
34597             }
34598         }
34599
34600
34601         function toggle() {
34602             if (d3.event) d3.event.preventDefault();
34603
34604             hidden = !hidden;
34605
34606             if (hidden) {
34607                 wrap
34608                     .style('display', 'block')
34609                     .style('opacity', 1)
34610                     .transition()
34611                     .duration(200)
34612                     .style('opacity', 0)
34613                     .each('end', function() {
34614                         d3.select(this).style('display', 'none');
34615                     });
34616             } else {
34617                 wrap
34618                     .style('display', 'block')
34619                     .style('opacity', 0)
34620                     .transition()
34621                     .duration(200)
34622                     .style('opacity', 1);
34623
34624                 redraw();
34625             }
34626         }
34627
34628
34629         var wrap = selection.selectAll('.infobox')
34630             .data([0]);
34631
34632         wrap.enter()
34633             .append('div')
34634             .attr('class', 'infobox fillD2')
34635             .style('display', (hidden ? 'none' : 'block'));
34636
34637         context.map()
34638             .on('drawn.info', redraw);
34639
34640         redraw();
34641
34642         var keybinding = d3.keybinding('info')
34643             .on(key, toggle);
34644
34645         d3.select(document)
34646             .call(keybinding);
34647     }
34648
34649     return info;
34650 };
34651 iD.ui.Inspector = function(context) {
34652     var presetList = iD.ui.PresetList(context),
34653         entityEditor = iD.ui.EntityEditor(context),
34654         state = 'select',
34655         entityID,
34656         newFeature = false;
34657
34658     function inspector(selection) {
34659         presetList
34660             .entityID(entityID)
34661             .autofocus(newFeature)
34662             .on('choose', setPreset);
34663
34664         entityEditor
34665             .state(state)
34666             .entityID(entityID)
34667             .on('choose', showList);
34668
34669         var $wrap = selection.selectAll('.panewrap')
34670             .data([0]);
34671
34672         var $enter = $wrap.enter().append('div')
34673             .attr('class', 'panewrap');
34674
34675         $enter.append('div')
34676             .attr('class', 'preset-list-pane pane');
34677
34678         $enter.append('div')
34679             .attr('class', 'entity-editor-pane pane');
34680
34681         var $presetPane = $wrap.select('.preset-list-pane');
34682         var $editorPane = $wrap.select('.entity-editor-pane');
34683
34684         var graph = context.graph(),
34685             entity = context.entity(entityID),
34686             showEditor = state === 'hover' ||
34687                 entity.isUsed(graph) ||
34688                 entity.isHighwayIntersection(graph);
34689
34690         if (showEditor) {
34691             $wrap.style('right', '0%');
34692             $editorPane.call(entityEditor);
34693         } else {
34694             $wrap.style('right', '-100%');
34695             $presetPane.call(presetList);
34696         }
34697
34698         var $footer = selection.selectAll('.footer')
34699             .data([0]);
34700
34701         $footer.enter().append('div')
34702             .attr('class', 'footer');
34703
34704         selection.select('.footer')
34705             .call(iD.ui.ViewOnOSM(context)
34706                 .entityID(entityID));
34707
34708         function showList(preset) {
34709             $wrap.transition()
34710                 .styleTween('right', function() { return d3.interpolate('0%', '-100%'); });
34711
34712             $presetPane.call(presetList
34713                 .preset(preset)
34714                 .autofocus(true));
34715         }
34716
34717         function setPreset(preset) {
34718             $wrap.transition()
34719                 .styleTween('right', function() { return d3.interpolate('-100%', '0%'); });
34720
34721             $editorPane.call(entityEditor
34722                 .preset(preset));
34723         }
34724     }
34725
34726     inspector.state = function(_) {
34727         if (!arguments.length) return state;
34728         state = _;
34729         entityEditor.state(state);
34730         return inspector;
34731     };
34732
34733     inspector.entityID = function(_) {
34734         if (!arguments.length) return entityID;
34735         entityID = _;
34736         return inspector;
34737     };
34738
34739     inspector.newFeature = function(_) {
34740         if (!arguments.length) return newFeature;
34741         newFeature = _;
34742         return inspector;
34743     };
34744
34745     return inspector;
34746 };
34747 iD.ui.intro = function(context) {
34748     var step;
34749
34750     function intro(selection) {
34751
34752         function localizedName(id) {
34753             var features = {
34754                 n2140018997: 'city_hall',
34755                 n367813436: 'fire_department',
34756                 w203988286: 'memory_isle_park',
34757                 w203972937: 'riverwalk_trail',
34758                 w203972938: 'riverwalk_trail',
34759                 w203972940: 'riverwalk_trail',
34760                 w41785752: 'w_michigan_ave',
34761                 w134150789: 'w_michigan_ave',
34762                 w134150795: 'w_michigan_ave',
34763                 w134150800: 'w_michigan_ave',
34764                 w134150811: 'w_michigan_ave',
34765                 w134150802: 'e_michigan_ave',
34766                 w134150836: 'e_michigan_ave',
34767                 w41074896: 'e_michigan_ave',
34768                 w17965834: 'spring_st',
34769                 w203986457: 'scidmore_park',
34770                 w203049587: 'petting_zoo',
34771                 w17967397: 'n_andrews_st',
34772                 w17967315: 's_andrews_st',
34773                 w17967326: 'n_constantine_st',
34774                 w17966400: 's_constantine_st',
34775                 w170848823: 'rocky_river',
34776                 w170848824: 'rocky_river',
34777                 w170848331: 'rocky_river',
34778                 w17967752: 'railroad_dr',
34779                 w17965998: 'conrail_rr',
34780                 w134150845: 'conrail_rr',
34781                 w170989131: 'st_joseph_river',
34782                 w143497377: 'n_main_st',
34783                 w134150801: 's_main_st',
34784                 w134150830: 's_main_st',
34785                 w17966462: 's_main_st',
34786                 w17967734: 'water_st',
34787                 w17964996: 'foster_st',
34788                 w170848330: 'portage_river',
34789                 w17965351: 'flower_st',
34790                 w17965502: 'elm_st',
34791                 w17965402: 'walnut_st',
34792                 w17964793: 'morris_ave',
34793                 w17967444: 'east_st',
34794                 w17966984: 'portage_ave'
34795             };
34796             return features[id] && t('intro.graph.' + features[id]);
34797         }
34798
34799         context.enter(iD.modes.Browse(context));
34800
34801         // Save current map state
34802         var history = context.history().toJSON(),
34803             hash = window.location.hash,
34804             center = context.map().center(),
34805             zoom = context.map().zoom(),
34806             background = context.background().baseLayerSource(),
34807             opacity = d3.selectAll('#map .layer-background').style('opacity'),
34808             loadedTiles = context.connection().loadedTiles(),
34809             baseEntities = context.history().graph().base().entities,
34810             introGraph, name;
34811
34812         // Block saving
34813         context.inIntro(true);
34814
34815         // Load semi-real data used in intro
34816         context.connection().toggle(false).flush();
34817         context.history().reset();
34818
34819         introGraph = JSON.parse(iD.introGraph);
34820         for (var key in introGraph) {
34821             introGraph[key] = iD.Entity(introGraph[key]);
34822             name = localizedName(key);
34823             if (name) {
34824                 introGraph[key].tags.name = name;
34825             }
34826         }
34827         context.history().merge(d3.values(iD.Graph().load(introGraph).entities));
34828         context.background().bing();
34829
34830         d3.selectAll('#map .layer-background').style('opacity', 1);
34831
34832         var curtain = d3.curtain();
34833         selection.call(curtain);
34834
34835         function reveal(box, text, options) {
34836             options = options || {};
34837             if (text) curtain.reveal(box, text, options.tooltipClass, options.duration);
34838             else curtain.reveal(box, '', '', options.duration);
34839         }
34840
34841         var steps = ['navigation', 'point', 'area', 'line', 'startEditing'].map(function(step, i) {
34842             var s = iD.ui.intro[step](context, reveal)
34843                 .on('done', function() {
34844                     entered.filter(function(d) {
34845                         return d.title === s.title;
34846                     }).classed('finished', true);
34847                     enter(steps[i + 1]);
34848                 });
34849             return s;
34850         });
34851
34852         steps[steps.length - 1].on('startEditing', function() {
34853             curtain.remove();
34854             navwrap.remove();
34855             d3.selectAll('#map .layer-background').style('opacity', opacity);
34856             context.connection().toggle(true).flush().loadedTiles(loadedTiles);
34857             context.history().reset().merge(d3.values(baseEntities));
34858             context.background().baseLayerSource(background);
34859             if (history) context.history().fromJSON(history, false);
34860             context.map().centerZoom(center, zoom);
34861             window.location.replace(hash);
34862             context.inIntro(false);
34863         });
34864
34865         var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
34866
34867         var buttonwrap = navwrap.append('div')
34868             .attr('class', 'joined')
34869             .selectAll('button.step');
34870
34871         var entered = buttonwrap
34872             .data(steps)
34873             .enter()
34874             .append('button')
34875             .attr('class', 'step')
34876             .on('click', enter);
34877
34878         entered
34879             .call(iD.svg.Icon('#icon-apply', 'pre-text'));
34880
34881         entered
34882             .append('label')
34883             .text(function(d) { return t(d.title); });
34884
34885         enter(steps[0]);
34886
34887         function enter (newStep) {
34888             if (step) { step.exit(); }
34889
34890             context.enter(iD.modes.Browse(context));
34891
34892             step = newStep;
34893             step.enter();
34894
34895             entered.classed('active', function(d) {
34896                 return d.title === step.title;
34897             });
34898         }
34899
34900     }
34901     return intro;
34902 };
34903
34904 iD.ui.intro.pointBox = function(point, context) {
34905     var rect = context.surfaceRect();
34906     point = context.projection(point);
34907     return {
34908         left: point[0] + rect.left - 30,
34909         top: point[1] + rect.top - 50,
34910         width: 60,
34911         height: 70
34912     };
34913 };
34914
34915 iD.ui.intro.pad = function(box, padding, context) {
34916     if (box instanceof Array) {
34917         var rect = context.surfaceRect();
34918         box = context.projection(box);
34919         box = {
34920             left: box[0] + rect.left,
34921             top: box[1] + rect.top
34922         };
34923     }
34924     return {
34925         left: box.left - padding,
34926         top: box.top - padding,
34927         width: (box.width || 0) + 2 * padding,
34928         height: (box.width || 0) + 2 * padding
34929     };
34930 };
34931
34932 iD.ui.intro.icon = function(name, svgklass) {
34933     return '<svg class="icon ' + (svgklass || '') + '">' +
34934         '<use xlink:href="' + name + '"></use></svg>';
34935 };
34936 iD.ui.Lasso = function(context) {
34937     var group, polygon;
34938
34939     lasso.coordinates = [];
34940
34941     function lasso(selection) {
34942
34943         context.container().classed('lasso', true);
34944
34945         group = selection.append('g')
34946             .attr('class', 'lasso hide');
34947
34948         polygon = group.append('path')
34949             .attr('class', 'lasso-path');
34950
34951         group.call(iD.ui.Toggle(true));
34952
34953     }
34954
34955     function draw() {
34956         if (polygon) {
34957             polygon.data([lasso.coordinates])
34958                 .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; });
34959         }
34960     }
34961
34962     lasso.extent = function () {
34963         return lasso.coordinates.reduce(function(extent, point) {
34964             return extent.extend(iD.geo.Extent(point));
34965         }, iD.geo.Extent());
34966     };
34967
34968     lasso.p = function(_) {
34969         if (!arguments.length) return lasso;
34970         lasso.coordinates.push(_);
34971         draw();
34972         return lasso;
34973     };
34974
34975     lasso.close = function() {
34976         if (group) {
34977             group.call(iD.ui.Toggle(false, function() {
34978                 d3.select(this).remove();
34979             }));
34980         }
34981         context.container().classed('lasso', false);
34982     };
34983
34984     return lasso;
34985 };
34986 iD.ui.Loading = function(context) {
34987     var message = '',
34988         blocking = false,
34989         modal;
34990
34991     var loading = function(selection) {
34992         modal = iD.ui.modal(selection, blocking);
34993
34994         var loadertext = modal.select('.content')
34995             .classed('loading-modal', true)
34996             .append('div')
34997             .attr('class', 'modal-section fillL');
34998
34999         loadertext.append('img')
35000             .attr('class', 'loader')
35001             .attr('src', context.imagePath('loader-white.gif'));
35002
35003         loadertext.append('h3')
35004             .text(message);
35005
35006         modal.select('button.close')
35007             .attr('class', 'hide');
35008
35009         return loading;
35010     };
35011
35012     loading.message = function(_) {
35013         if (!arguments.length) return message;
35014         message = _;
35015         return loading;
35016     };
35017
35018     loading.blocking = function(_) {
35019         if (!arguments.length) return blocking;
35020         blocking = _;
35021         return loading;
35022     };
35023
35024     loading.close = function() {
35025         modal.remove();
35026     };
35027
35028     return loading;
35029 };
35030 iD.ui.MapData = function(context) {
35031     var key = 'F',
35032         features = context.features().keys(),
35033         layers = context.layers(),
35034         fills = ['wireframe', 'partial', 'full'],
35035         fillDefault = context.storage('area-fill') || 'partial',
35036         fillSelected = fillDefault;
35037
35038
35039     function map_data(selection) {
35040
35041         function showsFeature(d) {
35042             return context.features().enabled(d);
35043         }
35044
35045         function autoHiddenFeature(d) {
35046             return context.features().autoHidden(d);
35047         }
35048
35049         function clickFeature(d) {
35050             context.features().toggle(d);
35051             update();
35052         }
35053
35054         function showsFill(d) {
35055             return fillSelected === d;
35056         }
35057
35058         function setFill(d) {
35059             _.each(fills, function(opt) {
35060                 context.surface().classed('fill-' + opt, Boolean(opt === d));
35061             });
35062
35063             fillSelected = d;
35064             if (d !== 'wireframe') {
35065                 fillDefault = d;
35066                 context.storage('area-fill', d);
35067             }
35068             update();
35069         }
35070
35071         function toggleLayer(which) {
35072             var layer = layers.layer(which);
35073             if (layer) {
35074                 layer.enabled(!layer.enabled());
35075                 update();
35076             }
35077         }
35078
35079         function clickGpx() {
35080             toggleLayer('gpx');
35081         }
35082
35083         function clickMapillaryImages() {
35084             toggleLayer('mapillary-images');
35085         }
35086
35087         function clickMapillarySigns() {
35088             toggleLayer('mapillary-signs');
35089         }
35090
35091
35092         function drawMapillaryItems(selection) {
35093             var mapillaryImages = layers.layer('mapillary-images'),
35094                 mapillarySigns = layers.layer('mapillary-signs'),
35095                 supportsMapillaryImages = mapillaryImages && mapillaryImages.supported(),
35096                 supportsMapillarySigns = mapillarySigns && mapillarySigns.supported(),
35097                 showsMapillaryImages = supportsMapillaryImages && mapillaryImages.enabled(),
35098                 showsMapillarySigns = supportsMapillarySigns && mapillarySigns.enabled();
35099
35100             var mapillaryList = selection
35101                 .selectAll('.layer-list-mapillary')
35102                 .data([0]);
35103
35104             // Enter
35105             mapillaryList
35106                 .enter()
35107                 .append('ul')
35108                 .attr('class', 'layer-list layer-list-mapillary');
35109
35110             var mapillaryImageLayerItem = mapillaryList
35111                 .selectAll('.list-item-mapillary-images')
35112                 .data(supportsMapillaryImages ? [0] : []);
35113
35114             var enterImages = mapillaryImageLayerItem.enter()
35115                 .append('li')
35116                 .attr('class', 'list-item-mapillary-images');
35117
35118             var labelImages = enterImages.append('label')
35119                 .call(bootstrap.tooltip()
35120                     .title(t('mapillary_images.tooltip'))
35121                     .placement('top'));
35122
35123             labelImages.append('input')
35124                 .attr('type', 'checkbox')
35125                 .on('change', clickMapillaryImages);
35126
35127             labelImages.append('span')
35128                 .text(t('mapillary_images.title'));
35129
35130
35131             var mapillarySignLayerItem = mapillaryList
35132                 .selectAll('.list-item-mapillary-signs')
35133                 .data(supportsMapillarySigns ? [0] : []);
35134
35135             var enterSigns = mapillarySignLayerItem.enter()
35136                 .append('li')
35137                 .attr('class', 'list-item-mapillary-signs');
35138
35139             var labelSigns = enterSigns.append('label')
35140                 .call(bootstrap.tooltip()
35141                     .title(t('mapillary_signs.tooltip'))
35142                     .placement('top'));
35143
35144             labelSigns.append('input')
35145                 .attr('type', 'checkbox')
35146                 .on('change', clickMapillarySigns);
35147
35148             labelSigns.append('span')
35149                 .text(t('mapillary_signs.title'));
35150
35151             // Update
35152             mapillaryImageLayerItem
35153                 .classed('active', showsMapillaryImages)
35154                 .selectAll('input')
35155                 .property('checked', showsMapillaryImages);
35156
35157             mapillarySignLayerItem
35158                 .classed('active', showsMapillarySigns)
35159                 .selectAll('input')
35160                 .property('checked', showsMapillarySigns);
35161
35162             // Exit
35163             mapillaryImageLayerItem.exit()
35164                 .remove();
35165             mapillarySignLayerItem.exit()
35166                 .remove();
35167         }
35168
35169
35170         function drawGpxItem(selection) {
35171             var gpx = layers.layer('gpx'),
35172                 hasGpx = gpx && gpx.hasGpx(),
35173                 showsGpx = hasGpx && gpx.enabled();
35174
35175             var gpxLayerItem = selection
35176                 .selectAll('.layer-list-gpx')
35177                 .data(gpx ? [0] : []);
35178
35179             // Enter
35180             var enter = gpxLayerItem.enter()
35181                 .append('ul')
35182                 .attr('class', 'layer-list layer-list-gpx')
35183                 .append('li')
35184                 .classed('list-item-gpx', true);
35185
35186             enter.append('button')
35187                 .attr('class', 'list-item-gpx-extent')
35188                 .call(bootstrap.tooltip()
35189                     .title(t('gpx.zoom'))
35190                     .placement('left'))
35191                 .on('click', function() {
35192                     d3.event.preventDefault();
35193                     d3.event.stopPropagation();
35194                     gpx.fitZoom();
35195                 })
35196                 .call(iD.svg.Icon('#icon-search'));
35197
35198             enter.append('button')
35199                 .attr('class', 'list-item-gpx-browse')
35200                 .call(bootstrap.tooltip()
35201                     .title(t('gpx.browse'))
35202                     .placement('left'))
35203                 .on('click', function() {
35204                     d3.select(document.createElement('input'))
35205                         .attr('type', 'file')
35206                         .on('change', function() {
35207                             gpx.files(d3.event.target.files);
35208                         })
35209                         .node().click();
35210                 })
35211                 .call(iD.svg.Icon('#icon-geolocate'));
35212
35213             var labelGpx = enter.append('label')
35214                 .call(bootstrap.tooltip()
35215                     .title(t('gpx.drag_drop'))
35216                     .placement('top'));
35217
35218             labelGpx.append('input')
35219                 .attr('type', 'checkbox')
35220                 .on('change', clickGpx);
35221
35222             labelGpx.append('span')
35223                 .text(t('gpx.local_layer'));
35224
35225             // Update
35226             gpxLayerItem
35227                 .classed('active', showsGpx)
35228                 .selectAll('input')
35229                 .property('disabled', !hasGpx)
35230                 .property('checked', showsGpx);
35231
35232             // Exit
35233             gpxLayerItem.exit()
35234                 .remove();
35235         }
35236
35237
35238         function drawList(selection, data, type, name, change, active) {
35239             var items = selection.selectAll('li')
35240                 .data(data);
35241
35242             // Enter
35243             var enter = items.enter()
35244                 .append('li')
35245                 .attr('class', 'layer')
35246                 .call(bootstrap.tooltip()
35247                     .html(true)
35248                     .title(function(d) {
35249                         var tip = t(name + '.' + d + '.tooltip'),
35250                             key = (d === 'wireframe' ? 'W' : null);
35251
35252                         if (name === 'feature' && autoHiddenFeature(d)) {
35253                             tip += '<div>' + t('map_data.autohidden') + '</div>';
35254                         }
35255                         return iD.ui.tooltipHtml(tip, key);
35256                     })
35257                     .placement('top')
35258                 );
35259
35260             var label = enter.append('label');
35261
35262             label.append('input')
35263                 .attr('type', type)
35264                 .attr('name', name)
35265                 .on('change', change);
35266
35267             label.append('span')
35268                 .text(function(d) { return t(name + '.' + d + '.description'); });
35269
35270             // Update
35271             items
35272                 .classed('active', active)
35273                 .selectAll('input')
35274                 .property('checked', active)
35275                 .property('indeterminate', function(d) {
35276                     return (name === 'feature' && autoHiddenFeature(d));
35277                 });
35278
35279             // Exit
35280             items.exit()
35281                 .remove();
35282         }
35283
35284
35285         function update() {
35286             dataLayerContainer.call(drawMapillaryItems);
35287             dataLayerContainer.call(drawGpxItem);
35288
35289             fillList.call(drawList, fills, 'radio', 'area_fill', setFill, showsFill);
35290
35291             featureList.call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature);
35292         }
35293
35294         function hidePanel() {
35295             setVisible(false);
35296         }
35297
35298         function togglePanel() {
35299             if (d3.event) d3.event.preventDefault();
35300             tooltip.hide(button);
35301             setVisible(!button.classed('active'));
35302         }
35303
35304         function toggleWireframe() {
35305             if (d3.event) {
35306                 d3.event.preventDefault();
35307                 d3.event.stopPropagation();
35308             }
35309             setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe'));
35310             context.map().pan([0,0]);  // trigger a redraw
35311         }
35312
35313         function setVisible(show) {
35314             if (show !== shown) {
35315                 button.classed('active', show);
35316                 shown = show;
35317
35318                 if (show) {
35319                     update();
35320                     selection.on('mousedown.map_data-inside', function() {
35321                         return d3.event.stopPropagation();
35322                     });
35323                     content.style('display', 'block')
35324                         .style('right', '-300px')
35325                         .transition()
35326                         .duration(200)
35327                         .style('right', '0px');
35328                 } else {
35329                     content.style('display', 'block')
35330                         .style('right', '0px')
35331                         .transition()
35332                         .duration(200)
35333                         .style('right', '-300px')
35334                         .each('end', function() {
35335                             d3.select(this).style('display', 'none');
35336                         });
35337                     selection.on('mousedown.map_data-inside', null);
35338                 }
35339             }
35340         }
35341
35342
35343         var content = selection.append('div')
35344                 .attr('class', 'fillL map-overlay col3 content hide'),
35345             tooltip = bootstrap.tooltip()
35346                 .placement('left')
35347                 .html(true)
35348                 .title(iD.ui.tooltipHtml(t('map_data.description'), key)),
35349             button = selection.append('button')
35350                 .attr('tabindex', -1)
35351                 .on('click', togglePanel)
35352                 .call(iD.svg.Icon('#icon-data', 'light'))
35353                 .call(tooltip),
35354             shown = false;
35355
35356         content.append('h4')
35357             .text(t('map_data.title'));
35358
35359
35360         // data layers
35361         content.append('a')
35362             .text(t('map_data.data_layers'))
35363             .attr('href', '#')
35364             .classed('hide-toggle', true)
35365             .classed('expanded', true)
35366             .on('click', function() {
35367                 var exp = d3.select(this).classed('expanded');
35368                 dataLayerContainer.style('display', exp ? 'none' : 'block');
35369                 d3.select(this).classed('expanded', !exp);
35370                 d3.event.preventDefault();
35371             });
35372
35373         var dataLayerContainer = content.append('div')
35374             .attr('class', 'data-data-layers')
35375             .style('display', 'block');
35376
35377
35378         // area fills
35379         content.append('a')
35380             .text(t('map_data.fill_area'))
35381             .attr('href', '#')
35382             .classed('hide-toggle', true)
35383             .classed('expanded', false)
35384             .on('click', function() {
35385                 var exp = d3.select(this).classed('expanded');
35386                 fillContainer.style('display', exp ? 'none' : 'block');
35387                 d3.select(this).classed('expanded', !exp);
35388                 d3.event.preventDefault();
35389             });
35390
35391         var fillContainer = content.append('div')
35392             .attr('class', 'data-area-fills')
35393             .style('display', 'none');
35394
35395         var fillList = fillContainer.append('ul')
35396             .attr('class', 'layer-list layer-fill-list');
35397
35398
35399         // feature filters
35400         content.append('a')
35401             .text(t('map_data.map_features'))
35402             .attr('href', '#')
35403             .classed('hide-toggle', true)
35404             .classed('expanded', false)
35405             .on('click', function() {
35406                 var exp = d3.select(this).classed('expanded');
35407                 featureContainer.style('display', exp ? 'none' : 'block');
35408                 d3.select(this).classed('expanded', !exp);
35409                 d3.event.preventDefault();
35410             });
35411
35412         var featureContainer = content.append('div')
35413             .attr('class', 'data-feature-filters')
35414             .style('display', 'none');
35415
35416         var featureList = featureContainer.append('ul')
35417             .attr('class', 'layer-list layer-feature-list');
35418
35419
35420         context.features()
35421             .on('change.map_data-update', update);
35422
35423         setFill(fillDefault);
35424
35425         var keybinding = d3.keybinding('features')
35426             .on(key, togglePanel)
35427             .on('W', toggleWireframe)
35428             .on('B', hidePanel)
35429             .on('H', hidePanel);
35430
35431         d3.select(document)
35432             .call(keybinding);
35433
35434         context.surface().on('mousedown.map_data-outside', hidePanel);
35435         context.container().on('mousedown.map_data-outside', hidePanel);
35436     }
35437
35438     return map_data;
35439 };
35440 iD.ui.MapInMap = function(context) {
35441     var key = '/';
35442
35443     function map_in_map(selection) {
35444         var backgroundLayer = iD.TileLayer(),
35445             overlayLayers = {},
35446             projection = iD.geo.RawMercator(),
35447             gpxLayer = iD.svg.Gpx(projection, context).showLabels(false),
35448             zoom = d3.behavior.zoom()
35449                 .scaleExtent([ztok(0.5), ztok(24)])
35450                 .on('zoom', zoomPan),
35451             transformed = false,
35452             panning = false,
35453             hidden = true,
35454             zDiff = 6,    // by default, minimap renders at (main zoom - 6)
35455             tStart, tLast, tCurr, kLast, kCurr, tiles, svg, timeoutId;
35456
35457         function ztok(z) { return 256 * Math.pow(2, z); }
35458         function ktoz(k) { return Math.log(k) / Math.LN2 - 8; }
35459
35460
35461         function startMouse() {
35462             context.surface().on('mouseup.map-in-map-outside', endMouse);
35463             context.container().on('mouseup.map-in-map-outside', endMouse);
35464
35465             tStart = tLast = tCurr = projection.translate();
35466             panning = true;
35467         }
35468
35469
35470         function zoomPan() {
35471             var e = d3.event.sourceEvent,
35472                 t = d3.event.translate,
35473                 k = d3.event.scale,
35474                 zMain = ktoz(context.projection.scale() * 2 * Math.PI),
35475                 zMini = ktoz(k);
35476
35477             // restrict minimap zoom to < (main zoom - 3)
35478             if (zMini > zMain - 3) {
35479                 zMini = zMain - 3;
35480                 zoom.scale(kCurr).translate(tCurr);  // restore last good values
35481                 return;
35482             }
35483
35484             tCurr = t;
35485             kCurr = k;
35486             zDiff = zMain - zMini;
35487
35488             var scale = kCurr / kLast,
35489                 tX = (tCurr[0] / scale - tLast[0]) * scale,
35490                 tY = (tCurr[1] / scale - tLast[1]) * scale;
35491
35492             iD.util.setTransform(tiles, tX, tY, scale);
35493             iD.util.setTransform(svg, 0, 0, scale);
35494             transformed = true;
35495
35496             queueRedraw();
35497
35498             e.preventDefault();
35499             e.stopPropagation();
35500         }
35501
35502
35503         function endMouse() {
35504             context.surface().on('mouseup.map-in-map-outside', null);
35505             context.container().on('mouseup.map-in-map-outside', null);
35506
35507             updateProjection();
35508             panning = false;
35509
35510             if (tCurr[0] !== tStart[0] && tCurr[1] !== tStart[1]) {
35511                 var dMini = wrap.dimensions(),
35512                     cMini = [ dMini[0] / 2, dMini[1] / 2 ];
35513
35514                 context.map().center(projection.invert(cMini));
35515             }
35516         }
35517
35518
35519         function updateProjection() {
35520             var loc = context.map().center(),
35521                 dMini = wrap.dimensions(),
35522                 cMini = [ dMini[0] / 2, dMini[1] / 2 ],
35523                 tMain = context.projection.translate(),
35524                 kMain = context.projection.scale(),
35525                 zMain = ktoz(kMain * 2 * Math.PI),
35526                 zMini = Math.max(zMain - zDiff, 0.5),
35527                 kMini = ztok(zMini);
35528
35529             projection
35530                 .translate(tMain)
35531                 .scale(kMini / (2 * Math.PI));
35532
35533             var s = projection(loc),
35534                 mouse = panning ? [ tCurr[0] - tStart[0], tCurr[1] - tStart[1] ] : [0, 0],
35535                 tMini = [
35536                     cMini[0] - s[0] + tMain[0] + mouse[0],
35537                     cMini[1] - s[1] + tMain[1] + mouse[1]
35538                 ];
35539
35540             projection
35541                 .translate(tMini)
35542                 .clipExtent([[0, 0], dMini]);
35543
35544             zoom
35545                 .center(cMini)
35546                 .translate(tMini)
35547                 .scale(kMini);
35548
35549             tLast = tCurr = tMini;
35550             kLast = kCurr = kMini;
35551
35552             if (transformed) {
35553                 iD.util.setTransform(tiles, 0, 0);
35554                 iD.util.setTransform(svg, 0, 0);
35555                 transformed = false;
35556             }
35557         }
35558
35559
35560         function redraw() {
35561             if (hidden) return;
35562
35563             updateProjection();
35564
35565             var dMini = wrap.dimensions(),
35566                 zMini = ktoz(projection.scale() * 2 * Math.PI);
35567
35568             // setup tile container
35569             tiles = wrap
35570                 .selectAll('.map-in-map-tiles')
35571                 .data([0]);
35572
35573             tiles
35574                 .enter()
35575                 .append('div')
35576                 .attr('class', 'map-in-map-tiles');
35577
35578             // redraw background
35579             backgroundLayer
35580                 .source(context.background().baseLayerSource())
35581                 .projection(projection)
35582                 .dimensions(dMini);
35583
35584             var background = tiles
35585                 .selectAll('.map-in-map-background')
35586                 .data([0]);
35587
35588             background.enter()
35589                 .append('div')
35590                 .attr('class', 'map-in-map-background');
35591
35592             background
35593                 .call(backgroundLayer);
35594
35595
35596             // redraw overlay
35597             var overlaySources = context.background().overlayLayerSources();
35598             var activeOverlayLayers = [];
35599             for (var i = 0; i < overlaySources.length; i++) {
35600                 if (overlaySources[i].validZoom(zMini)) {
35601                     if (!overlayLayers[i]) overlayLayers[i] = iD.TileLayer();
35602                     activeOverlayLayers.push(overlayLayers[i]
35603                         .source(overlaySources[i])
35604                         .projection(projection)
35605                         .dimensions(dMini));
35606                 }
35607             }
35608
35609             var overlay = tiles
35610                 .selectAll('.map-in-map-overlay')
35611                 .data([0]);
35612
35613             overlay.enter()
35614                 .append('div')
35615                 .attr('class', 'map-in-map-overlay');
35616
35617             var overlays = overlay
35618                 .selectAll('div')
35619                 .data(activeOverlayLayers, function(d) { return d.source().name(); });
35620
35621             overlays.enter().append('div');
35622             overlays.each(function(layer) {
35623                 d3.select(this).call(layer);
35624             });
35625
35626             overlays.exit()
35627                 .remove();
35628
35629
35630             var gpx = tiles
35631                 .selectAll('.map-in-map-gpx')
35632                 .data(gpxLayer.enabled() ? [0] : []);
35633
35634             gpx.enter()
35635                 .append('svg')
35636                 .attr('class', 'map-in-map-gpx');
35637
35638             gpx.exit()
35639                 .remove();
35640
35641             gpx.call(gpxLayer);
35642
35643
35644             // redraw bounding box
35645             if (!panning) {
35646                 var getPath = d3.geo.path().projection(projection),
35647                     bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
35648
35649                 svg = wrap.selectAll('.map-in-map-svg')
35650                     .data([0]);
35651
35652                 svg.enter()
35653                     .append('svg')
35654                     .attr('class', 'map-in-map-svg');
35655
35656                 var path = svg.selectAll('.map-in-map-bbox')
35657                     .data([bbox]);
35658
35659                 path.enter()
35660                     .append('path')
35661                     .attr('class', 'map-in-map-bbox');
35662
35663                 path
35664                     .attr('d', getPath)
35665                     .classed('thick', function(d) { return getPath.area(d) < 30; });
35666             }
35667         }
35668
35669
35670         function queueRedraw() {
35671             clearTimeout(timeoutId);
35672             timeoutId = setTimeout(function() { redraw(); }, 300);
35673         }
35674
35675
35676         function toggle() {
35677             if (d3.event) d3.event.preventDefault();
35678
35679             hidden = !hidden;
35680
35681             var label = d3.select('.minimap-toggle');
35682             label.classed('active', !hidden)
35683                 .select('input').property('checked', !hidden);
35684
35685             if (hidden) {
35686                 wrap
35687                     .style('display', 'block')
35688                     .style('opacity', 1)
35689                     .transition()
35690                     .duration(200)
35691                     .style('opacity', 0)
35692                     .each('end', function() {
35693                         d3.select(this).style('display', 'none');
35694                     });
35695             } else {
35696                 wrap
35697                     .style('display', 'block')
35698                     .style('opacity', 0)
35699                     .transition()
35700                     .duration(200)
35701                     .style('opacity', 1);
35702
35703                 redraw();
35704             }
35705         }
35706
35707         iD.ui.MapInMap.toggle = toggle;
35708
35709         var wrap = selection.selectAll('.map-in-map')
35710             .data([0]);
35711
35712         wrap.enter()
35713             .append('div')
35714             .attr('class', 'map-in-map')
35715             .style('display', (hidden ? 'none' : 'block'))
35716             .on('mousedown.map-in-map', startMouse)
35717             .on('mouseup.map-in-map', endMouse)
35718             .call(zoom)
35719             .on('dblclick.zoom', null);
35720
35721         context.map()
35722             .on('drawn.map-in-map', function(drawn) {
35723                 if (drawn.full === true) redraw();
35724             });
35725
35726         redraw();
35727
35728         var keybinding = d3.keybinding('map-in-map')
35729             .on(key, toggle);
35730
35731         d3.select(document)
35732             .call(keybinding);
35733     }
35734
35735     return map_in_map;
35736 };
35737 iD.ui.modal = function(selection, blocking) {
35738     var keybinding = d3.keybinding('modal');
35739     var previous = selection.select('div.modal');
35740     var animate = previous.empty();
35741
35742     previous.transition()
35743         .duration(200)
35744         .style('opacity', 0)
35745         .remove();
35746
35747     var shaded = selection
35748         .append('div')
35749         .attr('class', 'shaded')
35750         .style('opacity', 0);
35751
35752     shaded.close = function() {
35753         shaded
35754             .transition()
35755             .duration(200)
35756             .style('opacity',0)
35757             .remove();
35758         modal
35759             .transition()
35760             .duration(200)
35761             .style('top','0px');
35762
35763         keybinding.off();
35764     };
35765
35766
35767     var modal = shaded.append('div')
35768         .attr('class', 'modal fillL col6');
35769
35770     if (!blocking) {
35771         shaded.on('click.remove-modal', function() {
35772             if (d3.event.target === this) {
35773                 shaded.close();
35774             }
35775         });
35776
35777         modal.append('button')
35778             .attr('class', 'close')
35779             .on('click', shaded.close)
35780             .call(iD.svg.Icon('#icon-close'));
35781
35782         keybinding
35783             .on('⌫', shaded.close)
35784             .on('⎋', shaded.close);
35785
35786         d3.select(document).call(keybinding);
35787     }
35788
35789     modal.append('div')
35790         .attr('class', 'content');
35791
35792     if (animate) {
35793         shaded.transition().style('opacity', 1);
35794     } else {
35795         shaded.style('opacity', 1);
35796     }
35797
35798     return shaded;
35799 };
35800 iD.ui.Modes = function(context) {
35801     var modes = [
35802         iD.modes.AddPoint(context),
35803         iD.modes.AddLine(context),
35804         iD.modes.AddArea(context)];
35805
35806     function editable() {
35807         return context.editable() && context.mode().id !== 'save';
35808     }
35809
35810     return function(selection) {
35811         var buttons = selection.selectAll('button.add-button')
35812             .data(modes);
35813
35814        buttons.enter().append('button')
35815            .attr('tabindex', -1)
35816            .attr('class', function(mode) { return mode.id + ' add-button col4'; })
35817            .on('click.mode-buttons', function(mode) {
35818                if (mode.id === context.mode().id) {
35819                    context.enter(iD.modes.Browse(context));
35820                } else {
35821                    context.enter(mode);
35822                }
35823            })
35824            .call(bootstrap.tooltip()
35825                .placement('bottom')
35826                .html(true)
35827                .title(function(mode) {
35828                    return iD.ui.tooltipHtml(mode.description, mode.key);
35829                }));
35830
35831         context.map()
35832             .on('move.modes', _.debounce(update, 500));
35833
35834         context
35835             .on('enter.modes', update);
35836
35837         buttons.each(function(d) {
35838             d3.select(this)
35839                 .call(iD.svg.Icon('#icon-' + d.button, 'pre-text'));
35840         });
35841
35842         buttons.append('span')
35843             .attr('class', 'label')
35844             .text(function(mode) { return mode.title; });
35845
35846         context.on('enter.editor', function(entered) {
35847             buttons.classed('active', function(mode) { return entered.button === mode.button; });
35848             context.container()
35849                 .classed('mode-' + entered.id, true);
35850         });
35851
35852         context.on('exit.editor', function(exited) {
35853             context.container()
35854                 .classed('mode-' + exited.id, false);
35855         });
35856
35857         var keybinding = d3.keybinding('mode-buttons');
35858
35859         modes.forEach(function(m) {
35860             keybinding.on(m.key, function() { if (editable()) context.enter(m); });
35861         });
35862
35863         d3.select(document)
35864             .call(keybinding);
35865
35866         function update() {
35867             buttons.property('disabled', !editable());
35868         }
35869     };
35870 };
35871 iD.ui.Notice = function(context) {
35872     return function(selection) {
35873         var div = selection.append('div')
35874             .attr('class', 'notice');
35875
35876         var button = div.append('button')
35877             .attr('class', 'zoom-to notice')
35878             .on('click', function() { context.map().zoom(context.minEditableZoom()); });
35879
35880         button
35881             .call(iD.svg.Icon('#icon-plus', 'pre-text'))
35882             .append('span')
35883             .attr('class', 'label')
35884             .text(t('zoom_in_edit'));
35885
35886         function disableTooHigh() {
35887             div.style('display', context.editable() ? 'none' : 'block');
35888         }
35889
35890         context.map()
35891             .on('move.notice', _.debounce(disableTooHigh, 500));
35892
35893         disableTooHigh();
35894     };
35895 };
35896 iD.ui.PresetIcon = function() {
35897     var preset, geometry;
35898
35899     function presetIcon(selection) {
35900         selection.each(render);
35901     }
35902
35903     function render() {
35904         var selection = d3.select(this),
35905             p = preset.apply(this, arguments),
35906             geom = geometry.apply(this, arguments),
35907             icon = p.icon || (geom === 'line' ? 'other-line' : 'marker-stroked'),
35908             maki = iD.data.featureIcons.hasOwnProperty(icon + '-24');
35909
35910         if (icon === 'dentist') maki = true;  // workaround for dentist icon missing in `maki-sprite.json`
35911
35912         function tag_classes(p) {
35913             var s = '';
35914             for (var i in p.tags) {
35915                 s += ' tag-' + i;
35916                 if (p.tags[i] !== '*') {
35917                     s += ' tag-' + i + '-' + p.tags[i];
35918                 }
35919             }
35920             return s;
35921         }
35922
35923         var $fill = selection.selectAll('.preset-icon-fill')
35924             .data([0]);
35925
35926         $fill.enter().append('div');
35927
35928         $fill.attr('class', function() {
35929             return 'preset-icon-fill preset-icon-fill-' + geom + tag_classes(p);
35930         });
35931
35932         var $frame = selection.selectAll('.preset-icon-frame')
35933             .data([0]);
35934
35935         $frame.enter()
35936             .append('div')
35937             .call(iD.svg.Icon('#preset-icon-frame'));
35938
35939         $frame.attr('class', function() {
35940             return 'preset-icon-frame ' + (geom === 'area' ? '' : 'hide');
35941         });
35942
35943
35944         var $icon = selection.selectAll('.preset-icon')
35945             .data([0]);
35946
35947         $icon.enter()
35948             .append('div')
35949             .attr('class', 'preset-icon')
35950             .call(iD.svg.Icon(''));
35951
35952         $icon
35953             .attr('class', 'preset-icon preset-icon-' + (maki ? '32' : (geom === 'area' ? '44' : '60')));
35954
35955         $icon.selectAll('svg')
35956             .attr('class', function() {
35957                 return 'icon ' + icon + tag_classes(p);
35958             });
35959
35960         $icon.selectAll('use')       // workaround: maki parking-24 broken?
35961             .attr('href', '#' + icon + (maki ? ( icon === 'parking' ? '-18' : '-24') : ''));
35962     }
35963
35964     presetIcon.preset = function(_) {
35965         if (!arguments.length) return preset;
35966         preset = d3.functor(_);
35967         return presetIcon;
35968     };
35969
35970     presetIcon.geometry = function(_) {
35971         if (!arguments.length) return geometry;
35972         geometry = d3.functor(_);
35973         return presetIcon;
35974     };
35975
35976     return presetIcon;
35977 };
35978 iD.ui.preset = function(context) {
35979     var event = d3.dispatch('change'),
35980         state,
35981         fields,
35982         preset,
35983         tags,
35984         id;
35985
35986     function UIField(field, entity, show) {
35987         field = _.clone(field);
35988
35989         field.input = iD.ui.preset[field.type](field, context)
35990             .on('change', event.change);
35991
35992         if (field.input.entity) field.input.entity(entity);
35993
35994         field.keys = field.keys || [field.key];
35995
35996         field.show = show;
35997
35998         field.shown = function() {
35999             return field.id === 'name' || field.show || _.any(field.keys, function(key) { return !!tags[key]; });
36000         };
36001
36002         field.modified = function() {
36003             var original = context.graph().base().entities[entity.id];
36004             return _.any(field.keys, function(key) {
36005                 return original ? tags[key] !== original.tags[key] : tags[key];
36006             });
36007         };
36008
36009         field.revert = function() {
36010             var original = context.graph().base().entities[entity.id],
36011                 t = {};
36012             field.keys.forEach(function(key) {
36013                 t[key] = original ? original.tags[key] : undefined;
36014             });
36015             return t;
36016         };
36017
36018         field.present = function() {
36019             return _.any(field.keys, function(key) {
36020                 return tags[key];
36021             });
36022         };
36023
36024         field.remove = function() {
36025             var t = {};
36026             field.keys.forEach(function(key) {
36027                 t[key] = undefined;
36028             });
36029             return t;
36030         };
36031
36032         return field;
36033     }
36034
36035     function fieldKey(field) {
36036         return field.id;
36037     }
36038
36039     function presets(selection) {
36040         selection.call(iD.ui.Disclosure()
36041             .title(t('inspector.all_fields'))
36042             .expanded(context.storage('preset_fields.expanded') !== 'false')
36043             .on('toggled', toggled)
36044             .content(content));
36045
36046         function toggled(expanded) {
36047             context.storage('preset_fields.expanded', expanded);
36048         }
36049     }
36050
36051     function content(selection) {
36052         if (!fields) {
36053             var entity = context.entity(id),
36054                 geometry = context.geometry(id);
36055
36056             fields = [UIField(context.presets().field('name'), entity)];
36057
36058             preset.fields.forEach(function(field) {
36059                 if (field.matchGeometry(geometry)) {
36060                     fields.push(UIField(field, entity, true));
36061                 }
36062             });
36063
36064             if (entity.isHighwayIntersection(context.graph())) {
36065                 fields.push(UIField(context.presets().field('restrictions'), entity, true));
36066             }
36067
36068             context.presets().universal().forEach(function(field) {
36069                 if (preset.fields.indexOf(field) < 0) {
36070                     fields.push(UIField(field, entity));
36071                 }
36072             });
36073         }
36074
36075         var shown = fields.filter(function(field) { return field.shown(); }),
36076             notShown = fields.filter(function(field) { return !field.shown(); });
36077
36078         var $form = selection.selectAll('.preset-form')
36079             .data([0]);
36080
36081         $form.enter().append('div')
36082             .attr('class', 'preset-form inspector-inner fillL3');
36083
36084         var $fields = $form.selectAll('.form-field')
36085             .data(shown, fieldKey);
36086
36087         // Enter
36088
36089         var $enter = $fields.enter()
36090             .append('div')
36091             .attr('class', function(field) {
36092                 return 'form-field form-field-' + field.id;
36093             });
36094
36095         var $label = $enter.append('label')
36096             .attr('class', 'form-label')
36097             .attr('for', function(field) { return 'preset-input-' + field.id; })
36098             .text(function(field) { return field.label(); });
36099
36100         var wrap = $label.append('div')
36101             .attr('class', 'form-label-button-wrap');
36102
36103         wrap.append('button')
36104             .attr('class', 'remove-icon')
36105             .call(iD.svg.Icon('#operation-delete'));
36106
36107         wrap.append('button')
36108             .attr('class', 'modified-icon')
36109             .attr('tabindex', -1)
36110             .call(iD.svg.Icon('#icon-undo'));
36111
36112         // Update
36113
36114         $fields.select('.form-label-button-wrap .remove-icon')
36115             .on('click', remove);
36116
36117         $fields.select('.modified-icon')
36118             .on('click', revert);
36119
36120         $fields
36121             .order()
36122             .classed('modified', function(field) {
36123                 return field.modified();
36124             })
36125             .classed('present', function(field) {
36126                 return field.present();
36127             })
36128             .each(function(field) {
36129                 var reference = iD.ui.TagReference(field.reference || {key: field.key}, context);
36130
36131                 if (state === 'hover') {
36132                     reference.showing(false);
36133                 }
36134
36135                 d3.select(this)
36136                     .call(field.input)
36137                     .selectAll('input')
36138                     .on('keydown', function() {
36139                         if (d3.event.keyCode === 13) {  // enter
36140                             context.enter(iD.modes.Browse(context));
36141                         }
36142                     })
36143                     .call(reference.body)
36144                     .select('.form-label-button-wrap')
36145                     .call(reference.button);
36146
36147                 field.input.tags(tags);
36148             });
36149
36150         $fields.exit()
36151             .remove();
36152
36153         notShown = notShown.map(function(field) {
36154             return {
36155                 title: field.label(),
36156                 value: field.label(),
36157                 field: field
36158             };
36159         });
36160
36161         var $more = selection.selectAll('.more-fields')
36162             .data((notShown.length > 0) ? [0] : []);
36163
36164         $more.enter().append('div')
36165             .attr('class', 'more-fields')
36166             .append('label')
36167                 .text(t('inspector.add_fields'));
36168
36169         var $input = $more.selectAll('.value')
36170             .data([0]);
36171
36172         $input.enter().append('input')
36173             .attr('class', 'value')
36174             .attr('type', 'text');
36175
36176         $input.value('')
36177             .attr('placeholder', function() {
36178                 var placeholder = [];
36179                 for (var field in notShown) {
36180                     placeholder.push(notShown[field].title);
36181                 }
36182                 return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : '');
36183             })
36184             .call(d3.combobox().data(notShown)
36185                 .minItems(1)
36186                 .on('accept', show));
36187
36188         $more.exit()
36189             .remove();
36190
36191         $input.exit()
36192             .remove();
36193
36194         function show(field) {
36195             field = field.field;
36196             field.show = true;
36197             content(selection);
36198             field.input.focus();
36199         }
36200
36201         function revert(field) {
36202             d3.event.stopPropagation();
36203             d3.event.preventDefault();
36204             event.change(field.revert());
36205         }
36206
36207         function remove(field) {
36208             d3.event.stopPropagation();
36209             d3.event.preventDefault();
36210             event.change(field.remove());
36211         }
36212     }
36213
36214     presets.preset = function(_) {
36215         if (!arguments.length) return preset;
36216         if (preset && preset.id === _.id) return presets;
36217         preset = _;
36218         fields = null;
36219         return presets;
36220     };
36221
36222     presets.state = function(_) {
36223         if (!arguments.length) return state;
36224         state = _;
36225         return presets;
36226     };
36227
36228     presets.tags = function(_) {
36229         if (!arguments.length) return tags;
36230         tags = _;
36231         // Don't reset fields here.
36232         return presets;
36233     };
36234
36235     presets.entityID = function(_) {
36236         if (!arguments.length) return id;
36237         if (id === _) return presets;
36238         id = _;
36239         fields = null;
36240         return presets;
36241     };
36242
36243     return d3.rebind(presets, event, 'on');
36244 };
36245 iD.ui.PresetList = function(context) {
36246     var event = d3.dispatch('choose'),
36247         id,
36248         currentPreset,
36249         autofocus = false;
36250
36251     function presetList(selection) {
36252         var geometry = context.geometry(id),
36253             presets = context.presets().matchGeometry(geometry);
36254
36255         selection.html('');
36256
36257         var messagewrap = selection.append('div')
36258             .attr('class', 'header fillL cf');
36259
36260         var message = messagewrap.append('h3')
36261             .text(t('inspector.choose'));
36262
36263         if (context.entity(id).isUsed(context.graph())) {
36264             messagewrap.append('button')
36265                 .attr('class', 'preset-choose')
36266                 .on('click', function() { event.choose(currentPreset); })
36267                 .append('span')
36268                 .html('&#9658;');
36269         } else {
36270             messagewrap.append('button')
36271                 .attr('class', 'close')
36272                 .on('click', function() {
36273                     context.enter(iD.modes.Browse(context));
36274                 })
36275                 .call(iD.svg.Icon('#icon-close'));
36276         }
36277
36278         function keydown() {
36279             // hack to let delete shortcut work when search is autofocused
36280             if (search.property('value').length === 0 &&
36281                 (d3.event.keyCode === d3.keybinding.keyCodes['⌫'] ||
36282                  d3.event.keyCode === d3.keybinding.keyCodes['⌦'])) {
36283                 d3.event.preventDefault();
36284                 d3.event.stopPropagation();
36285                 iD.operations.Delete([id], context)();
36286             } else if (search.property('value').length === 0 &&
36287                 (d3.event.ctrlKey || d3.event.metaKey) &&
36288                 d3.event.keyCode === d3.keybinding.keyCodes.z) {
36289                 d3.event.preventDefault();
36290                 d3.event.stopPropagation();
36291                 context.undo();
36292             } else if (!d3.event.ctrlKey && !d3.event.metaKey) {
36293                 d3.select(this).on('keydown', null);
36294             }
36295         }
36296
36297         function keypress() {
36298             // enter
36299             var value = search.property('value');
36300             if (d3.event.keyCode === 13 && value.length) {
36301                 list.selectAll('.preset-list-item:first-child').datum().choose();
36302             }
36303         }
36304
36305         function inputevent() {
36306             var value = search.property('value');
36307             list.classed('filtered', value.length);
36308             if (value.length) {
36309                 var results = presets.search(value, geometry);
36310                 message.text(t('inspector.results', {
36311                     n: results.collection.length,
36312                     search: value
36313                 }));
36314                 list.call(drawList, results);
36315             } else {
36316                 list.call(drawList, context.presets().defaults(geometry, 36));
36317                 message.text(t('inspector.choose'));
36318             }
36319         }
36320
36321         var searchWrap = selection.append('div')
36322             .attr('class', 'search-header');
36323
36324         var search = searchWrap.append('input')
36325             .attr('class', 'preset-search-input')
36326             .attr('placeholder', t('inspector.search'))
36327             .attr('type', 'search')
36328             .on('keydown', keydown)
36329             .on('keypress', keypress)
36330             .on('input', inputevent);
36331
36332         searchWrap
36333             .call(iD.svg.Icon('#icon-search', 'pre-text'));
36334
36335         if (autofocus) {
36336             search.node().focus();
36337         }
36338
36339         var listWrap = selection.append('div')
36340             .attr('class', 'inspector-body');
36341
36342         var list = listWrap.append('div')
36343             .attr('class', 'preset-list fillL cf')
36344             .call(drawList, context.presets().defaults(geometry, 36));
36345     }
36346
36347     function drawList(list, presets) {
36348         var collection = presets.collection.map(function(preset) {
36349             return preset.members ? CategoryItem(preset) : PresetItem(preset);
36350         });
36351
36352         var items = list.selectAll('.preset-list-item')
36353             .data(collection, function(d) { return d.preset.id; });
36354
36355         items.enter().append('div')
36356             .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
36357             .classed('current', function(item) { return item.preset === currentPreset; })
36358             .each(function(item) {
36359                 d3.select(this).call(item);
36360             })
36361             .style('opacity', 0)
36362             .transition()
36363             .style('opacity', 1);
36364
36365         items.order();
36366
36367         items.exit()
36368             .remove();
36369     }
36370
36371     function CategoryItem(preset) {
36372         var box, sublist, shown = false;
36373
36374         function item(selection) {
36375             var wrap = selection.append('div')
36376                 .attr('class', 'preset-list-button-wrap category col12');
36377
36378             wrap.append('button')
36379                 .attr('class', 'preset-list-button')
36380                 .classed('expanded', false)
36381                 .call(iD.ui.PresetIcon()
36382                     .geometry(context.geometry(id))
36383                     .preset(preset))
36384                 .on('click', function() {
36385                     var isExpanded = d3.select(this).classed('expanded');
36386                     var triangle = isExpanded ? '▶ ' :  '▼ ';
36387                     d3.select(this).classed('expanded', !isExpanded);
36388                     d3.select(this).selectAll('.label').text(triangle + preset.name());
36389                     item.choose();
36390                 })
36391                 .append('div')
36392                 .attr('class', 'label')
36393                 .text(function() {
36394                   return '▶ ' + preset.name();
36395                 });
36396
36397             box = selection.append('div')
36398                 .attr('class', 'subgrid col12')
36399                 .style('max-height', '0px')
36400                 .style('opacity', 0);
36401
36402             box.append('div')
36403                 .attr('class', 'arrow');
36404
36405             sublist = box.append('div')
36406                 .attr('class', 'preset-list fillL3 cf fl');
36407         }
36408
36409         item.choose = function() {
36410             if (!box || !sublist) return;
36411
36412             if (shown) {
36413                 shown = false;
36414                 box.transition()
36415                     .duration(200)
36416                     .style('opacity', '0')
36417                     .style('max-height', '0px')
36418                     .style('padding-bottom', '0px');
36419             } else {
36420                 shown = true;
36421                 sublist.call(drawList, preset.members);
36422                 box.transition()
36423                     .duration(200)
36424                     .style('opacity', '1')
36425                     .style('max-height', 200 + preset.members.collection.length * 80 + 'px')
36426                     .style('padding-bottom', '20px');
36427             }
36428         };
36429
36430         item.preset = preset;
36431
36432         return item;
36433     }
36434
36435     function PresetItem(preset) {
36436         function item(selection) {
36437             var wrap = selection.append('div')
36438                 .attr('class', 'preset-list-button-wrap col12');
36439
36440             wrap.append('button')
36441                 .attr('class', 'preset-list-button')
36442                 .call(iD.ui.PresetIcon()
36443                     .geometry(context.geometry(id))
36444                     .preset(preset))
36445                 .on('click', item.choose)
36446                 .append('div')
36447                 .attr('class', 'label')
36448                 .text(preset.name());
36449
36450             wrap.call(item.reference.button);
36451             selection.call(item.reference.body);
36452         }
36453
36454         item.choose = function() {
36455             context.presets().choose(preset);
36456
36457             context.perform(
36458                 iD.actions.ChangePreset(id, currentPreset, preset),
36459                 t('operations.change_tags.annotation'));
36460
36461             event.choose(preset);
36462         };
36463
36464         item.help = function() {
36465             d3.event.stopPropagation();
36466             item.reference.toggle();
36467         };
36468
36469         item.preset = preset;
36470         item.reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context);
36471
36472         return item;
36473     }
36474
36475     presetList.autofocus = function(_) {
36476         if (!arguments.length) return autofocus;
36477         autofocus = _;
36478         return presetList;
36479     };
36480
36481     presetList.entityID = function(_) {
36482         if (!arguments.length) return id;
36483         id = _;
36484         presetList.preset(context.presets().match(context.entity(id), context.graph()));
36485         return presetList;
36486     };
36487
36488     presetList.preset = function(_) {
36489         if (!arguments.length) return currentPreset;
36490         currentPreset = _;
36491         return presetList;
36492     };
36493
36494     return d3.rebind(presetList, event, 'on');
36495 };
36496 iD.ui.RadialMenu = function(context, operations) {
36497     var menu,
36498         center = [0, 0],
36499         tooltip;
36500
36501     var radialMenu = function(selection) {
36502         if (!operations.length)
36503             return;
36504
36505         selection.node().parentNode.focus();
36506
36507         function click(operation) {
36508             d3.event.stopPropagation();
36509             if (operation.disabled())
36510                 return;
36511             operation();
36512             radialMenu.close();
36513         }
36514
36515         menu = selection.append('g')
36516             .attr('class', 'radial-menu')
36517             .attr('transform', 'translate(' + center + ')')
36518             .attr('opacity', 0);
36519
36520         menu.transition()
36521             .attr('opacity', 1);
36522
36523         var r = 50,
36524             a = Math.PI / 4,
36525             a0 = -Math.PI / 4,
36526             a1 = a0 + (operations.length - 1) * a;
36527
36528         menu.append('path')
36529             .attr('class', 'radial-menu-background')
36530             .attr('d', 'M' + r * Math.sin(a0) + ',' +
36531                              r * Math.cos(a0) +
36532                       ' A' + r + ',' + r + ' 0 ' + (operations.length > 5 ? '1' : '0') + ',0 ' +
36533                              (r * Math.sin(a1) + 1e-3) + ',' +
36534                              (r * Math.cos(a1) + 1e-3)) // Force positive-length path (#1305)
36535             .attr('stroke-width', 50)
36536             .attr('stroke-linecap', 'round');
36537
36538         var button = menu.selectAll()
36539             .data(operations)
36540             .enter()
36541             .append('g')
36542             .attr('class', function(d) { return 'radial-menu-item radial-menu-item-' + d.id; })
36543             .classed('disabled', function(d) { return d.disabled(); })
36544             .attr('transform', function(d, i) {
36545                 return 'translate(' + iD.geo.roundCoords([
36546                         r * Math.sin(a0 + i * a),
36547                         r * Math.cos(a0 + i * a)]).join(',') + ')';
36548             });
36549
36550         button.append('circle')
36551             .attr('r', 15)
36552             .on('click', click)
36553             .on('mousedown', mousedown)
36554             .on('mouseover', mouseover)
36555             .on('mouseout', mouseout);
36556
36557         button.append('use')
36558             .attr('transform', 'translate(-10,-10)')
36559             .attr('width', '20')
36560             .attr('height', '20')
36561             .attr('xlink:href', function(d) { return '#operation-' + d.id; });
36562
36563         tooltip = d3.select(document.body)
36564             .append('div')
36565             .attr('class', 'tooltip-inner radial-menu-tooltip');
36566
36567         function mousedown() {
36568             d3.event.stopPropagation(); // https://github.com/openstreetmap/iD/issues/1869
36569         }
36570
36571         function mouseover(d, i) {
36572             var rect = context.surfaceRect(),
36573                 angle = a0 + i * a,
36574                 top = rect.top + (r + 25) * Math.cos(angle) + center[1] + 'px',
36575                 left = rect.left + (r + 25) * Math.sin(angle) + center[0] + 'px',
36576                 bottom = rect.height - (r + 25) * Math.cos(angle) - center[1] + 'px',
36577                 right = rect.width - (r + 25) * Math.sin(angle) - center[0] + 'px';
36578
36579             tooltip
36580                 .style('top', null)
36581                 .style('left', null)
36582                 .style('bottom', null)
36583                 .style('right', null)
36584                 .style('display', 'block')
36585                 .html(iD.ui.tooltipHtml(d.tooltip(), d.keys[0]));
36586
36587             if (i === 0) {
36588                 tooltip
36589                     .style('right', right)
36590                     .style('top', top);
36591             } else if (i >= 4) {
36592                 tooltip
36593                     .style('left', left)
36594                     .style('bottom', bottom);
36595             } else {
36596                 tooltip
36597                     .style('left', left)
36598                     .style('top', top);
36599             }
36600         }
36601
36602         function mouseout() {
36603             tooltip.style('display', 'none');
36604         }
36605     };
36606
36607     radialMenu.close = function() {
36608         if (menu) {
36609             menu
36610                 .style('pointer-events', 'none')
36611                 .transition()
36612                 .attr('opacity', 0)
36613                 .remove();
36614         }
36615
36616         if (tooltip) {
36617             tooltip.remove();
36618         }
36619     };
36620
36621     radialMenu.center = function(_) {
36622         if (!arguments.length) return center;
36623         center = _;
36624         return radialMenu;
36625     };
36626
36627     return radialMenu;
36628 };
36629 iD.ui.RawMemberEditor = function(context) {
36630     var id;
36631
36632     function selectMember(d) {
36633         d3.event.preventDefault();
36634         context.enter(iD.modes.Select(context, [d.id]));
36635     }
36636
36637     function changeRole(d) {
36638         var role = d3.select(this).property('value');
36639         var member = {id: d.id, type: d.type, role: role};
36640         context.perform(
36641             iD.actions.ChangeMember(d.relation.id, member, d.index),
36642             t('operations.change_role.annotation'));
36643     }
36644
36645     function deleteMember(d) {
36646         context.perform(
36647             iD.actions.DeleteMember(d.relation.id, d.index),
36648             t('operations.delete_member.annotation'));
36649
36650         if (!context.hasEntity(d.relation.id)) {
36651             context.enter(iD.modes.Browse(context));
36652         }
36653     }
36654
36655     function rawMemberEditor(selection) {
36656         var entity = context.entity(id),
36657             memberships = [];
36658
36659         entity.members.forEach(function(member, index) {
36660             memberships.push({
36661                 index: index,
36662                 id: member.id,
36663                 type: member.type,
36664                 role: member.role,
36665                 relation: entity,
36666                 member: context.hasEntity(member.id)
36667             });
36668         });
36669
36670         selection.call(iD.ui.Disclosure()
36671             .title(t('inspector.all_members') + ' (' + memberships.length + ')')
36672             .expanded(true)
36673             .on('toggled', toggled)
36674             .content(content));
36675
36676         function toggled(expanded) {
36677             if (expanded) {
36678                 selection.node().parentNode.scrollTop += 200;
36679             }
36680         }
36681
36682         function content($wrap) {
36683             var $list = $wrap.selectAll('.member-list')
36684                 .data([0]);
36685
36686             $list.enter().append('ul')
36687                 .attr('class', 'member-list');
36688
36689             var $items = $list.selectAll('li')
36690                 .data(memberships, function(d) {
36691                     return iD.Entity.key(d.relation) + ',' + d.index + ',' +
36692                         (d.member ? iD.Entity.key(d.member) : 'incomplete');
36693                 });
36694
36695             var $enter = $items.enter().append('li')
36696                 .attr('class', 'member-row form-field')
36697                 .classed('member-incomplete', function(d) { return !d.member; });
36698
36699             $enter.each(function(d) {
36700                 if (d.member) {
36701                     var $label = d3.select(this).append('label')
36702                         .attr('class', 'form-label')
36703                         .append('a')
36704                         .attr('href', '#')
36705                         .on('click', selectMember);
36706
36707                     $label.append('span')
36708                         .attr('class', 'member-entity-type')
36709                         .text(function(d) { return context.presets().match(d.member, context.graph()).name(); });
36710
36711                     $label.append('span')
36712                         .attr('class', 'member-entity-name')
36713                         .text(function(d) { return iD.util.displayName(d.member); });
36714
36715                 } else {
36716                     d3.select(this).append('label')
36717                         .attr('class', 'form-label')
36718                         .text(t('inspector.incomplete'));
36719                 }
36720             });
36721
36722             $enter.append('input')
36723                 .attr('class', 'member-role')
36724                 .property('type', 'text')
36725                 .attr('maxlength', 255)
36726                 .attr('placeholder', t('inspector.role'))
36727                 .property('value', function(d) { return d.role; })
36728                 .on('change', changeRole);
36729
36730             $enter.append('button')
36731                 .attr('tabindex', -1)
36732                 .attr('class', 'remove button-input-action member-delete minor')
36733                 .on('click', deleteMember)
36734                 .call(iD.svg.Icon('#operation-delete'));
36735
36736             $items.exit()
36737                 .remove();
36738         }
36739     }
36740
36741     rawMemberEditor.entityID = function(_) {
36742         if (!arguments.length) return id;
36743         id = _;
36744         return rawMemberEditor;
36745     };
36746
36747     return rawMemberEditor;
36748 };
36749 iD.ui.RawMembershipEditor = function(context) {
36750     var id, showBlank;
36751
36752     function selectRelation(d) {
36753         d3.event.preventDefault();
36754         context.enter(iD.modes.Select(context, [d.relation.id]));
36755     }
36756
36757     function changeRole(d) {
36758         var role = d3.select(this).property('value');
36759         context.perform(
36760             iD.actions.ChangeMember(d.relation.id, _.extend({}, d.member, {role: role}), d.index),
36761             t('operations.change_role.annotation'));
36762     }
36763
36764     function addMembership(d, role) {
36765         showBlank = false;
36766
36767         if (d.relation) {
36768             context.perform(
36769                 iD.actions.AddMember(d.relation.id, {id: id, type: context.entity(id).type, role: role}),
36770                 t('operations.add_member.annotation'));
36771
36772         } else {
36773             var relation = iD.Relation();
36774
36775             context.perform(
36776                 iD.actions.AddEntity(relation),
36777                 iD.actions.AddMember(relation.id, {id: id, type: context.entity(id).type, role: role}),
36778                 t('operations.add.annotation.relation'));
36779
36780             context.enter(iD.modes.Select(context, [relation.id]));
36781         }
36782     }
36783
36784     function deleteMembership(d) {
36785         context.perform(
36786             iD.actions.DeleteMember(d.relation.id, d.index),
36787             t('operations.delete_member.annotation'));
36788     }
36789
36790     function relations(q) {
36791         var newRelation = {
36792                 relation: null,
36793                 value: t('inspector.new_relation')
36794             },
36795             result = [],
36796             graph = context.graph();
36797
36798         context.intersects(context.extent()).forEach(function(entity) {
36799             if (entity.type !== 'relation' || entity.id === id)
36800                 return;
36801
36802             var presetName = context.presets().match(entity, graph).name(),
36803                 entityName = iD.util.displayName(entity) || '';
36804
36805             var value = presetName + ' ' + entityName;
36806             if (q && value.toLowerCase().indexOf(q.toLowerCase()) === -1)
36807                 return;
36808
36809             result.push({
36810                 relation: entity,
36811                 value: value
36812             });
36813         });
36814
36815         result.sort(function(a, b) {
36816             return iD.Relation.creationOrder(a.relation, b.relation);
36817         });
36818
36819         // Dedupe identical names by appending relation id - see #2891
36820         var dupeGroups = _(result)
36821             .groupBy('value')
36822             .filter(function(v) { return v.length > 1; })
36823             .value();
36824
36825         dupeGroups.forEach(function(group) {
36826             group.forEach(function(obj) {
36827                 obj.value += ' ' + obj.relation.id;
36828             });
36829         });
36830
36831         result.unshift(newRelation);
36832
36833         return result;
36834     }
36835
36836     function rawMembershipEditor(selection) {
36837         var entity = context.entity(id),
36838             memberships = [];
36839
36840         context.graph().parentRelations(entity).forEach(function(relation) {
36841             relation.members.forEach(function(member, index) {
36842                 if (member.id === entity.id) {
36843                     memberships.push({relation: relation, member: member, index: index});
36844                 }
36845             });
36846         });
36847
36848         selection.call(iD.ui.Disclosure()
36849             .title(t('inspector.all_relations') + ' (' + memberships.length + ')')
36850             .expanded(true)
36851             .on('toggled', toggled)
36852             .content(content));
36853
36854         function toggled(expanded) {
36855             if (expanded) {
36856                 selection.node().parentNode.scrollTop += 200;
36857             }
36858         }
36859
36860         function content($wrap) {
36861             var $list = $wrap.selectAll('.member-list')
36862                 .data([0]);
36863
36864             $list.enter().append('ul')
36865                 .attr('class', 'member-list');
36866
36867             var $items = $list.selectAll('li.member-row-normal')
36868                 .data(memberships, function(d) { return iD.Entity.key(d.relation) + ',' + d.index; });
36869
36870             var $enter = $items.enter().append('li')
36871                 .attr('class', 'member-row member-row-normal form-field');
36872
36873             var $label = $enter.append('label')
36874                 .attr('class', 'form-label')
36875                 .append('a')
36876                 .attr('href', '#')
36877                 .on('click', selectRelation);
36878
36879             $label.append('span')
36880                 .attr('class', 'member-entity-type')
36881                 .text(function(d) { return context.presets().match(d.relation, context.graph()).name(); });
36882
36883             $label.append('span')
36884                 .attr('class', 'member-entity-name')
36885                 .text(function(d) { return iD.util.displayName(d.relation); });
36886
36887             $enter.append('input')
36888                 .attr('class', 'member-role')
36889                 .property('type', 'text')
36890                 .attr('maxlength', 255)
36891                 .attr('placeholder', t('inspector.role'))
36892                 .property('value', function(d) { return d.member.role; })
36893                 .on('change', changeRole);
36894
36895             $enter.append('button')
36896                 .attr('tabindex', -1)
36897                 .attr('class', 'remove button-input-action member-delete minor')
36898                 .on('click', deleteMembership)
36899                 .call(iD.svg.Icon('#operation-delete'));
36900
36901             $items.exit()
36902                 .remove();
36903
36904             if (showBlank) {
36905                 var $new = $list.selectAll('.member-row-new')
36906                     .data([0]);
36907
36908                 $enter = $new.enter().append('li')
36909                     .attr('class', 'member-row member-row-new form-field');
36910
36911                 $enter.append('input')
36912                     .attr('type', 'text')
36913                     .attr('class', 'member-entity-input')
36914                     .call(d3.combobox()
36915                         .minItems(1)
36916                         .fetcher(function(value, callback) {
36917                             callback(relations(value));
36918                         })
36919                         .on('accept', function(d) {
36920                             addMembership(d, $new.select('.member-role').property('value'));
36921                         }));
36922
36923                 $enter.append('input')
36924                     .attr('class', 'member-role')
36925                     .property('type', 'text')
36926                     .attr('maxlength', 255)
36927                     .attr('placeholder', t('inspector.role'))
36928                     .on('change', changeRole);
36929
36930                 $enter.append('button')
36931                     .attr('tabindex', -1)
36932                     .attr('class', 'remove button-input-action member-delete minor')
36933                     .on('click', deleteMembership)
36934                     .call(iD.svg.Icon('#operation-delete'));
36935
36936             } else {
36937                 $list.selectAll('.member-row-new')
36938                     .remove();
36939             }
36940
36941             var $add = $wrap.selectAll('.add-relation')
36942                 .data([0]);
36943
36944             $add.enter()
36945                 .append('button')
36946                 .attr('class', 'add-relation')
36947                 .call(iD.svg.Icon('#icon-plus', 'light'));
36948
36949             $wrap.selectAll('.add-relation')
36950                 .on('click', function() {
36951                     showBlank = true;
36952                     content($wrap);
36953                     $list.selectAll('.member-entity-input').node().focus();
36954                 });
36955         }
36956     }
36957
36958     rawMembershipEditor.entityID = function(_) {
36959         if (!arguments.length) return id;
36960         id = _;
36961         return rawMembershipEditor;
36962     };
36963
36964     return rawMembershipEditor;
36965 };
36966 iD.ui.RawTagEditor = function(context) {
36967     var event = d3.dispatch('change'),
36968         showBlank = false,
36969         state,
36970         preset,
36971         tags,
36972         id;
36973
36974     function rawTagEditor(selection) {
36975         var count = Object.keys(tags).filter(function(d) { return d; }).length;
36976
36977         selection.call(iD.ui.Disclosure()
36978             .title(t('inspector.all_tags') + ' (' + count + ')')
36979             .expanded(context.storage('raw_tag_editor.expanded') === 'true' || preset.isFallback())
36980             .on('toggled', toggled)
36981             .content(content));
36982
36983         function toggled(expanded) {
36984             context.storage('raw_tag_editor.expanded', expanded);
36985             if (expanded) {
36986                 selection.node().parentNode.scrollTop += 200;
36987             }
36988         }
36989     }
36990
36991     function content($wrap) {
36992         var entries = d3.entries(tags);
36993
36994         if (!entries.length || showBlank) {
36995             showBlank = false;
36996             entries.push({key: '', value: ''});
36997         }
36998
36999         var $list = $wrap.selectAll('.tag-list')
37000             .data([0]);
37001
37002         $list.enter().append('ul')
37003             .attr('class', 'tag-list');
37004
37005         var $newTag = $wrap.selectAll('.add-tag')
37006             .data([0]);
37007
37008         $newTag.enter()
37009             .append('button')
37010             .attr('class', 'add-tag')
37011             .call(iD.svg.Icon('#icon-plus', 'light'));
37012
37013         $newTag.on('click', addTag);
37014
37015         var $items = $list.selectAll('li')
37016             .data(entries, function(d) { return d.key; });
37017
37018         // Enter
37019
37020         var $enter = $items.enter().append('li')
37021             .attr('class', 'tag-row cf');
37022
37023         $enter.append('div')
37024             .attr('class', 'key-wrap')
37025             .append('input')
37026             .property('type', 'text')
37027             .attr('class', 'key')
37028             .attr('maxlength', 255);
37029
37030         $enter.append('div')
37031             .attr('class', 'input-wrap-position')
37032             .append('input')
37033             .property('type', 'text')
37034             .attr('class', 'value')
37035             .attr('maxlength', 255);
37036
37037         $enter.append('button')
37038             .attr('tabindex', -1)
37039             .attr('class', 'remove minor')
37040             .call(iD.svg.Icon('#operation-delete'));
37041
37042         if (context.taginfo()) {
37043             $enter.each(bindTypeahead);
37044         }
37045
37046         // Update
37047
37048         $items.order();
37049
37050         $items.each(function(tag) {
37051             var isRelation = (context.entity(id).type === 'relation'),
37052                 reference;
37053             if (isRelation && tag.key === 'type')
37054                 reference = iD.ui.TagReference({rtype: tag.value}, context);
37055             else
37056                 reference = iD.ui.TagReference({key: tag.key, value: tag.value}, context);
37057
37058             if (state === 'hover') {
37059                 reference.showing(false);
37060             }
37061
37062             d3.select(this)
37063                 .call(reference.button)
37064                 .call(reference.body);
37065         });
37066
37067         $items.select('input.key')
37068             .attr('title', function(d) { return d.key; })
37069             .value(function(d) { return d.key; })
37070             .on('blur', keyChange)
37071             .on('change', keyChange);
37072
37073         $items.select('input.value')
37074             .attr('title', function(d) { return d.value; })
37075             .value(function(d) { return d.value; })
37076             .on('blur', valueChange)
37077             .on('change', valueChange)
37078             .on('keydown.push-more', pushMore);
37079
37080         $items.select('button.remove')
37081             .on('click', removeTag);
37082
37083         $items.exit()
37084             .each(unbind)
37085             .remove();
37086
37087         function pushMore() {
37088             if (d3.event.keyCode === 9 && !d3.event.shiftKey &&
37089                 $list.selectAll('li:last-child input.value').node() === this) {
37090                 addTag();
37091             }
37092         }
37093
37094         function bindTypeahead() {
37095             var row = d3.select(this),
37096                 key = row.selectAll('input.key'),
37097                 value = row.selectAll('input.value');
37098
37099             function sort(value, data) {
37100                 var sameletter = [],
37101                     other = [];
37102                 for (var i = 0; i < data.length; i++) {
37103                     if (data[i].value.substring(0, value.length) === value) {
37104                         sameletter.push(data[i]);
37105                     } else {
37106                         other.push(data[i]);
37107                     }
37108                 }
37109                 return sameletter.concat(other);
37110             }
37111
37112             key.call(d3.combobox()
37113                 .fetcher(function(value, callback) {
37114                     context.taginfo().keys({
37115                         debounce: true,
37116                         geometry: context.geometry(id),
37117                         query: value
37118                     }, function(err, data) {
37119                         if (!err) callback(sort(value, data));
37120                     });
37121                 }));
37122
37123             value.call(d3.combobox()
37124                 .fetcher(function(value, callback) {
37125                     context.taginfo().values({
37126                         debounce: true,
37127                         key: key.value(),
37128                         geometry: context.geometry(id),
37129                         query: value
37130                     }, function(err, data) {
37131                         if (!err) callback(sort(value, data));
37132                     });
37133                 }));
37134         }
37135
37136         function unbind() {
37137             var row = d3.select(this);
37138
37139             row.selectAll('input.key')
37140                 .call(d3.combobox.off);
37141
37142             row.selectAll('input.value')
37143                 .call(d3.combobox.off);
37144         }
37145
37146         function keyChange(d) {
37147             var kOld = d.key,
37148                 kNew = this.value.trim(),
37149                 tag = {};
37150
37151             if (kNew && kNew !== kOld) {
37152                 var match = kNew.match(/^(.*?)(?:_(\d+))?$/),
37153                     base = match[1],
37154                     suffix = +(match[2] || 1);
37155                 while (tags[kNew]) {  // rename key if already in use
37156                     kNew = base + '_' + suffix++;
37157                 }
37158             }
37159             tag[kOld] = undefined;
37160             tag[kNew] = d.value;
37161             d.key = kNew; // Maintain DOM identity through the subsequent update.
37162             this.value = kNew;
37163             event.change(tag);
37164         }
37165
37166         function valueChange(d) {
37167             var tag = {};
37168             tag[d.key] = this.value;
37169             event.change(tag);
37170         }
37171
37172         function removeTag(d) {
37173             var tag = {};
37174             tag[d.key] = undefined;
37175             event.change(tag);
37176             d3.select(this.parentNode).remove();
37177         }
37178
37179         function addTag() {
37180             // Wrapped in a setTimeout in case it's being called from a blur
37181             // handler. Without the setTimeout, the call to `content` would
37182             // wipe out the pending value change.
37183             setTimeout(function() {
37184                 showBlank = true;
37185                 content($wrap);
37186                 $list.selectAll('li:last-child input.key').node().focus();
37187             }, 0);
37188         }
37189     }
37190
37191     rawTagEditor.state = function(_) {
37192         if (!arguments.length) return state;
37193         state = _;
37194         return rawTagEditor;
37195     };
37196
37197     rawTagEditor.preset = function(_) {
37198         if (!arguments.length) return preset;
37199         preset = _;
37200         return rawTagEditor;
37201     };
37202
37203     rawTagEditor.tags = function(_) {
37204         if (!arguments.length) return tags;
37205         tags = _;
37206         return rawTagEditor;
37207     };
37208
37209     rawTagEditor.entityID = function(_) {
37210         if (!arguments.length) return id;
37211         id = _;
37212         return rawTagEditor;
37213     };
37214
37215     return d3.rebind(rawTagEditor, event, 'on');
37216 };
37217 iD.ui.Restore = function(context) {
37218     return function(selection) {
37219         if (!context.history().lock() || !context.history().restorableChanges())
37220             return;
37221
37222         var modal = iD.ui.modal(selection, true);
37223
37224         modal.select('.modal')
37225             .attr('class', 'modal fillL col6');
37226
37227         var introModal = modal.select('.content');
37228
37229         introModal.attr('class','cf');
37230
37231         introModal.append('div')
37232             .attr('class', 'modal-section')
37233             .append('h3')
37234             .text(t('restore.heading'));
37235
37236         introModal.append('div')
37237             .attr('class','modal-section')
37238             .append('p')
37239             .text(t('restore.description'));
37240
37241         var buttonWrap = introModal.append('div')
37242             .attr('class', 'modal-actions cf');
37243
37244         var restore = buttonWrap.append('button')
37245             .attr('class', 'restore col6')
37246             .text(t('restore.restore'))
37247             .on('click', function() {
37248                 context.history().restore();
37249                 modal.remove();
37250             });
37251
37252         buttonWrap.append('button')
37253             .attr('class', 'reset col6')
37254             .text(t('restore.reset'))
37255             .on('click', function() {
37256                 context.history().clearSaved();
37257                 modal.remove();
37258             });
37259
37260         restore.node().focus();
37261     };
37262 };
37263 iD.ui.Save = function(context) {
37264     var history = context.history(),
37265         key = iD.ui.cmd('⌘S');
37266
37267     function saving() {
37268         return context.mode().id === 'save';
37269     }
37270
37271     function save() {
37272         d3.event.preventDefault();
37273         if (!context.inIntro() && !saving() && history.hasChanges()) {
37274             context.enter(iD.modes.Save(context));
37275         }
37276     }
37277
37278     return function(selection) {
37279         var tooltip = bootstrap.tooltip()
37280             .placement('bottom')
37281             .html(true)
37282             .title(iD.ui.tooltipHtml(t('save.no_changes'), key));
37283
37284         var button = selection.append('button')
37285             .attr('class', 'save col12 disabled')
37286             .attr('tabindex', -1)
37287             .on('click', save)
37288             .call(tooltip);
37289
37290         button.append('span')
37291             .attr('class', 'label')
37292             .text(t('save.title'));
37293
37294         button.append('span')
37295             .attr('class', 'count')
37296             .text('0');
37297
37298         var keybinding = d3.keybinding('undo-redo')
37299             .on(key, save, true);
37300
37301         d3.select(document)
37302             .call(keybinding);
37303
37304         var numChanges = 0;
37305
37306         context.history().on('change.save', function() {
37307             var _ = history.difference().summary().length;
37308             if (_ === numChanges)
37309                 return;
37310             numChanges = _;
37311
37312             tooltip.title(iD.ui.tooltipHtml(t(numChanges > 0 ?
37313                     'save.help' : 'save.no_changes'), key));
37314
37315             button
37316                 .classed('disabled', numChanges === 0)
37317                 .classed('has-count', numChanges > 0);
37318
37319             button.select('span.count')
37320                 .text(numChanges);
37321         });
37322
37323         context.on('enter.save', function() {
37324             button.property('disabled', saving());
37325             if (saving()) button.call(tooltip.hide);
37326         });
37327     };
37328 };
37329 iD.ui.Scale = function(context) {
37330     var projection = context.projection,
37331         maxLength = 180,
37332         tickHeight = 8;
37333
37334     function scaleDefs(loc1, loc2) {
37335         var lat = (loc2[1] + loc1[1]) / 2,
37336             imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
37337             conversion = (imperial ? 3.28084 : 1),
37338             dist = iD.geo.lonToMeters(loc2[0] - loc1[0], lat) * conversion,
37339             scale = { dist: 0, px: 0, text: '' },
37340             buckets, i, val, dLon;
37341
37342         if (imperial) {
37343             buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
37344         } else {
37345             buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
37346         }
37347
37348         // determine a user-friendly endpoint for the scale
37349         for (i = 0; i < buckets.length; i++) {
37350             val = buckets[i];
37351             if (dist >= val) {
37352                 scale.dist = Math.floor(dist / val) * val;
37353                 break;
37354             }
37355         }
37356
37357         dLon = iD.geo.metersToLon(scale.dist / conversion, lat);
37358         scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
37359
37360         if (imperial) {
37361             if (scale.dist >= 5280) {
37362                 scale.dist /= 5280;
37363                 scale.text = String(scale.dist) + ' mi';
37364             } else {
37365                 scale.text = String(scale.dist) + ' ft';
37366             }
37367         } else {
37368             if (scale.dist >= 1000) {
37369                 scale.dist /= 1000;
37370                 scale.text = String(scale.dist) + ' km';
37371             } else {
37372                 scale.text = String(scale.dist) + ' m';
37373             }
37374         }
37375
37376         return scale;
37377     }
37378
37379     function update(selection) {
37380         // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
37381         var dims = context.map().dimensions(),
37382             loc1 = projection.invert([0, dims[1]]),
37383             loc2 = projection.invert([maxLength, dims[1]]),
37384             scale = scaleDefs(loc1, loc2);
37385
37386         selection.select('#scalepath')
37387             .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
37388
37389         selection.select('#scaletext')
37390             .attr('x', scale.px + 8)
37391             .attr('y', tickHeight)
37392             .text(scale.text);
37393     }
37394
37395     return function(selection) {
37396         var g = selection.append('svg')
37397             .attr('id', 'scale')
37398             .append('g')
37399             .attr('transform', 'translate(10,11)');
37400
37401         g.append('path').attr('id', 'scalepath');
37402         g.append('text').attr('id', 'scaletext');
37403
37404         update(selection);
37405
37406         context.map().on('move.scale', function() {
37407             update(selection);
37408         });
37409     };
37410 };
37411 iD.ui.SelectionList = function(context, selectedIDs) {
37412
37413     function selectEntity(entity) {
37414         context.enter(iD.modes.Select(context, [entity.id]).suppressMenu(true));
37415     }
37416
37417
37418     function selectionList(selection) {
37419         selection.classed('selection-list-pane', true);
37420
37421         var header = selection.append('div')
37422             .attr('class', 'header fillL cf');
37423
37424         header.append('h3')
37425             .text(t('inspector.multiselect'));
37426
37427         var listWrap = selection.append('div')
37428             .attr('class', 'inspector-body');
37429
37430         var list = listWrap.append('div')
37431             .attr('class', 'feature-list cf');
37432
37433         context.history().on('change.selection-list', drawList);
37434         drawList();
37435
37436         function drawList() {
37437             var entities = selectedIDs
37438                 .map(function(id) { return context.hasEntity(id); })
37439                 .filter(function(entity) { return entity; });
37440
37441             var items = list.selectAll('.feature-list-item')
37442                 .data(entities, iD.Entity.key);
37443
37444             var enter = items.enter().append('button')
37445                 .attr('class', 'feature-list-item')
37446                 .on('click', selectEntity);
37447
37448             // Enter
37449             var label = enter.append('div')
37450                 .attr('class', 'label')
37451                 .call(iD.svg.Icon('', 'pre-text'));
37452
37453             label.append('span')
37454                 .attr('class', 'entity-type');
37455
37456             label.append('span')
37457                 .attr('class', 'entity-name');
37458
37459             // Update
37460             items.selectAll('use')
37461                 .attr('href', function() {
37462                     var entity = this.parentNode.parentNode.__data__;
37463                     return '#icon-' + context.geometry(entity.id);
37464                 });
37465
37466             items.selectAll('.entity-type')
37467                 .text(function(entity) { return context.presets().match(entity, context.graph()).name(); });
37468
37469             items.selectAll('.entity-name')
37470                 .text(function(entity) { return iD.util.displayName(entity); });
37471
37472             // Exit
37473             items.exit()
37474                 .remove();
37475         }
37476     }
37477
37478     return selectionList;
37479
37480 };
37481 iD.ui.Sidebar = function(context) {
37482     var inspector = iD.ui.Inspector(context),
37483         current;
37484
37485     function sidebar(selection) {
37486         var featureListWrap = selection.append('div')
37487             .attr('class', 'feature-list-pane')
37488             .call(iD.ui.FeatureList(context));
37489
37490         selection.call(iD.ui.Notice(context));
37491
37492         var inspectorWrap = selection.append('div')
37493             .attr('class', 'inspector-hidden inspector-wrap fr');
37494
37495         function hover(id) {
37496             if (!current && context.hasEntity(id)) {
37497                 featureListWrap.classed('inspector-hidden', true);
37498                 inspectorWrap.classed('inspector-hidden', false)
37499                     .classed('inspector-hover', true);
37500
37501                 if (inspector.entityID() !== id || inspector.state() !== 'hover') {
37502                     inspector
37503                         .state('hover')
37504                         .entityID(id);
37505
37506                     inspectorWrap.call(inspector);
37507                 }
37508             } else if (!current) {
37509                 featureListWrap.classed('inspector-hidden', false);
37510                 inspectorWrap.classed('inspector-hidden', true);
37511                 inspector.state('hide');
37512             }
37513         }
37514
37515         sidebar.hover = _.throttle(hover, 200);
37516
37517         sidebar.select = function(id, newFeature) {
37518             if (!current && id) {
37519                 featureListWrap.classed('inspector-hidden', true);
37520                 inspectorWrap.classed('inspector-hidden', false)
37521                     .classed('inspector-hover', false);
37522
37523                 if (inspector.entityID() !== id || inspector.state() !== 'select') {
37524                     inspector
37525                         .state('select')
37526                         .entityID(id)
37527                         .newFeature(newFeature);
37528
37529                     inspectorWrap.call(inspector);
37530                 }
37531             } else if (!current) {
37532                 featureListWrap.classed('inspector-hidden', false);
37533                 inspectorWrap.classed('inspector-hidden', true);
37534                 inspector.state('hide');
37535             }
37536         };
37537
37538         sidebar.show = function(component) {
37539             featureListWrap.classed('inspector-hidden', true);
37540             inspectorWrap.classed('inspector-hidden', true);
37541             if (current) current.remove();
37542             current = selection.append('div')
37543                 .attr('class', 'sidebar-component')
37544                 .call(component);
37545         };
37546
37547         sidebar.hide = function() {
37548             featureListWrap.classed('inspector-hidden', false);
37549             inspectorWrap.classed('inspector-hidden', true);
37550             if (current) current.remove();
37551             current = null;
37552         };
37553     }
37554
37555     sidebar.hover = function() {};
37556     sidebar.hover.cancel = function() {};
37557     sidebar.select = function() {};
37558     sidebar.show = function() {};
37559     sidebar.hide = function() {};
37560
37561     return sidebar;
37562 };
37563 iD.ui.SourceSwitch = function(context) {
37564     var keys;
37565
37566     function click() {
37567         d3.event.preventDefault();
37568
37569         if (context.history().hasChanges() &&
37570             !window.confirm(t('source_switch.lose_changes'))) return;
37571
37572         var live = d3.select(this)
37573             .classed('live');
37574
37575         context.connection()
37576             .switch(live ? keys[1] : keys[0]);
37577
37578         context.enter(iD.modes.Browse(context));
37579         context.flush();
37580
37581         d3.select(this)
37582             .text(live ? t('source_switch.dev') : t('source_switch.live'))
37583             .classed('live', !live);
37584     }
37585
37586     var sourceSwitch = function(selection) {
37587         selection.append('a')
37588             .attr('href', '#')
37589             .text(t('source_switch.live'))
37590             .classed('live', true)
37591             .attr('tabindex', -1)
37592             .on('click', click);
37593     };
37594
37595     sourceSwitch.keys = function(_) {
37596         if (!arguments.length) return keys;
37597         keys = _;
37598         return sourceSwitch;
37599     };
37600
37601     return sourceSwitch;
37602 };
37603 iD.ui.Spinner = function(context) {
37604     var connection = context.connection();
37605
37606     return function(selection) {
37607         var img = selection.append('img')
37608             .attr('src', context.imagePath('loader-black.gif'))
37609             .style('opacity', 0);
37610
37611         connection.on('loading.spinner', function() {
37612             img.transition()
37613                 .style('opacity', 1);
37614         });
37615
37616         connection.on('loaded.spinner', function() {
37617             img.transition()
37618                 .style('opacity', 0);
37619         });
37620     };
37621 };
37622 iD.ui.Splash = function(context) {
37623     return function(selection) {
37624         if (context.storage('sawSplash'))
37625              return;
37626
37627         context.storage('sawSplash', true);
37628
37629         var modal = iD.ui.modal(selection);
37630
37631         modal.select('.modal')
37632             .attr('class', 'modal-splash modal col6');
37633
37634         var introModal = modal.select('.content')
37635             .append('div')
37636             .attr('class', 'fillL');
37637
37638         introModal.append('div')
37639             .attr('class','modal-section cf')
37640             .append('h3').text(t('splash.welcome'));
37641
37642         introModal.append('div')
37643             .attr('class','modal-section')
37644             .append('p')
37645             .html(t('splash.text', {
37646                 version: iD.version,
37647                 website: '<a href="http://ideditor.com/">ideditor.com</a>',
37648                 github: '<a href="https://github.com/openstreetmap/iD">github.com</a>'
37649             }));
37650
37651         var buttons = introModal.append('div').attr('class', 'modal-actions cf');
37652
37653         buttons.append('button')
37654             .attr('class', 'col6 walkthrough')
37655             .text(t('splash.walkthrough'))
37656             .on('click', function() {
37657                 d3.select(document.body).call(iD.ui.intro(context));
37658                 modal.close();
37659             });
37660
37661         buttons.append('button')
37662             .attr('class', 'col6 start')
37663             .text(t('splash.start'))
37664             .on('click', modal.close);
37665
37666         modal.select('button.close').attr('class','hide');
37667
37668     };
37669 };
37670 iD.ui.Status = function(context) {
37671     var connection = context.connection(),
37672         errCount = 0;
37673
37674     return function(selection) {
37675
37676         function update() {
37677
37678             connection.status(function(err, apiStatus) {
37679
37680                 selection.html('');
37681
37682                 if (err && errCount++ < 2) return;
37683
37684                 if (err) {
37685                     selection.text(t('status.error'));
37686
37687                 } else if (apiStatus === 'readonly') {
37688                     selection.text(t('status.readonly'));
37689
37690                 } else if (apiStatus === 'offline') {
37691                     selection.text(t('status.offline'));
37692                 }
37693
37694                 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
37695                 if (!err) errCount = 0;
37696
37697             });
37698         }
37699
37700         connection.on('auth', function() { update(selection); });
37701         window.setInterval(update, 90000);
37702         update(selection);
37703     };
37704 };
37705 iD.ui.Success = function(context) {
37706     var dispatch = d3.dispatch('cancel'),
37707         changeset;
37708
37709     function success(selection) {
37710         var message = (changeset.comment || t('success.edited_osm')).substring(0, 130) +
37711             ' ' + context.connection().changesetURL(changeset.id);
37712
37713         var header = selection.append('div')
37714             .attr('class', 'header fillL');
37715
37716         header.append('button')
37717             .attr('class', 'fr')
37718             .on('click', function() { dispatch.cancel(); })
37719             .call(iD.svg.Icon('#icon-close'));
37720
37721         header.append('h3')
37722             .text(t('success.just_edited'));
37723
37724         var body = selection.append('div')
37725             .attr('class', 'body save-success fillL');
37726
37727         body.append('p')
37728             .html(t('success.help_html'));
37729
37730         body.append('a')
37731             .attr('class', 'details')
37732             .attr('target', '_blank')
37733             .attr('tabindex', -1)
37734             .call(iD.svg.Icon('#icon-out-link', 'inline'))
37735             .attr('href', t('success.help_link_url'))
37736             .append('span')
37737             .text(t('success.help_link_text'));
37738
37739         var changesetURL = context.connection().changesetURL(changeset.id);
37740
37741         body.append('a')
37742             .attr('class', 'button col12 osm')
37743             .attr('target', '_blank')
37744             .attr('href', changesetURL)
37745             .text(t('success.view_on_osm'));
37746
37747         var sharing = {
37748             facebook: 'https://facebook.com/sharer/sharer.php?u=' + encodeURIComponent(changesetURL),
37749             twitter: 'https://twitter.com/intent/tweet?source=webclient&text=' + encodeURIComponent(message),
37750             google: 'https://plus.google.com/share?url=' + encodeURIComponent(changesetURL)
37751         };
37752
37753         body.selectAll('.button.social')
37754             .data(d3.entries(sharing))
37755             .enter()
37756             .append('a')
37757             .attr('class', 'button social col4')
37758             .attr('target', '_blank')
37759             .attr('href', function(d) { return d.value; })
37760             .call(bootstrap.tooltip()
37761                 .title(function(d) { return t('success.' + d.key); })
37762                 .placement('bottom'))
37763             .each(function(d) { d3.select(this).call(iD.svg.Icon('#logo-' + d.key, 'social')); });
37764     }
37765
37766     success.changeset = function(_) {
37767         if (!arguments.length) return changeset;
37768         changeset = _;
37769         return success;
37770     };
37771
37772     return d3.rebind(success, dispatch, 'on');
37773 };
37774 iD.ui.TagReference = function(tag, context) {
37775     var tagReference = {},
37776         button,
37777         body,
37778         loaded,
37779         showing;
37780
37781     function findLocal(data) {
37782         var locale = iD.detect().locale.toLowerCase(),
37783             localized;
37784
37785         localized = _.find(data, function(d) {
37786             return d.lang.toLowerCase() === locale;
37787         });
37788         if (localized) return localized;
37789
37790         // try the non-regional version of a language, like
37791         // 'en' if the language is 'en-US'
37792         if (locale.indexOf('-') !== -1) {
37793             var first = locale.split('-')[0];
37794             localized = _.find(data, function(d) {
37795                 return d.lang.toLowerCase() === first;
37796             });
37797             if (localized) return localized;
37798         }
37799
37800         // finally fall back to english
37801         return _.find(data, function(d) {
37802             return d.lang.toLowerCase() === 'en';
37803         });
37804     }
37805
37806     function load(param) {
37807         button.classed('tag-reference-loading', true);
37808
37809         context.taginfo().docs(param, function show(err, data) {
37810             var docs;
37811             if (!err && data) {
37812                 docs = findLocal(data);
37813             }
37814
37815             body.html('');
37816
37817             if (!docs || !docs.description) {
37818                 if (param.hasOwnProperty('value')) {
37819                     load(_.omit(param, 'value'));   // retry with key only
37820                 } else {
37821                     body.append('p').text(t('inspector.no_documentation_key'));
37822                     done();
37823                 }
37824                 return;
37825             }
37826
37827             if (docs.image && docs.image.thumb_url_prefix) {
37828                 body
37829                     .append('img')
37830                     .attr('class', 'wiki-image')
37831                     .attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix)
37832                     .on('load', function() { done(); })
37833                     .on('error', function() { d3.select(this).remove(); done(); });
37834             } else {
37835                 done();
37836             }
37837
37838             body
37839                 .append('p')
37840                 .text(docs.description);
37841
37842             body
37843                 .append('a')
37844                 .attr('target', '_blank')
37845                 .attr('href', 'https://wiki.openstreetmap.org/wiki/' + docs.title)
37846                 .call(iD.svg.Icon('#icon-out-link', 'inline'))
37847                 .append('span')
37848                 .text(t('inspector.reference'));
37849         });
37850     }
37851
37852     function done() {
37853         loaded = true;
37854
37855         button.classed('tag-reference-loading', false);
37856
37857         body.transition()
37858             .duration(200)
37859             .style('max-height', '200px')
37860             .style('opacity', '1');
37861
37862         showing = true;
37863     }
37864
37865     function hide(selection) {
37866         selection = selection || body.transition().duration(200);
37867
37868         selection
37869             .style('max-height', '0px')
37870             .style('opacity', '0');
37871
37872         showing = false;
37873     }
37874
37875     tagReference.button = function(selection) {
37876         button = selection.selectAll('.tag-reference-button')
37877             .data([0]);
37878
37879         button.enter()
37880             .append('button')
37881             .attr('class', 'tag-reference-button')
37882             .attr('tabindex', -1)
37883             .call(iD.svg.Icon('#icon-inspect'));
37884
37885         button.on('click', function () {
37886             d3.event.stopPropagation();
37887             d3.event.preventDefault();
37888             if (showing) {
37889                 hide();
37890             } else if (loaded) {
37891                 done();
37892             } else {
37893                 if (context.taginfo()) {
37894                     load(tag);
37895                 }
37896             }
37897         });
37898     };
37899
37900     tagReference.body = function(selection) {
37901         body = selection.selectAll('.tag-reference-body')
37902             .data([0]);
37903
37904         body.enter().append('div')
37905             .attr('class', 'tag-reference-body cf')
37906             .style('max-height', '0')
37907             .style('opacity', '0');
37908
37909         if (showing === false) {
37910             hide(body);
37911         }
37912     };
37913
37914     tagReference.showing = function(_) {
37915         if (!arguments.length) return showing;
37916         showing = _;
37917         return tagReference;
37918     };
37919
37920     return tagReference;
37921 };
37922 // toggles the visibility of ui elements, using a combination of the
37923 // hide class, which sets display=none, and a d3 transition for opacity.
37924 // this will cause blinking when called repeatedly, so check that the
37925 // value actually changes between calls.
37926 iD.ui.Toggle = function(show, callback) {
37927     return function(selection) {
37928         selection
37929             .style('opacity', show ? 0 : 1)
37930             .classed('hide', false)
37931             .transition()
37932             .style('opacity', show ? 1 : 0)
37933             .each('end', function() {
37934                 d3.select(this)
37935                     .classed('hide', !show)
37936                     .style('opacity', null);
37937                 if (callback) callback.apply(this);
37938             });
37939     };
37940 };
37941 iD.ui.UndoRedo = function(context) {
37942     var commands = [{
37943         id: 'undo',
37944         cmd: iD.ui.cmd('⌘Z'),
37945         action: function() { if (!(context.inIntro() || saving())) context.undo(); },
37946         annotation: function() { return context.history().undoAnnotation(); }
37947     }, {
37948         id: 'redo',
37949         cmd: iD.ui.cmd('⌘⇧Z'),
37950         action: function() {if (!(context.inIntro() || saving())) context.redo(); },
37951         annotation: function() { return context.history().redoAnnotation(); }
37952     }];
37953
37954     function saving() {
37955         return context.mode().id === 'save';
37956     }
37957
37958     return function(selection) {
37959         var tooltip = bootstrap.tooltip()
37960             .placement('bottom')
37961             .html(true)
37962             .title(function (d) {
37963                 return iD.ui.tooltipHtml(d.annotation() ?
37964                     t(d.id + '.tooltip', {action: d.annotation()}) :
37965                     t(d.id + '.nothing'), d.cmd);
37966             });
37967
37968         var buttons = selection.selectAll('button')
37969             .data(commands)
37970             .enter().append('button')
37971             .attr('class', 'col6 disabled')
37972             .on('click', function(d) { return d.action(); })
37973             .call(tooltip);
37974
37975         buttons.each(function(d) {
37976             d3.select(this)
37977                 .call(iD.svg.Icon('#icon-' + d.id));
37978         });
37979
37980         var keybinding = d3.keybinding('undo')
37981             .on(commands[0].cmd, function() { d3.event.preventDefault(); commands[0].action(); })
37982             .on(commands[1].cmd, function() { d3.event.preventDefault(); commands[1].action(); });
37983
37984         d3.select(document)
37985             .call(keybinding);
37986
37987         context.history()
37988             .on('change.undo_redo', update);
37989
37990         context
37991             .on('enter.undo_redo', update);
37992
37993         function update() {
37994             buttons
37995                 .property('disabled', saving())
37996                 .classed('disabled', function(d) { return !d.annotation(); })
37997                 .each(function() {
37998                     var selection = d3.select(this);
37999                     if (selection.property('tooltipVisible')) {
38000                         selection.call(tooltip.show);
38001                     }
38002                 });
38003         }
38004     };
38005 };
38006 iD.ui.ViewOnOSM = function(context) {
38007     var id;
38008
38009     function viewOnOSM(selection) {
38010         var entity = context.entity(id);
38011
38012         selection.style('display', entity.isNew() ? 'none' : null);
38013
38014         var $link = selection.selectAll('.view-on-osm')
38015             .data([0]);
38016
38017         $link.enter()
38018             .append('a')
38019             .attr('class', 'view-on-osm')
38020             .attr('target', '_blank')
38021             .call(iD.svg.Icon('#icon-out-link', 'inline'))
38022             .append('span')
38023             .text(t('inspector.view_on_osm'));
38024
38025         $link
38026             .attr('href', context.connection().entityURL(entity));
38027     }
38028
38029     viewOnOSM.entityID = function(_) {
38030         if (!arguments.length) return id;
38031         id = _;
38032         return viewOnOSM;
38033     };
38034
38035     return viewOnOSM;
38036 };
38037 iD.ui.Zoom = function(context) {
38038     var zooms = [{
38039         id: 'zoom-in',
38040         icon: 'plus',
38041         title: t('zoom.in'),
38042         action: context.zoomIn,
38043         key: '+'
38044     }, {
38045         id: 'zoom-out',
38046         icon: 'minus',
38047         title: t('zoom.out'),
38048         action: context.zoomOut,
38049         key: '-'
38050     }];
38051
38052     function zoomIn() {
38053         d3.event.preventDefault();
38054         if (!context.inIntro()) context.zoomIn();
38055     }
38056
38057     function zoomOut() {
38058         d3.event.preventDefault();
38059         if (!context.inIntro()) context.zoomOut();
38060     }
38061
38062     function zoomInFurther() {
38063         d3.event.preventDefault();
38064         if (!context.inIntro()) context.zoomInFurther();
38065     }
38066
38067     function zoomOutFurther() {
38068         d3.event.preventDefault();
38069         if (!context.inIntro()) context.zoomOutFurther();
38070     }
38071
38072
38073     return function(selection) {
38074         var button = selection.selectAll('button')
38075             .data(zooms)
38076             .enter().append('button')
38077             .attr('tabindex', -1)
38078             .attr('class', function(d) { return d.id; })
38079             .on('click.editor', function(d) { d.action(); })
38080             .call(bootstrap.tooltip()
38081                 .placement('left')
38082                 .html(true)
38083                 .title(function(d) {
38084                     return iD.ui.tooltipHtml(d.title, d.key);
38085                 }));
38086
38087         button.each(function(d) {
38088             d3.select(this)
38089                 .call(iD.svg.Icon('#icon-' + d.icon, 'light'));
38090         });
38091
38092         var keybinding = d3.keybinding('zoom');
38093
38094         _.each(['=','ffequals','plus','ffplus'], function(key) {
38095             keybinding.on(key, zoomIn);
38096             keybinding.on('⇧' + key, zoomIn);
38097             keybinding.on(iD.ui.cmd('⌘' + key), zoomInFurther);
38098             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomInFurther);
38099         });
38100         _.each(['-','ffminus','_','dash'], function(key) {
38101             keybinding.on(key, zoomOut);
38102             keybinding.on('⇧' + key, zoomOut);
38103             keybinding.on(iD.ui.cmd('⌘' + key), zoomOutFurther);
38104             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomOutFurther);
38105         });
38106
38107         d3.select(document)
38108             .call(keybinding);
38109     };
38110 };
38111 iD.ui.preset.access = function(field) {
38112     var dispatch = d3.dispatch('change'),
38113         items;
38114
38115     function access(selection) {
38116         var wrap = selection.selectAll('.preset-input-wrap')
38117             .data([0]);
38118
38119         wrap.enter().append('div')
38120             .attr('class', 'cf preset-input-wrap')
38121             .append('ul');
38122
38123         items = wrap.select('ul').selectAll('li')
38124             .data(field.keys);
38125
38126         // Enter
38127
38128         var enter = items.enter().append('li')
38129             .attr('class', function(d) { return 'cf preset-access-' + d; });
38130
38131         enter.append('span')
38132             .attr('class', 'col6 label preset-label-access')
38133             .attr('for', function(d) { return 'preset-input-access-' + d; })
38134             .text(function(d) { return field.t('types.' + d); });
38135
38136         enter.append('div')
38137             .attr('class', 'col6 preset-input-access-wrap')
38138             .append('input')
38139             .attr('type', 'text')
38140             .attr('class', 'preset-input-access')
38141             .attr('id', function(d) { return 'preset-input-access-' + d; })
38142             .each(function(d) {
38143                 d3.select(this)
38144                     .call(d3.combobox()
38145                         .data(access.options(d)));
38146             });
38147
38148         // Update
38149
38150         wrap.selectAll('.preset-input-access')
38151             .on('change', change)
38152             .on('blur', change);
38153     }
38154
38155     function change(d) {
38156         var tag = {};
38157         tag[d] = d3.select(this).value() || undefined;
38158         dispatch.change(tag);
38159     }
38160
38161     access.options = function(type) {
38162         var options = ['no', 'permissive', 'private', 'destination'];
38163
38164         if (type !== 'access') {
38165             options.unshift('yes');
38166             options.push('designated');
38167
38168             if (type === 'bicycle') {
38169                 options.push('dismount');
38170             }
38171         }
38172
38173         return options.map(function(option) {
38174             return {
38175                 title: field.t('options.' + option + '.description'),
38176                 value: option
38177             };
38178         });
38179     };
38180
38181     var placeholders = {
38182         footway: {
38183             foot: 'designated',
38184             motor_vehicle: 'no'
38185         },
38186         steps: {
38187             foot: 'yes',
38188             motor_vehicle: 'no',
38189             bicycle: 'no',
38190             horse: 'no'
38191         },
38192         pedestrian: {
38193             foot: 'yes',
38194             motor_vehicle: 'no'
38195         },
38196         cycleway: {
38197             motor_vehicle: 'no',
38198             bicycle: 'designated'
38199         },
38200         bridleway: {
38201             motor_vehicle: 'no',
38202             horse: 'designated'
38203         },
38204         path: {
38205             foot: 'yes',
38206             motor_vehicle: 'no',
38207             bicycle: 'yes',
38208             horse: 'yes'
38209         },
38210         motorway: {
38211             foot: 'no',
38212             motor_vehicle: 'yes',
38213             bicycle: 'no',
38214             horse: 'no'
38215         },
38216         trunk: {
38217             motor_vehicle: 'yes'
38218         },
38219         primary: {
38220             foot: 'yes',
38221             motor_vehicle: 'yes',
38222             bicycle: 'yes',
38223             horse: 'yes'
38224         },
38225         secondary: {
38226             foot: 'yes',
38227             motor_vehicle: 'yes',
38228             bicycle: 'yes',
38229             horse: 'yes'
38230         },
38231         tertiary: {
38232             foot: 'yes',
38233             motor_vehicle: 'yes',
38234             bicycle: 'yes',
38235             horse: 'yes'
38236         },
38237         residential: {
38238             foot: 'yes',
38239             motor_vehicle: 'yes',
38240             bicycle: 'yes',
38241             horse: 'yes'
38242         },
38243         unclassified: {
38244             foot: 'yes',
38245             motor_vehicle: 'yes',
38246             bicycle: 'yes',
38247             horse: 'yes'
38248         },
38249         service: {
38250             foot: 'yes',
38251             motor_vehicle: 'yes',
38252             bicycle: 'yes',
38253             horse: 'yes'
38254         },
38255         motorway_link: {
38256             foot: 'no',
38257             motor_vehicle: 'yes',
38258             bicycle: 'no',
38259             horse: 'no'
38260         },
38261         trunk_link: {
38262             motor_vehicle: 'yes'
38263         },
38264         primary_link: {
38265             foot: 'yes',
38266             motor_vehicle: 'yes',
38267             bicycle: 'yes',
38268             horse: 'yes'
38269         },
38270         secondary_link: {
38271             foot: 'yes',
38272             motor_vehicle: 'yes',
38273             bicycle: 'yes',
38274             horse: 'yes'
38275         },
38276         tertiary_link: {
38277             foot: 'yes',
38278             motor_vehicle: 'yes',
38279             bicycle: 'yes',
38280             horse: 'yes'
38281         }
38282     };
38283
38284     access.tags = function(tags) {
38285         items.selectAll('.preset-input-access')
38286             .value(function(d) { return tags[d] || ''; })
38287             .attr('placeholder', function() {
38288                 return tags.access ? tags.access : field.placeholder();
38289             });
38290
38291         // items.selectAll('#preset-input-access-access')
38292         //     .attr('placeholder', 'yes');
38293
38294         _.forEach(placeholders[tags.highway], function(v, k) {
38295             items.selectAll('#preset-input-access-' + k)
38296                 .attr('placeholder', function() { return (tags.access || v); });
38297         });
38298     };
38299
38300     access.focus = function() {
38301         items.selectAll('.preset-input-access')
38302             .node().focus();
38303     };
38304
38305     return d3.rebind(access, dispatch, 'on');
38306 };
38307 iD.ui.preset.address = function(field, context) {
38308     var dispatch = d3.dispatch('init', 'change'),
38309         wrap,
38310         entity,
38311         isInitialized;
38312
38313     var widths = {
38314         housenumber: 1/3,
38315         street: 2/3,
38316         city: 2/3,
38317         state: 1/4,
38318         postcode: 1/3
38319     };
38320
38321     function getStreets() {
38322         var extent = entity.extent(context.graph()),
38323             l = extent.center(),
38324             box = iD.geo.Extent(l).padByMeters(200);
38325
38326         return context.intersects(box)
38327             .filter(isAddressable)
38328             .map(function(d) {
38329                 var loc = context.projection([
38330                     (extent[0][0] + extent[1][0]) / 2,
38331                     (extent[0][1] + extent[1][1]) / 2]),
38332                     choice = iD.geo.chooseEdge(context.childNodes(d), loc, context.projection);
38333                 return {
38334                     title: d.tags.name,
38335                     value: d.tags.name,
38336                     dist: choice.distance
38337                 };
38338             }).sort(function(a, b) {
38339                 return a.dist - b.dist;
38340             });
38341
38342         function isAddressable(d) {
38343             return d.tags.highway && d.tags.name && d.type === 'way';
38344         }
38345     }
38346
38347     function getCities() {
38348         var extent = entity.extent(context.graph()),
38349             l = extent.center(),
38350             box = iD.geo.Extent(l).padByMeters(200);
38351
38352         return context.intersects(box)
38353             .filter(isAddressable)
38354             .map(function(d) {
38355                 return {
38356                     title: d.tags['addr:city'] || d.tags.name,
38357                     value: d.tags['addr:city'] || d.tags.name,
38358                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
38359                 };
38360             }).sort(function(a, b) {
38361                 return a.dist - b.dist;
38362             });
38363
38364         function isAddressable(d) {
38365             if (d.tags.name &&
38366                 (d.tags.admin_level === '8' || d.tags.border_type === 'city'))
38367                 return true;
38368
38369             if (d.tags.place && d.tags.name && (
38370                     d.tags.place === 'city' ||
38371                     d.tags.place === 'town' ||
38372                     d.tags.place === 'village'))
38373                 return true;
38374
38375             if (d.tags['addr:city']) return true;
38376
38377             return false;
38378         }
38379     }
38380
38381     function getPostCodes() {
38382         var extent = entity.extent(context.graph()),
38383             l = extent.center(),
38384             box = iD.geo.Extent(l).padByMeters(200);
38385
38386         return context.intersects(box)
38387             .filter(isAddressable)
38388             .map(function(d) {
38389                 return {
38390                     title: d.tags['addr:postcode'],
38391                     value: d.tags['addr:postcode'],
38392                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
38393                 };
38394             }).sort(function(a, b) {
38395                 return a.dist - b.dist;
38396             });
38397
38398         function isAddressable(d) {
38399             return d.tags['addr:postcode'];
38400         }
38401     }
38402
38403     function address(selection) {
38404         isInitialized = false;
38405
38406         wrap = selection.selectAll('.preset-input-wrap')
38407             .data([0]);
38408
38409         // Enter
38410
38411         wrap.enter()
38412             .append('div')
38413             .attr('class', 'preset-input-wrap');
38414
38415         var center = entity.extent(context.graph()).center(),
38416             addressFormat;
38417
38418         iD.services.nominatim().countryCode(center, function (err, countryCode) {
38419             addressFormat = _.find(iD.data.addressFormats, function (a) {
38420                 return a && a.countryCodes && _.contains(a.countryCodes, countryCode);
38421             }) || _.first(iD.data.addressFormats);
38422
38423             function row(r) {
38424                 // Normalize widths.
38425                 var total = _.reduce(r, function(sum, field) {
38426                     return sum + (widths[field] || 0.5);
38427                 }, 0);
38428
38429                 return r.map(function (field) {
38430                     return {
38431                         id: field,
38432                         width: (widths[field] || 0.5) / total
38433                     };
38434                 });
38435             }
38436
38437             wrap.selectAll('div')
38438                 .data(addressFormat.format)
38439                 .enter()
38440                 .append('div')
38441                 .attr('class', 'addr-row')
38442                 .selectAll('input')
38443                 .data(row)
38444                 .enter()
38445                 .append('input')
38446                 .property('type', 'text')
38447                 .attr('placeholder', function (d) { return field.t('placeholders.' + d.id); })
38448                 .attr('class', function (d) { return 'addr-' + d.id; })
38449                 .style('width', function (d) { return d.width * 100 + '%'; });
38450
38451             // Update
38452
38453             wrap.selectAll('.addr-street')
38454                 .call(d3.combobox()
38455                     .fetcher(function(value, callback) {
38456                         callback(getStreets());
38457                     }));
38458
38459             wrap.selectAll('.addr-city')
38460                 .call(d3.combobox()
38461                     .fetcher(function(value, callback) {
38462                         callback(getCities());
38463                     }));
38464
38465             wrap.selectAll('.addr-postcode')
38466                 .call(d3.combobox()
38467                     .fetcher(function(value, callback) {
38468                         callback(getPostCodes());
38469                     }));
38470
38471             wrap.selectAll('input')
38472                 .on('blur', change())
38473                 .on('change', change());
38474
38475             wrap.selectAll('input:not(.combobox-input)')
38476                 .on('input', change(true));
38477
38478             dispatch.init();
38479             isInitialized = true;
38480         });
38481     }
38482
38483     function change(onInput) {
38484         return function() {
38485             var tags = {};
38486
38487             wrap.selectAll('input')
38488                 .each(function (field) {
38489                     tags['addr:' + field.id] = this.value || undefined;
38490                 });
38491
38492             dispatch.change(tags, onInput);
38493         };
38494     }
38495
38496     function updateTags(tags) {
38497         wrap.selectAll('input')
38498             .value(function (field) {
38499                 return tags['addr:' + field.id] || '';
38500             });
38501     }
38502
38503     address.entity = function(_) {
38504         if (!arguments.length) return entity;
38505         entity = _;
38506         return address;
38507     };
38508
38509     address.tags = function(tags) {
38510         if (isInitialized) {
38511             updateTags(tags);
38512         } else {
38513             dispatch.on('init', function () {
38514                 updateTags(tags);
38515             });
38516         }
38517     };
38518
38519     address.focus = function() {
38520         var node = wrap.selectAll('input').node();
38521         if (node) node.focus();
38522     };
38523
38524     return d3.rebind(address, dispatch, 'on');
38525 };
38526 iD.ui.preset.check =
38527 iD.ui.preset.defaultcheck = function(field) {
38528     var dispatch = d3.dispatch('change'),
38529         options = field.strings && field.strings.options,
38530         values = [],
38531         texts = [],
38532         entity, value, box, text, label;
38533
38534     if (options) {
38535         for (var k in options) {
38536             values.push(k === 'undefined' ? undefined : k);
38537             texts.push(field.t('options.' + k, { 'default': options[k] }));
38538         }
38539     } else {
38540         values = [undefined, 'yes'];
38541         texts = [t('inspector.unknown'), t('inspector.check.yes')];
38542         if (field.type === 'check') {
38543             values.push('no');
38544             texts.push(t('inspector.check.no'));
38545         }
38546     }
38547
38548     var check = function(selection) {
38549         // hack: pretend oneway field is a oneway_yes field
38550         // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
38551         if (field.id === 'oneway') {
38552             for (var key in entity.tags) {
38553                 if (key in iD.oneWayTags && (entity.tags[key] in iD.oneWayTags[key])) {
38554                     texts[0] = t('presets.fields.oneway_yes.options.undefined');
38555                     break;
38556                 }
38557             }
38558         }
38559
38560         selection.classed('checkselect', 'true');
38561
38562         label = selection.selectAll('.preset-input-wrap')
38563             .data([0]);
38564
38565         var enter = label.enter().append('label')
38566             .attr('class', 'preset-input-wrap');
38567
38568         enter.append('input')
38569             .property('indeterminate', field.type === 'check')
38570             .attr('type', 'checkbox')
38571             .attr('id', 'preset-input-' + field.id);
38572
38573         enter.append('span')
38574             .text(texts[0])
38575             .attr('class', 'value');
38576
38577         box = label.select('input')
38578             .on('click', function() {
38579                 var t = {};
38580                 t[field.key] = values[(values.indexOf(value) + 1) % values.length];
38581                 dispatch.change(t);
38582                 d3.event.stopPropagation();
38583             });
38584
38585         text = label.select('span.value');
38586     };
38587
38588     check.entity = function(_) {
38589         if (!arguments.length) return entity;
38590         entity = _;
38591         return check;
38592     };
38593
38594     check.tags = function(tags) {
38595         value = tags[field.key];
38596         box.property('indeterminate', field.type === 'check' && !value);
38597         box.property('checked', value === 'yes');
38598         text.text(texts[values.indexOf(value)]);
38599         label.classed('set', !!value);
38600     };
38601
38602     check.focus = function() {
38603         box.node().focus();
38604     };
38605
38606     return d3.rebind(check, dispatch, 'on');
38607 };
38608 iD.ui.preset.combo =
38609 iD.ui.preset.typeCombo = function(field, context) {
38610     var dispatch = d3.dispatch('change'),
38611         optstrings = field.strings && field.strings.options,
38612         optarray = field.options,
38613         snake_case = (field.snake_case || (field.snake_case === undefined)),
38614         strings = {},
38615         input;
38616
38617     function snake(s) {
38618         return s.replace(/\s+/g, '_');
38619     }
38620
38621     function unsnake(s) {
38622         return s.replace(/_+/g, ' ');
38623     }
38624
38625     function clean(s) {
38626         return s.split(';')
38627             .map(function(s) { return s.trim(); })
38628             .join(';');
38629     }
38630
38631     function optString() {
38632         return _.find(_.keys(strings), function(k) {
38633                 return strings[k] === input.value();
38634             });
38635     }
38636
38637     function combo(selection) {
38638         var combobox = d3.combobox();
38639
38640         input = selection.selectAll('input')
38641             .data([0]);
38642
38643         var enter = input.enter()
38644             .append('input')
38645             .attr('type', 'text')
38646             .attr('id', 'preset-input-' + field.id);
38647
38648         if (optstrings) { enter.attr('readonly', 'readonly'); }
38649
38650         input
38651             .call(combobox)
38652             .on('change', change)
38653             .on('blur', change)
38654             .each(function() {
38655                 if (optstrings) {
38656                     _.each(optstrings, function(v, k) {
38657                         strings[k] = field.t('options.' + k, { 'default': v });
38658                     });
38659                     stringsLoaded();
38660                 } else if (optarray) {
38661                     _.each(optarray, function(k) {
38662                         strings[k] = (snake_case ? unsnake(k) : k);
38663                     });
38664                     stringsLoaded();
38665                 } else if (context.taginfo()) {
38666                     context.taginfo().values({key: field.key}, function(err, data) {
38667                         if (!err) {
38668                             _.each(_.pluck(data, 'value'), function(k) {
38669                                 strings[k] = (snake_case ? unsnake(k) : k);
38670                             });
38671                             stringsLoaded();
38672                         }
38673                     });
38674                 }
38675             });
38676
38677         function stringsLoaded() {
38678             var keys = _.keys(strings),
38679                 strs = [],
38680                 placeholders;
38681
38682             combobox.data(keys.map(function(k) {
38683                 var s = strings[k],
38684                     o = {};
38685                 o.title = o.value = s;
38686                 if (s.length < 20) { strs.push(s); }
38687                 return o;
38688             }));
38689
38690             placeholders = strs.length > 1 ? strs : keys;
38691             input.attr('placeholder', field.placeholder() ||
38692                 (placeholders.slice(0, 3).join(', ') + '...'));
38693         }
38694     }
38695
38696     function change() {
38697         var value = optString() || clean(input.value());
38698
38699         if (snake_case) {
38700             value = snake(value);
38701         }
38702         if (field.type === 'typeCombo' && !value) {
38703             value = 'yes';
38704         }
38705
38706         var t = {};
38707         t[field.key] = value || undefined;
38708         dispatch.change(t);
38709     }
38710
38711     combo.tags = function(tags) {
38712         var key = tags[field.key],
38713             optstring = optString(),
38714             value = strings[key] || key || '';
38715
38716         if (field.type === 'typeCombo' && value.toLowerCase() === 'yes') {
38717             value = '';
38718         }
38719         if (!optstring && snake_case) {
38720             value = unsnake(value);
38721         }
38722         input.value(value);
38723     };
38724
38725     combo.focus = function() {
38726         input.node().focus();
38727     };
38728
38729     return d3.rebind(combo, dispatch, 'on');
38730 };
38731 iD.ui.preset.cycleway = function(field) {
38732     var dispatch = d3.dispatch('change'),
38733         items;
38734
38735     function cycleway(selection) {
38736         var wrap = selection.selectAll('.preset-input-wrap')
38737             .data([0]);
38738
38739         wrap.enter().append('div')
38740             .attr('class', 'cf preset-input-wrap')
38741             .append('ul');
38742
38743         items = wrap.select('ul').selectAll('li')
38744             .data(field.keys);
38745
38746         // Enter
38747
38748         var enter = items.enter().append('li')
38749             .attr('class', function(d) { return 'cf preset-cycleway-' + d; });
38750
38751         enter.append('span')
38752             .attr('class', 'col6 label preset-label-cycleway')
38753             .attr('for', function(d) { return 'preset-input-cycleway-' + d; })
38754             .text(function(d) { return field.t('types.' + d); });
38755
38756         enter.append('div')
38757             .attr('class', 'col6 preset-input-cycleway-wrap')
38758             .append('input')
38759             .attr('type', 'text')
38760             .attr('class', 'preset-input-cycleway')
38761             .attr('id', function(d) { return 'preset-input-cycleway-' + d; })
38762             .each(function(d) {
38763                 d3.select(this)
38764                     .call(d3.combobox()
38765                         .data(cycleway.options(d)));
38766             });
38767
38768         // Update
38769
38770         wrap.selectAll('.preset-input-cycleway')
38771             .on('change', change)
38772             .on('blur', change);
38773     }
38774
38775     function change() {
38776         var inputs = d3.selectAll('.preset-input-cycleway')[0],
38777             left = d3.select(inputs[0]).value(),
38778             right = d3.select(inputs[1]).value(),
38779             tag = {};
38780         if (left === 'none' || left === '') { left = undefined; }
38781         if (right === 'none' || right === '') { right = undefined; }
38782
38783         // Always set both left and right as changing one can affect the other
38784         tag = {
38785             cycleway: undefined,
38786             'cycleway:left': left,
38787             'cycleway:right': right
38788         };
38789
38790         // If the left and right tags match, use the cycleway tag to tag both
38791         // sides the same way
38792         if (left === right) {
38793             tag = {
38794                 cycleway: left,
38795                 'cycleway:left': undefined,
38796                 'cycleway:right': undefined
38797             };
38798         }
38799
38800         dispatch.change(tag);
38801     }
38802
38803     cycleway.options = function() {
38804         return d3.keys(field.strings.options).map(function(option) {
38805             return {
38806                 title: field.t('options.' + option + '.description'),
38807                 value: option
38808             };
38809         });
38810     };
38811
38812     cycleway.tags = function(tags) {
38813         items.selectAll('.preset-input-cycleway')
38814             .value(function(d) {
38815                 // If cycleway is set, always return that
38816                 if (tags.cycleway) {
38817                     return tags.cycleway;
38818                 }
38819                 return tags[d] || '';
38820             })
38821             .attr('placeholder', field.placeholder());
38822     };
38823
38824     cycleway.focus = function() {
38825         items.selectAll('.preset-input-cycleway')
38826             .node().focus();
38827     };
38828
38829     return d3.rebind(cycleway, dispatch, 'on');
38830 };
38831 iD.ui.preset.text =
38832 iD.ui.preset.number =
38833 iD.ui.preset.tel =
38834 iD.ui.preset.email =
38835 iD.ui.preset.url = function(field) {
38836
38837     var dispatch = d3.dispatch('change'),
38838         input;
38839
38840     function i(selection) {
38841         input = selection.selectAll('input')
38842             .data([0]);
38843
38844         input.enter().append('input')
38845             .attr('type', field.type)
38846             .attr('id', 'preset-input-' + field.id)
38847             .attr('placeholder', field.placeholder() || t('inspector.unknown'));
38848
38849         input
38850             .on('input', change(true))
38851             .on('blur', change())
38852             .on('change', change());
38853
38854         if (field.type === 'number') {
38855             input.attr('type', 'text');
38856
38857             var spinControl = selection.selectAll('.spin-control')
38858                 .data([0]);
38859
38860             var enter = spinControl.enter().append('div')
38861                 .attr('class', 'spin-control');
38862
38863             enter.append('button')
38864                 .datum(1)
38865                 .attr('class', 'increment');
38866
38867             enter.append('button')
38868                 .datum(-1)
38869                 .attr('class', 'decrement');
38870
38871             spinControl.selectAll('button')
38872                 .on('click', function(d) {
38873                     d3.event.preventDefault();
38874                     var num = parseInt(input.node().value || 0, 10);
38875                     if (!isNaN(num)) input.node().value = num + d;
38876                     change()();
38877                 });
38878         }
38879     }
38880
38881     function change(onInput) {
38882         return function() {
38883             var t = {};
38884             t[field.key] = input.value() || undefined;
38885             dispatch.change(t, onInput);
38886         };
38887     }
38888
38889     i.tags = function(tags) {
38890         input.value(tags[field.key] || '');
38891     };
38892
38893     i.focus = function() {
38894         input.node().focus();
38895     };
38896
38897     return d3.rebind(i, dispatch, 'on');
38898 };
38899 iD.ui.preset.localized = function(field, context) {
38900     var dispatch = d3.dispatch('change', 'input'),
38901         wikipedia = iD.services.wikipedia(),
38902         input, localizedInputs, wikiTitles,
38903         entity;
38904
38905     function i(selection) {
38906         input = selection.selectAll('.localized-main')
38907             .data([0]);
38908
38909         input.enter().append('input')
38910             .attr('type', 'text')
38911             .attr('id', 'preset-input-' + field.id)
38912             .attr('class', 'localized-main')
38913             .attr('placeholder', field.placeholder());
38914
38915         if (field.id === 'name') {
38916             var preset = context.presets().match(entity, context.graph());
38917             input.call(d3.combobox().fetcher(
38918                 iD.util.SuggestNames(preset, iD.data.suggestions)
38919             ));
38920         }
38921
38922         input
38923             .on('input', change(true))
38924             .on('blur', change())
38925             .on('change', change());
38926
38927         var translateButton = selection.selectAll('.localized-add')
38928             .data([0]);
38929
38930         translateButton.enter()
38931             .append('button')
38932             .attr('class', 'button-input-action localized-add minor')
38933             .call(iD.svg.Icon('#icon-plus'))
38934             .call(bootstrap.tooltip()
38935                 .title(t('translate.translate'))
38936                 .placement('left'));
38937
38938         translateButton
38939             .on('click', addNew);
38940
38941         localizedInputs = selection.selectAll('.localized-wrap')
38942             .data([0]);
38943
38944         localizedInputs.enter().append('div')
38945             .attr('class', 'localized-wrap');
38946     }
38947
38948     function addNew() {
38949         d3.event.preventDefault();
38950         var data = localizedInputs.selectAll('div.entry').data();
38951         var defaultLang = iD.detect().locale.toLowerCase().split('-')[0];
38952         var langExists = _.find(data, function(datum) { return datum.lang === defaultLang;});
38953         var isLangEn = defaultLang.indexOf('en') > -1;
38954         if (isLangEn || langExists) {
38955           defaultLang = '';
38956         }
38957         data.push({ lang: defaultLang, value: '' });
38958         localizedInputs.call(render, data);
38959     }
38960
38961     function change(onInput) {
38962         return function() {
38963             var t = {};
38964             t[field.key] = d3.select(this).value() || undefined;
38965             dispatch.change(t, onInput);
38966         };
38967     }
38968
38969     function key(lang) { return field.key + ':' + lang; }
38970
38971     function changeLang(d) {
38972         var lang = d3.select(this).value(),
38973             t = {},
38974             language = _.find(iD.data.wikipedia, function(d) {
38975                 return d[0].toLowerCase() === lang.toLowerCase() ||
38976                     d[1].toLowerCase() === lang.toLowerCase();
38977             });
38978
38979         if (language) lang = language[2];
38980
38981         if (d.lang && d.lang !== lang) {
38982             t[key(d.lang)] = undefined;
38983         }
38984
38985         var value = d3.select(this.parentNode)
38986             .selectAll('.localized-value')
38987             .value();
38988
38989         if (lang && value) {
38990             t[key(lang)] = value;
38991         } else if (lang && wikiTitles && wikiTitles[d.lang]) {
38992             t[key(lang)] = wikiTitles[d.lang];
38993         }
38994
38995         d.lang = lang;
38996         dispatch.change(t);
38997     }
38998
38999     function changeValue(d) {
39000         if (!d.lang) return;
39001         var t = {};
39002         t[key(d.lang)] = d3.select(this).value() || undefined;
39003         dispatch.change(t);
39004     }
39005
39006     function fetcher(value, cb) {
39007         var v = value.toLowerCase();
39008
39009         cb(iD.data.wikipedia.filter(function(d) {
39010             return d[0].toLowerCase().indexOf(v) >= 0 ||
39011             d[1].toLowerCase().indexOf(v) >= 0 ||
39012             d[2].toLowerCase().indexOf(v) >= 0;
39013         }).map(function(d) {
39014             return { value: d[1] };
39015         }));
39016     }
39017
39018     function render(selection, data) {
39019         var wraps = selection.selectAll('div.entry').
39020             data(data, function(d) { return d.lang; });
39021
39022         var innerWrap = wraps.enter()
39023             .insert('div', ':first-child');
39024
39025         innerWrap.attr('class', 'entry')
39026             .each(function() {
39027                 var wrap = d3.select(this);
39028                 var langcombo = d3.combobox().fetcher(fetcher);
39029
39030                 var label = wrap.append('label')
39031                     .attr('class','form-label')
39032                     .text(t('translate.localized_translation_label'))
39033                     .attr('for','localized-lang');
39034
39035                 label.append('button')
39036                     .attr('class', 'minor remove')
39037                     .on('click', function(d){
39038                         d3.event.preventDefault();
39039                         var t = {};
39040                         t[key(d.lang)] = undefined;
39041                         dispatch.change(t);
39042                         d3.select(this.parentNode.parentNode)
39043                             .style('top','0')
39044                             .style('max-height','240px')
39045                             .transition()
39046                             .style('opacity', '0')
39047                             .style('max-height','0px')
39048                             .remove();
39049                     })
39050                     .call(iD.svg.Icon('#operation-delete'));
39051
39052                 wrap.append('input')
39053                     .attr('class', 'localized-lang')
39054                     .attr('type', 'text')
39055                     .attr('placeholder',t('translate.localized_translation_language'))
39056                     .on('blur', changeLang)
39057                     .on('change', changeLang)
39058                     .call(langcombo);
39059
39060                 wrap.append('input')
39061                     .on('blur', changeValue)
39062                     .on('change', changeValue)
39063                     .attr('type', 'text')
39064                     .attr('placeholder', t('translate.localized_translation_name'))
39065                     .attr('class', 'localized-value');
39066             });
39067
39068         innerWrap
39069             .style('margin-top', '0px')
39070             .style('max-height', '0px')
39071             .style('opacity', '0')
39072             .transition()
39073             .duration(200)
39074             .style('margin-top', '10px')
39075             .style('max-height', '240px')
39076             .style('opacity', '1')
39077             .each('end', function() {
39078                 d3.select(this)
39079                     .style('max-height', '')
39080                     .style('overflow', 'visible');
39081             });
39082
39083         wraps.exit()
39084             .transition()
39085             .duration(200)
39086             .style('max-height','0px')
39087             .style('opacity', '0')
39088             .style('top','-10px')
39089             .remove();
39090
39091         var entry = selection.selectAll('.entry');
39092
39093         entry.select('.localized-lang')
39094             .value(function(d) {
39095                 var lang = _.find(iD.data.wikipedia, function(lang) { return lang[2] === d.lang; });
39096                 return lang ? lang[1] : d.lang;
39097             });
39098
39099         entry.select('.localized-value')
39100             .value(function(d) { return d.value; });
39101     }
39102
39103     i.tags = function(tags) {
39104         // Fetch translations from wikipedia
39105         if (tags.wikipedia && !wikiTitles) {
39106             wikiTitles = {};
39107             var wm = tags.wikipedia.match(/([^:]+):(.+)/);
39108             if (wm && wm[0] && wm[1]) {
39109                 wikipedia.translations(wm[1], wm[2], function(d) {
39110                     wikiTitles = d;
39111                 });
39112             }
39113         }
39114
39115         input.value(tags[field.key] || '');
39116
39117         var postfixed = [], k, m;
39118         for (k in tags) {
39119             m = k.match(/^(.*):([a-zA-Z_-]+)$/);
39120             if (m && m[1] === field.key && m[2]) {
39121                 postfixed.push({ lang: m[2], value: tags[k] });
39122             }
39123         }
39124
39125         localizedInputs.call(render, postfixed.reverse());
39126     };
39127
39128     i.focus = function() {
39129         input.node().focus();
39130     };
39131
39132     i.entity = function(_) {
39133         entity = _;
39134     };
39135
39136     return d3.rebind(i, dispatch, 'on');
39137 };
39138 iD.ui.preset.maxspeed = function(field, context) {
39139     var dispatch = d3.dispatch('change'),
39140         entity,
39141         imperial,
39142         unitInput,
39143         combobox,
39144         input;
39145
39146     var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
39147         imperialValues = [20, 25, 30, 35, 40, 45, 50, 55, 65, 70];
39148
39149     function maxspeed(selection) {
39150         combobox = d3.combobox();
39151         var unitCombobox = d3.combobox().data(['km/h', 'mph'].map(comboValues));
39152
39153         input = selection.selectAll('#preset-input-' + field.id)
39154             .data([0]);
39155
39156         input.enter().append('input')
39157             .attr('type', 'text')
39158             .attr('id', 'preset-input-' + field.id)
39159             .attr('placeholder', field.placeholder());
39160
39161         input
39162             .call(combobox)
39163             .on('change', change)
39164             .on('blur', change);
39165
39166         var childNodes = context.graph().childNodes(context.entity(entity.id)),
39167             loc = childNodes[~~(childNodes.length/2)].loc;
39168
39169         imperial = _.any(iD.data.imperial.features, function(f) {
39170             return _.any(f.geometry.coordinates, function(d) {
39171                 return iD.geo.pointInPolygon(loc, d[0]);
39172             });
39173         });
39174
39175         unitInput = selection.selectAll('input.maxspeed-unit')
39176             .data([0]);
39177
39178         unitInput.enter().append('input')
39179             .attr('type', 'text')
39180             .attr('class', 'maxspeed-unit');
39181
39182         unitInput
39183             .on('blur', changeUnits)
39184             .on('change', changeUnits)
39185             .call(unitCombobox);
39186
39187         function changeUnits() {
39188             imperial = unitInput.value() === 'mph';
39189             unitInput.value(imperial ? 'mph' : 'km/h');
39190             setSuggestions();
39191             change();
39192         }
39193
39194     }
39195
39196     function setSuggestions() {
39197         combobox.data((imperial ? imperialValues : metricValues).map(comboValues));
39198         unitInput.value(imperial ? 'mph' : 'km/h');
39199     }
39200
39201     function comboValues(d) {
39202         return {
39203             value: d.toString(),
39204             title: d.toString()
39205         };
39206     }
39207
39208     function change() {
39209         var tag = {},
39210             value = input.value();
39211
39212         if (!value) {
39213             tag[field.key] = undefined;
39214         } else if (isNaN(value) || !imperial) {
39215             tag[field.key] = value;
39216         } else {
39217             tag[field.key] = value + ' mph';
39218         }
39219
39220         dispatch.change(tag);
39221     }
39222
39223     maxspeed.tags = function(tags) {
39224         var value = tags[field.key];
39225
39226         if (value && value.indexOf('mph') >= 0) {
39227             value = parseInt(value, 10);
39228             imperial = true;
39229         } else if (value) {
39230             imperial = false;
39231         }
39232
39233         setSuggestions();
39234
39235         input.value(value || '');
39236     };
39237
39238     maxspeed.focus = function() {
39239         input.node().focus();
39240     };
39241
39242     maxspeed.entity = function(_) {
39243         entity = _;
39244     };
39245
39246     return d3.rebind(maxspeed, dispatch, 'on');
39247 };
39248 iD.ui.preset.radio = function(field) {
39249     var dispatch = d3.dispatch('change'),
39250         labels, radios, placeholder;
39251
39252     function radio(selection) {
39253         selection.classed('preset-radio', true);
39254
39255         var wrap = selection.selectAll('.preset-input-wrap')
39256             .data([0]);
39257
39258         var buttonWrap = wrap.enter().append('div')
39259             .attr('class', 'preset-input-wrap toggle-list');
39260
39261         buttonWrap.append('span')
39262             .attr('class', 'placeholder');
39263
39264         placeholder = selection.selectAll('.placeholder');
39265
39266         labels = wrap.selectAll('label')
39267             .data(field.options || field.keys);
39268
39269         var enter = labels.enter().append('label');
39270
39271         enter.append('input')
39272             .attr('type', 'radio')
39273             .attr('name', field.id)
39274             .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
39275             .attr('checked', false);
39276
39277         enter.append('span')
39278             .text(function(d) { return field.t('options.' + d, { 'default': d }); });
39279
39280         radios = labels.selectAll('input')
39281             .on('change', change);
39282     }
39283
39284     function change() {
39285         var t = {};
39286         if (field.key) t[field.key] = undefined;
39287         radios.each(function(d) {
39288             var active = d3.select(this).property('checked');
39289             if (field.key) {
39290                 if (active) t[field.key] = d;
39291             } else {
39292                 t[d] = active ? 'yes' : undefined;
39293             }
39294         });
39295         dispatch.change(t);
39296     }
39297
39298     radio.tags = function(tags) {
39299         function checked(d) {
39300             if (field.key) {
39301                 return tags[field.key] === d;
39302             } else {
39303                 return !!(tags[d] && tags[d] !== 'no');
39304             }
39305         }
39306
39307         labels.classed('active', checked);
39308         radios.property('checked', checked);
39309         var selection = radios.filter(function() { return this.checked; });
39310         if (selection.empty()) {
39311             placeholder.text(t('inspector.none'));
39312         } else {
39313             placeholder.text(selection.attr('value'));
39314         }
39315     };
39316
39317     radio.focus = function() {
39318         radios.node().focus();
39319     };
39320
39321     return d3.rebind(radio, dispatch, 'on');
39322 };
39323 iD.ui.preset.restrictions = function(field, context) {
39324     var dispatch = d3.dispatch('change'),
39325         hover = iD.behavior.Hover(context),
39326         vertexID,
39327         fromNodeID;
39328
39329
39330     function restrictions(selection) {
39331         // if form field is hidden or has detached from dom, clean up.
39332         if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
39333             selection.call(restrictions.off);
39334             return;
39335         }
39336
39337         var wrap = selection.selectAll('.preset-input-wrap')
39338             .data([0]);
39339
39340         var enter = wrap.enter()
39341             .append('div')
39342             .attr('class', 'preset-input-wrap');
39343
39344         enter
39345             .append('div')
39346             .attr('class', 'restriction-help');
39347
39348
39349         var intersection = iD.geo.Intersection(context.graph(), vertexID),
39350             graph = intersection.graph,
39351             vertex = graph.entity(vertexID),
39352             filter = d3.functor(true),
39353             extent = iD.geo.Extent(),
39354             projection = iD.geo.RawMercator();
39355
39356         var d = wrap.dimensions(),
39357             c = [d[0] / 2, d[1] / 2],
39358             z = 24;
39359
39360         projection
39361             .scale(256 * Math.pow(2, z) / (2 * Math.PI));
39362
39363         var s = projection(vertex.loc);
39364
39365         projection
39366             .translate([c[0] - s[0], c[1] - s[1]])
39367             .clipExtent([[0, 0], d]);
39368
39369         var drawLayers = iD.svg.Layers(projection, context).only('osm').dimensions(d),
39370             drawVertices = iD.svg.Vertices(projection, context),
39371             drawLines = iD.svg.Lines(projection, context),
39372             drawTurns = iD.svg.Turns(projection, context);
39373
39374         enter
39375             .call(drawLayers)
39376             .selectAll('.surface')
39377             .call(hover);
39378
39379
39380         var surface = wrap.selectAll('.surface');
39381
39382         surface
39383             .dimensions(d)
39384             .call(drawVertices, graph, [vertex], filter, extent, z)
39385             .call(drawLines, graph, intersection.ways, filter)
39386             .call(drawTurns, graph, intersection.turns(fromNodeID));
39387
39388         surface
39389             .on('click.restrictions', click)
39390             .on('mouseover.restrictions', mouseover)
39391             .on('mouseout.restrictions', mouseout);
39392
39393         surface
39394             .selectAll('.selected')
39395             .classed('selected', false);
39396
39397         if (fromNodeID) {
39398             surface
39399                 .selectAll('.' + intersection.highways[fromNodeID].id)
39400                 .classed('selected', true);
39401         }
39402
39403         mouseout();
39404
39405         context.history()
39406             .on('change.restrictions', render);
39407
39408         d3.select(window)
39409             .on('resize.restrictions', function() {
39410                 wrap.dimensions(null);
39411                 render();
39412             });
39413
39414         function click() {
39415             var datum = d3.event.target.__data__;
39416             if (datum instanceof iD.Entity) {
39417                 fromNodeID = intersection.adjacentNodeId(datum.id);
39418                 render();
39419             } else if (datum instanceof iD.geo.Turn) {
39420                 if (datum.restriction) {
39421                     context.perform(
39422                         iD.actions.UnrestrictTurn(datum, projection),
39423                         t('operations.restriction.annotation.delete'));
39424                 } else {
39425                     context.perform(
39426                         iD.actions.RestrictTurn(datum, projection),
39427                         t('operations.restriction.annotation.create'));
39428                 }
39429             }
39430         }
39431
39432         function mouseover() {
39433             var datum = d3.event.target.__data__;
39434             if (datum instanceof iD.geo.Turn) {
39435                 var graph = context.graph(),
39436                     presets = context.presets(),
39437                     preset;
39438
39439                 if (datum.restriction) {
39440                     preset = presets.match(graph.entity(datum.restriction), graph);
39441                 } else {
39442                     preset = presets.item('type/restriction/' +
39443                         iD.geo.inferRestriction(
39444                             graph,
39445                             datum.from,
39446                             datum.via,
39447                             datum.to,
39448                             projection));
39449                 }
39450
39451                 wrap.selectAll('.restriction-help')
39452                     .text(t('operations.restriction.help.' +
39453                         (datum.restriction ? 'toggle_off' : 'toggle_on'),
39454                         {restriction: preset.name()}));
39455             }
39456         }
39457
39458         function mouseout() {
39459             wrap.selectAll('.restriction-help')
39460                 .text(t('operations.restriction.help.' +
39461                     (fromNodeID ? 'toggle' : 'select')));
39462         }
39463
39464         function render() {
39465             if (context.hasEntity(vertexID)) {
39466                 restrictions(selection);
39467             }
39468         }
39469     }
39470
39471     restrictions.entity = function(_) {
39472         if (!vertexID || vertexID !== _.id) {
39473             fromNodeID = null;
39474             vertexID = _.id;
39475         }
39476     };
39477
39478     restrictions.tags = function() {};
39479     restrictions.focus = function() {};
39480
39481     restrictions.off = function(selection) {
39482         selection.selectAll('.surface')
39483             .call(hover.off)
39484             .on('click.restrictions', null)
39485             .on('mouseover.restrictions', null)
39486             .on('mouseout.restrictions', null);
39487
39488         context.history()
39489             .on('change.restrictions', null);
39490
39491         d3.select(window)
39492             .on('resize.restrictions', null);
39493     };
39494
39495     return d3.rebind(restrictions, dispatch, 'on');
39496 };
39497 iD.ui.preset.textarea = function(field) {
39498     var dispatch = d3.dispatch('change'),
39499         input;
39500
39501     function i(selection) {
39502         input = selection.selectAll('textarea')
39503             .data([0]);
39504
39505         input.enter().append('textarea')
39506             .attr('id', 'preset-input-' + field.id)
39507             .attr('placeholder', field.placeholder() || t('inspector.unknown'))
39508             .attr('maxlength', 255);
39509
39510         input
39511             .on('input', change(true))
39512             .on('blur', change())
39513             .on('change', change());
39514     }
39515
39516     function change(onInput) {
39517         return function() {
39518             var t = {};
39519             t[field.key] = input.value() || undefined;
39520             dispatch.change(t, onInput);
39521         };
39522     }
39523
39524     i.tags = function(tags) {
39525         input.value(tags[field.key] || '');
39526     };
39527
39528     i.focus = function() {
39529         input.node().focus();
39530     };
39531
39532     return d3.rebind(i, dispatch, 'on');
39533 };
39534 iD.ui.preset.wikipedia = function(field, context) {
39535     var dispatch = d3.dispatch('change'),
39536         wikipedia = iD.services.wikipedia(),
39537         link, entity, lang, title;
39538
39539     function i(selection) {
39540         var langcombo = d3.combobox()
39541             .fetcher(function(value, cb) {
39542                 var v = value.toLowerCase();
39543
39544                 cb(iD.data.wikipedia.filter(function(d) {
39545                     return d[0].toLowerCase().indexOf(v) >= 0 ||
39546                         d[1].toLowerCase().indexOf(v) >= 0 ||
39547                         d[2].toLowerCase().indexOf(v) >= 0;
39548                 }).map(function(d) {
39549                     return { value: d[1] };
39550                 }));
39551             });
39552
39553         var titlecombo = d3.combobox()
39554             .fetcher(function(value, cb) {
39555
39556                 if (!value) value = context.entity(entity.id).tags.name || '';
39557                 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
39558
39559                 searchfn(language()[2], value, function(query, data) {
39560                     cb(data.map(function(d) {
39561                         return { value: d };
39562                     }));
39563                 });
39564             });
39565
39566         lang = selection.selectAll('input.wiki-lang')
39567             .data([0]);
39568
39569         lang.enter().append('input')
39570             .attr('type', 'text')
39571             .attr('class', 'wiki-lang')
39572             .attr('placeholder', t('translate.localized_translation_language'))
39573             .value('English');
39574
39575         lang
39576             .call(langcombo)
39577             .on('blur', changeLang)
39578             .on('change', changeLang);
39579
39580         title = selection.selectAll('input.wiki-title')
39581             .data([0]);
39582
39583         title.enter().append('input')
39584             .attr('type', 'text')
39585             .attr('class', 'wiki-title')
39586             .attr('id', 'preset-input-' + field.id);
39587
39588         title
39589             .call(titlecombo)
39590             .on('blur', change)
39591             .on('change', change);
39592
39593         link = selection.selectAll('a.wiki-link')
39594             .data([0]);
39595
39596         link.enter().append('a')
39597             .attr('class', 'wiki-link button-input-action minor')
39598             .attr('target', '_blank')
39599             .call(iD.svg.Icon('#icon-out-link', 'inline'));
39600     }
39601
39602     function language() {
39603         var value = lang.value().toLowerCase();
39604         var locale = iD.detect().locale.toLowerCase();
39605         var localeLanguage;
39606         return _.find(iD.data.wikipedia, function(d) {
39607             if (d[2] === locale) localeLanguage = d;
39608             return d[0].toLowerCase() === value ||
39609                 d[1].toLowerCase() === value ||
39610                 d[2] === value;
39611         }) || localeLanguage || ['English', 'English', 'en'];
39612     }
39613
39614     function changeLang() {
39615         lang.value(language()[1]);
39616         change();
39617     }
39618
39619     function change() {
39620         var value = title.value(),
39621             m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/),
39622             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
39623             anchor;
39624
39625         if (l) {
39626             // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
39627             value = decodeURIComponent(m[2]).replace(/_/g, ' ');
39628             if (m[3]) {
39629                 try {
39630                     // Best-effort `anchordecode:` implementation
39631                     anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
39632                 } catch (e) {
39633                     anchor = decodeURIComponent(m[3]);
39634                 }
39635                 value += '#' + anchor.replace(/_/g, ' ');
39636             }
39637             value = value.slice(0, 1).toUpperCase() + value.slice(1);
39638             lang.value(l[1]);
39639             title.value(value);
39640         }
39641
39642         var t = {};
39643         t[field.key] = value ? language()[2] + ':' + value : undefined;
39644         dispatch.change(t);
39645     }
39646
39647     i.tags = function(tags) {
39648         var value = tags[field.key] || '',
39649             m = value.match(/([^:]+):([^#]+)(?:#(.+))?/),
39650             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
39651             anchor = m && m[3];
39652
39653         // value in correct format
39654         if (l) {
39655             lang.value(l[1]);
39656             title.value(m[2] + (anchor ? ('#' + anchor) : ''));
39657             if (anchor) {
39658                 try {
39659                     // Best-effort `anchorencode:` implementation
39660                     anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
39661                 } catch (e) {
39662                     anchor = anchor.replace(/ /g, '_');
39663                 }
39664             }
39665             link.attr('href', 'https://' + m[1] + '.wikipedia.org/wiki/' +
39666                       m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : ''));
39667
39668         // unrecognized value format
39669         } else {
39670             title.value(value);
39671             if (value && value !== '') {
39672                 lang.value('');
39673             }
39674             link.attr('href', 'https://en.wikipedia.org/wiki/Special:Search?search=' + value);
39675         }
39676     };
39677
39678     i.entity = function(_) {
39679         entity = _;
39680     };
39681
39682     i.focus = function() {
39683         title.node().focus();
39684     };
39685
39686     return d3.rebind(i, dispatch, 'on');
39687 };
39688 iD.ui.intro.area = function(context, reveal) {
39689     var event = d3.dispatch('done'),
39690         timeout;
39691
39692     var step = {
39693         title: 'intro.areas.title'
39694     };
39695
39696     step.enter = function() {
39697         var playground = [-85.63552, 41.94159],
39698             corner = [-85.63565411045074, 41.9417715536927];
39699         context.map().centerZoom(playground, 19);
39700         reveal('button.add-area',
39701             t('intro.areas.add', { button: iD.ui.intro.icon('#icon-area', 'pre-text') }),
39702             { tooltipClass: 'intro-areas-add' });
39703
39704         context.on('enter.intro', addArea);
39705
39706         function addArea(mode) {
39707             if (mode.id !== 'add-area') return;
39708             context.on('enter.intro', drawArea);
39709
39710             var padding = 120 * Math.pow(2, context.map().zoom() - 19);
39711             var pointBox = iD.ui.intro.pad(corner, padding, context);
39712             reveal(pointBox, t('intro.areas.corner'));
39713
39714             context.map().on('move.intro', function() {
39715                 padding = 120 * Math.pow(2, context.map().zoom() - 19);
39716                 pointBox = iD.ui.intro.pad(corner, padding, context);
39717                 reveal(pointBox, t('intro.areas.corner'), {duration: 0});
39718             });
39719         }
39720
39721         function drawArea(mode) {
39722             if (mode.id !== 'draw-area') return;
39723             context.on('enter.intro', enterSelect);
39724
39725             var padding = 150 * Math.pow(2, context.map().zoom() - 19);
39726             var pointBox = iD.ui.intro.pad(playground, padding, context);
39727             reveal(pointBox, t('intro.areas.place'));
39728
39729             context.map().on('move.intro', function() {
39730                 padding = 150 * Math.pow(2, context.map().zoom() - 19);
39731                 pointBox = iD.ui.intro.pad(playground, padding, context);
39732                 reveal(pointBox, t('intro.areas.place'), {duration: 0});
39733             });
39734         }
39735
39736         function enterSelect(mode) {
39737             if (mode.id !== 'select') return;
39738             context.map().on('move.intro', null);
39739             context.on('enter.intro', null);
39740
39741             timeout = setTimeout(function() {
39742                 reveal('.preset-search-input',
39743                     t('intro.areas.search',
39744                     { name: context.presets().item('leisure/playground').name() }));
39745                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
39746             }, 500);
39747         }
39748
39749         function keySearch() {
39750             var first = d3.select('.preset-list-item:first-child');
39751             if (first.classed('preset-leisure-playground')) {
39752                 reveal(first.select('.preset-list-button').node(), t('intro.areas.choose'));
39753                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
39754                 d3.select('.preset-search-input').on('keyup.intro', null);
39755             }
39756         }
39757
39758         function selectedPreset() {
39759             reveal('.pane',
39760                 t('intro.areas.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39761             context.on('exit.intro', event.done);
39762         }
39763     };
39764
39765     step.exit = function() {
39766         window.clearTimeout(timeout);
39767         context.on('enter.intro', null);
39768         context.on('exit.intro', null);
39769         context.history().on('change.intro', null);
39770         context.map().on('move.intro', null);
39771         d3.select('.preset-search-input').on('keyup.intro', null);
39772     };
39773
39774     return d3.rebind(step, event, 'on');
39775 };
39776 iD.ui.intro.line = function(context, reveal) {
39777     var event = d3.dispatch('done'),
39778         timeouts = [];
39779
39780     var step = {
39781         title: 'intro.lines.title'
39782     };
39783
39784     function timeout(f, t) {
39785         timeouts.push(window.setTimeout(f, t));
39786     }
39787
39788     function eventCancel() {
39789         d3.event.stopPropagation();
39790         d3.event.preventDefault();
39791     }
39792
39793     step.enter = function() {
39794         var centroid = [-85.62830, 41.95699];
39795         var midpoint = [-85.62975395449628, 41.95787501510204];
39796         var start = [-85.6297754121684, 41.95805253325314];
39797         var intersection = [-85.62974496187628, 41.95742515554585];
39798
39799         context.map().centerZoom(start, 18);
39800         reveal('button.add-line',
39801             t('intro.lines.add', { button: iD.ui.intro.icon('#icon-line', 'pre-text') }),
39802             { tooltipClass: 'intro-lines-add' });
39803
39804         context.on('enter.intro', addLine);
39805
39806         function addLine(mode) {
39807             if (mode.id !== 'add-line') return;
39808             context.on('enter.intro', drawLine);
39809
39810             var padding = 150 * Math.pow(2, context.map().zoom() - 18);
39811             var pointBox = iD.ui.intro.pad(start, padding, context);
39812             reveal(pointBox, t('intro.lines.start'));
39813
39814             context.map().on('move.intro', function() {
39815                 padding = 150 * Math.pow(2, context.map().zoom() - 18);
39816                 pointBox = iD.ui.intro.pad(start, padding, context);
39817                 reveal(pointBox, t('intro.lines.start'), {duration: 0});
39818             });
39819         }
39820
39821         function drawLine(mode) {
39822             if (mode.id !== 'draw-line') return;
39823             context.history().on('change.intro', addIntersection);
39824             context.on('enter.intro', retry);
39825
39826             var padding = 300 * Math.pow(2, context.map().zoom() - 19);
39827             var pointBox = iD.ui.intro.pad(midpoint, padding, context);
39828             reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}));
39829
39830             context.map().on('move.intro', function() {
39831                 padding = 300 * Math.pow(2, context.map().zoom() - 19);
39832                 pointBox = iD.ui.intro.pad(midpoint, padding, context);
39833                 reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}), {duration: 0});
39834             });
39835         }
39836
39837         // ended line before creating intersection
39838         function retry(mode) {
39839             if (mode.id !== 'select') return;
39840             var pointBox = iD.ui.intro.pad(intersection, 30, context),
39841                 ids = mode.selectedIDs();
39842             reveal(pointBox, t('intro.lines.restart', {name: t('intro.graph.flower_st')}));
39843             d3.select(window).on('mousedown.intro', eventCancel, true);
39844
39845             timeout(function() {
39846                 context.replace(iD.actions.DeleteMultiple(ids));
39847                 step.exit();
39848                 step.enter();
39849             }, 3000);
39850         }
39851
39852         function addIntersection(changes) {
39853             if ( _.any(changes.created(), function(d) {
39854                 return d.type === 'node' && context.graph().parentWays(d).length > 1;
39855             })) {
39856                 context.history().on('change.intro', null);
39857                 context.on('enter.intro', enterSelect);
39858
39859                 var padding = 900 * Math.pow(2, context.map().zoom() - 19);
39860                 var pointBox = iD.ui.intro.pad(centroid, padding, context);
39861                 reveal(pointBox, t('intro.lines.finish'));
39862
39863                 context.map().on('move.intro', function() {
39864                     padding = 900 * Math.pow(2, context.map().zoom() - 19);
39865                     pointBox = iD.ui.intro.pad(centroid, padding, context);
39866                     reveal(pointBox, t('intro.lines.finish'), {duration: 0});
39867                 });
39868             }
39869         }
39870
39871         function enterSelect(mode) {
39872             if (mode.id !== 'select') return;
39873             context.map().on('move.intro', null);
39874             context.on('enter.intro', null);
39875             d3.select('#curtain').style('pointer-events', 'all');
39876
39877             presetCategory();
39878         }
39879
39880         function presetCategory() {
39881             timeout(function() {
39882                 d3.select('#curtain').style('pointer-events', 'none');
39883                 var road = d3.select('.preset-category-road .preset-list-button');
39884                 reveal(road.node(), t('intro.lines.road'));
39885                 road.one('click.intro', roadCategory);
39886             }, 500);
39887         }
39888
39889         function roadCategory() {
39890             timeout(function() {
39891                 var grid = d3.select('.subgrid');
39892                 reveal(grid.node(), t('intro.lines.residential'));
39893                 grid.selectAll(':not(.preset-highway-residential) .preset-list-button')
39894                     .one('click.intro', retryPreset);
39895                 grid.selectAll('.preset-highway-residential .preset-list-button')
39896                     .one('click.intro', roadDetails);
39897             }, 500);
39898         }
39899
39900         // selected wrong road type
39901         function retryPreset() {
39902             timeout(function() {
39903                 var preset = d3.select('.entity-editor-pane .preset-list-button');
39904                 reveal(preset.node(), t('intro.lines.wrong_preset'));
39905                 preset.one('click.intro', presetCategory);
39906             }, 500);
39907         }
39908
39909         function roadDetails() {
39910             reveal('.pane',
39911                 t('intro.lines.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39912             context.on('exit.intro', event.done);
39913         }
39914
39915     };
39916
39917     step.exit = function() {
39918         d3.select(window).on('mousedown.intro', null, true);
39919         d3.select('#curtain').style('pointer-events', 'none');
39920         timeouts.forEach(window.clearTimeout);
39921         context.on('enter.intro', null);
39922         context.on('exit.intro', null);
39923         context.map().on('move.intro', null);
39924         context.history().on('change.intro', null);
39925     };
39926
39927     return d3.rebind(step, event, 'on');
39928 };
39929 iD.ui.intro.navigation = function(context, reveal) {
39930     var event = d3.dispatch('done'),
39931         timeouts = [];
39932
39933     var step = {
39934         title: 'intro.navigation.title'
39935     };
39936
39937     function set(f, t) {
39938         timeouts.push(window.setTimeout(f, t));
39939     }
39940
39941     function eventCancel() {
39942         d3.event.stopPropagation();
39943         d3.event.preventDefault();
39944     }
39945
39946     step.enter = function() {
39947         var rect = context.surfaceRect(),
39948             map = {
39949                 left: rect.left + 10,
39950                 top: rect.top + 70,
39951                 width: rect.width - 70,
39952                 height: rect.height - 170
39953             };
39954
39955         context.map().centerZoom([-85.63591, 41.94285], 19);
39956
39957         reveal(map, t('intro.navigation.drag'));
39958
39959         context.map().on('move.intro', _.debounce(function() {
39960             context.map().on('move.intro', null);
39961             townhall();
39962             context.on('enter.intro', inspectTownHall);
39963         }, 400));
39964
39965         function townhall() {
39966             var hall = [-85.63645945147184, 41.942986488012565];
39967
39968             var point = context.projection(hall);
39969             if (point[0] < 0 || point[0] > rect.width ||
39970                 point[1] < 0 || point[1] > rect.height) {
39971                 context.map().center(hall);
39972             }
39973
39974             var box = iD.ui.intro.pointBox(hall, context);
39975             reveal(box, t('intro.navigation.select'));
39976
39977             context.map().on('move.intro', function() {
39978                 var box = iD.ui.intro.pointBox(hall, context);
39979                 reveal(box, t('intro.navigation.select'), {duration: 0});
39980             });
39981         }
39982
39983         function inspectTownHall(mode) {
39984             if (mode.id !== 'select') return;
39985             context.on('enter.intro', null);
39986             context.map().on('move.intro', null);
39987             set(function() {
39988                 reveal('.entity-editor-pane',
39989                     t('intro.navigation.pane', { button: iD.ui.intro.icon('#icon-close', 'pre-text') }));
39990                 context.on('exit.intro', streetSearch);
39991             }, 700);
39992         }
39993
39994         function streetSearch() {
39995             context.on('exit.intro', null);
39996             reveal('.search-header input',
39997                 t('intro.navigation.search', { name: t('intro.graph.spring_st') }));
39998             d3.select('.search-header input').on('keyup.intro', searchResult);
39999         }
40000
40001         function searchResult() {
40002             var first = d3.select('.feature-list-item:nth-child(0n+2)'),  // skip No Results item
40003                 firstName = first.select('.entity-name'),
40004                 name = t('intro.graph.spring_st');
40005
40006             if (!firstName.empty() && firstName.text() === name) {
40007                 reveal(first.node(), t('intro.navigation.choose', { name: name }));
40008                 context.on('exit.intro', selectedStreet);
40009                 d3.select('.search-header input')
40010                     .on('keydown.intro', eventCancel, true)
40011                     .on('keyup.intro', null);
40012             }
40013         }
40014
40015         function selectedStreet() {
40016             var springSt = [-85.63585099140167, 41.942506848938926];
40017             context.map().center(springSt);
40018             context.on('exit.intro', event.done);
40019             set(function() {
40020                 reveal('.entity-editor-pane',
40021                     t('intro.navigation.chosen', {
40022                         name: t('intro.graph.spring_st'),
40023                         button: iD.ui.intro.icon('#icon-close', 'pre-text')
40024                     }));
40025             }, 400);
40026         }
40027     };
40028
40029     step.exit = function() {
40030         timeouts.forEach(window.clearTimeout);
40031         context.map().on('move.intro', null);
40032         context.on('enter.intro', null);
40033         context.on('exit.intro', null);
40034         d3.select('.search-header input')
40035             .on('keydown.intro', null)
40036             .on('keyup.intro', null);
40037     };
40038
40039     return d3.rebind(step, event, 'on');
40040 };
40041 iD.ui.intro.point = function(context, reveal) {
40042     var event = d3.dispatch('done'),
40043         timeouts = [];
40044
40045     var step = {
40046         title: 'intro.points.title'
40047     };
40048
40049     function setTimeout(f, t) {
40050         timeouts.push(window.setTimeout(f, t));
40051     }
40052
40053     function eventCancel() {
40054         d3.event.stopPropagation();
40055         d3.event.preventDefault();
40056     }
40057
40058     step.enter = function() {
40059         context.map().centerZoom([-85.63279, 41.94394], 19);
40060         reveal('button.add-point',
40061             t('intro.points.add', { button: iD.ui.intro.icon('#icon-point', 'pre-text') }),
40062             { tooltipClass: 'intro-points-add' });
40063
40064         var corner = [-85.632481,41.944094];
40065
40066         context.on('enter.intro', addPoint);
40067
40068         function addPoint(mode) {
40069             if (mode.id !== 'add-point') return;
40070             context.on('enter.intro', enterSelect);
40071
40072             var pointBox = iD.ui.intro.pad(corner, 150, context);
40073             reveal(pointBox, t('intro.points.place'));
40074
40075             context.map().on('move.intro', function() {
40076                 pointBox = iD.ui.intro.pad(corner, 150, context);
40077                 reveal(pointBox, t('intro.points.place'), {duration: 0});
40078             });
40079         }
40080
40081         function enterSelect(mode) {
40082             if (mode.id !== 'select') return;
40083             context.map().on('move.intro', null);
40084             context.on('enter.intro', null);
40085
40086             setTimeout(function() {
40087                 reveal('.preset-search-input',
40088                     t('intro.points.search', {name: context.presets().item('amenity/cafe').name()}));
40089                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
40090             }, 500);
40091         }
40092
40093         function keySearch() {
40094             var first = d3.select('.preset-list-item:first-child');
40095             if (first.classed('preset-amenity-cafe')) {
40096                 reveal(first.select('.preset-list-button').node(), t('intro.points.choose'));
40097                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
40098                 d3.select('.preset-search-input')
40099                     .on('keydown.intro', eventCancel, true)
40100                     .on('keyup.intro', null);
40101             }
40102         }
40103
40104         function selectedPreset() {
40105             setTimeout(function() {
40106                 reveal('.entity-editor-pane', t('intro.points.describe'), {tooltipClass: 'intro-points-describe'});
40107                 context.history().on('change.intro', closeEditor);
40108                 context.on('exit.intro', selectPoint);
40109             }, 400);
40110         }
40111
40112         function closeEditor() {
40113             d3.select('.preset-search-input').on('keydown.intro', null);
40114             context.history().on('change.intro', null);
40115             reveal('.entity-editor-pane',
40116                 t('intro.points.close', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
40117         }
40118
40119         function selectPoint() {
40120             context.on('exit.intro', null);
40121             context.history().on('change.intro', null);
40122             context.on('enter.intro', enterReselect);
40123
40124             var pointBox = iD.ui.intro.pad(corner, 150, context);
40125             reveal(pointBox, t('intro.points.reselect'));
40126
40127             context.map().on('move.intro', function() {
40128                 pointBox = iD.ui.intro.pad(corner, 150, context);
40129                 reveal(pointBox, t('intro.points.reselect'), {duration: 0});
40130             });
40131         }
40132
40133         function enterReselect(mode) {
40134             if (mode.id !== 'select') return;
40135             context.map().on('move.intro', null);
40136             context.on('enter.intro', null);
40137
40138             setTimeout(function() {
40139                 reveal('.entity-editor-pane',
40140                     t('intro.points.fixname', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
40141                 context.on('exit.intro', deletePoint);
40142             }, 500);
40143         }
40144
40145         function deletePoint() {
40146             context.on('exit.intro', null);
40147             context.on('enter.intro', enterDelete);
40148
40149             var pointBox = iD.ui.intro.pad(corner, 150, context);
40150             reveal(pointBox, t('intro.points.reselect_delete'));
40151
40152             context.map().on('move.intro', function() {
40153                 pointBox = iD.ui.intro.pad(corner, 150, context);
40154                 reveal(pointBox, t('intro.points.reselect_delete'), {duration: 0});
40155             });
40156         }
40157
40158         function enterDelete(mode) {
40159             if (mode.id !== 'select') return;
40160             context.map().on('move.intro', null);
40161             context.on('enter.intro', null);
40162             context.on('exit.intro', deletePoint);
40163             context.map().on('move.intro', deletePoint);
40164             context.history().on('change.intro', deleted);
40165
40166             setTimeout(function() {
40167                 var node = d3.select('.radial-menu-item-delete').node();
40168                 var pointBox = iD.ui.intro.pad(node.getBoundingClientRect(), 50, context);
40169                 reveal(pointBox,
40170                     t('intro.points.delete', { button: iD.ui.intro.icon('#operation-delete', 'pre-text') }));
40171             }, 300);
40172         }
40173
40174         function deleted(changed) {
40175             if (changed.deleted().length) event.done();
40176         }
40177
40178     };
40179
40180     step.exit = function() {
40181         timeouts.forEach(window.clearTimeout);
40182         context.on('exit.intro', null);
40183         context.on('enter.intro', null);
40184         context.map().on('move.intro', null);
40185         context.history().on('change.intro', null);
40186         d3.select('.preset-search-input')
40187             .on('keyup.intro', null)
40188             .on('keydown.intro', null);
40189     };
40190
40191     return d3.rebind(step, event, 'on');
40192 };
40193 iD.ui.intro.startEditing = function(context, reveal) {
40194     var event = d3.dispatch('done', 'startEditing'),
40195         modal,
40196         timeouts = [];
40197
40198     var step = {
40199         title: 'intro.startediting.title'
40200     };
40201
40202     function timeout(f, t) {
40203         timeouts.push(window.setTimeout(f, t));
40204     }
40205
40206     step.enter = function() {
40207         reveal('.map-control.help-control',
40208             t('intro.startediting.help', { button: iD.ui.intro.icon('#icon-help', 'pre-text') }));
40209
40210         timeout(function() {
40211             reveal('#bar button.save', t('intro.startediting.save'));
40212         }, 5000);
40213
40214         timeout(function() {
40215             reveal('#surface');
40216         }, 10000);
40217
40218         timeout(function() {
40219             modal = iD.ui.modal(context.container());
40220
40221             modal.select('.modal')
40222                 .attr('class', 'modal-splash modal col6');
40223
40224             modal.selectAll('.close').remove();
40225
40226             var startbutton = modal.select('.content')
40227                 .attr('class', 'fillL')
40228                     .append('button')
40229                         .attr('class', 'modal-section huge-modal-button')
40230                         .on('click', function() {
40231                             modal.remove();
40232                         });
40233
40234                 startbutton.append('div')
40235                     .attr('class','illustration');
40236                 startbutton.append('h2')
40237                     .text(t('intro.startediting.start'));
40238
40239             event.startEditing();
40240         }, 10500);
40241     };
40242
40243     step.exit = function() {
40244         if (modal) modal.remove();
40245         timeouts.forEach(window.clearTimeout);
40246     };
40247
40248     return d3.rebind(step, event, 'on');
40249 };
40250 iD.presets = function() {
40251
40252     // an iD.presets.Collection with methods for
40253     // loading new data and returning defaults
40254
40255     var all = iD.presets.Collection([]),
40256         defaults = { area: all, line: all, point: all, vertex: all, relation: all },
40257         fields = {},
40258         universal = [],
40259         recent = iD.presets.Collection([]);
40260
40261     // Index of presets by (geometry, tag key).
40262     var index = {
40263         point: {},
40264         vertex: {},
40265         line: {},
40266         area: {},
40267         relation: {}
40268     };
40269
40270     all.match = function(entity, resolver) {
40271         var geometry = entity.geometry(resolver),
40272             geometryMatches = index[geometry],
40273             best = -1,
40274             match;
40275
40276         for (var k in entity.tags) {
40277             var keyMatches = geometryMatches[k];
40278             if (!keyMatches) continue;
40279
40280             for (var i = 0; i < keyMatches.length; i++) {
40281                 var score = keyMatches[i].matchScore(entity);
40282                 if (score > best) {
40283                     best = score;
40284                     match = keyMatches[i];
40285                 }
40286             }
40287         }
40288
40289         return match || all.item(geometry);
40290     };
40291
40292     // Because of the open nature of tagging, iD will never have a complete
40293     // list of tags used in OSM, so we want it to have logic like "assume
40294     // that a closed way with an amenity tag is an area, unless the amenity
40295     // is one of these specific types". This function computes a structure
40296     // that allows testing of such conditions, based on the presets designated
40297     // as as supporting (or not supporting) the area geometry.
40298     //
40299     // The returned object L is a whitelist/blacklist of tags. A closed way
40300     // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
40301     // (see `iD.Way#isArea()`). In other words, the keys of L form the whitelist,
40302     // and the subkeys form the blacklist.
40303     all.areaKeys = function() {
40304         var areaKeys = {},
40305             ignore = ['barrier', 'highway', 'footway', 'railway', 'type'],
40306             presets = _.reject(all.collection, 'suggestion');
40307
40308         // whitelist
40309         presets.forEach(function(d) {
40310             for (var key in d.tags) break;
40311             if (!key) return;
40312             if (ignore.indexOf(key) !== -1) return;
40313
40314             if (d.geometry.indexOf('area') !== -1) {
40315                 areaKeys[key] = areaKeys[key] || {};
40316             }
40317         });
40318
40319         // blacklist
40320         presets.forEach(function(d) {
40321             for (var key in d.tags) break;
40322             if (!key) return;
40323             if (ignore.indexOf(key) !== -1) return;
40324
40325             var value = d.tags[key];
40326             if (d.geometry.indexOf('area') === -1 &&
40327                 d.geometry.indexOf('line') !== -1 &&
40328                 key in areaKeys && value !== '*') {
40329                 areaKeys[key][value] = true;
40330             }
40331         });
40332
40333         return areaKeys;
40334     };
40335
40336     all.load = function(d) {
40337
40338         if (d.fields) {
40339             _.forEach(d.fields, function(d, id) {
40340                 fields[id] = iD.presets.Field(id, d);
40341                 if (d.universal) universal.push(fields[id]);
40342             });
40343         }
40344
40345         if (d.presets) {
40346             _.forEach(d.presets, function(d, id) {
40347                 all.collection.push(iD.presets.Preset(id, d, fields));
40348             });
40349         }
40350
40351         if (d.categories) {
40352             _.forEach(d.categories, function(d, id) {
40353                 all.collection.push(iD.presets.Category(id, d, all));
40354             });
40355         }
40356
40357         if (d.defaults) {
40358             var getItem = _.bind(all.item, all);
40359             defaults = {
40360                 area: iD.presets.Collection(d.defaults.area.map(getItem)),
40361                 line: iD.presets.Collection(d.defaults.line.map(getItem)),
40362                 point: iD.presets.Collection(d.defaults.point.map(getItem)),
40363                 vertex: iD.presets.Collection(d.defaults.vertex.map(getItem)),
40364                 relation: iD.presets.Collection(d.defaults.relation.map(getItem))
40365             };
40366         }
40367
40368         for (var i = 0; i < all.collection.length; i++) {
40369             var preset = all.collection[i],
40370                 geometry = preset.geometry;
40371
40372             for (var j = 0; j < geometry.length; j++) {
40373                 var g = index[geometry[j]];
40374                 for (var k in preset.tags) {
40375                     (g[k] = g[k] || []).push(preset);
40376                 }
40377             }
40378         }
40379
40380         return all;
40381     };
40382
40383     all.field = function(id) {
40384         return fields[id];
40385     };
40386
40387     all.universal = function() {
40388         return universal;
40389     };
40390
40391     all.defaults = function(geometry, n) {
40392         var rec = recent.matchGeometry(geometry).collection.slice(0, 4),
40393             def = _.uniq(rec.concat(defaults[geometry].collection)).slice(0, n - 1);
40394         return iD.presets.Collection(_.unique(rec.concat(def).concat(all.item(geometry))));
40395     };
40396
40397     all.choose = function(preset) {
40398         if (!preset.isFallback()) {
40399             recent = iD.presets.Collection(_.unique([preset].concat(recent.collection)));
40400         }
40401         return all;
40402     };
40403
40404     return all;
40405 };
40406 iD.presets.Category = function(id, category, all) {
40407     category = _.clone(category);
40408
40409     category.id = id;
40410
40411     category.members = iD.presets.Collection(category.members.map(function(id) {
40412         return all.item(id);
40413     }));
40414
40415     category.matchGeometry = function(geometry) {
40416         return category.geometry.indexOf(geometry) >= 0;
40417     };
40418
40419     category.matchScore = function() { return -1; };
40420
40421     category.name = function() {
40422         return t('presets.categories.' + id + '.name', {'default': id});
40423     };
40424
40425     category.terms = function() {
40426         return [];
40427     };
40428
40429     return category;
40430 };
40431 iD.presets.Collection = function(collection) {
40432
40433     var maxSearchResults = 50,
40434         maxSuggestionResults = 10;
40435
40436     var presets = {
40437
40438         collection: collection,
40439
40440         item: function(id) {
40441             return _.find(collection, function(d) {
40442                 return d.id === id;
40443             });
40444         },
40445
40446         matchGeometry: function(geometry) {
40447             return iD.presets.Collection(collection.filter(function(d) {
40448                 return d.matchGeometry(geometry);
40449             }));
40450         },
40451
40452         search: function(value, geometry) {
40453             if (!value) return this;
40454
40455             value = value.toLowerCase();
40456
40457             var searchable = _.filter(collection, function(a) {
40458                     return a.searchable !== false && a.suggestion !== true;
40459                 }),
40460                 suggestions = _.filter(collection, function(a) {
40461                     return a.suggestion === true;
40462                 });
40463
40464             function leading(a) {
40465                 var index = a.indexOf(value);
40466                 return index === 0 || a[index - 1] === ' ';
40467             }
40468
40469             // matches value to preset.name
40470             var leading_name = _.filter(searchable, function(a) {
40471                     return leading(a.name().toLowerCase());
40472                 }).sort(function(a, b) {
40473                     var i = a.name().toLowerCase().indexOf(value) - b.name().toLowerCase().indexOf(value);
40474                     if (i === 0) return a.name().length - b.name().length;
40475                     else return i;
40476                 });
40477
40478             // matches value to preset.terms values
40479             var leading_terms = _.filter(searchable, function(a) {
40480                     return _.any(a.terms() || [], leading);
40481                 });
40482
40483             // matches value to preset.tags values
40484             var leading_tag_values = _.filter(searchable, function(a) {
40485                     return _.any(_.without(_.values(a.tags || {}), '*'), leading);
40486                 });
40487
40488
40489             // finds close matches to value in preset.name
40490             var levenstein_name = searchable.map(function(a) {
40491                     return {
40492                         preset: a,
40493                         dist: iD.util.editDistance(value, a.name().toLowerCase())
40494                     };
40495                 }).filter(function(a) {
40496                     return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
40497                 }).sort(function(a, b) {
40498                     return a.dist - b.dist;
40499                 }).map(function(a) {
40500                     return a.preset;
40501                 });
40502
40503             // finds close matches to value in preset.terms
40504             var leventstein_terms = _.filter(searchable, function(a) {
40505                     return _.any(a.terms() || [], function(b) {
40506                         return iD.util.editDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
40507                     });
40508                 });
40509
40510             function suggestionName(name) {
40511                 var nameArray = name.split(' - ');
40512                 if (nameArray.length > 1) {
40513                     name = nameArray.slice(0, nameArray.length-1).join(' - ');
40514                 }
40515                 return name.toLowerCase();
40516             }
40517
40518             var leading_suggestions = _.filter(suggestions, function(a) {
40519                     return leading(suggestionName(a.name()));
40520                 }).sort(function(a, b) {
40521                     a = suggestionName(a.name());
40522                     b = suggestionName(b.name());
40523                     var i = a.indexOf(value) - b.indexOf(value);
40524                     if (i === 0) return a.length - b.length;
40525                     else return i;
40526                 });
40527
40528             var leven_suggestions = suggestions.map(function(a) {
40529                     return {
40530                         preset: a,
40531                         dist: iD.util.editDistance(value, suggestionName(a.name()))
40532                     };
40533                 }).filter(function(a) {
40534                     return a.dist + Math.min(value.length - suggestionName(a.preset.name()).length, 0) < 1;
40535                 }).sort(function(a, b) {
40536                     return a.dist - b.dist;
40537                 }).map(function(a) {
40538                     return a.preset;
40539                 });
40540
40541             var other = presets.item(geometry);
40542
40543             var results = leading_name.concat(
40544                             leading_terms,
40545                             leading_tag_values,
40546                             leading_suggestions.slice(0, maxSuggestionResults+5),
40547                             levenstein_name,
40548                             leventstein_terms,
40549                             leven_suggestions.slice(0, maxSuggestionResults)
40550                         ).slice(0, maxSearchResults-1);
40551
40552             return iD.presets.Collection(_.unique(
40553                     results.concat(other)
40554                 ));
40555         }
40556     };
40557
40558     return presets;
40559 };
40560 iD.presets.Field = function(id, field) {
40561     field = _.clone(field);
40562
40563     field.id = id;
40564
40565     field.matchGeometry = function(geometry) {
40566         return !field.geometry || field.geometry === geometry;
40567     };
40568
40569     field.t = function(scope, options) {
40570         return t('presets.fields.' + id + '.' + scope, options);
40571     };
40572
40573     field.label = function() {
40574         return field.t('label', {'default': id});
40575     };
40576
40577     var placeholder = field.placeholder;
40578     field.placeholder = function() {
40579         return field.t('placeholder', {'default': placeholder});
40580     };
40581
40582     return field;
40583 };
40584 iD.presets.Preset = function(id, preset, fields) {
40585     preset = _.clone(preset);
40586
40587     preset.id = id;
40588     preset.fields = (preset.fields || []).map(getFields);
40589     preset.geometry = (preset.geometry || []);
40590
40591     function getFields(f) {
40592         return fields[f];
40593     }
40594
40595     preset.matchGeometry = function(geometry) {
40596         return preset.geometry.indexOf(geometry) >= 0;
40597     };
40598
40599     var matchScore = preset.matchScore || 1;
40600     preset.matchScore = function(entity) {
40601         var tags = preset.tags,
40602             score = 0;
40603
40604         for (var t in tags) {
40605             if (entity.tags[t] === tags[t]) {
40606                 score += matchScore;
40607             } else if (tags[t] === '*' && t in entity.tags) {
40608                 score += matchScore / 2;
40609             } else {
40610                 return -1;
40611             }
40612         }
40613
40614         return score;
40615     };
40616
40617     preset.t = function(scope, options) {
40618         return t('presets.presets.' + id + '.' + scope, options);
40619     };
40620
40621     var name = preset.name;
40622     preset.name = function() {
40623         if (preset.suggestion) {
40624             id = id.split('/');
40625             id = id[0] + '/' + id[1];
40626             return name + ' - ' + t('presets.presets.' + id + '.name');
40627         }
40628         return preset.t('name', {'default': name});
40629     };
40630
40631     preset.terms = function() {
40632         return preset.t('terms', {'default': ''}).toLowerCase().trim().split(/\s*,+\s*/);
40633     };
40634
40635     preset.isFallback = function() {
40636         var tagCount = Object.keys(preset.tags).length;
40637         return tagCount === 0 || (tagCount === 1 && preset.tags.hasOwnProperty('area'));
40638     };
40639
40640     preset.reference = function(geometry) {
40641         var key = Object.keys(preset.tags)[0],
40642             value = preset.tags[key];
40643
40644         if (geometry === 'relation' && key === 'type') {
40645             return { rtype: value };
40646         } else if (value === '*') {
40647             return { key: key };
40648         } else {
40649             return { key: key, value: value };
40650         }
40651     };
40652
40653     var removeTags = preset.removeTags || preset.tags;
40654     preset.removeTags = function(tags, geometry) {
40655         tags = _.omit(tags, _.keys(removeTags));
40656
40657         for (var f in preset.fields) {
40658             var field = preset.fields[f];
40659             if (field.matchGeometry(geometry) && field.default === tags[field.key]) {
40660                 delete tags[field.key];
40661             }
40662         }
40663
40664         delete tags.area;
40665         return tags;
40666     };
40667
40668     var applyTags = preset.addTags || preset.tags;
40669     preset.applyTags = function(tags, geometry) {
40670         var k;
40671
40672         tags = _.clone(tags);
40673
40674         for (k in applyTags) {
40675             if (applyTags[k] === '*') {
40676                 tags[k] = 'yes';
40677             } else {
40678                 tags[k] = applyTags[k];
40679             }
40680         }
40681
40682         // Add area=yes if necessary.
40683         // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
40684         // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
40685         // 2. chosen preset doesn't have a key in areaKeys (`railway=station`)
40686         if (geometry === 'area') {
40687             var needsAreaTag = true;
40688             if (preset.geometry.indexOf('line') === -1) {
40689                 for (k in applyTags) {
40690                     if (k in iD.areaKeys) {
40691                         needsAreaTag = false;
40692                         break;
40693                     }
40694                 }
40695             }
40696             if (needsAreaTag) {
40697                 tags.area = 'yes';
40698             }
40699         }
40700
40701         for (var f in preset.fields) {
40702             var field = preset.fields[f];
40703             if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
40704                 tags[field.key] = field.default;
40705             }
40706         }
40707
40708         return tags;
40709     };
40710
40711     return preset;
40712 };
40713 iD.validations = {};
40714 iD.validations.DeprecatedTag = function() {
40715
40716     var validation = function(changes) {
40717         var warnings = [];
40718         for (var i = 0; i < changes.created.length; i++) {
40719             var change = changes.created[i],
40720                 deprecatedTags = change.deprecatedTags();
40721
40722             if (!_.isEmpty(deprecatedTags)) {
40723                 var tags = iD.util.tagText({ tags: deprecatedTags });
40724                 warnings.push({
40725                     id: 'deprecated_tags',
40726                     message: t('validations.deprecated_tags', { tags: tags }),
40727                     entity: change
40728                 });
40729             }
40730         }
40731         return warnings;
40732     };
40733
40734     return validation;
40735 };
40736 iD.validations.ManyDeletions = function() {
40737     var threshold = 100;
40738
40739     var validation = function(changes) {
40740         var warnings = [];
40741         if (changes.deleted.length > threshold) {
40742             warnings.push({
40743                 id: 'many_deletions',
40744                 message: t('validations.many_deletions', { n: changes.deleted.length })
40745             });
40746         }
40747         return warnings;
40748     };
40749
40750     return validation;
40751 };
40752 iD.validations.MissingTag = function() {
40753
40754     var validation = function(changes, graph) {
40755         var warnings = [];
40756         for (var i = 0; i < changes.created.length; i++) {
40757             var change = changes.created[i],
40758                 geometry = change.geometry(graph);
40759
40760             if ((geometry === 'point' || geometry === 'line' || geometry === 'area') && !change.isUsed(graph)) {
40761                 warnings.push({
40762                     id: 'missing_tag',
40763                     message: t('validations.untagged_' + geometry),
40764                     tooltip: t('validations.untagged_' + geometry + '_tooltip'),
40765                     entity: change
40766                 });
40767             }
40768         }
40769         return warnings;
40770     };
40771
40772     return validation;
40773 };
40774 iD.validations.TagSuggestsArea = function() {
40775
40776     // https://github.com/openstreetmap/josm/blob/mirror/src/org/
40777     // openstreetmap/josm/data/validation/tests/UnclosedWays.java#L80
40778     function tagSuggestsArea(tags) {
40779         if (_.isEmpty(tags)) return false;
40780
40781         var presence = ['landuse', 'amenities', 'tourism', 'shop'];
40782         for (var i = 0; i < presence.length; i++) {
40783             if (tags[presence[i]] !== undefined) {
40784                 return presence[i] + '=' + tags[presence[i]];
40785             }
40786         }
40787
40788         if (tags.building && tags.building === 'yes') return 'building=yes';
40789     }
40790
40791     var validation = function(changes, graph) {
40792         var warnings = [];
40793         for (var i = 0; i < changes.created.length; i++) {
40794             var change = changes.created[i],
40795                 geometry = change.geometry(graph),
40796                 suggestion = (geometry === 'line' ? tagSuggestsArea(change.tags) : undefined);
40797
40798             if (suggestion) {
40799                 warnings.push({
40800                     id: 'tag_suggests_area',
40801                     message: t('validations.tag_suggests_area', { tag: suggestion }),
40802                     entity: change
40803                 });
40804             }
40805         }
40806         return warnings;
40807     };
40808
40809     return validation;
40810 };
40811 })();
40812 window.locale = { _current: 'en' };
40813
40814 locale.current = function(_) {
40815     if (!arguments.length) return locale._current;
40816     if (locale[_] !== undefined) locale._current = _;
40817     else if (locale[_.split('-')[0]]) locale._current = _.split('-')[0];
40818     return locale;
40819 };
40820
40821 function t(s, o, loc) {
40822     loc = loc || locale._current;
40823
40824     var path = s.split(".").reverse(),
40825         rep = locale[loc];
40826
40827     while (rep !== undefined && path.length) rep = rep[path.pop()];
40828
40829     if (rep !== undefined) {
40830         if (o) for (var k in o) rep = rep.replace('{' + k + '}', o[k]);
40831         return rep;
40832     }
40833
40834     if (loc !== 'en') {
40835         return t(s, o, 'en');
40836     }
40837
40838     if (o && 'default' in o) {
40839         return o['default'];
40840     }
40841
40842     var missing = 'Missing ' + loc + ' translation: ' + s;
40843     if (typeof console !== "undefined") console.error(missing);
40844
40845     return missing;
40846 }
40847 iD.introGraph = '{"n185954700":{"id":"n185954700","loc":[-85.642244,41.939081]},"n185964961":{"id":"n185964961","loc":[-85.6406588,41.942601]},"n185964962":{"id":"n185964962","loc":[-85.6394548,41.94261]},"n185970607":{"id":"n185970607","loc":[-85.641094,41.94006]},"n185970614":{"id":"n185970614","loc":[-85.641825,41.941316]},"n185970616":{"id":"n185970616","loc":[-85.641838,41.941556]},"n185973650":{"id":"n185973650","loc":[-85.639918,41.940064]},"n185973660":{"id":"n185973660","loc":[-85.640645,41.941339]},"n185973659":{"id":"n185973659","loc":[-85.6406115,41.9400658]},"n185974479":{"id":"n185974479","loc":[-85.639402,41.941344]},"n185974481":{"id":"n185974481","loc":[-85.643071,41.941288]},"n185976259":{"id":"n185976259","loc":[-85.642213,41.940043]},"n185976261":{"id":"n185976261","loc":[-85.643056,41.94001]},"n185964959":{"id":"n185964959","loc":[-85.6431031,41.9425754]},"n185964960":{"id":"n185964960","loc":[-85.6418749,41.9425864]},"n185981481":{"id":"n185981481","loc":[-85.6386827,41.9400828]},"n185981482":{"id":"n185981482","loc":[-85.6393664,41.9400854]},"n2138493844":{"id":"n2138493844","loc":[-85.6427969,41.940522]},"n2138493845":{"id":"n2138493845","loc":[-85.6425891,41.9405228]},"n2138493846":{"id":"n2138493846","loc":[-85.6425868,41.9402875]},"n2138493847":{"id":"n2138493847","loc":[-85.6427969,41.9402858]},"n2138493848":{"id":"n2138493848","loc":[-85.6425708,41.9405234]},"n2138493849":{"id":"n2138493849","loc":[-85.642568,41.9402855]},"n2138493850":{"id":"n2138493850","loc":[-85.6423157,41.9402886]},"n2138493851":{"id":"n2138493851","loc":[-85.6423212,41.9404362]},"n2138493852":{"id":"n2138493852","loc":[-85.6422923,41.9404578]},"n2138493853":{"id":"n2138493853","loc":[-85.6422868,41.9404834]},"n2138493854":{"id":"n2138493854","loc":[-85.6423226,41.9405091]},"n2138493855":{"id":"n2138493855","loc":[-85.6423847,41.9405111]},"n2138493856":{"id":"n2138493856","loc":[-85.6424081,41.9405265]},"n2140155811":{"id":"n2140155811","loc":[-85.6419547,41.9410956]},"n2140155814":{"id":"n2140155814","loc":[-85.6427577,41.9410884]},"n2140155816":{"id":"n2140155816","loc":[-85.6427545,41.9410052]},"n2140155818":{"id":"n2140155818","loc":[-85.6428057,41.9410028]},"n2140155821":{"id":"n2140155821","loc":[-85.6427993,41.9407339]},"n2140155823":{"id":"n2140155823","loc":[-85.6427385,41.9407339]},"n2140155825":{"id":"n2140155825","loc":[-85.6427417,41.9406435]},"n2140155827":{"id":"n2140155827","loc":[-85.6419515,41.9406482]},"n2140155828":{"id":"n2140155828","loc":[-85.6429368,41.9412407]},"n2140155829":{"id":"n2140155829","loc":[-85.6417756,41.9412526]},"n2140155830":{"id":"n2140155830","loc":[-85.641766,41.9405983]},"n2140155831":{"id":"n2140155831","loc":[-85.6419803,41.9405983]},"n2140155832":{"id":"n2140155832","loc":[-85.6419611,41.9401366]},"n2140155833":{"id":"n2140155833","loc":[-85.6429336,41.94012]},"n2140155834":{"id":"n2140155834","loc":[-85.6430697,41.9411732]},"n2140155835":{"id":"n2140155835","loc":[-85.6428411,41.9409974]},"n2140155837":{"id":"n2140155837","loc":[-85.6428388,41.9407211]},"n2140155839":{"id":"n2140155839","loc":[-85.6430624,41.9405521]},"n2140155840":{"id":"n2140155840","loc":[-85.6427323,41.9412396]},"n2140155842":{"id":"n2140155842","loc":[-85.6418147,41.9412457]},"n2140155844":{"id":"n2140155844","loc":[-85.641813,41.9411319]},"n2140155845":{"id":"n2140155845","loc":[-85.6418394,41.9411111]},"n2140155847":{"id":"n2140155847","loc":[-85.6418838,41.9410977]},"n2140155849":{"id":"n2140155849","loc":[-85.6427324,41.9410921]},"n2140155851":{"id":"n2140155851","loc":[-85.6427798,41.9412945]},"n2140155852":{"id":"n2140155852","loc":[-85.6427701,41.9411777]},"n2140155854":{"id":"n2140155854","loc":[-85.6427323,41.9411572]},"n2140155856":{"id":"n2140155856","loc":[-85.6418478,41.9411666]},"n2165942818":{"id":"n2165942818","loc":[-85.6437533,41.9415029]},"n2165942819":{"id":"n2165942819","loc":[-85.6437623,41.9421195]},"n2168510551":{"id":"n2168510551","loc":[-85.6423795,41.9422615]},"n2168510552":{"id":"n2168510552","loc":[-85.6423744,41.9419439]},"n2168510553":{"id":"n2168510553","loc":[-85.642518,41.9419427]},"n2168510554":{"id":"n2168510554","loc":[-85.6425186,41.9419801]},"n2168510555":{"id":"n2168510555","loc":[-85.6428314,41.9419773]},"n2168510556":{"id":"n2168510556","loc":[-85.6428368,41.9423116]},"n2168510557":{"id":"n2168510557","loc":[-85.6424947,41.9423146]},"n2168510558":{"id":"n2168510558","loc":[-85.6424938,41.9422605]},"n2189046007":{"id":"n2189046007","loc":[-85.6410866,41.9424327]},"n2189046009":{"id":"n2189046009","loc":[-85.6410805,41.9420061]},"n2189046011":{"id":"n2189046011","loc":[-85.6412443,41.9420048]},"n2189046012":{"id":"n2189046012","loc":[-85.6412505,41.9424314]},"n2189046014":{"id":"n2189046014","loc":[-85.6413311,41.942968]},"n2189046016":{"id":"n2189046016","loc":[-85.6413281,41.942713]},"n2189046018":{"id":"n2189046018","loc":[-85.641521,41.9427117]},"n2189046021":{"id":"n2189046021","loc":[-85.6415234,41.9429236]},"n2189046022":{"id":"n2189046022","loc":[-85.6415045,41.9429238]},"n2189046025":{"id":"n2189046025","loc":[-85.641505,41.9429668]},"n2189046053":{"id":"n2189046053","loc":[-85.6385988,41.942412]},"n2189046054":{"id":"n2189046054","loc":[-85.6385985,41.9423311]},"n2189046055":{"id":"n2189046055","loc":[-85.6387617,41.9423308]},"n2189046056":{"id":"n2189046056","loc":[-85.6387616,41.9423026]},"n2189046058":{"id":"n2189046058","loc":[-85.6388215,41.9423025]},"n2189046059":{"id":"n2189046059","loc":[-85.6388219,41.9424115]},"n2189046060":{"id":"n2189046060","loc":[-85.6391096,41.9424486]},"n2189046061":{"id":"n2189046061","loc":[-85.6391105,41.9423673]},"n2189046063":{"id":"n2189046063","loc":[-85.6392911,41.9423684]},"n2189046065":{"id":"n2189046065","loc":[-85.6392903,41.9424497]},"n2189046067":{"id":"n2189046067","loc":[-85.6397927,41.9423876]},"n2189046069":{"id":"n2189046069","loc":[-85.6397897,41.9422981]},"n2189046070":{"id":"n2189046070","loc":[-85.6399702,41.9422947]},"n2189046072":{"id":"n2189046072","loc":[-85.6399732,41.9423843]},"n2189046074":{"id":"n2189046074","loc":[-85.6396331,41.9430227]},"n2189046075":{"id":"n2189046075","loc":[-85.6398673,41.9430189]},"n2189046077":{"id":"n2189046077","loc":[-85.6398656,41.9429637]},"n2189046079":{"id":"n2189046079","loc":[-85.6398885,41.9429633]},"n2189046082":{"id":"n2189046082","loc":[-85.6398832,41.942779]},"n2189046083":{"id":"n2189046083","loc":[-85.6398513,41.9427796]},"n2189046085":{"id":"n2189046085","loc":[-85.6398502,41.9427401]},"n2189046087":{"id":"n2189046087","loc":[-85.6397889,41.9427411]},"n2189046089":{"id":"n2189046089","loc":[-85.6397892,41.942753]},"n2189046090":{"id":"n2189046090","loc":[-85.6396983,41.9427544]},"n2189046092":{"id":"n2189046092","loc":[-85.6396993,41.9427882]},"n2189046094":{"id":"n2189046094","loc":[-85.6396746,41.9427886]},"n2189046096":{"id":"n2189046096","loc":[-85.6396758,41.9428296]},"n2189046097":{"id":"n2189046097","loc":[-85.6397007,41.9428292]},"n2189046099":{"id":"n2189046099","loc":[-85.6397018,41.9428686]},"n2189046103":{"id":"n2189046103","loc":[-85.6396289,41.9428697]},"n2189046112":{"id":"n2189046112","loc":[-85.6435683,41.9429457]},"n2189046113":{"id":"n2189046113","loc":[-85.643568,41.9427766]},"n2189046115":{"id":"n2189046115","loc":[-85.6434011,41.9427767]},"n2189046116":{"id":"n2189046116","loc":[-85.6434012,41.9428631]},"n2189046117":{"id":"n2189046117","loc":[-85.643448,41.9428631]},"n2189046118":{"id":"n2189046118","loc":[-85.6434481,41.9429457]},"n2189046119":{"id":"n2189046119","loc":[-85.6428363,41.9429809]},"n2189046120":{"id":"n2189046120","loc":[-85.6429171,41.9429791]},"n2189046121":{"id":"n2189046121","loc":[-85.642914,41.9429041]},"n2189046122":{"id":"n2189046122","loc":[-85.6429385,41.9429035]},"n2189046123":{"id":"n2189046123","loc":[-85.6429348,41.9428126]},"n2189046124":{"id":"n2189046124","loc":[-85.6427746,41.9428163]},"n2189046125":{"id":"n2189046125","loc":[-85.6427783,41.942906]},"n2189046126":{"id":"n2189046126","loc":[-85.6428332,41.9429047]},"n2189046127":{"id":"n2189046127","loc":[-85.6423018,41.9428859]},"n2189046128":{"id":"n2189046128","loc":[-85.6422987,41.9427208]},"n2189046130":{"id":"n2189046130","loc":[-85.6424218,41.9427195]},"n2189046131":{"id":"n2189046131","loc":[-85.6424246,41.9428684]},"n2189046132":{"id":"n2189046132","loc":[-85.6423845,41.9428689]},"n2189046133":{"id":"n2189046133","loc":[-85.6423848,41.942885]},"n2189046134":{"id":"n2189046134","loc":[-85.641533,41.9429392]},"n2189046135":{"id":"n2189046135","loc":[-85.6416096,41.9428768]},"n2189046137":{"id":"n2189046137","loc":[-85.6416763,41.9429221]},"n2189046138":{"id":"n2189046138","loc":[-85.6415997,41.9429845]},"n2189046139":{"id":"n2189046139","loc":[-85.6420598,41.9428016]},"n2189046140":{"id":"n2189046140","loc":[-85.6420593,41.9427415]},"n2189046141":{"id":"n2189046141","loc":[-85.6421957,41.9427409]},"n2189046142":{"id":"n2189046142","loc":[-85.6421963,41.9428182]},"n2189046143":{"id":"n2189046143","loc":[-85.6421281,41.9428185]},"n2189046144":{"id":"n2189046144","loc":[-85.6421279,41.9428013]},"n2189046145":{"id":"n2189046145","loc":[-85.6409429,41.9429345]},"n2189046146":{"id":"n2189046146","loc":[-85.6410354,41.9429334]},"n2189046147":{"id":"n2189046147","loc":[-85.6410325,41.9427972]},"n2189046148":{"id":"n2189046148","loc":[-85.640997,41.9427976]},"n2189046149":{"id":"n2189046149","loc":[-85.6409963,41.9427643]},"n2189046150":{"id":"n2189046150","loc":[-85.6408605,41.9427659]},"n2189046152":{"id":"n2189046152","loc":[-85.6408623,41.9428482]},"n2189046153":{"id":"n2189046153","loc":[-85.640941,41.9428473]},"n2189152992":{"id":"n2189152992","loc":[-85.6437661,41.9422257]},"n2189152993":{"id":"n2189152993","loc":[-85.643768,41.9424067]},"n2189152994":{"id":"n2189152994","loc":[-85.6432176,41.9417705]},"n2189152995":{"id":"n2189152995","loc":[-85.6432097,41.941327]},"n2189152996":{"id":"n2189152996","loc":[-85.6436493,41.9413226]},"n2189152997":{"id":"n2189152997","loc":[-85.6436563,41.9417164]},"n2189152998":{"id":"n2189152998","loc":[-85.6435796,41.9417171]},"n2189152999":{"id":"n2189152999","loc":[-85.6435805,41.9417669]},"n2189153000":{"id":"n2189153000","loc":[-85.6438202,41.9414953]},"n2189153001":{"id":"n2189153001","loc":[-85.6438173,41.9413175]},"n2189153004":{"id":"n2189153004","loc":[-85.6432535,41.9418466]},"n2189153005":{"id":"n2189153005","loc":[-85.6433935,41.9418599]},"n2189153006":{"id":"n2189153006","loc":[-85.6434831,41.9418986]},"n2189153007":{"id":"n2189153007","loc":[-85.6435678,41.9419774]},"n2189153008":{"id":"n2189153008","loc":[-85.6435987,41.9420282]},"n2189153009":{"id":"n2189153009","loc":[-85.643438,41.9419573]},"n2189153010":{"id":"n2189153010","loc":[-85.6435284,41.9424676]},"n2189153011":{"id":"n2189153011","loc":[-85.6436207,41.9423631]},"n2189153012":{"id":"n2189153012","loc":[-85.6434957,41.9422973]},"n2189153013":{"id":"n2189153013","loc":[-85.6434457,41.9422458]},"n2189153014":{"id":"n2189153014","loc":[-85.6433976,41.9421772]},"n2189153015":{"id":"n2189153015","loc":[-85.6433861,41.9420785]},"n2189153016":{"id":"n2189153016","loc":[-85.6433765,41.9420313]},"n2189153017":{"id":"n2189153017","loc":[-85.6432207,41.9420284]},"n2189153018":{"id":"n2189153018","loc":[-85.6432245,41.9422759]},"n2189153019":{"id":"n2189153019","loc":[-85.6432649,41.9423474]},"n2189153020":{"id":"n2189153020","loc":[-85.6433226,41.9424132]},"n2189153021":{"id":"n2189153021","loc":[-85.6434111,41.9424704]},"n2189153022":{"id":"n2189153022","loc":[-85.6434591,41.9424347]},"n2189153025":{"id":"n2189153025","loc":[-85.6437669,41.9423073]},"n2189153026":{"id":"n2189153026","loc":[-85.6436611,41.942293]},"n2189153027":{"id":"n2189153027","loc":[-85.6435784,41.9422473]},"n2189153028":{"id":"n2189153028","loc":[-85.6435245,41.9421443]},"n2189153029":{"id":"n2189153029","loc":[-85.6435149,41.9420613]},"n2189153030":{"id":"n2189153030","loc":[-85.6433528,41.9419269]},"n2189153031":{"id":"n2189153031","loc":[-85.6432535,41.9419191]},"n2189153032":{"id":"n2189153032","loc":[-85.6430868,41.9419198]},"n2189153033":{"id":"n2189153033","loc":[-85.6434894,41.9420033]},"n2189153034":{"id":"n2189153034","loc":[-85.6432974,41.9419225]},"n2189153035":{"id":"n2189153035","loc":[-85.6433055,41.9421632]},"n2189153036":{"id":"n2189153036","loc":[-85.6433538,41.9422849]},"n2189153037":{"id":"n2189153037","loc":[-85.6434718,41.9423887]},"n2189153038":{"id":"n2189153038","loc":[-85.6436134,41.9422667]},"n2189153040":{"id":"n2189153040","loc":[-85.6438759,41.9414017]},"n2189153041":{"id":"n2189153041","loc":[-85.6438181,41.9413687]},"n2189153042":{"id":"n2189153042","loc":[-85.6436821,41.9413044]},"n2189153043":{"id":"n2189153043","loc":[-85.6435899,41.9412862]},"n2189153044":{"id":"n2189153044","loc":[-85.6433169,41.9417268]},"n2189153045":{"id":"n2189153045","loc":[-85.643301,41.9412859]},"n2189153046":{"id":"n2189153046","loc":[-85.6435531,41.9416981]},"n2189153047":{"id":"n2189153047","loc":[-85.6435427,41.9412863]},"n185948706":{"id":"n185948706","loc":[-85.6369439,41.940122]},"n185949348":{"id":"n185949348","loc":[-85.640039,41.931135]},"n185949870":{"id":"n185949870","loc":[-85.643195,41.949261]},"n185954680":{"id":"n185954680","loc":[-85.6337802,41.9401143]},"n185954784":{"id":"n185954784","loc":[-85.6487485,41.942527]},"n185958670":{"id":"n185958670","loc":[-85.637255,41.940104]},"n185958672":{"id":"n185958672","loc":[-85.636996,41.941355]},"n185960207":{"id":"n185960207","loc":[-85.634992,41.940118]},"n185963163":{"id":"n185963163","loc":[-85.638831,41.93398]},"n185963165":{"id":"n185963165","loc":[-85.640073,41.933968]},"n185963167":{"id":"n185963167","loc":[-85.641225,41.933972]},"n185963168":{"id":"n185963168","loc":[-85.642386,41.933952]},"n185964695":{"id":"n185964695","loc":[-85.6443608,41.9425645]},"n185964697":{"id":"n185964697","loc":[-85.644384,41.939941]},"n185964963":{"id":"n185964963","loc":[-85.6382347,41.9426146]},"n185964965":{"id":"n185964965","loc":[-85.637022,41.942622]},"n185964967":{"id":"n185964967","loc":[-85.6363706,41.9426606]},"n185964968":{"id":"n185964968","loc":[-85.6357988,41.9427748]},"n185964969":{"id":"n185964969","loc":[-85.6355409,41.9428465]},"n185964970":{"id":"n185964970","loc":[-85.6348729,41.9430443]},"n185966958":{"id":"n185966958","loc":[-85.641946,41.946413]},"n185966960":{"id":"n185966960","loc":[-85.643148,41.946389]},"n185967774":{"id":"n185967774","loc":[-85.641889,41.943852]},"n185967775":{"id":"n185967775","loc":[-85.641922,41.945121]},"n185967776":{"id":"n185967776","loc":[-85.641927,41.947544]},"n185967777":{"id":"n185967777","loc":[-85.641982,41.947622]},"n185969289":{"id":"n185969289","loc":[-85.63928,41.929221]},"n185969704":{"id":"n185969704","loc":[-85.6388186,41.9350099]},"n185969706":{"id":"n185969706","loc":[-85.6400709,41.9349957]},"n185969708":{"id":"n185969708","loc":[-85.6412214,41.9349827]},"n185969710":{"id":"n185969710","loc":[-85.6423509,41.934974]},"n185970602":{"id":"n185970602","loc":[-85.641293,41.931817]},"n185970604":{"id":"n185970604","loc":[-85.641258,41.932705]},"n185970605":{"id":"n185970605","loc":[-85.641148,41.936984]},"n185970606":{"id":"n185970606","loc":[-85.641112,41.938169]},"n185970906":{"id":"n185970906","loc":[-85.639454,41.943871]},"n185970908":{"id":"n185970908","loc":[-85.6394635,41.9450504]},"n185970909":{"id":"n185970909","loc":[-85.6394914,41.9451911]},"n185971368":{"id":"n185971368","loc":[-85.635769,41.940122]},"n185971978":{"id":"n185971978","loc":[-85.640003,41.936988]},"n185971980":{"id":"n185971980","loc":[-85.642299,41.936988]},"n185973633":{"id":"n185973633","loc":[-85.639023,41.92861]},"n185973635":{"id":"n185973635","loc":[-85.639153,41.928969]},"n185973637":{"id":"n185973637","loc":[-85.639213,41.929088]},"n185973639":{"id":"n185973639","loc":[-85.63935,41.929396]},"n185973641":{"id":"n185973641","loc":[-85.640143,41.931462]},"n185973644":{"id":"n185973644","loc":[-85.64019,41.931788]},"n185973646":{"id":"n185973646","loc":[-85.6401365,41.9327199]},"n185973648":{"id":"n185973648","loc":[-85.639983,41.938174]},"n185974477":{"id":"n185974477","loc":[-85.638206,41.941331]},"n185975928":{"id":"n185975928","loc":[-85.640683,41.94513]},"n185975930":{"id":"n185975930","loc":[-85.643102,41.945103]},"n185976255":{"id":"n185976255","loc":[-85.642424,41.931817]},"n185976257":{"id":"n185976257","loc":[-85.64242,41.932699]},"n185976258":{"id":"n185976258","loc":[-85.6422621,41.9381489]},"n185977452":{"id":"n185977452","loc":[-85.6457497,41.9398834]},"n185978772":{"id":"n185978772","loc":[-85.646656,41.939869]},"n185981472":{"id":"n185981472","loc":[-85.6388962,41.9321266]},"n185981474":{"id":"n185981474","loc":[-85.6388769,41.9327334]},"n185981476":{"id":"n185981476","loc":[-85.638829,41.934116]},"n185981478":{"id":"n185981478","loc":[-85.63876,41.937002]},"n185981480":{"id":"n185981480","loc":[-85.638682,41.93819]},"n185981999":{"id":"n185981999","loc":[-85.638194,41.9400866]},"n185982001":{"id":"n185982001","loc":[-85.646302,41.93988]},"n185982877":{"id":"n185982877","loc":[-85.640676,41.943867]},"n185982879":{"id":"n185982879","loc":[-85.640734,41.945887]},"n185985823":{"id":"n185985823","loc":[-85.643106,41.943841]},"n185985824":{"id":"n185985824","loc":[-85.643145,41.947641]},"n185985825":{"id":"n185985825","loc":[-85.643219,41.950829]},"n1475301385":{"id":"n1475301385","loc":[-85.6360612,41.9427042]},"n1475301397":{"id":"n1475301397","loc":[-85.6366651,41.9426328]},"n2139795811":{"id":"n2139795811","loc":[-85.6469154,41.9425427]},"n2139795830":{"id":"n2139795830","loc":[-85.6443194,41.9399444]},"n2139795834":{"id":"n2139795834","loc":[-85.6453506,41.9399002]},"n2139795837":{"id":"n2139795837","loc":[-85.645806,41.9398831]},"n2139858932":{"id":"n2139858932","loc":[-85.6351721,41.9429557]},"n2140019000":{"id":"n2140019000","loc":[-85.6359935,41.9427224]},"n2165942817":{"id":"n2165942817","loc":[-85.6442017,41.9414993]},"n2165942820":{"id":"n2165942820","loc":[-85.6442107,41.9421159]},"n2189152990":{"id":"n2189152990","loc":[-85.6442328,41.942404]},"n2189152991":{"id":"n2189152991","loc":[-85.6442309,41.9422229]},"n2189153002":{"id":"n2189153002","loc":[-85.6441329,41.9413147]},"n2189153003":{"id":"n2189153003","loc":[-85.6441357,41.9414925]},"n2189153023":{"id":"n2189153023","loc":[-85.6443453,41.9423074]},"n2189153024":{"id":"n2189153024","loc":[-85.6442318,41.9423045]},"n2189153039":{"id":"n2189153039","loc":[-85.6441343,41.9414025]},"w208643102":{"id":"w208643102","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153034","n2189153035","n2189153036","n2189153037","n2189153038"]},"w17966942":{"id":"w17966942","tags":{"highway":"residential","name":"Millard St"},"nodes":["n185954680","n185960207","n185971368","n185948706","n185958670","n185981999","n185981481","n185981482","n185973650","n185973659","n185970607","n185976259","n185976261","n2139795830","n185964697","n2139795834","n185977452","n2139795837","n185982001","n185978772"]},"w208643105":{"id":"w208643105","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153046","n2189153047"]},"w208631637":{"id":"w208631637","tags":{"area":"yes","building":"yes"},"nodes":["n2189046014","n2189046016","n2189046018","n2189046021","n2189046022","n2189046025","n2189046014"]},"w208643096":{"id":"w208643096","tags":{"amenity":"parking","area":"yes","fee":"no"},"nodes":["n2189152990","n2189153024","n2189152991","n2189152992","n2189153025","n2189152993","n2189152990"]},"w208631656":{"id":"w208631656","tags":{"area":"yes","building":"yes"},"nodes":["n2189046134","n2189046135","n2189046137","n2189046138","n2189046134"]},"w204003417":{"id":"w204003417","tags":{"area":"yes","building":"school"},"nodes":["n2140155811","n2140155814","n2140155816","n2140155818","n2140155821","n2140155823","n2140155825","n2140155827","n2140155811"]},"w208631654":{"id":"w208631654","tags":{"area":"yes","building":"yes"},"nodes":["n2189046127","n2189046128","n2189046130","n2189046131","n2189046132","n2189046133","n2189046127"]},"w17966327":{"id":"w17966327","tags":{"highway":"residential","name":"S Douglas Ave"},"nodes":["n185976261","n2140155839","n2140155834","n185974481","n2189153032","n185964959"]},"w41785752":{"id":"w41785752","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60","access":"yes"},"nodes":["n185954784","n2139795811","n185964695","n185964959","n185964960","n185964961","n185964962","n185964963","n185964965","n1475301397","n185964967","n1475301385","n2140019000","n185964968","n185964969","n2139858932","n185964970"]},"w203841842":{"id":"w203841842","tags":{"area":"yes","leisure":"playground"},"nodes":["n2138493848","n2138493849","n2138493850","n2138493851","n2138493852","n2138493853","n2138493854","n2138493855","n2138493856","n2138493848"]},"w208643103":{"id":"w208643103","tags":{"highway":"service"},"nodes":["n2189153039","n2189153040","n2189153041","n2189153042","n2189153043","n2189153047","n2189153045","n185974481"]},"w208643098":{"id":"w208643098","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153000","n2189153041","n2189153001","n2189153002","n2189153039","n2189153003","n2189153000"]},"w208631646":{"id":"w208631646","tags":{"area":"yes","building":"yes"},"nodes":["n2189046067","n2189046069","n2189046070","n2189046072","n2189046067"]},"w208631653":{"id":"w208631653","tags":{"area":"yes","building":"yes"},"nodes":["n2189046119","n2189046120","n2189046121","n2189046122","n2189046123","n2189046124","n2189046125","n2189046126","n2189046119"]},"w17966041":{"id":"w17966041","tags":{"highway":"residential","name":"S Lincoln Ave"},"nodes":["n185973659","n185973660","n185964961"]},"w208631645":{"id":"w208631645","tags":{"area":"yes","building":"yes"},"nodes":["n2189046060","n2189046061","n2189046063","n2189046065","n2189046060"]},"w206803397":{"id":"w206803397","tags":{"area":"yes","building":"yes"},"nodes":["n2168510551","n2168510552","n2168510553","n2168510554","n2168510555","n2168510556","n2168510557","n2168510558","n2168510551"]},"w17965792":{"id":"w17965792","tags":{"highway":"residential","name":"N Hooker Ave"},"nodes":["n185964962","n185970906","n185970908","n185970909"]},"w208631651":{"id":"w208631651","tags":{"area":"yes","building":"yes"},"nodes":["n2189046112","n2189046113","n2189046115","n2189046116","n2189046117","n2189046118","n2189046112"]},"w208631643":{"id":"w208631643","tags":{"area":"yes","building":"yes"},"nodes":["n2189046053","n2189046054","n2189046055","n2189046056","n2189046058","n2189046059","n2189046053"]},"w17966878":{"id":"w17966878","tags":{"highway":"residential","name":"S Hooker Ave"},"nodes":["n185981472","n185981474","n185963163","n185981476","n185969704","n185981478","n185981480","n185981481"]},"w17966102":{"id":"w17966102","tags":{"highway":"residential","name":"South St"},"nodes":["n185958672","n185974477","n185974479","n185973660","n185970614"]},"w208631660":{"id":"w208631660","tags":{"area":"yes","building":"yes"},"nodes":["n2189046145","n2189046146","n2189046147","n2189046148","n2189046149","n2189046150","n2189046152","n2189046153","n2189046145"]},"w208643101":{"id":"w208643101","tags":{"highway":"service"},"nodes":["n2189153023","n2189153024","n2189153025","n2189153026","n2189153038","n2189153027","n2189153028","n2189153029","n2189153033","n2189153009","n2189153030","n2189153034","n2189153031","n2189153032"]},"w204000205":{"id":"w204000205","tags":{"highway":"residential","name":"South St","oneway":"yes"},"nodes":["n185974481","n2140155851","n185970614"]},"w203841841":{"id":"w203841841","tags":{"area":"yes","leisure":"pitch","pitch":"basketball"},"nodes":["n2138493844","n2138493845","n2138493846","n2138493847","n2138493844"]},"w17965444":{"id":"w17965444","tags":{"highway":"residential","name":"N Grant Ave"},"nodes":["n185964960","n185967774","n185967775","n185966958","n185967776","n185967777"]},"w208631648":{"id":"w208631648","tags":{"area":"yes","building":"yes"},"nodes":["n2189046074","n2189046075","n2189046077","n2189046079","n2189046082","n2189046083","n2189046085","n2189046087","n2189046089","n2189046090","n2189046092","n2189046094","n2189046096","n2189046097","n2189046099","n2189046103","n2189046074"]},"w208643100":{"id":"w208643100","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153010","n2189153011","n2189153012","n2189153013","n2189153014","n2189153015","n2189153016","n2189153017","n2189153018","n2189153019","n2189153020","n2189153021","n2189153022","n2189153010"]},"w17965749":{"id":"w17965749","tags":{"highway":"residential","name":"S Grant Ave"},"nodes":["n185970614","n185970616","n185964960"]},"w206574482":{"id":"w206574482","tags":{"amenity":"library","area":"yes","building":"yes","name":"Three Rivers Public Library"},"nodes":["n2165942817","n2165942818","n2165942819","n2165942820","n2165942817"]},"w208643097":{"id":"w208643097","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189152994","n2189152995","n2189152996","n2189152997","n2189152998","n2189152999","n2189152994"]},"w17966879":{"id":"w17966879","tags":{"highway":"residential","name":"S Hooker Ave"},"nodes":["n185981482","n185974479","n185964962"]},"w17966325":{"id":"w17966325","tags":{"highway":"residential","name":"S Douglas Ave"},"nodes":["n185976255","n185976257","n185963168","n185969710","n185971980","n185976258","n185954700","n185976259"]},"w17967390":{"id":"w17967390","tags":{"highway":"residential","name":"N Douglas Ave"},"nodes":["n185964959","n185985823","n185975930","n185966960","n185985824","n185949870","n185985825"]},"w208631635":{"id":"w208631635","tags":{"area":"yes","building":"yes"},"nodes":["n2189046007","n2189046009","n2189046011","n2189046012","n2189046007"]},"w208643099":{"id":"w208643099","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153031","n2189153004","n2189153005","n2189153006","n2189153007","n2189153008","n2189153029","n2189153033","n2189153009","n2189153030","n2189153031"]},"w208631658":{"id":"w208631658","tags":{"area":"yes","building":"yes"},"nodes":["n2189046139","n2189046140","n2189046141","n2189046142","n2189046143","n2189046144","n2189046139"]},"w208643104":{"id":"w208643104","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153044","n2189153045"]},"w17966039":{"id":"w17966039","tags":{"highway":"residential","name":"S Lincoln Ave"},"nodes":["n185973633","n185973635","n185973637","n185969289","n185973639","n185949348","n185973641","n185973644","n185973646","n185963165","n185969706","n185971978","n185973648","n185973650"]},"w204003420":{"id":"w204003420","tags":{"amenity":"parking","area":"yes"},"nodes":["n2140155840","n2140155842","n2140155844","n2140155845","n2140155847","n2140155849","n2140155854","n2140155840"]},"w204003419":{"id":"w204003419","tags":{"highway":"service"},"nodes":["n2140155834","n2140155835","n2140155837","n2140155839"]},"w204003418":{"id":"w204003418","tags":{"amenity":"school","area":"yes","name":"Andrews Elementary School"},"nodes":["n2140155828","n2140155829","n2140155830","n2140155831","n2140155832","n2140155833","n2140155828"]},"w17965747":{"id":"w17965747","tags":{"highway":"residential","name":"S Grant Ave"},"nodes":["n185970602","n185970604","n185963167","n185969708","n185970605","n185970606","n185970607"]},"w17967073":{"id":"w17967073","tags":{"highway":"residential","name":"N Lincoln Ave"},"nodes":["n185964961","n185982877","n185975928","n185982879"]},"w204003421":{"id":"w204003421","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2140155851","n2140155852","n2140155854","n2140155856"]},"r1943857":{"id":"r1943857","tags":{"modifier":"Business","name":"US 131 Business (Three Rivers, MI)","network":"US:US","ref":"131","route":"road","type":"route"},"members":[{"id":"w17966509","type":"way","role":"forward"},{"id":"w143497377","type":"way","role":""},{"id":"w134150811","type":"way","role":""},{"id":"w134150800","type":"way","role":""},{"id":"w134150789","type":"way","role":""},{"id":"w134150795","type":"way","role":""},{"id":"w41785752","type":"way","role":""},{"id":"w17965146","type":"way","role":"forward"},{"id":"w17964031","type":"way","role":"forward"}]},"r270277":{"id":"r270277","tags":{"network":"US:MI","ref":"60","route":"road","state_id":"MI","type":"route","url":"http://en.wikipedia.org/wiki/M-60_%28Michigan_highway%29"},"members":[{"id":"w17751087","type":"way","role":"east"},{"id":"w117148312","type":"way","role":"east"},{"id":"w40942155","type":"way","role":"west"},{"id":"w17751017","type":"way","role":""},{"id":"w17751083","type":"way","role":""},{"id":"w17747780","type":"way","role":""},{"id":"w41068082","type":"way","role":""},{"id":"w197025212","type":"way","role":""},{"id":"w17743874","type":"way","role":""},{"id":"w17751044","type":"way","role":""},{"id":"w17752167","type":"way","role":""},{"id":"w17751089","type":"way","role":""},{"id":"w17743879","type":"way","role":""},{"id":"w17751064","type":"way","role":""},{"id":"w197057073","type":"way","role":""},{"id":"w167699963","type":"way","role":""},{"id":"w167699972","type":"way","role":""},{"id":"w17967584","type":"way","role":""},{"id":"w167699964","type":"way","role":""},{"id":"w17967582","type":"way","role":"west"},{"id":"w41260270","type":"way","role":"west"},{"id":"w17965146","type":"way","role":"west"},{"id":"w41785752","type":"way","role":""},{"id":"w134150795","type":"way","role":""},{"id":"w134150789","type":"way","role":""},{"id":"w134150800","type":"way","role":""},{"id":"w134150811","type":"way","role":""},{"id":"w134150836","type":"way","role":""},{"id":"w134150802","type":"way","role":""},{"id":"w41074896","type":"way","role":""},{"id":"w17966773","type":"way","role":""},{"id":"w17967415","type":"way","role":""},{"id":"w41074899","type":"way","role":""},{"id":"w17967581","type":"way","role":""},{"id":"w41074902","type":"way","role":""},{"id":"w41074906","type":"way","role":""},{"id":"w209707997","type":"way","role":""},{"id":"w209707998","type":"way","role":""},{"id":"w17964798","type":"way","role":""},{"id":"w17966034","type":"way","role":""},{"id":"w17967593","type":"way","role":""},{"id":"w41074888","type":"way","role":""},{"id":"w17733772","type":"way","role":""},{"id":"w41074813","type":"way","role":""},{"id":"w17742213","type":"way","role":""},{"id":"w17746863","type":"way","role":""},{"id":"w17745772","type":"way","role":""},{"id":"w17742222","type":"way","role":""},{"id":"w17745922","type":"way","role":""},{"id":"w17742198","type":"way","role":""},{"id":"w17747675","type":"way","role":""},{"id":"w17739927","type":"way","role":""},{"id":"w17745708","type":"way","role":""},{"id":"w41006323","type":"way","role":""},{"id":"w17744233","type":"way","role":""},{"id":"w17739436","type":"way","role":""},{"id":"w17742201","type":"way","role":""},{"id":"w151418616","type":"way","role":""},{"id":"w17750062","type":"way","role":""},{"id":"w17742227","type":"way","role":"east"},{"id":"w41006348","type":"way","role":"east"},{"id":"w41260984","type":"way","role":""},{"id":"w17832427","type":"way","role":""},{"id":"w17838408","type":"way","role":""},{"id":"w17835846","type":"way","role":""},{"id":"w17832923","type":"way","role":""},{"id":"w17839388","type":"way","role":""},{"id":"w17838390","type":"way","role":""},{"id":"w17831272","type":"way","role":""},{"id":"w17828581","type":"way","role":""},{"id":"w38240686","type":"way","role":""},{"id":"w17838405","type":"way","role":"east"},{"id":"w123323711","type":"way","role":"east"},{"id":"w17830167","type":"way","role":"east"},{"id":"w99011909","type":"way","role":"east"},{"id":"w41911361","type":"way","role":"east"},{"id":"w41911355","type":"way","role":"east"},{"id":"w41911356","type":"way","role":"east"},{"id":"w117148326","type":"way","role":"west"},{"id":"w41911352","type":"way","role":"west"},{"id":"w41911353","type":"way","role":"west"},{"id":"w41911354","type":"way","role":"west"},{"id":"w41911360","type":"way","role":"west"},{"id":"w38240676","type":"way","role":"west"},{"id":"w123323710","type":"way","role":"west"},{"id":"w41260271","type":"way","role":"east"},{"id":"w41260273","type":"way","role":"east"},{"id":"w17964031","type":"way","role":"east"},{"id":"w41006344","type":"way","role":"west"},{"id":"w41006351","type":"way","role":"west"}]},"n367813436":{"id":"n367813436","loc":[-85.63605205663384,41.94305506683346],"tags":{"amenity":"fire_station","name":"Three Rivers Fire Department"}},"n185948708":{"id":"n185948708","loc":[-85.6369828,41.9408789]},"n185948710":{"id":"n185948710","loc":[-85.6370184,41.9411346]},"n185954691":{"id":"n185954691","loc":[-85.634476,41.941475]},"n185954692":{"id":"n185954692","loc":[-85.635008,41.941846]},"n185954693":{"id":"n185954693","loc":[-85.635362,41.941962]},"n185954695":{"id":"n185954695","loc":[-85.63578,41.941978]},"n185972903":{"id":"n185972903","loc":[-85.63295,41.9430062]},"n185964971":{"id":"n185964971","loc":[-85.6346811,41.9431023]},"n1819805854":{"id":"n1819805854","loc":[-85.6331275,41.9404837]},"n1819805918":{"id":"n1819805918","loc":[-85.6331168,41.942798]},"n1819805762":{"id":"n1819805762","loc":[-85.6333034,41.9424123]},"n1819805907":{"id":"n1819805907","loc":[-85.6334819,41.9419121]},"n1819805915":{"id":"n1819805915","loc":[-85.6334554,41.9413588]},"n1819848888":{"id":"n1819848888","loc":[-85.6331625,41.942679]},"n1819848930":{"id":"n1819848930","loc":[-85.6338684,41.9431252]},"n1819858505":{"id":"n1819858505","loc":[-85.6346782,41.9429092]},"n1819858507":{"id":"n1819858507","loc":[-85.6339003,41.9414534]},"n1819858508":{"id":"n1819858508","loc":[-85.6345709,41.9427742]},"n1819858509":{"id":"n1819858509","loc":[-85.63419,41.9417322]},"n1819858511":{"id":"n1819858511","loc":[-85.6340666,41.9415652]},"n1819858512":{"id":"n1819858512","loc":[-85.6343295,41.9423027]},"n1819858514":{"id":"n1819858514","loc":[-85.6343241,41.942207]},"n1819858521":{"id":"n1819858521","loc":[-85.633391,41.941231]},"n1819858528":{"id":"n1819858528","loc":[-85.6343027,41.9419716]},"n185954683":{"id":"n185954683","loc":[-85.6335412,41.940147]},"n185954685":{"id":"n185954685","loc":[-85.6334296,41.9403023]},"n185954687":{"id":"n185954687","loc":[-85.6333988,41.9404704]},"n185954689":{"id":"n185954689","loc":[-85.6335511,41.9410225]},"n185954690":{"id":"n185954690","loc":[-85.6336721,41.9411669]},"n1820938802":{"id":"n1820938802","loc":[-85.6330671,41.941845]},"n1821006702":{"id":"n1821006702","loc":[-85.6344047,41.9395496]},"n2130304133":{"id":"n2130304133","loc":[-85.6349025,41.9427659]},"n2130304136":{"id":"n2130304136","loc":[-85.6346027,41.9422017]},"n2130304138":{"id":"n2130304138","loc":[-85.6348577,41.9421517]},"n2130304140":{"id":"n2130304140","loc":[-85.6348419,41.9422694]},"n2130304142":{"id":"n2130304142","loc":[-85.6349071,41.9423135]},"n2130304144":{"id":"n2130304144","loc":[-85.6350495,41.9423312]},"n2130304146":{"id":"n2130304146","loc":[-85.6351009,41.9422812]},"n2130304147":{"id":"n2130304147","loc":[-85.6351227,41.9421532]},"n2130304148":{"id":"n2130304148","loc":[-85.635526,41.9421547]},"n2130304149":{"id":"n2130304149","loc":[-85.6355339,41.9425768]},"n2130304150":{"id":"n2130304150","loc":[-85.6351582,41.9426562]},"n2130304151":{"id":"n2130304151","loc":[-85.6351207,41.9427032]},"n2138493807":{"id":"n2138493807","loc":[-85.6350923,41.9415216]},"n2138493808":{"id":"n2138493808","loc":[-85.6353603,41.9411061]},"n2138493809":{"id":"n2138493809","loc":[-85.6354421,41.9410942]},"n2138493810":{"id":"n2138493810","loc":[-85.6355079,41.9411044]},"n2138493811":{"id":"n2138493811","loc":[-85.6355693,41.9411246]},"n2138493812":{"id":"n2138493812","loc":[-85.6355829,41.9411061]},"n2138493813":{"id":"n2138493813","loc":[-85.6355624,41.9409777]},"n2138493814":{"id":"n2138493814","loc":[-85.6355011,41.9409152]},"n2138493815":{"id":"n2138493815","loc":[-85.635383,41.9409219]},"n2138493816":{"id":"n2138493816","loc":[-85.635299,41.9409658]},"n2138493817":{"id":"n2138493817","loc":[-85.6351695,41.941204]},"n2138493818":{"id":"n2138493818","loc":[-85.6348879,41.9415166]},"n2138493819":{"id":"n2138493819","loc":[-85.634897,41.9415757]},"n2138493820":{"id":"n2138493820","loc":[-85.6349606,41.9416399]},"n2138493821":{"id":"n2138493821","loc":[-85.6350219,41.9416669]},"n2138493822":{"id":"n2138493822","loc":[-85.6351241,41.9416314]},"n2138493823":{"id":"n2138493823","loc":[-85.6350855,41.9415622]},"n2138493824":{"id":"n2138493824","loc":[-85.6350401,41.9413603]},"n2138493825":{"id":"n2138493825","loc":[-85.6352206,41.9410765]},"n2138493826":{"id":"n2138493826","loc":[-85.6343865,41.9415594]},"n2138493827":{"id":"n2138493827","loc":[-85.6343506,41.9415873]},"n2138493828":{"id":"n2138493828","loc":[-85.6344158,41.9417557]},"n2138493829":{"id":"n2138493829","loc":[-85.6344614,41.9417968]},"n2138493830":{"id":"n2138493830","loc":[-85.6345005,41.9418186]},"n2138493831":{"id":"n2138493831","loc":[-85.6345965,41.9418162]},"n2138493832":{"id":"n2138493832","loc":[-85.6347317,41.9417242]},"n2138493833":{"id":"n2138493833","loc":[-85.6346722,41.941775]},"n2139858909":{"id":"n2139858909","loc":[-85.633403,41.9391006]},"n2139858910":{"id":"n2139858910","loc":[-85.6332973,41.9393967]},"n2139858911":{"id":"n2139858911","loc":[-85.633205,41.9396742]},"n2139858912":{"id":"n2139858912","loc":[-85.6332203,41.9397772]},"n2139858913":{"id":"n2139858913","loc":[-85.6333453,41.939936]},"n2139858914":{"id":"n2139858914","loc":[-85.6333761,41.9400018]},"n2139858915":{"id":"n2139858915","loc":[-85.63328,41.9402249]},"n2139858916":{"id":"n2139858916","loc":[-85.6332357,41.9403523]},"n2139858917":{"id":"n2139858917","loc":[-85.6332838,41.9405831]},"n2139858918":{"id":"n2139858918","loc":[-85.6333643,41.9408744]},"n2139858919":{"id":"n2139858919","loc":[-85.6334394,41.9410519]},"n2139858920":{"id":"n2139858920","loc":[-85.6335815,41.9411717]},"n2139858921":{"id":"n2139858921","loc":[-85.6337478,41.9412734]},"n2139858922":{"id":"n2139858922","loc":[-85.6343174,41.9415268]},"n2139858923":{"id":"n2139858923","loc":[-85.6343886,41.9417397]},"n2139858924":{"id":"n2139858924","loc":[-85.6344407,41.9418015]},"n2139858925":{"id":"n2139858925","loc":[-85.6345139,41.9418366]},"n2139858926":{"id":"n2139858926","loc":[-85.6344846,41.942005]},"n2139858927":{"id":"n2139858927","loc":[-85.6345775,41.9422218]},"n2139858928":{"id":"n2139858928","loc":[-85.6348771,41.9427814]},"n2139858929":{"id":"n2139858929","loc":[-85.6349487,41.9427995]},"n2139858930":{"id":"n2139858930","loc":[-85.6350415,41.9427874]},"n2139858931":{"id":"n2139858931","loc":[-85.6351246,41.9428589]},"n2139858978":{"id":"n2139858978","loc":[-85.6349658,41.9431481]},"n2139858979":{"id":"n2139858979","loc":[-85.6350081,41.9431287]},"n2139858980":{"id":"n2139858980","loc":[-85.6349967,41.9430997]},"n2139858981":{"id":"n2139858981","loc":[-85.6352158,41.9430352]},"n2139858982":{"id":"n2139858982","loc":[-85.6348174,41.94267]},"n2139858983":{"id":"n2139858983","loc":[-85.6346142,41.9425989]},"n2139858984":{"id":"n2139858984","loc":[-85.6344938,41.9423809]},"n2139858985":{"id":"n2139858985","loc":[-85.6344856,41.9422997]},"n2139870380":{"id":"n2139870380","loc":[-85.6346707,41.9417955]},"n2139870381":{"id":"n2139870381","loc":[-85.6345949,41.9418311]},"n2139870382":{"id":"n2139870382","loc":[-85.6343322,41.9418659]},"n2139870383":{"id":"n2139870383","loc":[-85.6342072,41.941885]},"n2139870384":{"id":"n2139870384","loc":[-85.6341325,41.9418919]},"n2139870385":{"id":"n2139870385","loc":[-85.6341314,41.9422028]},"n2139870386":{"id":"n2139870386","loc":[-85.6340472,41.9423271]},"n2139870387":{"id":"n2139870387","loc":[-85.6342185,41.9427933]},"n2139870388":{"id":"n2139870388","loc":[-85.6340605,41.9423924]},"n2139870389":{"id":"n2139870389","loc":[-85.6339889,41.9424069]},"n2139870390":{"id":"n2139870390","loc":[-85.633971,41.942356]},"n2139870391":{"id":"n2139870391","loc":[-85.63361,41.9424235]},"n2139870392":{"id":"n2139870392","loc":[-85.6337137,41.9426819]},"n2139870393":{"id":"n2139870393","loc":[-85.6336977,41.9428632]},"n2139870394":{"id":"n2139870394","loc":[-85.6338823,41.9428647]},"n2139870395":{"id":"n2139870395","loc":[-85.6339412,41.9430069]},"n2139870396":{"id":"n2139870396","loc":[-85.6338873,41.9430353]},"n2139870397":{"id":"n2139870397","loc":[-85.6337676,41.942815]},"n2139870398":{"id":"n2139870398","loc":[-85.6336822,41.9423505]},"n2139870399":{"id":"n2139870399","loc":[-85.634037,41.9422725]},"n2139870400":{"id":"n2139870400","loc":[-85.6340294,41.9422518]},"n2139870401":{"id":"n2139870401","loc":[-85.6336726,41.9423312]},"n2139870402":{"id":"n2139870402","loc":[-85.6342188,41.9425715]},"n2139870403":{"id":"n2139870403","loc":[-85.6342524,41.942565]},"n2139870404":{"id":"n2139870404","loc":[-85.6341438,41.942299]},"n2139870405":{"id":"n2139870405","loc":[-85.6341149,41.9423061]},"n2139870407":{"id":"n2139870407","loc":[-85.6340846,41.9431458]},"n2139870408":{"id":"n2139870408","loc":[-85.6339436,41.9429032]},"n2139870409":{"id":"n2139870409","loc":[-85.6343143,41.9428207]},"n2139870410":{"id":"n2139870410","loc":[-85.6343507,41.94277]},"n2139870411":{"id":"n2139870411","loc":[-85.6341527,41.942254]},"n2139870412":{"id":"n2139870412","loc":[-85.6340925,41.9422199]},"n2139870413":{"id":"n2139870413","loc":[-85.6335435,41.9423433]},"n2139870414":{"id":"n2139870414","loc":[-85.6335023,41.9423975]},"n2139870415":{"id":"n2139870415","loc":[-85.6335086,41.9424552]},"n2139870416":{"id":"n2139870416","loc":[-85.6336296,41.942665]},"n2139870417":{"id":"n2139870417","loc":[-85.6341396,41.9428596]},"n2139870418":{"id":"n2139870418","loc":[-85.6339701,41.9424487]},"n2139870419":{"id":"n2139870419","loc":[-85.6335514,41.9425294]},"n2139870420":{"id":"n2139870420","loc":[-85.6337406,41.9424929]},"n2139870421":{"id":"n2139870421","loc":[-85.6338939,41.9428687]},"n2139870422":{"id":"n2139870422","loc":[-85.6341323,41.9419538]},"n2139870423":{"id":"n2139870423","loc":[-85.6340321,41.9420376]},"n2139870424":{"id":"n2139870424","loc":[-85.6337648,41.942238]},"n2139870425":{"id":"n2139870425","loc":[-85.6337604,41.9422685]},"n2139870426":{"id":"n2139870426","loc":[-85.6337682,41.9422928]},"n2139870427":{"id":"n2139870427","loc":[-85.6338086,41.9423862]},"n2139870428":{"id":"n2139870428","loc":[-85.6349465,41.9416631]},"n2139870429":{"id":"n2139870429","loc":[-85.6351097,41.9416973]},"n2139870430":{"id":"n2139870430","loc":[-85.6353371,41.9416798]},"n2139870431":{"id":"n2139870431","loc":[-85.6349627,41.9422506]},"n2139870432":{"id":"n2139870432","loc":[-85.634979,41.9421815]},"n2139870433":{"id":"n2139870433","loc":[-85.634885,41.9421679]},"n2139870434":{"id":"n2139870434","loc":[-85.6348689,41.9422377]},"n2139870435":{"id":"n2139870435","loc":[-85.6349779,41.9419486]},"n2139870436":{"id":"n2139870436","loc":[-85.6349505,41.9418933]},"n2139870437":{"id":"n2139870437","loc":[-85.6347327,41.9419505]},"n2139870438":{"id":"n2139870438","loc":[-85.6347614,41.9420087]},"n2139870439":{"id":"n2139870439","loc":[-85.6351889,41.9416912]},"n2139870440":{"id":"n2139870440","loc":[-85.6351092,41.9418426]},"n2139870441":{"id":"n2139870441","loc":[-85.635086,41.9419659]},"n2139870442":{"id":"n2139870442","loc":[-85.6350584,41.9421466]},"n2139870443":{"id":"n2139870443","loc":[-85.6350993,41.9421606]},"n2139870444":{"id":"n2139870444","loc":[-85.6350993,41.9422132]},"n2139870445":{"id":"n2139870445","loc":[-85.6350794,41.9422855]},"n2139870446":{"id":"n2139870446","loc":[-85.6350474,41.9423159]},"n2139870447":{"id":"n2139870447","loc":[-85.6349251,41.9422998]},"n2139870448":{"id":"n2139870448","loc":[-85.634911,41.9422755]},"n2139870449":{"id":"n2139870449","loc":[-85.6349157,41.9422553]},"n2139870450":{"id":"n2139870450","loc":[-85.6347213,41.9419324]},"n2139870451":{"id":"n2139870451","loc":[-85.6349535,41.9418771]},"n2139870452":{"id":"n2139870452","loc":[-85.6350135,41.9419421]},"n2139870453":{"id":"n2139870453","loc":[-85.6348584,41.9418997]},"n2139870454":{"id":"n2139870454","loc":[-85.6348113,41.9418101]},"n2139870455":{"id":"n2139870455","loc":[-85.6347306,41.9417449]},"n2139870456":{"id":"n2139870456","loc":[-85.6349123,41.941776]},"n2139870457":{"id":"n2139870457","loc":[-85.6349423,41.9421448]},"n2139870458":{"id":"n2139870458","loc":[-85.6349436,41.9420652]},"n2139870459":{"id":"n2139870459","loc":[-85.6349136,41.9419963]},"n2139870460":{"id":"n2139870460","loc":[-85.6349814,41.9419789]},"n2139989328":{"id":"n2139989328","loc":[-85.6334188,41.9421725]},"n2139989330":{"id":"n2139989330","loc":[-85.6335087,41.9416308]},"n2139989335":{"id":"n2139989335","loc":[-85.6336856,41.9429371]},"n2139989337":{"id":"n2139989337","loc":[-85.6333713,41.9427217]},"n2139989339":{"id":"n2139989339","loc":[-85.6332912,41.9425383]},"n2139989341":{"id":"n2139989341","loc":[-85.6339369,41.9409198]},"n2139989344":{"id":"n2139989344","loc":[-85.634097,41.9409469]},"n2139989346":{"id":"n2139989346","loc":[-85.634137,41.9412852]},"n2139989348":{"id":"n2139989348","loc":[-85.6344536,41.9414151]},"n2139989350":{"id":"n2139989350","loc":[-85.6350794,41.9412392]},"n2139989351":{"id":"n2139989351","loc":[-85.6352541,41.9409387]},"n2139989353":{"id":"n2139989353","loc":[-85.6357198,41.9408007]},"n2139989355":{"id":"n2139989355","loc":[-85.6357235,41.9427088]},"n2139989357":{"id":"n2139989357","loc":[-85.6337119,41.9421256]},"n2139989359":{"id":"n2139989359","loc":[-85.6336913,41.9420655]},"n2139989360":{"id":"n2139989360","loc":[-85.633582,41.9420867]},"n2139989362":{"id":"n2139989362","loc":[-85.6336058,41.9421491]},"n2139989364":{"id":"n2139989364","loc":[-85.6339685,41.9410995]},"n2139989366":{"id":"n2139989366","loc":[-85.6339067,41.9411383]},"n2139989368":{"id":"n2139989368","loc":[-85.6339685,41.9411972]},"n2139989370":{"id":"n2139989370","loc":[-85.6340398,41.9411619]},"n2139870379":{"id":"n2139870379","loc":[-85.6348391,41.9416651]},"n2140006363":{"id":"n2140006363","loc":[-85.6353144,41.9430345]},"n2140006364":{"id":"n2140006364","loc":[-85.6349191,41.9431422]},"n2140018997":{"id":"n2140018997","loc":[-85.63645945147184,41.942986488012565],"tags":{"amenity":"townhall","name":"Three Rivers City Hall"}},"n2140018998":{"id":"n2140018998","loc":[-85.6370319,41.9427919]},"n2140018999":{"id":"n2140018999","loc":[-85.6360687,41.9427808]},"n2199856288":{"id":"n2199856288","loc":[-85.6344968,41.9407307]},"n2199856289":{"id":"n2199856289","loc":[-85.634492,41.9406036]},"n2199856290":{"id":"n2199856290","loc":[-85.634891,41.9406001]},"n2199856291":{"id":"n2199856291","loc":[-85.6348894,41.9405288]},"n2199856292":{"id":"n2199856292","loc":[-85.6349166,41.94053]},"n2199856293":{"id":"n2199856293","loc":[-85.6349166,41.9404956]},"n2199856294":{"id":"n2199856294","loc":[-85.6350219,41.9404956]},"n2199856295":{"id":"n2199856295","loc":[-85.6350251,41.94053]},"n2199856296":{"id":"n2199856296","loc":[-85.6350538,41.9405288]},"n2199856297":{"id":"n2199856297","loc":[-85.6350602,41.94079]},"n2199856298":{"id":"n2199856298","loc":[-85.6351703,41.9407912]},"n2199856299":{"id":"n2199856299","loc":[-85.6351688,41.9409171]},"n2199856300":{"id":"n2199856300","loc":[-85.6347889,41.9409135]},"n2199856301":{"id":"n2199856301","loc":[-85.6347921,41.94079]},"n2199856302":{"id":"n2199856302","loc":[-85.6348942,41.9407888]},"n2199856303":{"id":"n2199856303","loc":[-85.6348926,41.9407283]},"n185951869":{"id":"n185951869","loc":[-85.6387639,41.957288]},"n185958643":{"id":"n185958643","loc":[-85.636746,41.929221]},"n185958645":{"id":"n185958645","loc":[-85.636791,41.929363]},"n185958647":{"id":"n185958647","loc":[-85.6375975,41.9314987]},"n185958649":{"id":"n185958649","loc":[-85.637669,41.931667]},"n185958651":{"id":"n185958651","loc":[-85.637728,41.931901]},"n185958653":{"id":"n185958653","loc":[-85.637724,41.932187]},"n185958656":{"id":"n185958656","loc":[-85.637732,41.932761]},"n185958658":{"id":"n185958658","loc":[-85.637688,41.93398]},"n185958660":{"id":"n185958660","loc":[-85.637685,41.934223]},"n185958662":{"id":"n185958662","loc":[-85.6376468,41.9350232]},"n185958664":{"id":"n185958664","loc":[-85.637564,41.937028]},"n185958666":{"id":"n185958666","loc":[-85.637458,41.938197]},"n185958668":{"id":"n185958668","loc":[-85.637424,41.938692]},"n185964972":{"id":"n185964972","loc":[-85.6341901,41.9432732]},"n185971361":{"id":"n185971361","loc":[-85.635762,41.938208]},"n185971364":{"id":"n185971364","loc":[-85.635732,41.9384]},"n185971366":{"id":"n185971366","loc":[-85.635736,41.938697]},"n185972775":{"id":"n185972775","loc":[-85.635638,42.070357]},"n185972777":{"id":"n185972777","loc":[-85.635724,42.069929]},"n185972779":{"id":"n185972779","loc":[-85.635804,42.069248]},"n185972781":{"id":"n185972781","loc":[-85.635869,42.068361]},"n185972783":{"id":"n185972783","loc":[-85.635883,42.067582]},"n185972785":{"id":"n185972785","loc":[-85.635875,42.067114]},"n185972787":{"id":"n185972787","loc":[-85.635778,42.065359]},"n185972788":{"id":"n185972788","loc":[-85.635728,42.063416]},"n185972789":{"id":"n185972789","loc":[-85.635665,42.062491]},"n185972790":{"id":"n185972790","loc":[-85.635617,42.061928]},"n185972791":{"id":"n185972791","loc":[-85.635614,42.061898]},"n185972793":{"id":"n185972793","loc":[-85.635379,42.060288]},"n185972795":{"id":"n185972795","loc":[-85.635092,42.05799]},"n185972797":{"id":"n185972797","loc":[-85.634843,42.055781]},"n185972798":{"id":"n185972798","loc":[-85.634817,42.055549]},"n185972800":{"id":"n185972800","loc":[-85.634708,42.053942]},"n185972802":{"id":"n185972802","loc":[-85.634447,42.051809]},"n185972805":{"id":"n185972805","loc":[-85.634241,42.04946]},"n185972807":{"id":"n185972807","loc":[-85.633787,42.045926]},"n185972809":{"id":"n185972809","loc":[-85.633811,42.045645]},"n185972811":{"id":"n185972811","loc":[-85.63373,42.043626]},"n185972813":{"id":"n185972813","loc":[-85.633698,42.042184]},"n185972814":{"id":"n185972814","loc":[-85.63369,42.04181]},"n185972815":{"id":"n185972815","loc":[-85.633681,42.040714]},"n185972816":{"id":"n185972816","loc":[-85.633571,42.036322]},"n185972817":{"id":"n185972817","loc":[-85.633537,42.034044]},"n185972819":{"id":"n185972819","loc":[-85.633481,42.030785]},"n185972821":{"id":"n185972821","loc":[-85.633452,42.027538]},"n185972824":{"id":"n185972824","loc":[-85.633438,42.027427]},"n185972826":{"id":"n185972826","loc":[-85.633342,42.022656]},"n185972830":{"id":"n185972830","loc":[-85.63327,42.020724]},"n185972832":{"id":"n185972832","loc":[-85.633198,42.019106]},"n185972834":{"id":"n185972834","loc":[-85.633249,42.018363]},"n185972835":{"id":"n185972835","loc":[-85.633139,42.012944]},"n185972836":{"id":"n185972836","loc":[-85.63309,42.008284]},"n185972839":{"id":"n185972839","loc":[-85.63298,42.00005]},"n185972845":{"id":"n185972845","loc":[-85.6325369,41.9764959]},"n185972847":{"id":"n185972847","loc":[-85.6327549,41.9750005]},"n185972849":{"id":"n185972849","loc":[-85.6329374,41.9742527]},"n185972851":{"id":"n185972851","loc":[-85.6331387,41.9736039]},"n185972862":{"id":"n185972862","loc":[-85.6383589,41.9585023]},"n185972868":{"id":"n185972868","loc":[-85.6393633,41.9551716]},"n185972878":{"id":"n185972878","loc":[-85.639377,41.95335]},"n185972882":{"id":"n185972882","loc":[-85.6389179,41.9516944]},"n185972885":{"id":"n185972885","loc":[-85.6387444,41.9512105]},"n185972891":{"id":"n185972891","loc":[-85.636421,41.946392]},"n185972895":{"id":"n185972895","loc":[-85.635965,41.945809]},"n185972897":{"id":"n185972897","loc":[-85.635683,41.945449]},"n185972899":{"id":"n185972899","loc":[-85.635281,41.9450252]},"n185972905":{"id":"n185972905","loc":[-85.6324428,41.9425743]},"n185985217":{"id":"n185985217","loc":[-85.638243,41.943674]},"n185985219":{"id":"n185985219","loc":[-85.638228,41.943747]},"n185985221":{"id":"n185985221","loc":[-85.638163,41.943797]},"n185985222":{"id":"n185985222","loc":[-85.638089,41.943832]},"n185985223":{"id":"n185985223","loc":[-85.637969,41.943841]},"n185985225":{"id":"n185985225","loc":[-85.637841,41.943833]},"n185985227":{"id":"n185985227","loc":[-85.637601,41.943789]},"n185985229":{"id":"n185985229","loc":[-85.637449,41.943754]},"n185985231":{"id":"n185985231","loc":[-85.637342,41.943734]},"n185985233":{"id":"n185985233","loc":[-85.637218,41.943703]},"n185985235":{"id":"n185985235","loc":[-85.637151,41.943663]},"n185985238":{"id":"n185985238","loc":[-85.637118,41.943615]},"n185985240":{"id":"n185985240","loc":[-85.637073,41.943494]},"n185990434":{"id":"n185990434","loc":[-85.6329028,41.9984292],"tags":{"railway":"level_crossing"}},"n1475284023":{"id":"n1475284023","loc":[-85.6336163,41.9435806],"tags":{"railway":"level_crossing"}},"n1475293222":{"id":"n1475293222","loc":[-85.6394045,41.953658],"tags":{"railway":"level_crossing"}},"n1475293226":{"id":"n1475293226","loc":[-85.6364975,41.9638663],"tags":{"railway":"level_crossing"}},"n1475293234":{"id":"n1475293234","loc":[-85.6390449,41.9565145]},"n1475293240":{"id":"n1475293240","loc":[-85.636943,41.9473114]},"n1475293252":{"id":"n1475293252","loc":[-85.6392115,41.9559003]},"n1475293254":{"id":"n1475293254","loc":[-85.6348931,41.9685127],"tags":{"railway":"level_crossing"}},"n1475293260":{"id":"n1475293260","loc":[-85.6375999,41.9485401]},"n1475293261":{"id":"n1475293261","loc":[-85.6391256,41.9523817],"tags":{"railway":"level_crossing"}},"n1475293264":{"id":"n1475293264","loc":[-85.6394155,41.9546493],"tags":{"railway":"level_crossing"}},"n1819805614":{"id":"n1819805614","loc":[-85.6345652,41.9363097]},"n1819805618":{"id":"n1819805618","loc":[-85.6295334,41.9426862]},"n1819805622":{"id":"n1819805622","loc":[-85.6308208,41.9430773]},"n1819805626":{"id":"n1819805626","loc":[-85.6274734,41.9406592]},"n1819805629":{"id":"n1819805629","loc":[-85.6296943,41.9430533]},"n1819805632":{"id":"n1819805632","loc":[-85.6340931,41.9354477]},"n1819805636":{"id":"n1819805636","loc":[-85.6304131,41.9436598]},"n1819805639":{"id":"n1819805639","loc":[-85.6304882,41.9426623]},"n1819805641":{"id":"n1819805641","loc":[-85.6336103,41.9367487]},"n1819805643":{"id":"n1819805643","loc":[-85.6300376,41.9418084]},"n1819805645":{"id":"n1819805645","loc":[-85.6365286,41.9336679]},"n1819805647":{"id":"n1819805647","loc":[-85.632016,41.9429221]},"n1819805666":{"id":"n1819805666","loc":[-85.6314753,41.9442663]},"n1819805669":{"id":"n1819805669","loc":[-85.6268619,41.9402203]},"n1819805673":{"id":"n1819805673","loc":[-85.6296728,41.9412099]},"n1819805676":{"id":"n1819805676","loc":[-85.6354557,41.932766]},"n1819805680":{"id":"n1819805680","loc":[-85.632752,41.9431012]},"n1819805683":{"id":"n1819805683","loc":[-85.631147,41.9432014]},"n1819805687":{"id":"n1819805687","loc":[-85.635284,41.9343942]},"n1819805690":{"id":"n1819805690","loc":[-85.6249736,41.9405794]},"n1819805694":{"id":"n1819805694","loc":[-85.6294153,41.9417925]},"n1819805698":{"id":"n1819805698","loc":[-85.6323486,41.9426986]},"n1819805702":{"id":"n1819805702","loc":[-85.6340287,41.9373871]},"n1819805707":{"id":"n1819805707","loc":[-85.6353698,41.9316326]},"n1819805711":{"id":"n1819805711","loc":[-85.6284176,41.940356]},"n1819805715":{"id":"n1819805715","loc":[-85.6291471,41.9412897]},"n1819805718":{"id":"n1819805718","loc":[-85.6311105,41.943979]},"n1819805722":{"id":"n1819805722","loc":[-85.6320868,41.9400128]},"n1819805724":{"id":"n1819805724","loc":[-85.635166,41.9324627]},"n1819805727":{"id":"n1819805727","loc":[-85.6344686,41.9350567]},"n1819805728":{"id":"n1819805728","loc":[-85.6357132,41.9332369]},"n1819805731":{"id":"n1819805731","loc":[-85.629984,41.9434444]},"n1819805760":{"id":"n1819805760","loc":[-85.6330996,41.9378784]},"n1819805766":{"id":"n1819805766","loc":[-85.625274,41.9411141]},"n1819805770":{"id":"n1819805770","loc":[-85.6326321,41.9412173]},"n1819805774":{"id":"n1819805774","loc":[-85.6347047,41.9312096]},"n1819805777":{"id":"n1819805777","loc":[-85.6363569,41.9339552]},"n1819805780":{"id":"n1819805780","loc":[-85.6327392,41.941926]},"n1819805783":{"id":"n1819805783","loc":[-85.6357239,41.9338435]},"n1819805786":{"id":"n1819805786","loc":[-85.6356595,41.9346576]},"n1819805789":{"id":"n1819805789","loc":[-85.6316469,41.9436598]},"n1819805792":{"id":"n1819805792","loc":[-85.6350587,41.9354557]},"n1819805795":{"id":"n1819805795","loc":[-85.6360028,41.9322791]},"n1819805798":{"id":"n1819805798","loc":[-85.63125,41.9443062]},"n1819805802":{"id":"n1819805802","loc":[-85.6263362,41.9408109]},"n1819805805":{"id":"n1819805805","loc":[-85.6315075,41.9438753]},"n1819805808":{"id":"n1819805808","loc":[-85.6340008,41.9316051]},"n1819805810":{"id":"n1819805810","loc":[-85.6345545,41.9320557]},"n1819805812":{"id":"n1819805812","loc":[-85.6250809,41.9408587]},"n1819805814":{"id":"n1819805814","loc":[-85.6257783,41.9400926]},"n1819805834":{"id":"n1819805834","loc":[-85.6326408,41.9424363]},"n1819805838":{"id":"n1819805838","loc":[-85.6365607,41.9334365]},"n1819805842":{"id":"n1819805842","loc":[-85.6288253,41.9410343]},"n1819805846":{"id":"n1819805846","loc":[-85.6279133,41.9402921]},"n1819805849":{"id":"n1819805849","loc":[-85.6289433,41.9405156]},"n1819805852":{"id":"n1819805852","loc":[-85.6313787,41.9439152]},"n1819805858":{"id":"n1819805858","loc":[-85.6300805,41.9420398]},"n1819805861":{"id":"n1819805861","loc":[-85.6321941,41.9396297]},"n1819805864":{"id":"n1819805864","loc":[-85.6329129,41.9393903]},"n1819805868":{"id":"n1819805868","loc":[-85.632001,41.9434922]},"n1819805870":{"id":"n1819805870","loc":[-85.6314903,41.9431535]},"n1819805873":{"id":"n1819805873","loc":[-85.6251667,41.9401166]},"n1819805876":{"id":"n1819805876","loc":[-85.63287,41.939941]},"n1819805878":{"id":"n1819805878","loc":[-85.6307886,41.9437317]},"n1819805880":{"id":"n1819805880","loc":[-85.6321727,41.940348]},"n1819805883":{"id":"n1819805883","loc":[-85.6265872,41.940113]},"n1819805885":{"id":"n1819805885","loc":[-85.6268404,41.9406672]},"n1819805887":{"id":"n1819805887","loc":[-85.6325267,41.9389035]},"n1819805889":{"id":"n1819805889","loc":[-85.6364964,41.933189]},"n1819805911":{"id":"n1819805911","loc":[-85.6248663,41.9401804]},"n1819805922":{"id":"n1819805922","loc":[-85.633267,41.9387199]},"n1819805925":{"id":"n1819805925","loc":[-85.6293402,41.9408428]},"n1819848849":{"id":"n1819848849","loc":[-85.6464957,41.9695178]},"n1819848850":{"id":"n1819848850","loc":[-85.6497642,41.9611355]},"n1819848851":{"id":"n1819848851","loc":[-85.6480943,41.9624818]},"n1819848854":{"id":"n1819848854","loc":[-85.6500362,41.9657367]},"n1819848855":{"id":"n1819848855","loc":[-85.6493673,41.9783496]},"n1819848856":{"id":"n1819848856","loc":[-85.6457409,41.9548007]},"n1819848857":{"id":"n1819848857","loc":[-85.651313,41.9760426]},"n1819848858":{"id":"n1819848858","loc":[-85.6495819,41.9784772]},"n1819848859":{"id":"n1819848859","loc":[-85.6495105,41.9833722]},"n1819848860":{"id":"n1819848860","loc":[-85.6405053,41.9492792]},"n1819848863":{"id":"n1819848863","loc":[-85.6502293,41.9786826]},"n1819848865":{"id":"n1819848865","loc":[-85.6406877,41.9495106]},"n1819848870":{"id":"n1819848870","loc":[-85.6493136,41.9704611]},"n1819848871":{"id":"n1819848871","loc":[-85.6372249,41.9441284]},"n1819848873":{"id":"n1819848873","loc":[-85.6512379,41.9659441]},"n1819848875":{"id":"n1819848875","loc":[-85.6508087,41.9650187]},"n1819848877":{"id":"n1819848877","loc":[-85.6487166,41.9605352]},"n1819848878":{"id":"n1819848878","loc":[-85.6506478,41.9760665]},"n1819848879":{"id":"n1819848879","loc":[-85.651431,41.9758512]},"n1819848886":{"id":"n1819848886","loc":[-85.6477617,41.9563945]},"n1819848889":{"id":"n1819848889","loc":[-85.6497895,41.9832286]},"n1819848892":{"id":"n1819848892","loc":[-85.6504868,41.9791931]},"n1819848893":{"id":"n1819848893","loc":[-85.6498002,41.9615085]},"n1819848894":{"id":"n1819848894","loc":[-85.6404302,41.9502846]},"n1819848901":{"id":"n1819848901","loc":[-85.6354412,41.9439886]},"n1819848903":{"id":"n1819848903","loc":[-85.6472145,41.9698528]},"n1819848904":{"id":"n1819848904","loc":[-85.6401979,41.9486233]},"n1819848905":{"id":"n1819848905","loc":[-85.6475042,41.963503]},"n1819848909":{"id":"n1819848909","loc":[-85.6343405,41.94358]},"n1819848914":{"id":"n1819848914","loc":[-85.6503474,41.9737773]},"n1819848915":{"id":"n1819848915","loc":[-85.6389533,41.9470992]},"n1819848916":{"id":"n1819848916","loc":[-85.6483625,41.9577907]},"n1819848917":{"id":"n1819848917","loc":[-85.6484768,41.9617419]},"n1819848918":{"id":"n1819848918","loc":[-85.644078,41.9545693]},"n1819848919":{"id":"n1819848919","loc":[-85.6437169,41.9543041]},"n1819848920":{"id":"n1819848920","loc":[-85.6478331,41.9627949]},"n1819848922":{"id":"n1819848922","loc":[-85.6499144,41.9785889]},"n1819848924":{"id":"n1819848924","loc":[-85.647633,41.9720066]},"n1819848926":{"id":"n1819848926","loc":[-85.6487987,41.978868]},"n1819848927":{"id":"n1819848927","loc":[-85.6495105,41.9730355]},"n1819848928":{"id":"n1819848928","loc":[-85.648223,41.9829654]},"n1819848929":{"id":"n1819848929","loc":[-85.6514846,41.9659122]},"n1819848931":{"id":"n1819848931","loc":[-85.6498753,41.9731871]},"n1819848932":{"id":"n1819848932","loc":[-85.640906,41.9508575]},"n1819848933":{"id":"n1819848933","loc":[-85.649775,41.9799767]},"n1819848934":{"id":"n1819848934","loc":[-85.6507014,41.9739927]},"n1819848937":{"id":"n1819848937","loc":[-85.6479763,41.9840899]},"n1819848938":{"id":"n1819848938","loc":[-85.6501113,41.9600884]},"n1819848939":{"id":"n1819848939","loc":[-85.6389962,41.9478253]},"n1819848941":{"id":"n1819848941","loc":[-85.637469,41.9445791]},"n1819848942":{"id":"n1819848942","loc":[-85.6494569,41.9601682]},"n1819848943":{"id":"n1819848943","loc":[-85.6368803,41.9439351]},"n1819848945":{"id":"n1819848945","loc":[-85.6474398,41.9724213]},"n1819848946":{"id":"n1819848946","loc":[-85.6382629,41.9463666]},"n1819848948":{"id":"n1819848948","loc":[-85.6489633,41.9830771]},"n1819848952":{"id":"n1819848952","loc":[-85.6488882,41.9600326]},"n1819848953":{"id":"n1819848953","loc":[-85.6488094,41.9774324]},"n1819848954":{"id":"n1819848954","loc":[-85.6491135,41.9600485]},"n1819848955":{"id":"n1819848955","loc":[-85.6501435,41.9734583]},"n1819848956":{"id":"n1819848956","loc":[-85.6495534,41.960958]},"n1819848958":{"id":"n1819848958","loc":[-85.6474683,41.9561491]},"n1819848959":{"id":"n1819848959","loc":[-85.6401083,41.9485451]},"n1819848960":{"id":"n1819848960","loc":[-85.6481764,41.9678686]},"n1819848961":{"id":"n1819848961","loc":[-85.6484017,41.967382]},"n1819848962":{"id":"n1819848962","loc":[-85.6501328,41.959897]},"n1819848964":{"id":"n1819848964","loc":[-85.6403695,41.9504586]},"n1819848966":{"id":"n1819848966","loc":[-85.6398975,41.9491499]},"n1819848967":{"id":"n1819848967","loc":[-85.6412455,41.9510187]},"n1819848968":{"id":"n1819848968","loc":[-85.6482622,41.9619493]},"n1819848969":{"id":"n1819848969","loc":[-85.6405841,41.9501474]},"n1819848970":{"id":"n1819848970","loc":[-85.6478583,41.9703394]},"n1819848971":{"id":"n1819848971","loc":[-85.6493388,41.9832845]},"n1819848972":{"id":"n1819848972","loc":[-85.6485664,41.9829415]},"n1819848974":{"id":"n1819848974","loc":[-85.6491457,41.9779887]},"n1819848975":{"id":"n1819848975","loc":[-85.6468889,41.9697033]},"n1819848976":{"id":"n1819848976","loc":[-85.6452726,41.9546072]},"n1819848977":{"id":"n1819848977","loc":[-85.6448435,41.9546072]},"n1819848979":{"id":"n1819848979","loc":[-85.6485342,41.9763138]},"n1819848980":{"id":"n1819848980","loc":[-85.6495282,41.9664087]},"n1819848986":{"id":"n1819848986","loc":[-85.6486307,41.9603278]},"n1819848987":{"id":"n1819848987","loc":[-85.6492278,41.9791871]},"n1819848990":{"id":"n1819848990","loc":[-85.6501934,41.9800724]},"n1819848992":{"id":"n1819848992","loc":[-85.6482445,41.9819685]},"n1819848993":{"id":"n1819848993","loc":[-85.6481871,41.9704451]},"n1819848994":{"id":"n1819848994","loc":[-85.6371364,41.9457602]},"n1819848996":{"id":"n1819848996","loc":[-85.6500362,41.9801023]},"n1819849000":{"id":"n1819849000","loc":[-85.639007,41.9485914]},"n1819849001":{"id":"n1819849001","loc":[-85.6488882,41.9669253]},"n1819849002":{"id":"n1819849002","loc":[-85.6484698,41.9565062]},"n1819849004":{"id":"n1819849004","loc":[-85.6510769,41.9761064]},"n1819849005":{"id":"n1819849005","loc":[-85.6503581,41.9799029]},"n1819849006":{"id":"n1819849006","loc":[-85.6489381,41.9703893]},"n1819849008":{"id":"n1819849008","loc":[-85.6497457,41.9833588]},"n1819849011":{"id":"n1819849011","loc":[-85.6497358,41.9717593]},"n1819849012":{"id":"n1819849012","loc":[-85.6494676,41.9796796]},"n1819849019":{"id":"n1819849019","loc":[-85.6486093,41.9771034]},"n1819849021":{"id":"n1819849021","loc":[-85.6504546,41.9796556]},"n1819849022":{"id":"n1819849022","loc":[-85.6371294,41.9454154]},"n1819849023":{"id":"n1819849023","loc":[-85.6503436,41.9759249]},"n1819849025":{"id":"n1819849025","loc":[-85.6462382,41.9693822]},"n1819849026":{"id":"n1819849026","loc":[-85.6497573,41.983093]},"n1819849028":{"id":"n1819849028","loc":[-85.6497465,41.9602799]},"n1819849029":{"id":"n1819849029","loc":[-85.6374728,41.9460698]},"n1819849030":{"id":"n1819849030","loc":[-85.6486592,41.9566039]},"n1819849031":{"id":"n1819849031","loc":[-85.6515989,41.9654993]},"n1819849032":{"id":"n1819849032","loc":[-85.6387028,41.9482658]},"n1819849033":{"id":"n1819849033","loc":[-85.6464742,41.9688398]},"n1819849034":{"id":"n1819849034","loc":[-85.6495212,41.9589236]},"n1819849035":{"id":"n1819849035","loc":[-85.6490599,41.9790096]},"n1819849036":{"id":"n1819849036","loc":[-85.6489918,41.9800724]},"n1819849038":{"id":"n1819849038","loc":[-85.6499182,41.9659042]},"n1819849040":{"id":"n1819849040","loc":[-85.639758,41.9490143]},"n1819849041":{"id":"n1819849041","loc":[-85.6514846,41.9755241]},"n1819849042":{"id":"n1819849042","loc":[-85.6436633,41.9540647]},"n1819849045":{"id":"n1819849045","loc":[-85.6475541,41.9726387]},"n1819849046":{"id":"n1819849046","loc":[-85.6488308,41.9718331]},"n1819849047":{"id":"n1819849047","loc":[-85.6377694,41.9460953]},"n1819849048":{"id":"n1819849048","loc":[-85.6490706,41.9804452]},"n1819849049":{"id":"n1819849049","loc":[-85.6485449,41.9766248]},"n1819849051":{"id":"n1819849051","loc":[-85.6483625,41.9790256]},"n1819849052":{"id":"n1819849052","loc":[-85.6490706,41.9585167]},"n1819849053":{"id":"n1819849053","loc":[-85.6425008,41.9522874]},"n1819849054":{"id":"n1819849054","loc":[-85.6475793,41.9632158]},"n1819849055":{"id":"n1819849055","loc":[-85.6408631,41.9499399]},"n1819849056":{"id":"n1819849056","loc":[-85.6483373,41.9814681]},"n1819849057":{"id":"n1819849057","loc":[-85.6313548,41.9442876]},"n1819849058":{"id":"n1819849058","loc":[-85.6432663,41.9529796]},"n1819849059":{"id":"n1819849059","loc":[-85.6487128,41.9582873]},"n1819849060":{"id":"n1819849060","loc":[-85.6482338,41.9817612]},"n1819849062":{"id":"n1819849062","loc":[-85.6485664,41.9788661]},"n1819849063":{"id":"n1819849063","loc":[-85.6373081,41.9448824]},"n1819849064":{"id":"n1819849064","loc":[-85.6472215,41.9557582]},"n1819849065":{"id":"n1819849065","loc":[-85.6348984,41.9440414]},"n1819849066":{"id":"n1819849066","loc":[-85.6501972,41.9647315]},"n1819849067":{"id":"n1819849067","loc":[-85.6489741,41.9808281]},"n1819849068":{"id":"n1819849068","loc":[-85.6420111,41.9515034]},"n1819849069":{"id":"n1819849069","loc":[-85.6397972,41.9488882]},"n1819849070":{"id":"n1819849070","loc":[-85.6499718,41.9593465]},"n1819849071":{"id":"n1819849071","loc":[-85.6486844,41.9811311]},"n1819849072":{"id":"n1819849072","loc":[-85.6390392,41.9474663]},"n1819849074":{"id":"n1819849074","loc":[-85.6495642,41.9616362]},"n1819849075":{"id":"n1819849075","loc":[-85.6483518,41.9791931]},"n1819849076":{"id":"n1819849076","loc":[-85.6478974,41.9833104]},"n1819849077":{"id":"n1819849077","loc":[-85.640155,41.948719]},"n1819849078":{"id":"n1819849078","loc":[-85.6399366,41.9487845]},"n1819849079":{"id":"n1819849079","loc":[-85.6492959,41.9825348]},"n1819849080":{"id":"n1819849080","loc":[-85.6505083,41.9648352]},"n1819849081":{"id":"n1819849081","loc":[-85.6492959,41.9645241]},"n1819849082":{"id":"n1819849082","loc":[-85.6402049,41.9491835]},"n1819849083":{"id":"n1819849083","loc":[-85.6495175,41.9826963]},"n1819849084":{"id":"n1819849084","loc":[-85.6480836,41.9728361]},"n1819849085":{"id":"n1819849085","loc":[-85.6374349,41.9443425]},"n1819849086":{"id":"n1819849086","loc":[-85.6478331,41.9681238]},"n1819849089":{"id":"n1819849089","loc":[-85.639368,41.9486169]},"n1819849092":{"id":"n1819849092","loc":[-85.6503581,41.9788022]},"n1819849093":{"id":"n1819849093","loc":[-85.64862,41.9568014]},"n1819849094":{"id":"n1819849094","loc":[-85.6496999,41.9828877]},"n1819849095":{"id":"n1819849095","loc":[-85.647472,41.972198]},"n1819849096":{"id":"n1819849096","loc":[-85.6485771,41.9644523]},"n1819849097":{"id":"n1819849097","loc":[-85.6388353,41.9480488]},"n1819849099":{"id":"n1819849099","loc":[-85.6472752,41.9683312]},"n1819849104":{"id":"n1819849104","loc":[-85.6479548,41.9836035]},"n1819849105":{"id":"n1819849105","loc":[-85.6462489,41.9691668]},"n1819849107":{"id":"n1819849107","loc":[-85.6511912,41.9746328]},"n1819849108":{"id":"n1819849108","loc":[-85.6498646,41.9714881]},"n1819849111":{"id":"n1819849111","loc":[-85.6488239,41.961684]},"n1819849112":{"id":"n1819849112","loc":[-85.6469356,41.9553812]},"n1819849114":{"id":"n1819849114","loc":[-85.6479548,41.9640853]},"n1819849119":{"id":"n1819849119","loc":[-85.6491565,41.961692]},"n1819849121":{"id":"n1819849121","loc":[-85.651667,41.9656728]},"n1819849124":{"id":"n1819849124","loc":[-85.6388423,41.9484414]},"n1819849126":{"id":"n1819849126","loc":[-85.6371686,41.9450978]},"n1819849127":{"id":"n1819849127","loc":[-85.6502615,41.9656728]},"n1819849129":{"id":"n1819849129","loc":[-85.6498501,41.9613031]},"n1819849131":{"id":"n1819849131","loc":[-85.6513881,41.9653298]},"n1819849133":{"id":"n1819849133","loc":[-85.639883,41.9485291]},"n1819849139":{"id":"n1819849139","loc":[-85.6508693,41.9658264]},"n1819849140":{"id":"n1819849140","loc":[-85.6486806,41.9761642]},"n1819849141":{"id":"n1819849141","loc":[-85.6483159,41.9717613]},"n1819849144":{"id":"n1819849144","loc":[-85.6443714,41.9546232]},"n1819849146":{"id":"n1819849146","loc":[-85.641775,41.9513359]},"n1819849147":{"id":"n1819849147","loc":[-85.6495604,41.9757335]},"n1819849148":{"id":"n1819849148","loc":[-85.6465671,41.9551678]},"n1819849150":{"id":"n1819849150","loc":[-85.6485127,41.9794084]},"n1819849151":{"id":"n1819849151","loc":[-85.6499144,41.9757096]},"n1819849152":{"id":"n1819849152","loc":[-85.6433736,41.9531072]},"n1819849154":{"id":"n1819849154","loc":[-85.6489741,41.9607426]},"n1819849155":{"id":"n1819849155","loc":[-85.640627,41.9507697]},"n1819849156":{"id":"n1819849156","loc":[-85.6509659,41.9743058]},"n1819849157":{"id":"n1819849157","loc":[-85.6486844,41.9704431]},"n1819849158":{"id":"n1819849158","loc":[-85.6498538,41.9711132]},"n1819849159":{"id":"n1819849159","loc":[-85.6358937,41.943719]},"n1819849160":{"id":"n1819849160","loc":[-85.6497358,41.9707702]},"n1819849161":{"id":"n1819849161","loc":[-85.6480476,41.9564842]},"n1819849162":{"id":"n1819849162","loc":[-85.6482982,41.9574556]},"n1819849163":{"id":"n1819849163","loc":[-85.6501757,41.9757794]},"n1819849164":{"id":"n1819849164","loc":[-85.6372973,41.9459916]},"n1819849165":{"id":"n1819849165","loc":[-85.6513773,41.9750775]},"n1819849166":{"id":"n1819849166","loc":[-85.6436418,41.9537455]},"n1819849167":{"id":"n1819849167","loc":[-85.6483625,41.9571524]},"n1819849169":{"id":"n1819849169","loc":[-85.647751,41.9727962]},"n1819849170":{"id":"n1819849170","loc":[-85.6504546,41.9656808]},"n1819849171":{"id":"n1819849171","loc":[-85.6479977,41.971839]},"n1819849172":{"id":"n1819849172","loc":[-85.6482767,41.9642449]},"n1819849174":{"id":"n1819849174","loc":[-85.6414317,41.9512086]},"n1819849176":{"id":"n1819849176","loc":[-85.6469034,41.9685287]},"n1819849179":{"id":"n1819849179","loc":[-85.6408631,41.9497564]},"n1819849182":{"id":"n1819849182","loc":[-85.6476721,41.96384]},"n1819849183":{"id":"n1819849183","loc":[-85.6479725,41.983111]},"n1819849184":{"id":"n1819849184","loc":[-85.640788,41.9500516]},"n1819849185":{"id":"n1819849185","loc":[-85.6427798,41.9528778]},"n1819849186":{"id":"n1819849186","loc":[-85.6435308,41.9534124]},"n1819849187":{"id":"n1819849187","loc":[-85.6483733,41.9821998]},"n1819849189":{"id":"n1819849189","loc":[-85.6351752,41.9440796]},"n1819849191":{"id":"n1819849191","loc":[-85.6487021,41.9601463]},"n1819849192":{"id":"n1819849192","loc":[-85.6363811,41.9437605]},"n1819849193":{"id":"n1819849193","loc":[-85.6490883,41.9759728]},"n1819849194":{"id":"n1819849194","loc":[-85.6423292,41.9520081]},"n1819849195":{"id":"n1819849195","loc":[-85.6500003,41.960242]},"n1819849196":{"id":"n1819849196","loc":[-85.6385778,41.9466443]},"n1819849197":{"id":"n1819849197","loc":[-85.6494032,41.9718789]},"n1819849198":{"id":"n1819849198","loc":[-85.6404339,41.9506501]},"n1819849199":{"id":"n1819849199","loc":[-85.6426226,41.9527083]},"n1819849200":{"id":"n1819849200","loc":[-85.6439101,41.9545035]},"n1819849201":{"id":"n1819849201","loc":[-85.6516563,41.9657845]},"n1819849202":{"id":"n1819849202","loc":[-85.6473395,41.9699585]},"n1819858501":{"id":"n1819858501","loc":[-85.6361263,41.9437126]},"n1819858503":{"id":"n1819858503","loc":[-85.6350068,41.944034]},"n1819858513":{"id":"n1819858513","loc":[-85.6371402,41.9453282]},"n1819858518":{"id":"n1819858518","loc":[-85.6348713,41.9432923]},"n1819858523":{"id":"n1819858523","loc":[-85.6357047,41.943799]},"n1819858526":{"id":"n1819858526","loc":[-85.6349947,41.9435756]},"n1819858531":{"id":"n1819858531","loc":[-85.6350376,41.943827]},"n1820937508":{"id":"n1820937508","loc":[-85.1026013,42.0881722]},"n1820937509":{"id":"n1820937509","loc":[-85.0558088,42.102493]},"n1820937511":{"id":"n1820937511","loc":[-85.3030116,41.9724451]},"n1820937513":{"id":"n1820937513","loc":[-85.0353221,42.1027398]},"n1820937514":{"id":"n1820937514","loc":[-85.0835468,42.1015469]},"n1820937515":{"id":"n1820937515","loc":[-85.2421298,42.0106305]},"n1820937517":{"id":"n1820937517","loc":[-85.0090632,42.0910452]},"n1820937518":{"id":"n1820937518","loc":[-85.086626,42.0948838]},"n1820937520":{"id":"n1820937520","loc":[-85.2552039,42.0015448]},"n1820937521":{"id":"n1820937521","loc":[-85.3739614,41.9969917]},"n1820937522":{"id":"n1820937522","loc":[-85.4831166,41.993898]},"n1820937523":{"id":"n1820937523","loc":[-85.0341084,42.0977657]},"n1820937524":{"id":"n1820937524","loc":[-85.3272802,41.9710333]},"n1820937525":{"id":"n1820937525","loc":[-85.2125568,42.0414521]},"n1820937526":{"id":"n1820937526","loc":[-85.3798022,41.9992458]},"n1820937527":{"id":"n1820937527","loc":[-85.2652021,41.999768]},"n1820937528":{"id":"n1820937528","loc":[-85.3852739,42.0004896]},"n1820937529":{"id":"n1820937529","loc":[-85.3911919,42.0030513]},"n1820937530":{"id":"n1820937530","loc":[-85.5440349,41.9717109]},"n1820937531":{"id":"n1820937531","loc":[-85.2790155,41.9911764]},"n1820937532":{"id":"n1820937532","loc":[-85.4723277,41.9950518]},"n1820937533":{"id":"n1820937533","loc":[-85.5690546,41.9653931]},"n1820937535":{"id":"n1820937535","loc":[-85.5674882,41.9649623]},"n1820937536":{"id":"n1820937536","loc":[-85.6362815,41.9189165]},"n1820937537":{"id":"n1820937537","loc":[-85.5659003,41.963638]},"n1820937539":{"id":"n1820937539","loc":[-85.6391353,41.9122262]},"n1820937540":{"id":"n1820937540","loc":[-85.4834385,41.9894803]},"n1820937541":{"id":"n1820937541","loc":[-85.6399078,41.9160744]},"n1820937542":{"id":"n1820937542","loc":[-85.632874,41.941031]},"n1820937543":{"id":"n1820937543","loc":[-85.1307591,42.0726961]},"n1820937544":{"id":"n1820937544","loc":[-85.6444397,41.9128378]},"n1820937545":{"id":"n1820937545","loc":[-85.6197204,41.9420365]},"n1820937546":{"id":"n1820937546","loc":[-85.1164857,42.0864631]},"n1820937547":{"id":"n1820937547","loc":[-85.6476111,41.9142222]},"n1820937548":{"id":"n1820937548","loc":[-85.2915747,41.9774223]},"n1820937549":{"id":"n1820937549","loc":[-85.6430192,41.9102461]},"n1820937550":{"id":"n1820937550","loc":[-85.1597495,42.0639017]},"n1820937551":{"id":"n1820937551","loc":[-85.5504079,41.9701793]},"n1820937553":{"id":"n1820937553","loc":[-85.2781317,41.9948951]},"n1820937555":{"id":"n1820937555","loc":[-85.3724594,41.997518]},"n1820937556":{"id":"n1820937556","loc":[-85.5629434,41.9665155]},"n1820937557":{"id":"n1820937557","loc":[-85.3791971,41.9990808]},"n1820937558":{"id":"n1820937558","loc":[-85.001891,42.0878843]},"n1820937560":{"id":"n1820937560","loc":[-85.3140838,41.9709056]},"n1820937561":{"id":"n1820937561","loc":[-85.2468032,42.0146987]},"n1820937563":{"id":"n1820937563","loc":[-85.0877378,42.097255]},"n1820937564":{"id":"n1820937564","loc":[-85.2442498,42.0150654]},"n1820937566":{"id":"n1820937566","loc":[-85.3108973,41.9701478]},"n1820937568":{"id":"n1820937568","loc":[-85.0344584,42.1016572]},"n1820937569":{"id":"n1820937569","loc":[-85.2331025,42.0297387]},"n1820937570":{"id":"n1820937570","loc":[-85.5058446,41.9746996]},"n1820937571":{"id":"n1820937571","loc":[-85.5622739,41.9676427]},"n1820937572":{"id":"n1820937572","loc":[-85.2792687,41.9890337]},"n1820937574":{"id":"n1820937574","loc":[-84.9909302,42.08695]},"n1820937575":{"id":"n1820937575","loc":[-85.6218233,41.9418609]},"n1820937576":{"id":"n1820937576","loc":[-85.3577437,41.9931062]},"n1820937577":{"id":"n1820937577","loc":[-85.639028,41.9165853]},"n1820937578":{"id":"n1820937578","loc":[-84.9956576,42.0865348]},"n1820937579":{"id":"n1820937579","loc":[-85.4828376,41.990198]},"n1820937580":{"id":"n1820937580","loc":[-85.3244478,41.9720543]},"n1820937582":{"id":"n1820937582","loc":[-85.0517479,42.1035159]},"n1820937583":{"id":"n1820937583","loc":[-85.225646,42.0338025]},"n1820937584":{"id":"n1820937584","loc":[-84.9941019,42.0862163]},"n1820937586":{"id":"n1820937586","loc":[-85.1051762,42.0879452]},"n1820937587":{"id":"n1820937587","loc":[-85.1245203,42.0753162]},"n1820937588":{"id":"n1820937588","loc":[-85.3250808,41.9719506]},"n1820937589":{"id":"n1820937589","loc":[-85.2720109,41.997933]},"n1820937590":{"id":"n1820937590","loc":[-85.2556653,42.0027248]},"n1820937591":{"id":"n1820937591","loc":[-85.0872483,42.0943544]},"n1820937592":{"id":"n1820937592","loc":[-85.2778353,41.9955023]},"n1820937593":{"id":"n1820937593","loc":[-85.2984733,41.9735538]},"n1820937594":{"id":"n1820937594","loc":[-85.101578,42.0889552]},"n1820937595":{"id":"n1820937595","loc":[-85.3888745,42.0016959]},"n1820937596":{"id":"n1820937596","loc":[-84.9903508,42.0870654]},"n1820937597":{"id":"n1820937597","loc":[-85.6405558,41.9146261]},"n1820937598":{"id":"n1820937598","loc":[-85.6460704,41.9141311]},"n1820937599":{"id":"n1820937599","loc":[-85.0377468,42.1037428]},"n1820937600":{"id":"n1820937600","loc":[-85.2298345,42.0312899]},"n1820937601":{"id":"n1820937601","loc":[-85.1080958,42.0861964]},"n1820937602":{"id":"n1820937602","loc":[-85.6325307,41.9402329]},"n1820937603":{"id":"n1820937603","loc":[-85.1165984,42.0832184]},"n1820937604":{"id":"n1820937604","loc":[-85.6354446,41.9190602]},"n1820937605":{"id":"n1820937605","loc":[-85.1114592,42.0862959]},"n1820937606":{"id":"n1820937606","loc":[-85.0858763,42.1001646]},"n1820937607":{"id":"n1820937607","loc":[-85.0472083,42.1015151]},"n1820937608":{"id":"n1820937608","loc":[-85.0802477,42.1027609]},"n1820937610":{"id":"n1820937610","loc":[-85.0924585,42.0928564]},"n1820937611":{"id":"n1820937611","loc":[-85.0329617,42.09827]},"n1820937612":{"id":"n1820937612","loc":[-85.2814617,41.993465]},"n1820937613":{"id":"n1820937613","loc":[-85.3097708,41.9700282]},"n1820937614":{"id":"n1820937614","loc":[-85.2809427,41.993695]},"n1820937615":{"id":"n1820937615","loc":[-85.0583233,42.1026494]},"n1820937617":{"id":"n1820937617","loc":[-85.2801592,41.9840021]},"n1820937619":{"id":"n1820937619","loc":[-85.1064154,42.0863449]},"n1820937620":{"id":"n1820937620","loc":[-85.0423173,42.1014662]},"n1820937621":{"id":"n1820937621","loc":[-85.2168913,42.0398107]},"n1820937622":{"id":"n1820937622","loc":[-85.2798481,41.9833401]},"n1820937623":{"id":"n1820937623","loc":[-85.0575468,42.1028672]},"n1820937625":{"id":"n1820937625","loc":[-85.0130369,42.0893067]},"n1820937626":{"id":"n1820937626","loc":[-85.0346985,42.1018256]},"n1820937627":{"id":"n1820937627","loc":[-85.2231569,42.0372768]},"n1820937628":{"id":"n1820937628","loc":[-85.2956195,41.9732268]},"n1820937629":{"id":"n1820937629","loc":[-85.1052312,42.086893]},"n1820937630":{"id":"n1820937630","loc":[-85.4813356,41.9958436]},"n1820937631":{"id":"n1820937631","loc":[-85.0961599,42.0914672]},"n1820937632":{"id":"n1820937632","loc":[-85.308419,41.9704749]},"n1820937633":{"id":"n1820937633","loc":[-85.295952,41.9715119]},"n1820937634":{"id":"n1820937634","loc":[-85.3310933,41.9703923]},"n1820937635":{"id":"n1820937635","loc":[-85.2940745,41.9739686]},"n1820937636":{"id":"n1820937636","loc":[-85.3803343,42.000484]},"n1820937637":{"id":"n1820937637","loc":[-85.1174231,42.0845533]},"n1820937638":{"id":"n1820937638","loc":[-85.0095836,42.089839]},"n1820937639":{"id":"n1820937639","loc":[-85.3179354,41.9705866]},"n1820937640":{"id":"n1820937640","loc":[-85.257708,42.0001189]},"n1820937641":{"id":"n1820937641","loc":[-85.2563522,42.0002771]},"n1820937642":{"id":"n1820937642","loc":[-85.3181929,41.970419]},"n1820937643":{"id":"n1820937643","loc":[-85.2911884,41.9757154]},"n1820937644":{"id":"n1820937644","loc":[-85.2714423,41.9975862]},"n1820937645":{"id":"n1820937645","loc":[-85.0193669,42.089888]},"n1820937646":{"id":"n1820937646","loc":[-85.3889818,42.0039921]},"n1820937647":{"id":"n1820937647","loc":[-85.3408093,41.9853965]},"n1820937648":{"id":"n1820937648","loc":[-85.1258091,42.0748332]},"n1820937649":{"id":"n1820937649","loc":[-85.5722561,41.962782]},"n1820937650":{"id":"n1820937650","loc":[-85.3266902,41.9721819]},"n1820937651":{"id":"n1820937651","loc":[-85.1473255,42.065192]},"n1820937652":{"id":"n1820937652","loc":[-85.1462526,42.0655106]},"n1820937653":{"id":"n1820937653","loc":[-85.4641051,42.0013929]},"n1820937654":{"id":"n1820937654","loc":[-85.5620379,41.9700677]},"n1820937655":{"id":"n1820937655","loc":[-85.3226025,41.971121]},"n1820937656":{"id":"n1820937656","loc":[-85.0200965,42.0899516]},"n1820937657":{"id":"n1820937657","loc":[-85.0624714,42.1044711]},"n1820937658":{"id":"n1820937658","loc":[-85.5649562,41.9637178]},"n1820937659":{"id":"n1820937659","loc":[-85.2360315,42.0253315]},"n1820937660":{"id":"n1820937660","loc":[-85.3881449,41.9994475]},"n1820937661":{"id":"n1820937661","loc":[-85.5032911,41.976263]},"n1820937662":{"id":"n1820937662","loc":[-85.481297,41.9871414]},"n1820937663":{"id":"n1820937663","loc":[-85.1167056,42.0841898]},"n1820937664":{"id":"n1820937664","loc":[-85.2891714,41.9787223]},"n1820937665":{"id":"n1820937665","loc":[-85.4393429,42.0058736]},"n1820937666":{"id":"n1820937666","loc":[-85.0077007,42.0895762]},"n1820937667":{"id":"n1820937667","loc":[-85.2736202,41.9979171]},"n1820937668":{"id":"n1820937668","loc":[-84.9935332,42.0859296]},"n1820937669":{"id":"n1820937669","loc":[-85.0622769,42.1046713]},"n1820937670":{"id":"n1820937670","loc":[-85.2309031,42.0311249]},"n1820937671":{"id":"n1820937671","loc":[-85.0343726,42.10069]},"n1820937672":{"id":"n1820937672","loc":[-85.0596551,42.1048612]},"n1820937673":{"id":"n1820937673","loc":[-85.1338597,42.0707449]},"n1820937674":{"id":"n1820937674","loc":[-85.3117663,41.9689194]},"n1820937675":{"id":"n1820937675","loc":[-85.0705649,42.1057499]},"n1820937676":{"id":"n1820937676","loc":[-85.2441425,42.0180944]},"n1820937677":{"id":"n1820937677","loc":[-85.1171174,42.0862692]},"n1820937678":{"id":"n1820937678","loc":[-85.0346824,42.1005519]},"n1820937680":{"id":"n1820937680","loc":[-85.2389927,42.0229245]},"n1820937681":{"id":"n1820937681","loc":[-85.0834892,42.1018642]},"n1820937682":{"id":"n1820937682","loc":[-85.0619443,42.1049459]},"n1820937683":{"id":"n1820937683","loc":[-85.2845366,41.9811868]},"n1820937684":{"id":"n1820937684","loc":[-85.210411,42.0394123]},"n1820937685":{"id":"n1820937685","loc":[-85.4377383,42.0055942]},"n1820937686":{"id":"n1820937686","loc":[-85.2882058,41.9789138]},"n1820937687":{"id":"n1820937687","loc":[-85.2741191,41.9955808]},"n1820937688":{"id":"n1820937688","loc":[-85.3442211,41.9903575]},"n1820937689":{"id":"n1820937689","loc":[-85.2641413,41.9995237]},"n1820937690":{"id":"n1820937690","loc":[-85.2804489,41.9829174]},"n1820937691":{"id":"n1820937691","loc":[-85.5593342,41.9729074]},"n1820937692":{"id":"n1820937692","loc":[-85.3590912,41.9932601]},"n1820937694":{"id":"n1820937694","loc":[-85.4826445,41.9957479]},"n1820937695":{"id":"n1820937695","loc":[-85.4539127,42.0063041]},"n1820937696":{"id":"n1820937696","loc":[-85.2456767,42.0153683]},"n1820937697":{"id":"n1820937697","loc":[-85.5794015,41.9489631]},"n1820937698":{"id":"n1820937698","loc":[-85.4108686,42.0078507]},"n1820937699":{"id":"n1820937699","loc":[-85.0616386,42.1051529]},"n1820937700":{"id":"n1820937700","loc":[-85.4977979,41.978241]},"n1820937701":{"id":"n1820937701","loc":[-85.2488417,42.0086319]},"n1820937702":{"id":"n1820937702","loc":[-85.5588836,41.9728116]},"n1820937703":{"id":"n1820937703","loc":[-85.4557366,42.0051241]},"n1820937705":{"id":"n1820937705","loc":[-85.0723151,42.1056094]},"n1820937706":{"id":"n1820937706","loc":[-85.0057909,42.0887323]},"n1820937707":{"id":"n1820937707","loc":[-85.0756786,42.105677]},"n1820937708":{"id":"n1820937708","loc":[-85.0901504,42.0940001]},"n1820937709":{"id":"n1820937709","loc":[-85.0979999,42.0910213]},"n1820937710":{"id":"n1820937710","loc":[-85.2376301,42.0239686]},"n1820937711":{"id":"n1820937711","loc":[-85.2780671,41.9902299]},"n1820937712":{"id":"n1820937712","loc":[-85.251481,42.0113188]},"n1820937713":{"id":"n1820937713","loc":[-85.3114767,41.9690311]},"n1820937714":{"id":"n1820937714","loc":[-85.2649621,41.9975662]},"n1820937715":{"id":"n1820937715","loc":[-85.283807,41.9813383]},"n1820937716":{"id":"n1820937716","loc":[-85.5515451,41.9703867]},"n1820937717":{"id":"n1820937717","loc":[-85.1176605,42.0850896]},"n1820937718":{"id":"n1820937718","loc":[-85.1069317,42.0862441]},"n1820937719":{"id":"n1820937719","loc":[-85.2739314,41.9976938]},"n1820937720":{"id":"n1820937720","loc":[-85.5550212,41.9702112]},"n1820937721":{"id":"n1820937721","loc":[-85.3076679,41.9719904]},"n1820937722":{"id":"n1820937722","loc":[-85.592319,41.9440316]},"n1820937723":{"id":"n1820937723","loc":[-85.3139979,41.9704031]},"n1820937724":{"id":"n1820937724","loc":[-85.0421134,42.1013149]},"n1820937725":{"id":"n1820937725","loc":[-85.2508373,42.0102741]},"n1820937726":{"id":"n1820937726","loc":[-85.0830922,42.1038821]},"n1820937727":{"id":"n1820937727","loc":[-85.6342473,41.9360031]},"n1820937730":{"id":"n1820937730","loc":[-85.0500192,42.1024942]},"n1820937731":{"id":"n1820937731","loc":[-85.3498644,41.9926221]},"n1820937732":{"id":"n1820937732","loc":[-85.0234117,42.0918903]},"n1820937733":{"id":"n1820937733","loc":[-85.0464425,42.1009408]},"n1820937734":{"id":"n1820937734","loc":[-85.033938,42.099886]},"n1820937736":{"id":"n1820937736","loc":[-85.0152752,42.0886009]},"n1820937737":{"id":"n1820937737","loc":[-85.0441894,42.1012671]},"n1820937738":{"id":"n1820937738","loc":[-85.4668731,41.9979804]},"n1820937739":{"id":"n1820937739","loc":[-85.4407377,42.006033]},"n1820937740":{"id":"n1820937740","loc":[-85.2262253,42.0344878]},"n1820937741":{"id":"n1820937741","loc":[-85.2550001,42.0033706]},"n1820937742":{"id":"n1820937742","loc":[-85.3071422,41.9722617]},"n1820937743":{"id":"n1820937743","loc":[-85.6147852,41.942228]},"n1820937744":{"id":"n1820937744","loc":[-85.0183853,42.0901825]},"n1820937745":{"id":"n1820937745","loc":[-85.6323161,41.9228489]},"n1820937746":{"id":"n1820937746","loc":[-85.0095568,42.0901376]},"n1820937747":{"id":"n1820937747","loc":[-85.2524037,42.0113826]},"n1820937748":{"id":"n1820937748","loc":[-85.3186864,41.9708578]},"n1820937749":{"id":"n1820937749","loc":[-85.2805669,41.9870883]},"n1820937750":{"id":"n1820937750","loc":[-85.0585768,42.1038144]},"n1820937751":{"id":"n1820937751","loc":[-85.2970786,41.9715358]},"n1820937752":{"id":"n1820937752","loc":[-85.1315758,42.0723445]},"n1820937753":{"id":"n1820937753","loc":[-85.2448291,42.0175444]},"n1820937754":{"id":"n1820937754","loc":[-85.2446468,42.0174248]},"n1820937755":{"id":"n1820937755","loc":[-85.229165,42.032129]},"n1820937756":{"id":"n1820937756","loc":[-85.5612654,41.9724926]},"n1820937757":{"id":"n1820937757","loc":[-85.2331776,42.030854]},"n1820937758":{"id":"n1820937758","loc":[-85.2271909,42.0334519]},"n1820937759":{"id":"n1820937759","loc":[-85.1032396,42.0879214]},"n1820937760":{"id":"n1820937760","loc":[-85.0638447,42.1044154]},"n1820937761":{"id":"n1820937761","loc":[-85.1260706,42.0745556]},"n1820937762":{"id":"n1820937762","loc":[-85.3454485,41.99132]},"n1820937763":{"id":"n1820937763","loc":[-85.2639321,41.9980088]},"n1820937764":{"id":"n1820937764","loc":[-85.0837681,42.1013746]},"n1820937765":{"id":"n1820937765","loc":[-85.2808137,41.9869368]},"n1820937766":{"id":"n1820937766","loc":[-85.6338997,41.9309373]},"n1820937767":{"id":"n1820937767","loc":[-85.2267403,42.0332766]},"n1820937768":{"id":"n1820937768","loc":[-85.0605831,42.1052074]},"n1820937769":{"id":"n1820937769","loc":[-85.0259021,42.0930037]},"n1820937770":{"id":"n1820937770","loc":[-85.232963,42.0313162]},"n1820937771":{"id":"n1820937771","loc":[-85.2404947,42.0125381]},"n1820937772":{"id":"n1820937772","loc":[-85.0910892,42.0935742]},"n1820937773":{"id":"n1820937773","loc":[-85.2554829,42.0019435]},"n1820937774":{"id":"n1820937774","loc":[-85.2799339,41.9867773]},"n1820937775":{"id":"n1820937775","loc":[-85.1075432,42.0852767]},"n1820937776":{"id":"n1820937776","loc":[-85.1176927,42.0854001]},"n1820937777":{"id":"n1820937777","loc":[-85.1067064,42.0863357]},"n1820937778":{"id":"n1820937778","loc":[-85.2517492,42.0106333]},"n1820937779":{"id":"n1820937779","loc":[-85.0987174,42.0909031]},"n1820937780":{"id":"n1820937780","loc":[-85.1160083,42.0863994]},"n1820937781":{"id":"n1820937781","loc":[-85.1268645,42.0739703]},"n1820937782":{"id":"n1820937782","loc":[-85.0454702,42.1002852]},"n1820937783":{"id":"n1820937783","loc":[-85.1334145,42.0705418]},"n1820937784":{"id":"n1820937784","loc":[-85.5866542,41.947431]},"n1820937786":{"id":"n1820937786","loc":[-85.2359886,42.0250366]},"n1820937787":{"id":"n1820937787","loc":[-85.3138048,41.9698527]},"n1820937788":{"id":"n1820937788","loc":[-85.1274291,42.0733081]},"n1820937790":{"id":"n1820937790","loc":[-85.6292905,41.9411267]},"n1820937791":{"id":"n1820937791","loc":[-85.5958809,41.9417333]},"n1820937792":{"id":"n1820937792","loc":[-85.1271019,42.0737581]},"n1820937793":{"id":"n1820937793","loc":[-85.2312679,42.0314437]},"n1820937794":{"id":"n1820937794","loc":[-85.1081387,42.0863516]},"n1820937795":{"id":"n1820937795","loc":[-85.2424473,42.0212109]},"n1820937796":{"id":"n1820937796","loc":[-85.2710654,41.9975236]},"n1820937797":{"id":"n1820937797","loc":[-85.4798408,41.9863223]},"n1820937798":{"id":"n1820937798","loc":[-85.035939,42.104296]},"n1820937799":{"id":"n1820937799","loc":[-85.2178139,42.0395398]},"n1820937800":{"id":"n1820937800","loc":[-85.0630709,42.1042614]},"n1820937801":{"id":"n1820937801","loc":[-85.0440124,42.1014861]},"n1820937802":{"id":"n1820937802","loc":[-85.1321874,42.0720458]},"n1820937804":{"id":"n1820937804","loc":[-85.079427,42.1029121]},"n1820937805":{"id":"n1820937805","loc":[-85.2962632,41.9738968]},"n1820937806":{"id":"n1820937806","loc":[-85.6334748,41.9274627]},"n1820937807":{"id":"n1820937807","loc":[-85.1057341,42.0872804]},"n1820937808":{"id":"n1820937808","loc":[-85.4960169,41.9778263]},"n1820937809":{"id":"n1820937809","loc":[-85.2821226,41.9910273]},"n1820937810":{"id":"n1820937810","loc":[-85.0013868,42.0885054]},"n1820937811":{"id":"n1820937811","loc":[-85.2952547,41.9729795]},"n1820937812":{"id":"n1820937812","loc":[-85.1298375,42.0667842]},"n1820937813":{"id":"n1820937813","loc":[-85.1339201,42.0710025]},"n1820937814":{"id":"n1820937814","loc":[-85.0374356,42.103691]},"n1820937815":{"id":"n1820937815","loc":[-85.0061115,42.0880607]},"n1820937817":{"id":"n1820937817","loc":[-85.2398402,42.0226934]},"n1820937818":{"id":"n1820937818","loc":[-85.123501,42.076236]},"n1820937819":{"id":"n1820937819","loc":[-85.1209489,42.0791294]},"n1820937820":{"id":"n1820937820","loc":[-85.0818624,42.1025778]},"n1820937821":{"id":"n1820937821","loc":[-85.4428835,42.0054749]},"n1820937822":{"id":"n1820937822","loc":[-85.4710359,41.9961147]},"n1820937823":{"id":"n1820937823","loc":[-85.4253354,42.006198]},"n1820937824":{"id":"n1820937824","loc":[-85.5486483,41.9709451]},"n1820937825":{"id":"n1820937825","loc":[-85.2303238,42.0310452]},"n1820937826":{"id":"n1820937826","loc":[-85.6450405,41.9136361]},"n1820937828":{"id":"n1820937828","loc":[-85.2606853,41.9964073]},"n1820937830":{"id":"n1820937830","loc":[-85.097383,42.0911447]},"n1820937831":{"id":"n1820937831","loc":[-85.0498207,42.102136]},"n1820937832":{"id":"n1820937832","loc":[-85.1232435,42.0763793]},"n1820937833":{"id":"n1820937833","loc":[-85.394093,42.0055921]},"n1820937834":{"id":"n1820937834","loc":[-85.3566665,41.9928295]},"n1820937835":{"id":"n1820937835","loc":[-85.3543276,41.9920002]},"n1820937837":{"id":"n1820937837","loc":[-85.084668,42.1034932]},"n1820937838":{"id":"n1820937838","loc":[-85.4400296,42.0060649]},"n1820937839":{"id":"n1820937839","loc":[-85.2362246,42.025714]},"n1820937840":{"id":"n1820937840","loc":[-85.0409225,42.1012791]},"n1820937841":{"id":"n1820937841","loc":[-85.2442283,42.019832]},"n1820937842":{"id":"n1820937842","loc":[-85.1123001,42.084824]},"n1820937843":{"id":"n1820937843","loc":[-85.1603074,42.0638061]},"n1820937844":{"id":"n1820937844","loc":[-85.1359744,42.0650646]},"n1820937845":{"id":"n1820937845","loc":[-85.1757569,42.053849]},"n1820937846":{"id":"n1820937846","loc":[-85.5200925,41.9716686]},"n1820937848":{"id":"n1820937848","loc":[-85.5525322,41.9701315]},"n1820937849":{"id":"n1820937849","loc":[-85.0406489,42.10149]},"n1820937850":{"id":"n1820937850","loc":[-85.0142547,42.088825]},"n1820937851":{"id":"n1820937851","loc":[-85.343749,41.9881884]},"n1820937852":{"id":"n1820937852","loc":[-85.074996,42.1060205]},"n1820937853":{"id":"n1820937853","loc":[-85.2436275,42.0136864]},"n1820937854":{"id":"n1820937854","loc":[-85.2641453,41.9980897]},"n1820937856":{"id":"n1820937856","loc":[-85.2802343,41.9870086]},"n1820937857":{"id":"n1820937857","loc":[-85.0099256,42.0909946]},"n1820937858":{"id":"n1820937858","loc":[-85.493957,41.9786079]},"n1820937859":{"id":"n1820937859","loc":[-85.0739405,42.1059795]},"n1820937860":{"id":"n1820937860","loc":[-85.2331605,42.0301423]},"n1820937862":{"id":"n1820937862","loc":[-85.2035231,42.0438425]},"n1820937863":{"id":"n1820937863","loc":[-85.0884928,42.0986971]},"n1820937864":{"id":"n1820937864","loc":[-85.131597,42.0690142]},"n1820937865":{"id":"n1820937865","loc":[-85.3937454,42.0052677]},"n1820937866":{"id":"n1820937866","loc":[-85.2212729,42.0378561]},"n1820937867":{"id":"n1820937867","loc":[-85.0886068,42.0982421]},"n1820937868":{"id":"n1820937868","loc":[-85.0875004,42.0968064]},"n1820937869":{"id":"n1820937869","loc":[-85.0771323,42.1042642]},"n1820937870":{"id":"n1820937870","loc":[-85.0164554,42.0894887]},"n1820937871":{"id":"n1820937871","loc":[-85.6069102,41.9415577]},"n1820937872":{"id":"n1820937872","loc":[-85.3273875,41.9704908]},"n1820937873":{"id":"n1820937873","loc":[-85.3890891,41.9997983]},"n1820937875":{"id":"n1820937875","loc":[-85.5091276,41.9723705]},"n1820937876":{"id":"n1820937876","loc":[-85.0770626,42.1047696]},"n1820937877":{"id":"n1820937877","loc":[-85.612575,41.9419567]},"n1820937878":{"id":"n1820937878","loc":[-85.3868146,42.0036094]},"n1820937879":{"id":"n1820937879","loc":[-85.2722738,41.9981204]},"n1820937880":{"id":"n1820937880","loc":[-85.3064878,41.9723733]},"n1820937882":{"id":"n1820937882","loc":[-85.1270845,42.0727678]},"n1820937884":{"id":"n1820937884","loc":[-85.3316512,41.97923]},"n1820937885":{"id":"n1820937885","loc":[-85.3932519,42.0042472]},"n1820937886":{"id":"n1820937886","loc":[-85.2457411,42.0175444]},"n1820937887":{"id":"n1820937887","loc":[-85.1397509,42.0648415]},"n1820937891":{"id":"n1820937891","loc":[-85.3196735,41.9719665]},"n1820937892":{"id":"n1820937892","loc":[-85.3372473,41.9845033]},"n1820937894":{"id":"n1820937894","loc":[-85.3254778,41.9719745]},"n1820937897":{"id":"n1820937897","loc":[-85.3185148,41.9691268]},"n1820937899":{"id":"n1820937899","loc":[-85.5419106,41.9714556]},"n1820937901":{"id":"n1820937901","loc":[-85.3293509,41.9748368]},"n1820937903":{"id":"n1820937903","loc":[-85.0798078,42.1028365]},"n1820937905":{"id":"n1820937905","loc":[-85.3954191,42.0056025]},"n1820937909":{"id":"n1820937909","loc":[-85.3417534,41.9857155]},"n1820937913":{"id":"n1820937913","loc":[-84.9927822,42.0857107]},"n1820937915":{"id":"n1820937915","loc":[-85.5444212,41.9712801]},"n1820937917":{"id":"n1820937917","loc":[-85.259088,41.9981682]},"n1820937921":{"id":"n1820937921","loc":[-85.2784576,41.9876358]},"n1820937922":{"id":"n1820937922","loc":[-84.9971918,42.087753]},"n1820937924":{"id":"n1820937924","loc":[-85.5310688,41.966899]},"n1820937928":{"id":"n1820937928","loc":[-85.3766436,41.9979326]},"n1820937930":{"id":"n1820937930","loc":[-85.5494852,41.9704346]},"n1820937933":{"id":"n1820937933","loc":[-85.5548281,41.9695412]},"n1820937935":{"id":"n1820937935","loc":[-85.0768588,42.105088]},"n1820937937":{"id":"n1820937937","loc":[-85.2646885,41.9978054]},"n1820937939":{"id":"n1820937939","loc":[-85.2441532,42.0176082]},"n1820937941":{"id":"n1820937941","loc":[-85.105553,42.0877928]},"n1820937943":{"id":"n1820937943","loc":[-85.0879457,42.0958909]},"n1820937944":{"id":"n1820937944","loc":[-85.3187015,41.9704402]},"n1820937945":{"id":"n1820937945","loc":[-85.5624456,41.970626]},"n1820937946":{"id":"n1820937946","loc":[-85.0580176,42.1028644]},"n1820937948":{"id":"n1820937948","loc":[-85.3016061,41.9726286]},"n1820937949":{"id":"n1820937949","loc":[-85.4310388,42.0069418]},"n1820937950":{"id":"n1820937950","loc":[-85.2945144,41.9740723]},"n1820937951":{"id":"n1820937951","loc":[-85.1170222,42.082657]},"n1820937952":{"id":"n1820937952","loc":[-85.0864503,42.0947632]},"n1820937953":{"id":"n1820937953","loc":[-85.4285926,42.0059533]},"n1820937970":{"id":"n1820937970","loc":[-85.3629965,41.9938023]},"n1820937972":{"id":"n1820937972","loc":[-85.2438099,42.0199755]},"n1820937974":{"id":"n1820937974","loc":[-85.1327654,42.0699285]},"n1820937977":{"id":"n1820937977","loc":[-85.1515956,42.0611935]},"n1820937978":{"id":"n1820937978","loc":[-85.0107369,42.0896638]},"n1820937979":{"id":"n1820937979","loc":[-85.1152626,42.0862083]},"n1820937980":{"id":"n1820937980","loc":[-85.4531831,42.0062881]},"n1820937981":{"id":"n1820937981","loc":[-85.0341473,42.0985924]},"n1820937982":{"id":"n1820937982","loc":[-85.0877485,42.0960171]},"n1820937983":{"id":"n1820937983","loc":[-85.2756373,41.9951742]},"n1820937984":{"id":"n1820937984","loc":[-85.2965421,41.9714401]},"n1820937985":{"id":"n1820937985","loc":[-85.2409775,42.0226934]},"n1820937986":{"id":"n1820937986","loc":[-85.0170723,42.0900579]},"n1820937987":{"id":"n1820937987","loc":[-85.1034663,42.0880555]},"n1820937988":{"id":"n1820937988","loc":[-85.0585071,42.1031577]},"n1820937990":{"id":"n1820937990","loc":[-85.0819174,42.1032373]},"n1820937992":{"id":"n1820937992","loc":[-85.0546608,42.1030542]},"n1820937993":{"id":"n1820937993","loc":[-85.0100811,42.0906125]},"n1820937995":{"id":"n1820937995","loc":[-85.6304278,41.9432655]},"n1820937997":{"id":"n1820937997","loc":[-85.0255628,42.092778]},"n1820938011":{"id":"n1820938011","loc":[-85.2316756,42.0317146]},"n1820938012":{"id":"n1820938012","loc":[-85.4067917,42.008042]},"n1820938013":{"id":"n1820938013","loc":[-85.390398,42.0028759]},"n1820938014":{"id":"n1820938014","loc":[-85.0161604,42.0886527]},"n1820938015":{"id":"n1820938015","loc":[-85.125337,42.0744589]},"n1820938016":{"id":"n1820938016","loc":[-85.2151317,42.0404801]},"n1820938017":{"id":"n1820938017","loc":[-85.3165085,41.9706025]},"n1820938018":{"id":"n1820938018","loc":[-85.5641193,41.9640688]},"n1820938019":{"id":"n1820938019","loc":[-85.147583,42.0642203]},"n1820938022":{"id":"n1820938022","loc":[-85.2803781,41.9947886]},"n1820938024":{"id":"n1820938024","loc":[-85.2692469,41.9982053]},"n1820938026":{"id":"n1820938026","loc":[-85.4321975,42.0067505]},"n1820938028":{"id":"n1820938028","loc":[-85.572535,41.9633405]},"n1820938030":{"id":"n1820938030","loc":[-85.3237505,41.9716475]},"n1820938032":{"id":"n1820938032","loc":[-85.6487698,41.9141583]},"n1820938033":{"id":"n1820938033","loc":[-85.0526371,42.1038315]},"n1820938034":{"id":"n1820938034","loc":[-85.088069,42.0978731]},"n1820938035":{"id":"n1820938035","loc":[-85.2516312,42.0102267]},"n1820938039":{"id":"n1820938039","loc":[-85.2731374,41.9982958]},"n1820938040":{"id":"n1820938040","loc":[-85.5453224,41.9713439]},"n1820938041":{"id":"n1820938041","loc":[-85.4480548,42.0049647]},"n1820938043":{"id":"n1820938043","loc":[-85.2504081,42.010322]},"n1820938045":{"id":"n1820938045","loc":[-85.2663447,41.99919]},"n1820938046":{"id":"n1820938046","loc":[-85.0507287,42.102907]},"n1820938047":{"id":"n1820938047","loc":[-85.0408246,42.1024743]},"n1820938048":{"id":"n1820938048","loc":[-85.2796335,41.9866099]},"n1820938050":{"id":"n1820938050","loc":[-85.452475,42.0061127]},"n1820938051":{"id":"n1820938051","loc":[-85.2410569,42.0128147]},"n1820938052":{"id":"n1820938052","loc":[-85.0413302,42.1011477]},"n1820938053":{"id":"n1820938053","loc":[-85.6327409,41.9197627]},"n1820938056":{"id":"n1820938056","loc":[-85.1072039,42.0857994]},"n1820938057":{"id":"n1820938057","loc":[-85.2001114,42.0448145]},"n1820938058":{"id":"n1820938058","loc":[-85.2655347,41.9978186]},"n1820938059":{"id":"n1820938059","loc":[-85.2330918,42.0304874]},"n1820938060":{"id":"n1820938060","loc":[-85.2601113,41.9966545]},"n1820938061":{"id":"n1820938061","loc":[-85.5397863,41.9708494]},"n1820938062":{"id":"n1820938062","loc":[-85.2702085,41.9977217]},"n1820938063":{"id":"n1820938063","loc":[-85.2219982,42.03699]},"n1820938064":{"id":"n1820938064","loc":[-85.0668957,42.105121]},"n1820938065":{"id":"n1820938065","loc":[-85.2328665,42.0270769]},"n1820938066":{"id":"n1820938066","loc":[-85.3189654,41.9694778]},"n1820938067":{"id":"n1820938067","loc":[-85.3814115,42.0022915]},"n1820938068":{"id":"n1820938068","loc":[-85.2759108,41.9956008]},"n1820938069":{"id":"n1820938069","loc":[-85.0391938,42.1034853]},"n1820938070":{"id":"n1820938070","loc":[-85.2850623,41.9810353]},"n1820938071":{"id":"n1820938071","loc":[-85.538074,41.970855]},"n1820938073":{"id":"n1820938073","loc":[-85.1319661,42.0670932]},"n1820938074":{"id":"n1820938074","loc":[-85.2816763,41.9913678]},"n1820938075":{"id":"n1820938075","loc":[-85.3182144,41.9700282]},"n1820938076":{"id":"n1820938076","loc":[-85.5909028,41.9458989]},"n1820938077":{"id":"n1820938077","loc":[-85.4057617,42.0074361]},"n1820938078":{"id":"n1820938078","loc":[-85.2620438,41.9967729]},"n1820938079":{"id":"n1820938079","loc":[-85.1122143,42.0851107]},"n1820938080":{"id":"n1820938080","loc":[-85.2443785,42.0174567]},"n1820938081":{"id":"n1820938081","loc":[-85.0319733,42.0953853]},"n1820938082":{"id":"n1820938082","loc":[-85.0878276,42.09443]},"n1820938083":{"id":"n1820938083","loc":[-85.0271789,42.0935809]},"n1820938084":{"id":"n1820938084","loc":[-85.0326399,42.0974222]},"n1820938085":{"id":"n1820938085","loc":[-85.3989167,42.0065592]},"n1820938086":{"id":"n1820938086","loc":[-85.3263361,41.9721261]},"n1820938087":{"id":"n1820938087","loc":[-85.2547855,42.0037134]},"n1820938088":{"id":"n1820938088","loc":[-85.4373259,42.005746]},"n1820938089":{"id":"n1820938089","loc":[-85.3094275,41.9699245]},"n1820938090":{"id":"n1820938090","loc":[-85.2783246,41.9872793]},"n1820938092":{"id":"n1820938092","loc":[-85.0815633,42.1025169]},"n1820938093":{"id":"n1820938093","loc":[-85.1788511,42.0522134]},"n1820938095":{"id":"n1820938095","loc":[-85.2830345,41.9816733]},"n1820938096":{"id":"n1820938096","loc":[-85.0744984,42.1059835]},"n1820938097":{"id":"n1820938097","loc":[-85.2788396,41.9879333]},"n1820938098":{"id":"n1820938098","loc":[-85.3640093,41.9946531]},"n1820938099":{"id":"n1820938099","loc":[-85.291167,41.9787463]},"n1820938100":{"id":"n1820938100","loc":[-85.0772436,42.1038156]},"n1820938101":{"id":"n1820938101","loc":[-85.00563,42.0887482]},"n1820938102":{"id":"n1820938102","loc":[-85.0326881,42.0961245]},"n1820938104":{"id":"n1820938104","loc":[-85.0530448,42.1038634]},"n1820938105":{"id":"n1820938105","loc":[-85.2625266,41.9970639]},"n1820938106":{"id":"n1820938106","loc":[-85.2827556,41.9823512]},"n1820938107":{"id":"n1820938107","loc":[-85.2784319,41.9910752]},"n1820938108":{"id":"n1820938108","loc":[-85.0882099,42.094393]},"n1820938109":{"id":"n1820938109","loc":[-85.5718484,41.9645371]},"n1820938110":{"id":"n1820938110","loc":[-85.2559764,42.0099317]},"n1820938111":{"id":"n1820938111","loc":[-85.2969284,41.973179]},"n1820938113":{"id":"n1820938113","loc":[-85.3875055,42.0019726]},"n1820938114":{"id":"n1820938114","loc":[-85.4250779,42.0068199]},"n1820938115":{"id":"n1820938115","loc":[-85.0645367,42.104889]},"n1820938116":{"id":"n1820938116","loc":[-85.1636762,42.0623724]},"n1820938117":{"id":"n1820938117","loc":[-85.0757322,42.1055935]},"n1820938118":{"id":"n1820938118","loc":[-85.3695197,41.9981559]},"n1820938120":{"id":"n1820938120","loc":[-85.1297516,42.0671027]},"n1820938121":{"id":"n1820938121","loc":[-85.1057448,42.0875551]},"n1820938122":{"id":"n1820938122","loc":[-85.2805175,41.9943182]},"n1820938123":{"id":"n1820938123","loc":[-85.2545173,42.0040722]},"n1820938124":{"id":"n1820938124","loc":[-84.9966607,42.0871319]},"n1820938125":{"id":"n1820938125","loc":[-85.0099899,42.0904612]},"n1820938126":{"id":"n1820938126","loc":[-85.2489919,42.0091102]},"n1820938127":{"id":"n1820938127","loc":[-85.0342706,42.0979476]},"n1820938128":{"id":"n1820938128","loc":[-85.1080891,42.0855884]},"n1820938129":{"id":"n1820938129","loc":[-85.0128183,42.0905356]},"n1820938130":{"id":"n1820938130","loc":[-85.631608,41.9434251]},"n1820938131":{"id":"n1820938131","loc":[-85.2551975,42.0008524]},"n1820938132":{"id":"n1820938132","loc":[-85.6421823,41.9096233]},"n1820938133":{"id":"n1820938133","loc":[-85.0125059,42.0906284]},"n1820938134":{"id":"n1820938134","loc":[-85.5499358,41.9701793]},"n1820938135":{"id":"n1820938135","loc":[-85.5472107,41.9712323]},"n1820938136":{"id":"n1820938136","loc":[-85.2760758,41.9958691]},"n1820938137":{"id":"n1820938137","loc":[-85.276678,41.9960433]},"n1820938138":{"id":"n1820938138","loc":[-85.0570319,42.1024731]},"n1820938140":{"id":"n1820938140","loc":[-85.2394325,42.0227492]},"n1820938142":{"id":"n1820938142","loc":[-85.5666341,41.9638829]},"n1820938144":{"id":"n1820938144","loc":[-85.258101,41.9996353]},"n1820938147":{"id":"n1820938147","loc":[-85.2129645,42.0413565]},"n1820938149":{"id":"n1820938149","loc":[-84.9962369,42.0868373]},"n1820938151":{"id":"n1820938151","loc":[-85.2570386,42.0084968]},"n1820938153":{"id":"n1820938153","loc":[-85.3971142,42.0050285]},"n1820938155":{"id":"n1820938155","loc":[-85.1072093,42.0855566]},"n1820938157":{"id":"n1820938157","loc":[-85.2840323,41.9920959]},"n1820938159":{"id":"n1820938159","loc":[-85.1187924,42.0816458]},"n1820938161":{"id":"n1820938161","loc":[-85.2681324,41.9985788]},"n1820938163":{"id":"n1820938163","loc":[-85.0887034,42.0984969]},"n1820938165":{"id":"n1820938165","loc":[-85.4133405,42.0073141]},"n1820938166":{"id":"n1820938166","loc":[-85.0097445,42.0902888]},"n1820938167":{"id":"n1820938167","loc":[-85.0828133,42.1037388]},"n1820938168":{"id":"n1820938168","loc":[-85.0549599,42.1030833]},"n1820938169":{"id":"n1820938169","loc":[-85.4571528,42.0010421]},"n1820938178":{"id":"n1820938178","loc":[-85.2706644,41.9975941]},"n1820938180":{"id":"n1820938180","loc":[-85.2258606,42.0335794]},"n1820938182":{"id":"n1820938182","loc":[-85.2832276,41.9814659]},"n1820938184":{"id":"n1820938184","loc":[-85.1082299,42.0860928]},"n1820938185":{"id":"n1820938185","loc":[-85.3839392,42.0022381]},"n1820938186":{"id":"n1820938186","loc":[-85.2772131,41.995905]},"n1820938187":{"id":"n1820938187","loc":[-85.1044895,42.0879214]},"n1820938188":{"id":"n1820938188","loc":[-85.2135267,42.0407087]},"n1820938189":{"id":"n1820938189","loc":[-85.2543993,42.0044628]},"n1820938190":{"id":"n1820938190","loc":[-85.1501793,42.0617351]},"n1820938191":{"id":"n1820938191","loc":[-85.3350587,41.9820469]},"n1820938192":{"id":"n1820938192","loc":[-85.1350731,42.0655735]},"n1820938193":{"id":"n1820938193","loc":[-85.0404008,42.1028843]},"n1820938194":{"id":"n1820938194","loc":[-85.6323161,41.943042]},"n1820938195":{"id":"n1820938195","loc":[-85.1259593,42.0742837]},"n1820938196":{"id":"n1820938196","loc":[-85.4562988,42.0033758]},"n1820938197":{"id":"n1820938197","loc":[-85.256824,42.0056826]},"n1820938198":{"id":"n1820938198","loc":[-85.2742103,41.9963862]},"n1820938199":{"id":"n1820938199","loc":[-85.0380888,42.1037877]},"n1820938200":{"id":"n1820938200","loc":[-85.47404,41.9944721]},"n1820938201":{"id":"n1820938201","loc":[-85.103021,42.087948]},"n1820938202":{"id":"n1820938202","loc":[-85.4030151,42.0065113]},"n1820938203":{"id":"n1820938203","loc":[-85.2113981,42.040735]},"n1820938204":{"id":"n1820938204","loc":[-85.2603433,41.9965137]},"n1820938206":{"id":"n1820938206","loc":[-85.1669378,42.0607634]},"n1820938207":{"id":"n1820938207","loc":[-85.0642027,42.1046076]},"n1820938208":{"id":"n1820938208","loc":[-85.2812428,41.9915696]},"n1820938209":{"id":"n1820938209","loc":[-85.0839559,42.1038343]},"n1820938210":{"id":"n1820938210","loc":[-85.1239946,42.0769368]},"n1820938211":{"id":"n1820938211","loc":[-85.2311177,42.0283042]},"n1820938212":{"id":"n1820938212","loc":[-85.2791614,41.9882682]},"n1820938213":{"id":"n1820938213","loc":[-85.2674941,41.9987582]},"n1820938214":{"id":"n1820938214","loc":[-85.352787,41.9919579]},"n1820938215":{"id":"n1820938215","loc":[-85.0874146,42.0952182]},"n1820938216":{"id":"n1820938216","loc":[-85.0069711,42.0877092]},"n1820938217":{"id":"n1820938217","loc":[-85.2059049,42.0404004]},"n1820938218":{"id":"n1820938218","loc":[-85.2403552,42.0227332]},"n1820938219":{"id":"n1820938219","loc":[-85.2492923,42.0098915]},"n1820938220":{"id":"n1820938220","loc":[-85.269778,41.9979541]},"n1820938221":{"id":"n1820938221","loc":[-85.2097673,42.0389024]},"n1820938222":{"id":"n1820938222","loc":[-85.0845942,42.1032015]},"n1820938223":{"id":"n1820938223","loc":[-84.993206,42.0858142]},"n1820938224":{"id":"n1820938224","loc":[-85.2108187,42.0402729]},"n1820938225":{"id":"n1820938225","loc":[-84.9893959,42.0873043]},"n1820938226":{"id":"n1820938226","loc":[-85.2952332,41.9719984]},"n1820938227":{"id":"n1820938227","loc":[-85.4100961,42.0081536]},"n1820938228":{"id":"n1820938228","loc":[-85.3299088,41.9785696]},"n1820938229":{"id":"n1820938229","loc":[-85.2258176,42.0340097]},"n1820938230":{"id":"n1820938230","loc":[-85.3146739,41.9711449]},"n1820938231":{"id":"n1820938231","loc":[-85.5447645,41.9712801]},"n1820938232":{"id":"n1820938232","loc":[-85.5510087,41.9705941]},"n1820938233":{"id":"n1820938233","loc":[-85.5122389,41.9703445]},"n1820938234":{"id":"n1820938234","loc":[-85.2792687,41.9865381]},"n1820938235":{"id":"n1820938235","loc":[-85.1475229,42.0630151]},"n1820938237":{"id":"n1820938237","loc":[-85.0332889,42.0996034]},"n1820938238":{"id":"n1820938238","loc":[-85.2588882,41.9986877]},"n1820938239":{"id":"n1820938239","loc":[-85.0656458,42.1050892]},"n1820938240":{"id":"n1820938240","loc":[-84.9913915,42.086098]},"n1820938241":{"id":"n1820938241","loc":[-85.4752416,41.9944402]},"n1820938242":{"id":"n1820938242","loc":[-85.1214304,42.0791147]},"n1820938243":{"id":"n1820938243","loc":[-85.0075183,42.0886925]},"n1820938244":{"id":"n1820938244","loc":[-85.1052888,42.0872087]},"n1820938245":{"id":"n1820938245","loc":[-85.3104252,41.9703393]},"n1820938246":{"id":"n1820938246","loc":[-85.232109,42.0318158]},"n1820938247":{"id":"n1820938247","loc":[-85.0756075,42.1059528]},"n1820938248":{"id":"n1820938248","loc":[-85.0075612,42.0890866]},"n1820938249":{"id":"n1820938249","loc":[-85.1013312,42.0897474]},"n1820938250":{"id":"n1820938250","loc":[-85.1168076,42.0828919]},"n1820938251":{"id":"n1820938251","loc":[-85.2951367,41.9723334]},"n1820938252":{"id":"n1820938252","loc":[-85.0879363,42.0976053]},"n1820938253":{"id":"n1820938253","loc":[-85.0354763,42.1021838]},"n1820938254":{"id":"n1820938254","loc":[-85.2379627,42.0236339]},"n1820938255":{"id":"n1820938255","loc":[-85.1308245,42.0685364]},"n1820938256":{"id":"n1820938256","loc":[-85.0914446,42.0934774]},"n1820938257":{"id":"n1820938257","loc":[-85.2436812,42.014069]},"n1820938258":{"id":"n1820938258","loc":[-85.0682529,42.1056106]},"n1820938259":{"id":"n1820938259","loc":[-85.290652,41.9766805]},"n1820938260":{"id":"n1820938260","loc":[-85.0133494,42.0897434]},"n1820938261":{"id":"n1820938261","loc":[-85.2753047,41.9949429]},"n1820938262":{"id":"n1820938262","loc":[-85.0314691,42.0950788]},"n1820938263":{"id":"n1820938263","loc":[-85.3444786,41.9908359]},"n1820938264":{"id":"n1820938264","loc":[-85.0443115,42.1009061]},"n1820938265":{"id":"n1820938265","loc":[-85.0634853,42.1043159]},"n1820938267":{"id":"n1820938267","loc":[-85.3978223,42.0053952]},"n1820938268":{"id":"n1820938268","loc":[-85.0228659,42.0911885]},"n1820938269":{"id":"n1820938269","loc":[-85.0220237,42.0906272]},"n1820938270":{"id":"n1820938270","loc":[-85.1061525,42.0863369]},"n1820938271":{"id":"n1820938271","loc":[-85.2382309,42.0233708]},"n1820938272":{"id":"n1820938272","loc":[-85.310672,41.9702755]},"n1820938273":{"id":"n1820938273","loc":[-85.1448192,42.0652613]},"n1820938274":{"id":"n1820938274","loc":[-85.6036057,41.9403766]},"n1820938275":{"id":"n1820938275","loc":[-85.0778941,42.1032413]},"n1820938276":{"id":"n1820938276","loc":[-85.1279374,42.0723974]},"n1820938277":{"id":"n1820938277","loc":[-85.2806635,41.9847836]},"n1820938278":{"id":"n1820938278","loc":[-85.2653201,41.9976352]},"n1820938279":{"id":"n1820938279","loc":[-85.0351665,42.1001805]},"n1820938280":{"id":"n1820938280","loc":[-85.0718269,42.1056253]},"n1820938281":{"id":"n1820938281","loc":[-85.2574248,42.0075322]},"n1820938282":{"id":"n1820938282","loc":[-85.126666,42.0740778]},"n1820938283":{"id":"n1820938283","loc":[-85.077705,42.1034733]},"n1820938284":{"id":"n1820938284","loc":[-85.3535552,41.9919045]},"n1820938286":{"id":"n1820938286","loc":[-85.2810711,41.9866657]},"n1820938287":{"id":"n1820938287","loc":[-85.4567494,42.0019885]},"n1820938288":{"id":"n1820938288","loc":[-85.2642419,41.9992936]},"n1820938289":{"id":"n1820938289","loc":[-85.2643344,41.9980925]},"n1820938290":{"id":"n1820938290","loc":[-85.3270335,41.9776125]},"n1820938291":{"id":"n1820938291","loc":[-85.1200584,42.0795077]},"n1820938292":{"id":"n1820938292","loc":[-85.2290792,42.0340256]},"n1820938293":{"id":"n1820938293","loc":[-85.6015887,41.9401372]},"n1820938294":{"id":"n1820938294","loc":[-85.5370869,41.970488]},"n1820938295":{"id":"n1820938295","loc":[-85.3108866,41.9698048]},"n1820938297":{"id":"n1820938297","loc":[-85.1556511,42.0628184]},"n1820938298":{"id":"n1820938298","loc":[-85.0027922,42.0875221]},"n1820938300":{"id":"n1820938300","loc":[-85.3873338,42.0040614]},"n1820938301":{"id":"n1820938301","loc":[-85.0350753,42.1004034]},"n1820938302":{"id":"n1820938302","loc":[-85.6239476,41.9411906]},"n1820938304":{"id":"n1820938304","loc":[-85.0118246,42.0897964]},"n1820938306":{"id":"n1820938306","loc":[-85.4796877,41.995275]},"n1820938307":{"id":"n1820938307","loc":[-85.5388636,41.9707856]},"n1820938309":{"id":"n1820938309","loc":[-85.2971902,41.9727773]},"n1820938310":{"id":"n1820938310","loc":[-85.5426831,41.9715513]},"n1820938311":{"id":"n1820938311","loc":[-85.2798373,41.9836671]},"n1820938312":{"id":"n1820938312","loc":[-85.2432198,42.0104017]},"n1820938313":{"id":"n1820938313","loc":[-85.2650412,41.9987554]},"n1820938317":{"id":"n1820938317","loc":[-85.0015423,42.0882386]},"n1820938318":{"id":"n1820938318","loc":[-85.1409783,42.064879]},"n1820938319":{"id":"n1820938319","loc":[-85.1691908,42.058995]},"n1820938320":{"id":"n1820938320","loc":[-85.1059165,42.0864882]},"n1820938321":{"id":"n1820938321","loc":[-85.3664941,41.9965771]},"n1820938323":{"id":"n1820938323","loc":[-85.3143198,41.9710971]},"n1820938324":{"id":"n1820938324","loc":[-85.0016067,42.0880675]},"n1820938325":{"id":"n1820938325","loc":[-85.0148139,42.0887164]},"n1820938326":{"id":"n1820938326","loc":[-85.0324682,42.0959056]},"n1820938327":{"id":"n1820938327","loc":[-85.0898661,42.0939921]},"n1820938328":{"id":"n1820938328","loc":[-85.2556427,42.0004936]},"n1820938329":{"id":"n1820938329","loc":[-85.6287112,41.9407437]},"n1820938330":{"id":"n1820938330","loc":[-84.9913392,42.0866701]},"n1820938331":{"id":"n1820938331","loc":[-85.2685777,41.9984632]},"n1820938332":{"id":"n1820938332","loc":[-85.0078884,42.0901614]},"n1820938333":{"id":"n1820938333","loc":[-84.999642,42.0878616]},"n1820938334":{"id":"n1820938334","loc":[-85.0188909,42.0899186]},"n1820938335":{"id":"n1820938335","loc":[-85.2830238,41.9819843]},"n1820938336":{"id":"n1820938336","loc":[-85.2491421,42.0096204]},"n1820938337":{"id":"n1820938337","loc":[-85.0585701,42.1034295]},"n1820938338":{"id":"n1820938338","loc":[-85.0651965,42.1051636]},"n1820938339":{"id":"n1820938339","loc":[-85.0583944,42.104292]},"n1820938340":{"id":"n1820938340","loc":[-85.119876,42.0801567]},"n1820938341":{"id":"n1820938341","loc":[-85.0943937,42.0931323]},"n1820938342":{"id":"n1820938342","loc":[-85.1504583,42.0613209]},"n1820938343":{"id":"n1820938343","loc":[-85.0425426,42.1019836]},"n1820938345":{"id":"n1820938345","loc":[-84.9991391,42.0878206]},"n1820938346":{"id":"n1820938346","loc":[-85.2563841,42.0094614]},"n1820938347":{"id":"n1820938347","loc":[-85.0515387,42.103297]},"n1820938348":{"id":"n1820938348","loc":[-85.0857261,42.1003636]},"n1820938349":{"id":"n1820938349","loc":[-85.078971,42.1029241]},"n1820938350":{"id":"n1820938350","loc":[-85.5699558,41.958931]},"n1820938351":{"id":"n1820938351","loc":[-85.3181285,41.9696533]},"n1820938352":{"id":"n1820938352","loc":[-85.5998506,41.9402329]},"n1820938353":{"id":"n1820938353","loc":[-85.2567277,42.000317]},"n1820938354":{"id":"n1820938354","loc":[-85.3082795,41.9708338]},"n1820938355":{"id":"n1820938355","loc":[-85.3127856,41.9692784]},"n1820938356":{"id":"n1820938356","loc":[-85.0340775,42.1010721]},"n1820938357":{"id":"n1820938357","loc":[-85.3158111,41.9706583]},"n1820938359":{"id":"n1820938359","loc":[-85.2312035,42.0280412]},"n1820938360":{"id":"n1820938360","loc":[-85.2448613,42.018477]},"n1820938361":{"id":"n1820938361","loc":[-85.29077,41.9759068]},"n1820938364":{"id":"n1820938364","loc":[-85.3677387,41.9976615]},"n1820938365":{"id":"n1820938365","loc":[-85.0785204,42.1030355]},"n1820938366":{"id":"n1820938366","loc":[-85.2262039,42.0333722]},"n1820938367":{"id":"n1820938367","loc":[-85.1226011,42.0780902]},"n1820938368":{"id":"n1820938368","loc":[-85.3229673,41.971129]},"n1820938369":{"id":"n1820938369","loc":[-85.385334,42.0000056]},"n1820938370":{"id":"n1820938370","loc":[-85.000098,42.0879094]},"n1820938372":{"id":"n1820938372","loc":[-85.3852481,42.0025091]},"n1820938373":{"id":"n1820938373","loc":[-85.3770513,41.9982515]},"n1820938374":{"id":"n1820938374","loc":[-85.6278314,41.9405362]},"n1820938375":{"id":"n1820938375","loc":[-85.6355133,41.9344068]},"n1820938376":{"id":"n1820938376","loc":[-85.635642,41.9324753]},"n1820938377":{"id":"n1820938377","loc":[-85.3154463,41.970778]},"n1820938378":{"id":"n1820938378","loc":[-85.0920334,42.093411]},"n1820938379":{"id":"n1820938379","loc":[-85.3269155,41.9722297]},"n1820938381":{"id":"n1820938381","loc":[-85.1134334,42.0849184]},"n1820938382":{"id":"n1820938382","loc":[-85.005968,42.088585]},"n1820938384":{"id":"n1820938384","loc":[-85.1245203,42.0757183]},"n1820938385":{"id":"n1820938385","loc":[-85.020704,42.0905396]},"n1820938386":{"id":"n1820938386","loc":[-85.119585,42.0808984]},"n1820938387":{"id":"n1820938387","loc":[-85.0072447,42.0880117]},"n1820938388":{"id":"n1820938388","loc":[-85.2742908,41.9960273]},"n1820938389":{"id":"n1820938389","loc":[-85.3275807,41.9696852]},"n1820938390":{"id":"n1820938390","loc":[-85.2385635,42.0231556]},"n1820938392":{"id":"n1820938392","loc":[-85.0202856,42.0900778]},"n1820938393":{"id":"n1820938393","loc":[-85.2067847,42.0395398]},"n1820938394":{"id":"n1820938394","loc":[-85.5183544,41.9713495]},"n1820938396":{"id":"n1820938396","loc":[-85.5073037,41.9736787]},"n1820938397":{"id":"n1820938397","loc":[-85.2519638,42.0114225]},"n1820938398":{"id":"n1820938398","loc":[-85.287487,41.9793285]},"n1820938399":{"id":"n1820938399","loc":[-85.2298088,42.0336431]},"n1820938400":{"id":"n1820938400","loc":[-85.229444,42.0339141]},"n1820938401":{"id":"n1820938401","loc":[-85.2421791,42.0220239]},"n1820938402":{"id":"n1820938402","loc":[-85.2976687,41.9737612]},"n1820938403":{"id":"n1820938403","loc":[-85.3622069,41.993473]},"n1820938404":{"id":"n1820938404","loc":[-85.2465458,42.014906]},"n1820938405":{"id":"n1820938405","loc":[-85.5724663,41.9639412]},"n1820938406":{"id":"n1820938406","loc":[-85.3708501,41.9982037]},"n1820938408":{"id":"n1820938408","loc":[-85.2564592,42.0055311]},"n1820938409":{"id":"n1820938409","loc":[-85.1192846,42.0810856]},"n1820938410":{"id":"n1820938410","loc":[-85.5623812,41.971663]},"n1820938411":{"id":"n1820938411","loc":[-85.3221948,41.9719665]},"n1820938412":{"id":"n1820938412","loc":[-85.5168738,41.9710305]},"n1820938413":{"id":"n1820938413","loc":[-85.4546852,42.0061127]},"n1820938414":{"id":"n1820938414","loc":[-85.5896153,41.9463617]},"n1820938415":{"id":"n1820938415","loc":[-85.2978189,41.9722138]},"n1820938416":{"id":"n1820938416","loc":[-85.1021681,42.0883581]},"n1820938417":{"id":"n1820938417","loc":[-85.2797193,41.9912984]},"n1820938419":{"id":"n1820938419","loc":[-85.2362461,42.0248533]},"n1820938420":{"id":"n1820938420","loc":[-85.4833639,41.9846252]},"n1820938422":{"id":"n1820938422","loc":[-85.3281064,41.9689433]},"n1820938424":{"id":"n1820938424","loc":[-85.2416963,42.0130088]},"n1820938425":{"id":"n1820938425","loc":[-85.5718655,41.9564577]},"n1820938426":{"id":"n1820938426","loc":[-85.0512812,42.1030701]},"n1820938427":{"id":"n1820938427","loc":[-85.1273527,42.0723616]},"n1820938428":{"id":"n1820938428","loc":[-85.0215033,42.0904083]},"n1820938429":{"id":"n1820938429","loc":[-85.6169953,41.942228]},"n1820938430":{"id":"n1820938430","loc":[-85.2829165,41.9907243]},"n1820938431":{"id":"n1820938431","loc":[-85.2240796,42.0374203]},"n1820938432":{"id":"n1820938432","loc":[-85.0167598,42.0898442]},"n1820938433":{"id":"n1820938433","loc":[-85.2132649,42.0411334]},"n1820938434":{"id":"n1820938434","loc":[-85.2293839,42.031513]},"n1820938435":{"id":"n1820938435","loc":[-85.1203374,42.0792608]},"n1820938436":{"id":"n1820938436","loc":[-85.109571,42.086268]},"n1820938437":{"id":"n1820938437","loc":[-85.1079026,42.0853842]},"n1820938438":{"id":"n1820938438","loc":[-85.109237,42.0862413]},"n1820938439":{"id":"n1820938439","loc":[-85.2259936,42.0350831]},"n1820938440":{"id":"n1820938440","loc":[-85.3669705,41.99679]},"n1820938441":{"id":"n1820938441","loc":[-85.2418143,42.0223507]},"n1820938442":{"id":"n1820938442","loc":[-85.3101248,41.9702515]},"n1820938443":{"id":"n1820938443","loc":[-85.069315,42.1059688]},"n1820938444":{"id":"n1820938444","loc":[-85.205862,42.0410378]},"n1820938445":{"id":"n1820938445","loc":[-85.0388076,42.1036604]},"n1820938446":{"id":"n1820938446","loc":[-85.2225389,42.0370115]},"n1820938447":{"id":"n1820938447","loc":[-85.3241474,41.9719346]},"n1820938448":{"id":"n1820938448","loc":[-85.3125496,41.9690789]},"n1820938449":{"id":"n1820938449","loc":[-85.1146497,42.0857039]},"n1820938450":{"id":"n1820938450","loc":[-85.1333944,42.0714963]},"n1820938451":{"id":"n1820938451","loc":[-85.5619306,41.9720937]},"n1820938452":{"id":"n1820938452","loc":[-85.2553651,42.0006479]},"n1820938453":{"id":"n1820938453","loc":[-85.3151137,41.9710093]},"n1820938454":{"id":"n1820938454","loc":[-85.2592315,41.9977947]},"n1820938455":{"id":"n1820938455","loc":[-85.2655723,41.9995966]},"n1820938456":{"id":"n1820938456","loc":[-85.4820652,41.9959233]},"n1820938459":{"id":"n1820938459","loc":[-85.450737,42.0055068]},"n1820938460":{"id":"n1820938460","loc":[-85.2428658,42.0205573]},"n1820938461":{"id":"n1820938461","loc":[-85.0835576,42.1021559]},"n1820938462":{"id":"n1820938462","loc":[-85.244636,42.0194733]},"n1820938463":{"id":"n1820938463","loc":[-85.5702562,41.9581332]},"n1820938465":{"id":"n1820938465","loc":[-85.5680031,41.9659515]},"n1820938467":{"id":"n1820938467","loc":[-85.2798752,41.9948353]},"n1820938468":{"id":"n1820938468","loc":[-85.0477407,42.1015537]},"n1820938469":{"id":"n1820938469","loc":[-85.6403842,41.913732]},"n1820938470":{"id":"n1820938470","loc":[-85.0396029,42.103289]},"n1820938471":{"id":"n1820938471","loc":[-85.2824702,41.9907777]},"n1820938472":{"id":"n1820938472","loc":[-85.2969284,41.9735538]},"n1820938474":{"id":"n1820938474","loc":[-85.401041,42.0070853]},"n1820938475":{"id":"n1820938475","loc":[-85.4116625,42.0073883]},"n1820938476":{"id":"n1820938476","loc":[-85.0437764,42.1016214]},"n1820938477":{"id":"n1820938477","loc":[-85.3643269,41.9958436]},"n1820938478":{"id":"n1820938478","loc":[-85.3895182,42.0009465]},"n1820938479":{"id":"n1820938479","loc":[-85.636157,41.9333373]},"n1820938480":{"id":"n1820938480","loc":[-85.2811355,41.9858044]},"n1820938481":{"id":"n1820938481","loc":[-85.0239052,42.092153]},"n1820938482":{"id":"n1820938482","loc":[-85.2558798,42.0053557]},"n1820938483":{"id":"n1820938483","loc":[-85.2544422,42.0047339]},"n1820938484":{"id":"n1820938484","loc":[-85.4864683,41.9843183]},"n1820938485":{"id":"n1820938485","loc":[-85.2554185,42.0031075]},"n1820938486":{"id":"n1820938486","loc":[-85.3082795,41.9712486]},"n1820938487":{"id":"n1820938487","loc":[-85.2433378,42.0133436]},"n1820938488":{"id":"n1820938488","loc":[-85.0216696,42.0904162]},"n1820938489":{"id":"n1820938489","loc":[-85.2546138,42.0050289]},"n1820938490":{"id":"n1820938490","loc":[-85.2717521,41.9977349]},"n1820938491":{"id":"n1820938491","loc":[-85.0100489,42.0908195]},"n1820938492":{"id":"n1820938492","loc":[-85.207879,42.0392211]},"n1820938493":{"id":"n1820938493","loc":[-85.0007363,42.0882836]},"n1820938494":{"id":"n1820938494","loc":[-85.5775303,41.9504097]},"n1820938495":{"id":"n1820938495","loc":[-85.1131584,42.0847683]},"n1820938496":{"id":"n1820938496","loc":[-85.0887825,42.0941633]},"n1820938497":{"id":"n1820938497","loc":[-85.1185926,42.0818938]},"n1820938498":{"id":"n1820938498","loc":[-85.2748487,41.9948712]},"n1820938499":{"id":"n1820938499","loc":[-85.2566952,42.0090788]},"n1820938500":{"id":"n1820938500","loc":[-85.0774757,42.1036234]},"n1820938501":{"id":"n1820938501","loc":[-85.4190869,42.008903]},"n1820938502":{"id":"n1820938502","loc":[-85.1140395,42.0850577]},"n1820938503":{"id":"n1820938503","loc":[-85.1136104,42.0848627]},"n1820938504":{"id":"n1820938504","loc":[-85.5828089,41.9480638]},"n1820938505":{"id":"n1820938505","loc":[-85.625514,41.9405202]},"n1820938506":{"id":"n1820938506","loc":[-85.2063384,42.0398322]},"n1820938507":{"id":"n1820938507","loc":[-85.3395476,41.9851636]},"n1820938508":{"id":"n1820938508","loc":[-85.0328853,42.0963606]},"n1820938510":{"id":"n1820938510","loc":[-85.1170369,42.0843702]},"n1820938511":{"id":"n1820938511","loc":[-85.2784748,41.9868487]},"n1820938512":{"id":"n1820938512","loc":[-85.6310501,41.9435528]},"n1820938514":{"id":"n1820938514","loc":[-85.0334284,42.0981028]},"n1820938515":{"id":"n1820938515","loc":[-84.9912091,42.0868226]},"n1820938516":{"id":"n1820938516","loc":[-85.2806141,41.9940351]},"n1820938517":{"id":"n1820938517","loc":[-85.1233025,42.0776734]},"n1820938518":{"id":"n1820938518","loc":[-85.2047891,42.0429023]},"n1820938519":{"id":"n1820938519","loc":[-85.1475443,42.0648312]},"n1820938520":{"id":"n1820938520","loc":[-85.2644685,41.9990891]},"n1820938521":{"id":"n1820938521","loc":[-85.1056281,42.0872553]},"n1820938522":{"id":"n1820938522","loc":[-85.4813184,41.9930105]},"n1820938523":{"id":"n1820938523","loc":[-85.321551,41.9722936]},"n1820938524":{"id":"n1820938524","loc":[-85.1564664,42.0631211]},"n1820938525":{"id":"n1820938525","loc":[-85.4149885,42.0079144]},"n1820938527":{"id":"n1820938527","loc":[-85.2861888,41.9803653]},"n1820938528":{"id":"n1820938528","loc":[-85.1301379,42.0682178]},"n1820938529":{"id":"n1820938529","loc":[-85.4156537,42.0084247]},"n1820938530":{"id":"n1820938530","loc":[-85.245151,42.0176082]},"n1820938531":{"id":"n1820938531","loc":[-85.457818,42.0001651]},"n1820938532":{"id":"n1820938532","loc":[-85.310951,41.9694538]},"n1820938533":{"id":"n1820938533","loc":[-85.1509089,42.0611298]},"n1820938534":{"id":"n1820938534","loc":[-85.1108249,42.086321]},"n1820938535":{"id":"n1820938535","loc":[-85.1260344,42.0740687]},"n1820938536":{"id":"n1820938536","loc":[-85.4561228,42.0042791]},"n1820938537":{"id":"n1820938537","loc":[-85.2805082,41.9945761]},"n1820938538":{"id":"n1820938538","loc":[-85.273352,41.9981921]},"n1820938539":{"id":"n1820938539","loc":[-85.1084216,42.0864364]},"n1820938540":{"id":"n1820938540","loc":[-85.5009737,41.9773637]},"n1820938541":{"id":"n1820938541","loc":[-85.3960843,42.0051879]},"n1820938542":{"id":"n1820938542","loc":[-85.3425088,41.9865034]},"n1820938545":{"id":"n1820938545","loc":[-84.9937907,42.0860849]},"n1820938546":{"id":"n1820938546","loc":[-85.1084176,42.086065]},"n1820938547":{"id":"n1820938547","loc":[-85.3492851,41.9924786]},"n1820938548":{"id":"n1820938548","loc":[-85.2512235,42.0101147]},"n1820938549":{"id":"n1820938549","loc":[-85.3717298,41.9979326]},"n1820938551":{"id":"n1820938551","loc":[-85.2573712,42.0064081]},"n1820938552":{"id":"n1820938552","loc":[-85.2514596,42.010139]},"n1820938553":{"id":"n1820938553","loc":[-85.416512,42.0088073]},"n1820938554":{"id":"n1820938554","loc":[-85.4365964,42.0061606]},"n1820938555":{"id":"n1820938555","loc":[-85.4552431,42.0057301]},"n1820938556":{"id":"n1820938556","loc":[-85.2916283,41.9778769]},"n1820938557":{"id":"n1820938557","loc":[-85.100709,42.0902968]},"n1820938558":{"id":"n1820938558","loc":[-85.4703064,41.9965771]},"n1820938559":{"id":"n1820938559","loc":[-85.3134722,41.9696134]},"n1820938560":{"id":"n1820938560","loc":[-85.4834213,41.9885768]},"n1820938561":{"id":"n1820938561","loc":[-85.2740641,41.9975236]},"n1820938562":{"id":"n1820938562","loc":[-85.148334,42.0623405]},"n1820938563":{"id":"n1820938563","loc":[-85.2358598,42.0263675]},"n1820938565":{"id":"n1820938565","loc":[-85.2902979,41.9790892]},"n1820938566":{"id":"n1820938566","loc":[-85.2528865,42.0112869]},"n1820938567":{"id":"n1820938567","loc":[-85.2595319,41.9973003]},"n1820938568":{"id":"n1820938568","loc":[-85.071151,42.105689]},"n1820938570":{"id":"n1820938570","loc":[-85.299278,41.9732188]},"n1820938571":{"id":"n1820938571","loc":[-85.0354669,42.1024771]},"n1820938583":{"id":"n1820938583","loc":[-85.3313937,41.972562]},"n1820938585":{"id":"n1820938585","loc":[-85.0756933,42.1058334]},"n1820938587":{"id":"n1820938587","loc":[-85.3130324,41.9694219]},"n1820938590":{"id":"n1820938590","loc":[-85.0934227,42.0931681]},"n1820938592":{"id":"n1820938592","loc":[-85.3517956,41.9922553]},"n1820938593":{"id":"n1820938593","loc":[-85.4023971,42.0065169]},"n1820938594":{"id":"n1820938594","loc":[-85.3506798,41.9925583]},"n1820938595":{"id":"n1820938595","loc":[-85.3673524,41.9971193]},"n1820938596":{"id":"n1820938596","loc":[-85.1073608,42.0853523]},"n1820938597":{"id":"n1820938597","loc":[-85.2976579,41.972477]},"n1820938598":{"id":"n1820938598","loc":[-85.5616517,41.9694295]},"n1820938599":{"id":"n1820938599","loc":[-85.3552074,41.9921915]},"n1820938600":{"id":"n1820938600","loc":[-85.4665126,41.9999953]},"n1820938601":{"id":"n1820938601","loc":[-85.2740695,41.9966226]},"n1820938602":{"id":"n1820938602","loc":[-85.279376,41.9886669]},"n1820938603":{"id":"n1820938603","loc":[-85.0771109,42.1040413]},"n1820938604":{"id":"n1820938604","loc":[-85.2636049,41.9977895]},"n1820938605":{"id":"n1820938605","loc":[-85.3762145,41.9976456]},"n1820938606":{"id":"n1820938606","loc":[-85.2321369,42.0289577]},"n1820938620":{"id":"n1820938620","loc":[-85.4947724,41.9776189]},"n1820938622":{"id":"n1820938622","loc":[-85.1547069,42.0622768]},"n1820938624":{"id":"n1820938624","loc":[-85.0005056,42.0880249]},"n1820938626":{"id":"n1820938626","loc":[-85.0735596,42.1059357]},"n1820938628":{"id":"n1820938628","loc":[-85.4665298,41.99932]},"n1820938629":{"id":"n1820938629","loc":[-85.434515,42.0065273]},"n1820938630":{"id":"n1820938630","loc":[-85.117462,42.0823823]},"n1820938631":{"id":"n1820938631","loc":[-85.0131777,42.0890707]},"n1820938632":{"id":"n1820938632","loc":[-85.0875326,42.0961934]},"n1820938634":{"id":"n1820938634","loc":[-85.6433839,41.9112042]},"n1820938635":{"id":"n1820938635","loc":[-85.1366181,42.064969]},"n1820938636":{"id":"n1820938636","loc":[-85.073109,42.1057925]},"n1820938638":{"id":"n1820938638","loc":[-85.161406,42.0632541]},"n1820938640":{"id":"n1820938640","loc":[-85.6343932,41.9188845]},"n1820938642":{"id":"n1820938642","loc":[-85.2500004,42.010306]},"n1820938644":{"id":"n1820938644","loc":[-85.291918,41.9753166]},"n1820938663":{"id":"n1820938663","loc":[-85.2841611,41.9916812]},"n1820938664":{"id":"n1820938664","loc":[-85.1052955,42.0868134]},"n1820938665":{"id":"n1820938665","loc":[-85.4606118,42.0005534]},"n1820938666":{"id":"n1820938666","loc":[-85.5672736,41.9642922]},"n1820938667":{"id":"n1820938667","loc":[-85.6348481,41.9316932]},"n1820938668":{"id":"n1820938668","loc":[-85.0224904,42.0909576]},"n1820938669":{"id":"n1820938669","loc":[-85.0133856,42.0899755]},"n1820938670":{"id":"n1820938670","loc":[-85.344779,41.991139]},"n1820938671":{"id":"n1820938671","loc":[-85.632874,41.9425313]},"n1820938673":{"id":"n1820938673","loc":[-85.4941501,41.9779698]},"n1820938675":{"id":"n1820938675","loc":[-85.0862559,42.0997519]},"n1820938676":{"id":"n1820938676","loc":[-85.0097874,42.0898032]},"n1820938678":{"id":"n1820938678","loc":[-84.9913553,42.0863675]},"n1820938680":{"id":"n1820938680","loc":[-85.0533666,42.1038315]},"n1820938682":{"id":"n1820938682","loc":[-85.2950294,41.9743914]},"n1820938684":{"id":"n1820938684","loc":[-85.2517385,42.0104499]},"n1820938686":{"id":"n1820938686","loc":[-85.0247971,42.0922514]},"n1820938688":{"id":"n1820938688","loc":[-85.0807037,42.1026017]},"n1820938690":{"id":"n1820938690","loc":[-85.52462,41.9722748]},"n1820938694":{"id":"n1820938694","loc":[-85.2586535,41.9988818]},"n1820938695":{"id":"n1820938695","loc":[-85.0931612,42.092948]},"n1820938697":{"id":"n1820938697","loc":[-85.2470822,42.016564]},"n1820938698":{"id":"n1820938698","loc":[-85.4143018,42.0075158]},"n1820938699":{"id":"n1820938699","loc":[-85.0771484,42.104487]},"n1820938700":{"id":"n1820938700","loc":[-85.0291208,42.0942775]},"n1820938701":{"id":"n1820938701","loc":[-85.6367964,41.9185971]},"n1820938702":{"id":"n1820938702","loc":[-85.085419,42.1010693]},"n1820938703":{"id":"n1820938703","loc":[-85.0583877,42.1040584]},"n1820938705":{"id":"n1820938705","loc":[-85.2573379,42.0003182]},"n1820938706":{"id":"n1820938706","loc":[-85.2655937,41.9981575]},"n1820938707":{"id":"n1820938707","loc":[-85.023181,42.0915758]},"n1820938708":{"id":"n1820938708","loc":[-85.2318687,42.0274674]},"n1820938709":{"id":"n1820938709","loc":[-85.1056389,42.0866184]},"n1820938710":{"id":"n1820938710","loc":[-85.5276265,41.9700978]},"n1820938711":{"id":"n1820938711","loc":[-85.0864128,42.0945761]},"n1820938712":{"id":"n1820938712","loc":[-84.9897071,42.0871888]},"n1820938714":{"id":"n1820938714","loc":[-85.1328845,42.0665611]},"n1820938715":{"id":"n1820938715","loc":[-85.0336537,42.0991377]},"n1820938716":{"id":"n1820938716","loc":[-85.087597,42.0986692]},"n1820938717":{"id":"n1820938717","loc":[-85.1241394,42.0761882]},"n1820938718":{"id":"n1820938718","loc":[-85.1176002,42.0847723]},"n1820938719":{"id":"n1820938719","loc":[-85.2423615,42.0216572]},"n1820938721":{"id":"n1820938721","loc":[-85.2196378,42.0387908]},"n1820938722":{"id":"n1820938722","loc":[-85.0164272,42.0890082]},"n1820938723":{"id":"n1820938723","loc":[-85.5917182,41.9451807]},"n1820938724":{"id":"n1820938724","loc":[-85.2458806,42.0086638]},"n1820938725":{"id":"n1820938725","loc":[-85.1264474,42.0740527]},"n1820938726":{"id":"n1820938726","loc":[-85.1961631,42.04738]},"n1820938727":{"id":"n1820938727","loc":[-85.2784643,41.9943648]},"n1820938728":{"id":"n1820938728","loc":[-85.2905554,41.9763216]},"n1820938729":{"id":"n1820938729","loc":[-85.2913386,41.9771511]},"n1820938730":{"id":"n1820938730","loc":[-85.0112519,42.0895683]},"n1820938732":{"id":"n1820938732","loc":[-85.4290261,42.0064531]},"n1820938733":{"id":"n1820938733","loc":[-85.3867073,42.0031629]},"n1820938734":{"id":"n1820938734","loc":[-85.4943647,41.9836005]},"n1820938735":{"id":"n1820938735","loc":[-85.4900303,41.9860728]},"n1820938736":{"id":"n1820938736","loc":[-85.0866153,42.0944539]},"n1820938737":{"id":"n1820938737","loc":[-85.0869532,42.0990911]},"n1820938738":{"id":"n1820938738","loc":[-85.6321659,41.9208851]},"n1820938739":{"id":"n1820938739","loc":[-85.5930485,41.9433453]},"n1820938740":{"id":"n1820938740","loc":[-85.0406851,42.102733]},"n1820938741":{"id":"n1820938741","loc":[-85.1051131,42.0869846]},"n1820938742":{"id":"n1820938742","loc":[-85.1377554,42.0648893]},"n1820938743":{"id":"n1820938743","loc":[-85.2795694,41.994604]},"n1820938745":{"id":"n1820938745","loc":[-85.4948153,41.9826594]},"n1820938746":{"id":"n1820938746","loc":[-85.4488916,42.0050923]},"n1820938747":{"id":"n1820938747","loc":[-85.1052526,42.0866144]},"n1820938748":{"id":"n1820938748","loc":[-85.1468749,42.0653991]},"n1820938749":{"id":"n1820938749","loc":[-85.0856886,42.1006104]},"n1820938750":{"id":"n1820938750","loc":[-85.2144022,42.0404004]},"n1820938751":{"id":"n1820938751","loc":[-85.277771,41.9907458]},"n1820938752":{"id":"n1820938752","loc":[-85.1474542,42.0636149]},"n1820938753":{"id":"n1820938753","loc":[-85.0820515,42.1028075]},"n1820938754":{"id":"n1820938754","loc":[-85.1122948,42.08525]},"n1820938756":{"id":"n1820938756","loc":[-85.0173352,42.0901933]},"n1820938757":{"id":"n1820938757","loc":[-85.2259721,42.0354018]},"n1820938758":{"id":"n1820938758","loc":[-85.0872389,42.0987795]},"n1820938759":{"id":"n1820938759","loc":[-85.2291436,42.031874]},"n1820938760":{"id":"n1820938760","loc":[-85.3802485,42.0016002]},"n1820938761":{"id":"n1820938761","loc":[-85.3945822,42.0057938]},"n1820938762":{"id":"n1820938762","loc":[-85.5273237,41.9713017]},"n1820938763":{"id":"n1820938763","loc":[-85.2868862,41.9798629]},"n1820938764":{"id":"n1820938764","loc":[-85.2516677,42.0107899]},"n1820938766":{"id":"n1820938766","loc":[-85.3183002,41.9693103]},"n1820938768":{"id":"n1820938768","loc":[-85.2159042,42.0401932]},"n1820938770":{"id":"n1820938770","loc":[-85.0094481,42.0911141]},"n1820938771":{"id":"n1820938771","loc":[-85.0244538,42.0922155]},"n1820938772":{"id":"n1820938772","loc":[-85.231697,42.028862]},"n1820938773":{"id":"n1820938773","loc":[-85.2102394,42.0390617]},"n1820938774":{"id":"n1820938774","loc":[-85.2463419,42.0151212]},"n1820938775":{"id":"n1820938775","loc":[-85.0726195,42.1056424]},"n1820938776":{"id":"n1820938776","loc":[-85.0060431,42.0883262]},"n1820938778":{"id":"n1820938778","loc":[-85.425889,42.0056982]},"n1820938779":{"id":"n1820938779","loc":[-85.1183042,42.0820638]},"n1820938780":{"id":"n1820938780","loc":[-85.441596,42.0058257]},"n1820938781":{"id":"n1820938781","loc":[-85.1124879,42.0847086]},"n1820938782":{"id":"n1820938782","loc":[-85.2452733,42.0153894]},"n1820938783":{"id":"n1820938783","loc":[-85.2741191,41.9969244]},"n1820938784":{"id":"n1820938784","loc":[-85.2829487,41.9822236]},"n1820938785":{"id":"n1820938785","loc":[-85.3202743,41.972142]},"n1820938786":{"id":"n1820938786","loc":[-85.2345402,42.0266465]},"n1820938787":{"id":"n1820938787","loc":[-85.3037626,41.9724611]},"n1820938789":{"id":"n1820938789","loc":[-85.2474792,42.0161973]},"n1820938790":{"id":"n1820938790","loc":[-85.2951045,41.9727323]},"n1820938791":{"id":"n1820938791","loc":[-85.322345,41.9712726]},"n1820938792":{"id":"n1820938792","loc":[-85.2402372,42.0110394]},"n1820938793":{"id":"n1820938793","loc":[-85.5135693,41.9698659]},"n1820938794":{"id":"n1820938794","loc":[-85.4695339,41.9967366]},"n1820938796":{"id":"n1820938796","loc":[-85.0418492,42.1011131]},"n1820938797":{"id":"n1820938797","loc":[-85.3334107,41.9806337]},"n1820938798":{"id":"n1820938798","loc":[-85.5625314,41.9711685]},"n1820938799":{"id":"n1820938799","loc":[-85.3755707,41.9973585]},"n1820938800":{"id":"n1820938800","loc":[-85.5227532,41.9722429]},"n1820938801":{"id":"n1820938801","loc":[-85.4267687,42.0052836]},"n1820938803":{"id":"n1820938803","loc":[-85.0284704,42.0940837]},"n1820938804":{"id":"n1820938804","loc":[-85.015585,42.0885305]},"n1820938805":{"id":"n1820938805","loc":[-85.0765905,42.1053865]},"n1820938806":{"id":"n1820938806","loc":[-85.2614953,41.9964551]},"n1820938808":{"id":"n1820938808","loc":[-85.0307355,42.0947313]},"n1820938810":{"id":"n1820938810","loc":[-85.3894753,42.0003565]},"n1820938812":{"id":"n1820938812","loc":[-85.0868848,42.095006]},"n1820938813":{"id":"n1820938813","loc":[-85.3854198,42.0009465]},"n1820938814":{"id":"n1820938814","loc":[-85.2659692,41.9993534]},"n1820938815":{"id":"n1820938815","loc":[-85.1234259,42.0765266]},"n1820938816":{"id":"n1820938816","loc":[-85.1426906,42.0648893]},"n1820938818":{"id":"n1820938818","loc":[-85.1014533,42.0893067]},"n1820938819":{"id":"n1820938819","loc":[-85.0883064,42.098067]},"n1820938820":{"id":"n1820938820","loc":[-85.0503156,42.102704]},"n1820938821":{"id":"n1820938821","loc":[-85.1179649,42.0821884]},"n1820938822":{"id":"n1820938822","loc":[-85.3484697,41.9921596]},"n1820938823":{"id":"n1820938823","loc":[-85.3732962,41.9970874]},"n1820938824":{"id":"n1820938824","loc":[-85.2784104,41.9898312]},"n1820938825":{"id":"n1820938825","loc":[-85.4441709,42.0052198]},"n1820938826":{"id":"n1820938826","loc":[-85.3925438,42.0038326]},"n1820938829":{"id":"n1820938829","loc":[-85.5717582,41.9621861]},"n1820938830":{"id":"n1820938830","loc":[-85.0866314,42.0995051]},"n1820938831":{"id":"n1820938831","loc":[-85.576672,41.9522769]},"n1820938832":{"id":"n1820938832","loc":[-85.1587238,42.0636205]},"n1820938833":{"id":"n1820938833","loc":[-85.3804245,41.9999155]},"n1820938834":{"id":"n1820938834","loc":[-85.280083,41.9948843]},"n1820938836":{"id":"n1820938836","loc":[-85.561892,41.9686693]},"n1820938837":{"id":"n1820938837","loc":[-85.0158975,42.0885253]},"n1820938838":{"id":"n1820938838","loc":[-85.4248204,42.007633]},"n1820938839":{"id":"n1820938839","loc":[-85.0352738,42.1039657]},"n1820938840":{"id":"n1820938840","loc":[-85.211956,42.0411812]},"n1820938841":{"id":"n1820938841","loc":[-85.4816575,41.9908997]},"n1820938842":{"id":"n1820938842","loc":[-85.3807635,42.0020308]},"n1820938843":{"id":"n1820938843","loc":[-85.0100865,42.0898521]},"n1820938844":{"id":"n1820938844","loc":[-85.0103936,42.0897434]},"n1820938848":{"id":"n1820938848","loc":[-85.2430052,42.0131363]},"n1820938849":{"id":"n1820938849","loc":[-85.112559,42.0853723]},"n1820938851":{"id":"n1820938851","loc":[-85.3641553,41.9952535]},"n1820938852":{"id":"n1820938852","loc":[-85.2087373,42.0390777]},"n1820938853":{"id":"n1820938853","loc":[-85.2473933,42.0148263]},"n1820938854":{"id":"n1820938854","loc":[-85.0213464,42.090509]},"n1820938855":{"id":"n1820938855","loc":[-85.0673208,42.1052353]},"n1820938856":{"id":"n1820938856","loc":[-85.1003053,42.0905528]},"n1820938857":{"id":"n1820938857","loc":[-85.2617367,41.9965389]},"n1820938858":{"id":"n1820938858","loc":[-85.280363,41.9916015]},"n1820938859":{"id":"n1820938859","loc":[-85.0038866,42.0873469]},"n1820938860":{"id":"n1820938860","loc":[-85.2476401,42.0151451]},"n1820938861":{"id":"n1820938861","loc":[-85.193717,42.0499294]},"n1820938862":{"id":"n1820938862","loc":[-85.3478689,41.9917609]},"n1820938863":{"id":"n1820938863","loc":[-85.5638017,41.9648881]},"n1820938864":{"id":"n1820938864","loc":[-85.4356308,42.0064476]},"n1820938865":{"id":"n1820938865","loc":[-85.0561722,42.1023509]},"n1820938867":{"id":"n1820938867","loc":[-85.2256031,42.0356034]},"n1820938868":{"id":"n1820938868","loc":[-85.6102576,41.9420844]},"n1820938869":{"id":"n1820938869","loc":[-85.2285213,42.0339938]},"n1820938870":{"id":"n1820938870","loc":[-85.0326238,42.0978003]},"n1820938871":{"id":"n1820938871","loc":[-85.0131389,42.0903736]},"n1820938872":{"id":"n1820938872","loc":[-85.2550859,42.0012259]},"n1820938873":{"id":"n1820938873","loc":[-85.1130029,42.0846966]},"n1820938874":{"id":"n1820938874","loc":[-85.1579041,42.06336]},"n1820938875":{"id":"n1820938875","loc":[-85.0430522,42.1020234]},"n1820938876":{"id":"n1820938876","loc":[-85.2786679,41.9865935]},"n1820938877":{"id":"n1820938877","loc":[-85.1221666,42.0788706]},"n1820938878":{"id":"n1820938878","loc":[-85.2554614,42.0103303]},"n1820938879":{"id":"n1820938879","loc":[-85.2349801,42.0265748]},"n1820938880":{"id":"n1820938880","loc":[-85.0997434,42.0907864]},"n1820938881":{"id":"n1820938881","loc":[-85.0045464,42.0878167]},"n1820938882":{"id":"n1820938882","loc":[-85.2728048,41.9982519]},"n1820938883":{"id":"n1820938883","loc":[-85.3111333,41.9691587]},"n1820938884":{"id":"n1820938884","loc":[-85.3219802,41.9721899]},"n1820938885":{"id":"n1820938885","loc":[-85.3091378,41.9699325]},"n1820938887":{"id":"n1820938887","loc":[-85.4242367,42.0085203]},"n1820938888":{"id":"n1820938888","loc":[-84.9968377,42.0874504]},"n1820938890":{"id":"n1820938890","loc":[-85.5443139,41.9714078]},"n1820938891":{"id":"n1820938891","loc":[-85.6404013,41.9154676]},"n1820938892":{"id":"n1820938892","loc":[-85.3644986,41.9962582]},"n1820938893":{"id":"n1820938893","loc":[-85.0496772,42.1018323]},"n1820938894":{"id":"n1820938894","loc":[-85.297261,41.9737373]},"n1820938895":{"id":"n1820938895","loc":[-85.0327096,42.098071]},"n1820938896":{"id":"n1820938896","loc":[-85.3856773,41.9996867]},"n1820938897":{"id":"n1820938897","loc":[-85.0493862,42.1015509]},"n1820938898":{"id":"n1820938898","loc":[-84.9969879,42.0876614]},"n1820938899":{"id":"n1820938899","loc":[-85.0848625,42.1013587]},"n1820938900":{"id":"n1820938900","loc":[-85.5853195,41.9479201]},"n1820938901":{"id":"n1820938901","loc":[-85.6329169,41.9387964]},"n1820938902":{"id":"n1820938902","loc":[-85.0843046,42.1029468]},"n1820938903":{"id":"n1820938903","loc":[-85.1228747,42.0778474]},"n1820938904":{"id":"n1820938904","loc":[-85.4855456,41.984095]},"n1820938905":{"id":"n1820938905","loc":[-85.0573269,42.1026801]},"n1820938906":{"id":"n1820938906","loc":[-85.2425868,42.0131523]},"n1820938907":{"id":"n1820938907","loc":[-85.1149622,42.0860053]},"n1820938908":{"id":"n1820938908","loc":[-85.4833097,41.9951578]},"n1820938909":{"id":"n1820938909","loc":[-85.075979,42.1056372]},"n1820938910":{"id":"n1820938910","loc":[-85.0338509,42.0977139]},"n1820938911":{"id":"n1820938911","loc":[-85.6384272,41.9115715]},"n1820938912":{"id":"n1820938912","loc":[-85.0458363,42.1004074]},"n1820938913":{"id":"n1820938913","loc":[-85.0592138,42.1048305]},"n1820938914":{"id":"n1820938914","loc":[-85.2807493,41.9916653]},"n1820938915":{"id":"n1820938915","loc":[-85.1103274,42.0864193]},"n1820938916":{"id":"n1820938916","loc":[-85.6267156,41.9404404]},"n1820938918":{"id":"n1820938918","loc":[-85.0331374,42.0982911]},"n1820938919":{"id":"n1820938919","loc":[-85.5637331,41.965409]},"n1820938920":{"id":"n1820938920","loc":[-85.5457515,41.9714237]},"n1820938922":{"id":"n1820938922","loc":[-85.082073,42.1030104]},"n1820938923":{"id":"n1820938923","loc":[-85.0780765,42.103102]},"n1820938924":{"id":"n1820938924","loc":[-85.4208035,42.0089508]},"n1820938925":{"id":"n1820938925","loc":[-85.3469934,41.9914795]},"n1820938926":{"id":"n1820938926","loc":[-85.0322,42.095619]},"n1820938927":{"id":"n1820938927","loc":[-85.4784431,41.9949401]},"n1820938928":{"id":"n1820938928","loc":[-85.1303095,42.0667523]},"n1820938929":{"id":"n1820938929","loc":[-85.2463784,42.0084781]},"n1820938930":{"id":"n1820938930","loc":[-85.6299986,41.9427707]},"n1820938931":{"id":"n1820938931","loc":[-85.6325907,41.9238499]},"n1820938932":{"id":"n1820938932","loc":[-85.4808464,41.9914476]},"n1820938934":{"id":"n1820938934","loc":[-85.2411599,42.0105292]},"n1820938935":{"id":"n1820938935","loc":[-85.0163213,42.0892379]},"n1820938936":{"id":"n1820938936","loc":[-85.3290934,41.9682322]},"n1820938937":{"id":"n1820938937","loc":[-85.4925623,41.9853231]},"n1820938938":{"id":"n1820938938","loc":[-85.0338294,42.09892]},"n1820938940":{"id":"n1820938940","loc":[-85.4174561,42.008903]},"n1820938941":{"id":"n1820938941","loc":[-85.1165595,42.0838845]},"n1820938942":{"id":"n1820938942","loc":[-85.2954585,41.9717192]},"n1820938943":{"id":"n1820938943","loc":[-85.6330199,41.9257338]},"n1820938944":{"id":"n1820938944","loc":[-85.2294654,42.0324478]},"n1820938945":{"id":"n1820938945","loc":[-85.5601282,41.9728914]},"n1820938946":{"id":"n1820938946","loc":[-85.1176324,42.08568]},"n1820938947":{"id":"n1820938947","loc":[-85.0210245,42.0906005]},"n1820938948":{"id":"n1820938948","loc":[-85.0251887,42.09253]},"n1820938949":{"id":"n1820938949","loc":[-85.0895832,42.0939551]},"n1820938950":{"id":"n1820938950","loc":[-84.9915109,42.085842]},"n1820938951":{"id":"n1820938951","loc":[-85.2187366,42.0393486]},"n1820938952":{"id":"n1820938952","loc":[-85.006605,42.087579]},"n1820938953":{"id":"n1820938953","loc":[-85.046641,42.1012393]},"n1820938954":{"id":"n1820938954","loc":[-85.052102,42.103695]},"n1820938955":{"id":"n1820938955","loc":[-85.283925,41.9912825]},"n1820938956":{"id":"n1820938956","loc":[-85.2326626,42.0316349]},"n1820938957":{"id":"n1820938957","loc":[-85.1174298,42.0859694]},"n1820938958":{"id":"n1820938958","loc":[-85.3802056,41.9994794]},"n1820938959":{"id":"n1820938959","loc":[-85.4586334,41.9999737]},"n1820938960":{"id":"n1820938960","loc":[-85.4302234,42.0069418]},"n1820938961":{"id":"n1820938961","loc":[-85.092201,42.0930674]},"n1820938962":{"id":"n1820938962","loc":[-85.3684511,41.9979382]},"n1820938963":{"id":"n1820938963","loc":[-85.4618735,42.0011856]},"n1820938964":{"id":"n1820938964","loc":[-85.4828205,41.9877793]},"n1820938965":{"id":"n1820938965","loc":[-85.0837789,42.1025726]},"n1820938966":{"id":"n1820938966","loc":[-85.0176195,42.090253]},"n1820938967":{"id":"n1820938967","loc":[-85.3801627,42.001074]},"n1820938968":{"id":"n1820938968","loc":[-85.4767007,41.994488]},"n1820938969":{"id":"n1820938969","loc":[-85.274268,41.9957495]},"n1820938970":{"id":"n1820938970","loc":[-85.2977438,41.9719506]},"n1820938971":{"id":"n1820938971","loc":[-85.2425546,42.0208682]},"n1820938972":{"id":"n1820938972","loc":[-85.2557082,42.002382]},"n1820938973":{"id":"n1820938973","loc":[-85.3187937,41.9691986]},"n1820938975":{"id":"n1820938975","loc":[-85.2448077,42.0153045]},"n1820938977":{"id":"n1820938977","loc":[-85.0343015,42.0997718]},"n1820938978":{"id":"n1820938978","loc":[-85.2449364,42.01874]},"n1820938979":{"id":"n1820938979","loc":[-85.2598391,41.9969602]},"n1820938980":{"id":"n1820938980","loc":[-85.4294724,42.0067665]},"n1820938981":{"id":"n1820938981","loc":[-85.428082,42.0055124]},"n1820938983":{"id":"n1820938983","loc":[-85.5436315,41.9717484]},"n1820938985":{"id":"n1820938985","loc":[-85.5978336,41.9407437]},"n1820938986":{"id":"n1820938986","loc":[-85.491661,41.9860249]},"n1820938987":{"id":"n1820938987","loc":[-85.4942789,41.9801392]},"n1820938988":{"id":"n1820938988","loc":[-85.0416306,42.1010841]},"n1820938989":{"id":"n1820938989","loc":[-85.2653644,41.9984433]},"n1820938990":{"id":"n1820938990","loc":[-85.1028266,42.0881124]},"n1820938991":{"id":"n1820938991","loc":[-85.0163146,42.0887932]},"n1820938992":{"id":"n1820938992","loc":[-85.5282209,41.9678112]},"n1820938993":{"id":"n1820938993","loc":[-85.5442752,41.9715888]},"n1820938994":{"id":"n1820938994","loc":[-85.5634327,41.9658558]},"n1820938995":{"id":"n1820938995","loc":[-85.0384227,42.1037627]},"n1820938996":{"id":"n1820938996","loc":[-85.1144258,42.0854439]},"n1820938997":{"id":"n1820938997","loc":[-85.1870651,42.0506305]},"n1820938998":{"id":"n1820938998","loc":[-85.1256159,42.0747376]},"n1820938999":{"id":"n1820938999","loc":[-85.3272695,41.9715836]},"n1820939000":{"id":"n1820939000","loc":[-85.0543067,42.103098]},"n1820939001":{"id":"n1820939001","loc":[-85.4678173,41.9973585]},"n1820939003":{"id":"n1820939003","loc":[-85.0266626,42.0933154]},"n1820939004":{"id":"n1820939004","loc":[-85.0353046,42.1019728]},"n1820939005":{"id":"n1820939005","loc":[-85.1237961,42.0762798]},"n1820939006":{"id":"n1820939006","loc":[-85.2812214,41.9826702]},"n1820939007":{"id":"n1820939007","loc":[-85.2927763,41.9747343]},"n1820939008":{"id":"n1820939008","loc":[-85.3270979,41.9720862]},"n1820939009":{"id":"n1820939009","loc":[-85.488657,41.9856581]},"n1820939010":{"id":"n1820939010","loc":[-85.3087301,41.9701399]},"n1820939011":{"id":"n1820939011","loc":[-85.0276939,42.093768]},"n1820939012":{"id":"n1820939012","loc":[-85.2956516,41.9748779]},"n1820939013":{"id":"n1820939013","loc":[-85.1298579,42.0726443]},"n1820939014":{"id":"n1820939014","loc":[-85.105144,42.0870893]},"n1820939015":{"id":"n1820939015","loc":[-85.0677486,42.1053917]},"n1820939016":{"id":"n1820939016","loc":[-85.0333681,42.0993459]},"n1820939017":{"id":"n1820939017","loc":[-85.6384272,41.910805]},"n1820939018":{"id":"n1820939018","loc":[-85.399496,42.006894]},"n1820939019":{"id":"n1820939019","loc":[-85.2648427,41.9998318]},"n1820939020":{"id":"n1820939020","loc":[-85.1237424,42.0766779]},"n1820939021":{"id":"n1820939021","loc":[-85.2515025,42.0109442]},"n1820939022":{"id":"n1820939022","loc":[-85.5566306,41.9718385]},"n1820939023":{"id":"n1820939023","loc":[-85.090644,42.0938369]},"n1820939024":{"id":"n1820939024","loc":[-85.1245525,42.074914]},"n1820939025":{"id":"n1820939025","loc":[-85.1099934,42.0863926]},"n1820939026":{"id":"n1820939026","loc":[-85.1251653,42.0744589]},"n1820939027":{"id":"n1820939027","loc":[-85.401792,42.0068143]},"n1820939028":{"id":"n1820939028","loc":[-85.0094763,42.0899584]},"n1820939029":{"id":"n1820939029","loc":[-85.1330779,42.0705605]},"n1820939030":{"id":"n1820939030","loc":[-85.4935064,41.984398]},"n1820939031":{"id":"n1820939031","loc":[-85.5713334,41.9613939]},"n1820939032":{"id":"n1820939032","loc":[-85.0873945,42.0964669]},"n1820939033":{"id":"n1820939033","loc":[-85.0886497,42.0986481]},"n1820939034":{"id":"n1820939034","loc":[-85.3276343,41.9758897]},"n1820939035":{"id":"n1820939035","loc":[-85.1304386,42.0727387]},"n1820939036":{"id":"n1820939036","loc":[-85.2551932,42.0052999]},"n1820939037":{"id":"n1820939037","loc":[-85.2206936,42.0384458]},"n1820939038":{"id":"n1820939038","loc":[-85.2313645,42.0286389]},"n1820939039":{"id":"n1820939039","loc":[-85.0754586,42.1059835]},"n1820939040":{"id":"n1820939040","loc":[-85.0663324,42.1050812]},"n1820939041":{"id":"n1820939041","loc":[-85.2406234,42.0106887]},"n1820939042":{"id":"n1820939042","loc":[-85.0685962,42.1058175]},"n1820939043":{"id":"n1820939043","loc":[-85.0689462,42.1059437]},"n1820939044":{"id":"n1820939044","loc":[-85.0586144,42.1046144]},"n1820939045":{"id":"n1820939045","loc":[-85.3650565,41.9965452]},"n1820939047":{"id":"n1820939047","loc":[-85.5752558,41.9536014]},"n1820939048":{"id":"n1820939048","loc":[-85.5110159,41.9710624]},"n1820939050":{"id":"n1820939050","loc":[-85.2832641,41.9926477]},"n1820939051":{"id":"n1820939051","loc":[-85.0078402,42.0898947]},"n1820939052":{"id":"n1820939052","loc":[-85.3882737,42.0017916]},"n1820939053":{"id":"n1820939053","loc":[-85.1718945,42.0564937]},"n1820939054":{"id":"n1820939054","loc":[-85.0947048,42.0929293]},"n1820939055":{"id":"n1820939055","loc":[-85.4456944,42.0051082]},"n1820939056":{"id":"n1820939056","loc":[-85.3139872,41.9706903]},"n1820939057":{"id":"n1820939057","loc":[-85.3893895,42.0034021]},"n1820939058":{"id":"n1820939058","loc":[-85.2425332,42.0106089]},"n1820939059":{"id":"n1820939059","loc":[-85.6085624,41.9420844]},"n1820939060":{"id":"n1820939060","loc":[-85.210411,42.0397789]},"n1820939061":{"id":"n1820939061","loc":[-85.2762542,41.9960473]},"n1820939062":{"id":"n1820939062","loc":[-85.4686584,41.9969973]},"n1820939063":{"id":"n1820939063","loc":[-85.3860421,42.0018394]},"n1820939064":{"id":"n1820939064","loc":[-85.5636944,41.9644414]},"n1820939065":{"id":"n1820939065","loc":[-85.3267331,41.9766554]},"n1820939066":{"id":"n1820939066","loc":[-85.0868996,42.0943822]},"n1820939067":{"id":"n1820939067","loc":[-85.104861,42.0880038]},"n1820939068":{"id":"n1820939068","loc":[-85.5537123,41.9695093]},"n1820939069":{"id":"n1820939069","loc":[-85.6325092,41.9396743]},"n1820939070":{"id":"n1820939070","loc":[-85.3869648,42.0024454]},"n1820939071":{"id":"n1820939071","loc":[-85.2775349,41.9957335]},"n1820939072":{"id":"n1820939072","loc":[-85.2055616,42.0421533]},"n1820939073":{"id":"n1820939073","loc":[-85.4731431,41.9946531]},"n1820939074":{"id":"n1820939074","loc":[-85.0399609,42.1030833]},"n1820939075":{"id":"n1820939075","loc":[-85.3055758,41.9725169]},"n1820939076":{"id":"n1820939076","loc":[-85.4834599,41.994488]},"n1820939077":{"id":"n1820939077","loc":[-85.3819866,42.0023018]},"n1820939078":{"id":"n1820939078","loc":[-85.1218756,42.0789992]},"n1820939079":{"id":"n1820939079","loc":[-85.2793159,41.9944458]},"n1820939080":{"id":"n1820939080","loc":[-85.2495498,42.0101466]},"n1820939081":{"id":"n1820939081","loc":[-85.0035969,42.0872434]},"n1820939082":{"id":"n1820939082","loc":[-85.1054243,42.0865626]},"n1820939083":{"id":"n1820939083","loc":[-85.0917665,42.0934774]},"n1820939084":{"id":"n1820939084","loc":[-85.3442211,41.988938]},"n1820939086":{"id":"n1820939086","loc":[-85.273989,41.9953588]},"n1820939087":{"id":"n1820939087","loc":[-85.1142541,42.0852488]},"n1820939089":{"id":"n1820939089","loc":[-85.1526684,42.0615758]},"n1820939090":{"id":"n1820939090","loc":[-85.2538843,42.0110159]},"n1820939091":{"id":"n1820939091","loc":[-85.28341,41.9909635]},"n1820939092":{"id":"n1820939092","loc":[-85.3963178,42.0050217]},"n1820939093":{"id":"n1820939093","loc":[-85.0851682,42.1012472]},"n1820939095":{"id":"n1820939095","loc":[-85.2811784,41.986243]},"n1820939096":{"id":"n1820939096","loc":[-85.4274125,42.0052995]},"n1820939097":{"id":"n1820939097","loc":[-85.0871262,42.0951652]},"n1820939099":{"id":"n1820939099","loc":[-85.1314253,42.0671665]},"n1820939100":{"id":"n1820939100","loc":[-85.2778997,41.991001]},"n1820939101":{"id":"n1820939101","loc":[-85.112107,42.0862812]},"n1820939102":{"id":"n1820939102","loc":[-85.299911,41.9729955]},"n1820939103":{"id":"n1820939103","loc":[-85.639822,41.9094796]},"n1820939104":{"id":"n1820939104","loc":[-85.122294,42.0785334]},"n1820939105":{"id":"n1820939105","loc":[-85.2476294,42.015719]},"n1820939106":{"id":"n1820939106","loc":[-85.4946007,41.9814631]},"n1820939107":{"id":"n1820939107","loc":[-85.0879524,42.0986919]},"n1820939108":{"id":"n1820939108","loc":[-85.0342814,42.098274]},"n1820939109":{"id":"n1820939109","loc":[-85.2450695,42.0095463]},"n1820939110":{"id":"n1820939110","loc":[-85.3847546,42.0024135]},"n1820939111":{"id":"n1820939111","loc":[-85.2961344,41.9742558]},"n1820939112":{"id":"n1820939112","loc":[-85.27899,41.994317]},"n1820939114":{"id":"n1820939114","loc":[-85.1017644,42.0886618]},"n1820939115":{"id":"n1820939115","loc":[-85.076215,42.1056333]},"n1820939116":{"id":"n1820939116","loc":[-85.1198009,42.0805349]},"n1820939117":{"id":"n1820939117","loc":[-85.11988,42.0798513]},"n1820939118":{"id":"n1820939118","loc":[-85.147819,42.0625476]},"n1820939119":{"id":"n1820939119","loc":[-85.0585969,42.1029042]},"n1820939120":{"id":"n1820939120","loc":[-85.1248596,42.0745744]},"n1820939121":{"id":"n1820939121","loc":[-85.3023786,41.9725249]},"n1820939123":{"id":"n1820939123","loc":[-85.0119332,42.0900699]},"n1820939124":{"id":"n1820939124","loc":[-85.2466852,42.0170343]},"n1820939125":{"id":"n1820939125","loc":[-85.0033019,42.0872792]},"n1820939126":{"id":"n1820939126","loc":[-85.0042084,42.0875778]},"n1820939128":{"id":"n1820939128","loc":[-85.0052961,42.0885424]},"n1820939130":{"id":"n1820939130","loc":[-85.0647942,42.10508]},"n1820939131":{"id":"n1820939131","loc":[-85.2824123,41.9825107]},"n1820939132":{"id":"n1820939132","loc":[-85.3210039,41.9723255]},"n1820939133":{"id":"n1820939133","loc":[-85.0491033,42.1014184]},"n1820939134":{"id":"n1820939134","loc":[-85.1127776,42.0855168]},"n1820939135":{"id":"n1820939135","loc":[-85.1243768,42.0759322]},"n1820939137":{"id":"n1820939137","loc":[-85.125974,42.0747547]},"n1820939138":{"id":"n1820939138","loc":[-85.1071248,42.0859973]},"n1820939139":{"id":"n1820939139","loc":[-85.5326175,41.9674833]},"n1820939140":{"id":"n1820939140","loc":[-85.1338715,42.0660833]},"n1820939142":{"id":"n1820939142","loc":[-85.649671,41.9135675]},"n1820939144":{"id":"n1820939144","loc":[-85.0236545,42.0920444]},"n1820939145":{"id":"n1820939145","loc":[-85.1084391,42.0859376]},"n1820939146":{"id":"n1820939146","loc":[-85.1539988,42.0618626]},"n1820939147":{"id":"n1820939147","loc":[-85.2354521,42.026511]},"n1820939148":{"id":"n1820939148","loc":[-85.2362246,42.0260408]},"n1820939149":{"id":"n1820939149","loc":[-85.2401342,42.0115233]},"n1820939150":{"id":"n1820939150","loc":[-85.295319,41.9747423]},"n1820939151":{"id":"n1820939151","loc":[-85.1164696,42.0835409]},"n1820939152":{"id":"n1820939152","loc":[-85.3232891,41.9712885]},"n1820939153":{"id":"n1820939153","loc":[-85.2574463,42.0068944]},"n1820939155":{"id":"n1820939155","loc":[-85.5704064,41.9598246]},"n1820939156":{"id":"n1820939156","loc":[-85.0349077,42.0998116]},"n1820939157":{"id":"n1820939157","loc":[-85.0949529,42.0925619]},"n1820939159":{"id":"n1820939159","loc":[-85.0179829,42.0902343]},"n1820939160":{"id":"n1820939160","loc":[-85.0405832,42.1016942]},"n1820939161":{"id":"n1820939161","loc":[-85.2534015,42.0111833]},"n1820939162":{"id":"n1820939162","loc":[-85.0839881,42.102708]},"n1820939163":{"id":"n1820939163","loc":[-85.0341996,42.1008385]},"n1820939164":{"id":"n1820939164","loc":[-85.1037761,42.0879731]},"n1820939173":{"id":"n1820939173","loc":[-85.0460616,42.1005786]},"n1820939177":{"id":"n1820939177","loc":[-85.0061651,42.0878059]},"n1820939181":{"id":"n1820939181","loc":[-85.1456775,42.0654684]},"n1820939183":{"id":"n1820939183","loc":[-85.1325508,42.0718439]},"n1820939185":{"id":"n1820939185","loc":[-85.2485842,42.008329]},"n1820939187":{"id":"n1820939187","loc":[-85.2744128,41.9949322]},"n1820939189":{"id":"n1820939189","loc":[-85.2579025,41.9999542]},"n1820939191":{"id":"n1820939191","loc":[-85.3358998,41.9828987]},"n1820939193":{"id":"n1820939193","loc":[-85.3192658,41.9716714]},"n1820939194":{"id":"n1820939194","loc":[-85.6400795,41.9130725]},"n1820939195":{"id":"n1820939195","loc":[-85.3278489,41.9780591]},"n1820939196":{"id":"n1820939196","loc":[-85.2800197,41.983061]},"n1820939197":{"id":"n1820939197","loc":[-85.3278167,41.9692943]},"n1820939198":{"id":"n1820939198","loc":[-85.3366894,41.9838653]},"n1820939199":{"id":"n1820939199","loc":[-85.0328383,42.0969923]},"n1820939201":{"id":"n1820939201","loc":[-85.3259284,41.9720383]},"n1820939217":{"id":"n1820939217","loc":[-85.1840181,42.0503277]},"n1820939220":{"id":"n1820939220","loc":[-85.422563,42.0089986]},"n1820939222":{"id":"n1820939222","loc":[-85.555386,41.9707856]},"n1820939224":{"id":"n1820939224","loc":[-85.3830809,42.002254]},"n1820939226":{"id":"n1820939226","loc":[-84.9917938,42.0857517]},"n1820939227":{"id":"n1820939227","loc":[-85.2936775,41.9740484]},"n1820939228":{"id":"n1820939228","loc":[-85.2632133,41.9975024]},"n1820939229":{"id":"n1820939229","loc":[-85.2809424,41.9853259]},"n1820939230":{"id":"n1820939230","loc":[-85.242104,42.0131204]},"n1820939232":{"id":"n1820939232","loc":[-85.2610246,41.9963901]},"n1820939233":{"id":"n1820939233","loc":[-85.2335531,42.0268378]},"n1820939234":{"id":"n1820939234","loc":[-85.3188839,41.9713575]},"n1820939235":{"id":"n1820939235","loc":[-85.2413637,42.0225658]},"n1820939237":{"id":"n1820939237","loc":[-85.0010796,42.0887215]},"n1820939239":{"id":"n1820939239","loc":[-85.2241697,42.0362624]},"n1820939243":{"id":"n1820939243","loc":[-85.0368456,42.1040134]},"n1820939244":{"id":"n1820939244","loc":[-85.1327986,42.069524]},"n1820939260":{"id":"n1820939260","loc":[-85.5408163,41.9711206]},"n1820939261":{"id":"n1820939261","loc":[-85.2959199,41.9746546]},"n1820939262":{"id":"n1820939262","loc":[-85.3298659,41.9683598]},"n1820939263":{"id":"n1820939263","loc":[-85.2240581,42.0358425]},"n1820939264":{"id":"n1820939264","loc":[-85.2438206,42.0101944]},"n1820939265":{"id":"n1820939265","loc":[-85.3984489,42.0059589]},"n1820939266":{"id":"n1820939266","loc":[-85.2330811,42.0294279]},"n1820939268":{"id":"n1820939268","loc":[-85.1126877,42.0857704]},"n1820939271":{"id":"n1820939271","loc":[-85.254925,42.0106253]},"n1820939273":{"id":"n1820939273","loc":[-85.4328046,42.0064662]},"n1820939277":{"id":"n1820939277","loc":[-85.289622,41.9789616]},"n1820939279":{"id":"n1820939279","loc":[-85.4574532,42.0004043]},"n1820939281":{"id":"n1820939281","loc":[-85.4803486,41.9867211]},"n1820939283":{"id":"n1820939283","loc":[-85.157475,42.0631848]},"n1820939285":{"id":"n1820939285","loc":[-85.2571458,42.0059935]},"n1820939287":{"id":"n1820939287","loc":[-85.2818544,41.9825984]},"n1820939289":{"id":"n1820939289","loc":[-85.2298302,42.0328781]},"n1820939291":{"id":"n1820939291","loc":[-85.4819523,41.984821]},"n1820939301":{"id":"n1820939301","loc":[-85.3139765,41.9701159]},"n1820939304":{"id":"n1820939304","loc":[-85.0424447,42.101742]},"n1820939306":{"id":"n1820939306","loc":[-85.6360283,41.9338482]},"n1820939310":{"id":"n1820939310","loc":[-85.3463025,41.9913622]},"n1820939312":{"id":"n1820939312","loc":[-85.4664869,41.9988097]},"n1820939314":{"id":"n1820939314","loc":[-85.149364,42.0622449]},"n1820939316":{"id":"n1820939316","loc":[-85.2460415,42.0153125]},"n1820939318":{"id":"n1820939318","loc":[-85.4806103,41.9924523]},"n1820939320":{"id":"n1820939320","loc":[-85.2449042,42.0190987]},"n1820939322":{"id":"n1820939322","loc":[-85.5280165,41.9689263]},"n1820939324":{"id":"n1820939324","loc":[-85.0051204,42.0882625]},"n1820939326":{"id":"n1820939326","loc":[-85.1240925,42.0771546]},"n1820939329":{"id":"n1820939329","loc":[-85.2261653,42.0342225]},"n1820939331":{"id":"n1820939331","loc":[-85.5259933,41.972211]},"n1820939333":{"id":"n1820939333","loc":[-85.0074754,42.0883183]},"n1820939335":{"id":"n1820939335","loc":[-85.0764014,42.1055549]},"n1820939336":{"id":"n1820939336","loc":[-85.2908773,41.9769597]},"n1820939337":{"id":"n1820939337","loc":[-85.4095382,42.0083449]},"n1820939346":{"id":"n1820939346","loc":[-85.2514166,42.0111753]},"n1820939348":{"id":"n1820939348","loc":[-85.0030377,42.0873799]},"n1820939350":{"id":"n1820939350","loc":[-85.3659362,41.9964974]},"n1820939352":{"id":"n1820939352","loc":[-85.226058,42.0348281]},"n1820939355":{"id":"n1820939355","loc":[-85.1902408,42.0507101]},"n1820939357":{"id":"n1820939357","loc":[-85.2781854,41.9946001]},"n1820939359":{"id":"n1820939359","loc":[-85.2139988,42.0405175]},"n1820939361":{"id":"n1820939361","loc":[-85.0086609,42.0908262]},"n1820939363":{"id":"n1820939363","loc":[-85.0627128,42.1043398]},"n1820939365":{"id":"n1820939365","loc":[-85.1311346,42.072501]},"n1820939369":{"id":"n1820939369","loc":[-85.248198,42.0082652]},"n1820939370":{"id":"n1820939370","loc":[-84.99792,42.087794]},"n1820939371":{"id":"n1820939371","loc":[-85.2786775,41.9942783]},"n1820939372":{"id":"n1820939372","loc":[-85.0342103,42.1013957]},"n1820939373":{"id":"n1820939373","loc":[-85.2022357,42.0444799]},"n1820939374":{"id":"n1820939374","loc":[-85.2279205,42.0337388]},"n1820939375":{"id":"n1820939375","loc":[-85.1337699,42.0712614]},"n1820939376":{"id":"n1820939376","loc":[-85.317517,41.9707062]},"n1820939377":{"id":"n1820939377","loc":[-85.1326326,42.070218]},"n1820939394":{"id":"n1820939394","loc":[-85.0197746,42.0899118]},"n1820939397":{"id":"n1820939397","loc":[-85.2590076,41.9984632]},"n1820939399":{"id":"n1820939399","loc":[-85.2469964,42.0083449]},"n1820939400":{"id":"n1820939400","loc":[-85.2470929,42.0146668]},"n1820939401":{"id":"n1820939401","loc":[-84.9984095,42.0878087]},"n1820939402":{"id":"n1820939402","loc":[-85.2372653,42.0243273]},"n1820939403":{"id":"n1820939403","loc":[-85.2454986,42.0091955]},"n1820939404":{"id":"n1820939404","loc":[-85.0539205,42.1035995]},"n1820939405":{"id":"n1820939405","loc":[-85.550601,41.9706101]},"n1820939406":{"id":"n1820939406","loc":[-85.0351343,42.0999656]},"n1820939407":{"id":"n1820939407","loc":[-85.0082908,42.0905755]},"n1820939408":{"id":"n1820939408","loc":[-85.0132904,42.0902251]},"n1820939410":{"id":"n1820939410","loc":[-85.0892546,42.094012]},"n1820939412":{"id":"n1820939412","loc":[-85.0350793,42.1030315]},"n1820939416":{"id":"n1820939416","loc":[-85.0012406,42.0886777]},"n1820939418":{"id":"n1820939418","loc":[-85.0577453,42.1029229]},"n1820939420":{"id":"n1820939420","loc":[-85.1230786,42.0776722]},"n1820939422":{"id":"n1820939422","loc":[-85.571136,41.9649304]},"n1820939436":{"id":"n1820939436","loc":[-85.1137968,42.0848997]},"n1820939437":{"id":"n1820939437","loc":[-85.3559584,41.9925105]},"n1820939438":{"id":"n1820939438","loc":[-85.0080172,42.0903565]},"n1820939439":{"id":"n1820939439","loc":[-85.0048897,42.0880913]},"n1820939441":{"id":"n1820939441","loc":[-85.0406959,42.1018574]},"n1820939443":{"id":"n1820939443","loc":[-85.3897328,42.0029078]},"n1820939445":{"id":"n1820939445","loc":[-85.122349,42.0782814]},"n1820939448":{"id":"n1820939448","loc":[-85.4872193,41.985036]},"n1820939450":{"id":"n1820939450","loc":[-85.0120459,42.0904919]},"n1820939452":{"id":"n1820939452","loc":[-85.6320543,41.921982]},"n1820939456":{"id":"n1820939456","loc":[-85.0844749,42.1036843]},"n1820939458":{"id":"n1820939458","loc":[-85.0968037,42.091296]},"n1820939463":{"id":"n1820939463","loc":[-85.5339747,41.9681841]},"n1820939465":{"id":"n1820939465","loc":[-85.4125423,42.0072129]},"n1820939467":{"id":"n1820939467","loc":[-85.6335563,41.9303626]},"n1820939469":{"id":"n1820939469","loc":[-85.2821014,41.9932126]},"n1820939471":{"id":"n1820939471","loc":[-85.374691,41.9969917]},"n1820939485":{"id":"n1820939485","loc":[-85.4471321,42.0049806]},"n1820939487":{"id":"n1820939487","loc":[-85.3752532,41.9972206]},"n1820939489":{"id":"n1820939489","loc":[-85.4517283,42.005927]},"n1820939492":{"id":"n1820939492","loc":[-85.4662552,42.0005693]},"n1820939494":{"id":"n1820939494","loc":[-85.0120083,42.0902928]},"n1820939496":{"id":"n1820939496","loc":[-85.044463,42.1004631]},"n1820939498":{"id":"n1820939498","loc":[-85.418293,42.0089667]},"n1820939500":{"id":"n1820939500","loc":[-85.0554762,42.1027358]},"n1820939504":{"id":"n1820939504","loc":[-85.1246289,42.0746858]},"n1820939507":{"id":"n1820939507","loc":[-85.0408139,42.1021838]},"n1820939508":{"id":"n1820939508","loc":[-85.1236204,42.0775169]},"n1820939509":{"id":"n1820939509","loc":[-85.0350109,42.1037428]},"n1820939510":{"id":"n1820939510","loc":[-85.0551583,42.1029878]},"n1820939511":{"id":"n1820939511","loc":[-85.0956771,42.0916662]},"n1820939512":{"id":"n1820939512","loc":[-85.2323408,42.0273638]},"n1820939513":{"id":"n1820939513","loc":[-85.1232771,42.0762388]},"n1820939531":{"id":"n1820939531","loc":[-85.264608,41.9997828]},"n1820939533":{"id":"n1820939533","loc":[-85.4198808,42.0087914]},"n1820939535":{"id":"n1820939535","loc":[-85.3080864,41.9715677]},"n1820939536":{"id":"n1820939536","loc":[-85.1189426,42.0812596]},"n1820939537":{"id":"n1820939537","loc":[-85.2642741,41.9996764]},"n1820939538":{"id":"n1820939538","loc":[-85.2572531,42.0079627]},"n1820939539":{"id":"n1820939539","loc":[-85.2907807,41.9790174]},"n1820939540":{"id":"n1820939540","loc":[-85.3171415,41.9707301]},"n1820939541":{"id":"n1820939541","loc":[-85.08777,42.0953841]},"n1820939542":{"id":"n1820939542","loc":[-85.1239262,42.0773218]},"n1820939543":{"id":"n1820939543","loc":[-84.9973956,42.0877968]},"n1820939544":{"id":"n1820939544","loc":[-85.011606,42.0896161]},"n1820939545":{"id":"n1820939545","loc":[-85.4077358,42.0082971]},"n1820939546":{"id":"n1820939546","loc":[-85.3614945,41.9933717]},"n1820939547":{"id":"n1820939547","loc":[-85.3189118,41.9697649]},"n1820939550":{"id":"n1820939550","loc":[-85.1262691,42.0740221]},"n1820939551":{"id":"n1820939551","loc":[-85.3863639,41.9994635]},"n1820939552":{"id":"n1820939552","loc":[-85.2836034,41.9923953]},"n1820939554":{"id":"n1820939554","loc":[-85.3222377,41.9715916]},"n1820939555":{"id":"n1820939555","loc":[-85.0122658,42.0906312]},"n1820939556":{"id":"n1820939556","loc":[-85.0022652,42.0877581]},"n1820939557":{"id":"n1820939557","loc":[-85.1011314,42.0899954]},"n1820939559":{"id":"n1820939559","loc":[-85.0008181,42.0885293]},"n1820939561":{"id":"n1820939561","loc":[-85.3637046,41.9942488]},"n1820939562":{"id":"n1820939562","loc":[-85.4500117,42.0052892]},"n1820939563":{"id":"n1820939563","loc":[-85.0537636,42.1036365]},"n1820939565":{"id":"n1820939565","loc":[-85.2367503,42.0246939]},"n1820939566":{"id":"n1820939566","loc":[-85.0448479,42.1002653]},"n1820939567":{"id":"n1820939567","loc":[-85.6337065,41.9295006]},"n1820939568":{"id":"n1820939568","loc":[-85.0879792,42.095623]},"n1820939569":{"id":"n1820939569","loc":[-85.6347623,41.9352369]},"n1820939570":{"id":"n1820939570","loc":[-85.1497931,42.0620378]},"n1820939571":{"id":"n1820939571","loc":[-85.5676169,41.9656324]},"n1820939572":{"id":"n1820939572","loc":[-85.638041,41.9166971]},"n1820939573":{"id":"n1820939573","loc":[-85.4993429,41.9781293]},"n1820939574":{"id":"n1820939574","loc":[-85.5352831,41.9692127]},"n1820939575":{"id":"n1820939575","loc":[-84.9924429,42.0857118]},"n1820939577":{"id":"n1820939577","loc":[-85.0581101,42.1026721]},"n1820939578":{"id":"n1820939578","loc":[-85.641088,41.9094477]},"n1820939579":{"id":"n1820939579","loc":[-85.2548821,42.0052282]},"n1820939580":{"id":"n1820939580","loc":[-85.1124463,42.0859734]},"n1820939581":{"id":"n1820939581","loc":[-85.1083479,42.0857624]},"n1820939583":{"id":"n1820939583","loc":[-85.1387424,42.0648893]},"n1820939584":{"id":"n1820939584","loc":[-85.5152645,41.9700892]},"n1820939585":{"id":"n1820939585","loc":[-85.5463738,41.9713439]},"n1820939586":{"id":"n1820939586","loc":[-85.360207,41.9933717]},"n1820939587":{"id":"n1820939587","loc":[-85.2402372,42.0120917]},"n1820939588":{"id":"n1820939588","loc":[-85.3936381,42.0047255]},"n1820939589":{"id":"n1820939589","loc":[-85.3310246,41.973784]},"n1820939590":{"id":"n1820939590","loc":[-85.0329403,42.096642]},"n1820939591":{"id":"n1820939591","loc":[-85.0097271,42.0910981]},"n1820939593":{"id":"n1820939593","loc":[-85.0446562,42.1003437]},"n1820939595":{"id":"n1820939595","loc":[-85.0856671,42.1008452]},"n1820939596":{"id":"n1820939596","loc":[-85.4087228,42.0083449]},"n1820939597":{"id":"n1820939597","loc":[-85.0609519,42.1052564]},"n1820939598":{"id":"n1820939598","loc":[-85.3432126,41.9874548]},"n1820939599":{"id":"n1820939599","loc":[-85.4041738,42.0067027]},"n1820939600":{"id":"n1820939600","loc":[-85.0825437,42.1035768]},"n1820939601":{"id":"n1820939601","loc":[-85.048422,42.101498]},"n1820939602":{"id":"n1820939602","loc":[-85.0336256,42.0999031]},"n1820939603":{"id":"n1820939603","loc":[-85.046818,42.1014104]},"n1820939605":{"id":"n1820939605","loc":[-85.2856524,41.98078]},"n1820939607":{"id":"n1820939607","loc":[-85.1118173,42.0864245]},"n1820939609":{"id":"n1820939609","loc":[-85.0443397,42.1006263]},"n1820939610":{"id":"n1820939610","loc":[-85.0336698,42.0978361]},"n1820939611":{"id":"n1820939611","loc":[-85.4630322,42.0014248]},"n1820939612":{"id":"n1820939612","loc":[-85.0613127,42.1052353]},"n1820939613":{"id":"n1820939613","loc":[-85.0137571,42.0887801]},"n1820939614":{"id":"n1820939614","loc":[-85.272487,41.9982013]},"n1820939616":{"id":"n1820939616","loc":[-85.4665727,41.9983791]},"n1820939617":{"id":"n1820939617","loc":[-85.1288078,42.0725476]},"n1820939618":{"id":"n1820939618","loc":[-85.4653282,42.00109]},"n1820939619":{"id":"n1820939619","loc":[-85.2314717,42.0276746]},"n1820939620":{"id":"n1820939620","loc":[-85.255982,42.0003569]},"n1820939621":{"id":"n1820939621","loc":[-85.2886779,41.9787223]},"n1820939622":{"id":"n1820939622","loc":[-85.22438,42.0367509]},"n1820939623":{"id":"n1820939623","loc":[-85.0334713,42.0998382]},"n1820939624":{"id":"n1820939624","loc":[-85.2236504,42.037484]},"n1820939625":{"id":"n1820939625","loc":[-85.636908,41.9175162]},"n1820939627":{"id":"n1820939627","loc":[-85.2669187,41.9989707]},"n1820939628":{"id":"n1820939628","loc":[-85.3247268,41.9720702]},"n1820939629":{"id":"n1820939629","loc":[-85.3785104,41.9987299]},"n1820939630":{"id":"n1820939630","loc":[-85.5267658,41.9720515]},"n1820939631":{"id":"n1820939631","loc":[-85.2445116,42.0098811]},"n1820939632":{"id":"n1820939632","loc":[-85.1271448,42.0725077]},"n1820939633":{"id":"n1820939633","loc":[-85.0345751,42.099724]},"n1820939634":{"id":"n1820939634","loc":[-85.4217476,42.0089986]},"n1820939635":{"id":"n1820939635","loc":[-85.3121848,41.9689433]},"n1820939636":{"id":"n1820939636","loc":[-85.2826419,41.9929985]},"n1820939637":{"id":"n1820939637","loc":[-85.3160257,41.9706344]},"n1820939638":{"id":"n1820939638","loc":[-85.5684967,41.9657919]},"n1820939640":{"id":"n1820939640","loc":[-85.225131,42.0356194]},"n1820939642":{"id":"n1820939642","loc":[-85.1324124,42.0693328]},"n1820939644":{"id":"n1820939644","loc":[-84.9994073,42.0878843]},"n1820939645":{"id":"n1820939645","loc":[-85.1087596,42.0863329]},"n1820939646":{"id":"n1820939646","loc":[-85.2915532,41.9782996]},"n1820939647":{"id":"n1820939647","loc":[-84.9988708,42.0877808]},"n1820939648":{"id":"n1820939648","loc":[-85.2243628,42.0356728]},"n1820939649":{"id":"n1820939649","loc":[-85.0427397,42.1020524]},"n1820939650":{"id":"n1820939650","loc":[-85.6388392,41.9100752]},"n1820939651":{"id":"n1820939651","loc":[-85.0133709,42.0888557]},"n1820939652":{"id":"n1820939652","loc":[-85.318798,41.9701211]},"n1820939653":{"id":"n1820939653","loc":[-85.6335778,41.9190602]},"n1820939654":{"id":"n1820939654","loc":[-85.6338396,41.9370247]},"n1820939655":{"id":"n1820939655","loc":[-85.0939069,42.0931988]},"n1820939656":{"id":"n1820939656","loc":[-85.5702347,41.9651378]},"n1820939657":{"id":"n1820939657","loc":[-85.4235286,42.0088392]},"n1820939658":{"id":"n1820939658","loc":[-85.2740856,41.9972206]},"n1820939659":{"id":"n1820939659","loc":[-85.4824299,41.9934195]},"n1820939660":{"id":"n1820939660","loc":[-85.3857846,42.0014408]},"n1820939661":{"id":"n1820939661","loc":[-85.0451658,42.10028]},"n1820939662":{"id":"n1820939662","loc":[-85.3893036,42.001377]},"n1820939664":{"id":"n1820939664","loc":[-85.2455845,42.0088607]},"n1820939665":{"id":"n1820939665","loc":[-85.2741071,41.9951116]},"n1820939666":{"id":"n1820939666","loc":[-85.1298375,42.0677718]},"n1820939667":{"id":"n1820939667","loc":[-85.5491848,41.9707377]},"n1820939669":{"id":"n1820939669","loc":[-85.2780298,41.995238]},"n1820939670":{"id":"n1820939670","loc":[-85.1330068,42.0716926]},"n1820939671":{"id":"n1820939671","loc":[-85.0811342,42.1025129]},"n1820939672":{"id":"n1820939672","loc":[-85.2325124,42.0290135]},"n1820939673":{"id":"n1820939673","loc":[-85.2975077,41.9716953]},"n1820939674":{"id":"n1820939674","loc":[-85.0951729,42.0922394]},"n1820939676":{"id":"n1820939676","loc":[-85.0363252,42.1043119]},"n1820939677":{"id":"n1820939677","loc":[-85.2960057,41.97349]},"n1820939678":{"id":"n1820939678","loc":[-85.3701849,41.9982515]},"n1820939679":{"id":"n1820939679","loc":[-85.3381486,41.9848861]},"n1820939680":{"id":"n1820939680","loc":[-85.2058448,42.0417286]},"n1820939682":{"id":"n1820939682","loc":[-85.0819335,42.1034443]},"n1820939683":{"id":"n1820939683","loc":[-85.3872223,41.9993359]},"n1820939684":{"id":"n1820939684","loc":[-85.095366,42.091909]},"n1820939685":{"id":"n1820939685","loc":[-85.2327914,42.0291888]},"n1820939686":{"id":"n1820939686","loc":[-85.0433459,42.1018773]},"n1820939687":{"id":"n1820939687","loc":[-85.0585339,42.1027318]},"n1820939688":{"id":"n1820939688","loc":[-85.0062885,42.0876347]},"n1820939689":{"id":"n1820939689","loc":[-85.246299,42.017377]},"n1820939690":{"id":"n1820939690","loc":[-85.2932376,41.9742877]},"n1820939691":{"id":"n1820939691","loc":[-85.2962846,41.9736815]},"n1820939692":{"id":"n1820939692","loc":[-85.6052365,41.9409193]},"n1820939693":{"id":"n1820939693","loc":[-85.2570536,42.0003341]},"n1820939694":{"id":"n1820939694","loc":[-85.0488458,42.1014064]},"n1820939695":{"id":"n1820939695","loc":[-85.4050321,42.0069578]},"n1820939696":{"id":"n1820939696","loc":[-85.4847517,41.9845894]},"n1820939697":{"id":"n1820939697","loc":[-85.0844655,42.1013826]},"n1820939698":{"id":"n1820939698","loc":[-85.1437206,42.0650008]},"n1820939699":{"id":"n1820939699","loc":[-85.1168183,42.0864034]},"n1820939700":{"id":"n1820939700","loc":[-85.5479831,41.9711366]},"n1820939701":{"id":"n1820939701","loc":[-85.0349948,42.1034124]},"n1820939702":{"id":"n1820939702","loc":[-85.0835589,42.1038821]},"n1820939703":{"id":"n1820939703","loc":[-85.0203875,42.0902649]},"n1820939704":{"id":"n1820939704","loc":[-85.0371191,42.1038184]},"n1820939705":{"id":"n1820939705","loc":[-85.1273312,42.0735681]},"n1820939707":{"id":"n1820939707","loc":[-85.1272239,42.0730226]},"n1820939710":{"id":"n1820939710","loc":[-85.0349881,42.1019012]},"n1820939712":{"id":"n1820939712","loc":[-85.2440459,42.0178313]},"n1820939713":{"id":"n1820939713","loc":[-85.2444751,42.0182618]},"n1820939714":{"id":"n1820939714","loc":[-85.0539996,42.1032863]},"n1820939715":{"id":"n1820939715","loc":[-85.2215905,42.0373246]},"n1820939716":{"id":"n1820939716","loc":[-85.0649712,42.1051994]},"n1820939717":{"id":"n1820939717","loc":[-85.0927146,42.0927581]},"n1820939718":{"id":"n1820939718","loc":[-85.3884668,42.0042312]},"n1820939719":{"id":"n1820939719","loc":[-85.0840672,42.1013241]},"n1820939720":{"id":"n1820939720","loc":[-85.304739,41.9725408]},"n1820939721":{"id":"n1820939721","loc":[-85.2243585,42.0371334]},"n1820939722":{"id":"n1820939722","loc":[-85.0599823,42.1049686]},"n1820939723":{"id":"n1820939723","loc":[-85.0298825,42.0944288]},"n1820939724":{"id":"n1820939724","loc":[-85.0366095,42.1042443]},"n1820939725":{"id":"n1820939725","loc":[-85.0698783,42.1058135]},"n1820939726":{"id":"n1820939726","loc":[-85.1054551,42.0873361]},"n1820939727":{"id":"n1820939727","loc":[-84.9952324,42.0864285]},"n1820939728":{"id":"n1820939728","loc":[-85.3442211,41.9897993]},"n1820939729":{"id":"n1820939729","loc":[-85.4386134,42.0056822]},"n1820939730":{"id":"n1820939730","loc":[-85.2438528,42.0146589]},"n1820939731":{"id":"n1820939731","loc":[-85.0355581,42.1041846]},"n1820939732":{"id":"n1820939732","loc":[-85.557682,41.9724447]},"n1820939734":{"id":"n1820939734","loc":[-85.2299418,42.033314]},"n1820939735":{"id":"n1820939735","loc":[-85.6297412,41.9419088]},"n1820939736":{"id":"n1820939736","loc":[-85.2645101,41.9980259]},"n1820939738":{"id":"n1820939738","loc":[-85.082195,42.1035649]},"n1820939739":{"id":"n1820939739","loc":[-85.234272,42.0267102]},"n1820939740":{"id":"n1820939740","loc":[-85.0130758,42.0895006]},"n1820939741":{"id":"n1820939741","loc":[-85.4594702,42.0000375]},"n1820939742":{"id":"n1820939742","loc":[-84.9946745,42.0863687]},"n1820939743":{"id":"n1820939743","loc":[-85.6438775,41.9120186]},"n1820939744":{"id":"n1820939744","loc":[-85.6372685,41.9168089]},"n1820939745":{"id":"n1820939745","loc":[-85.2789468,41.9893208]},"n1820939747":{"id":"n1820939747","loc":[-85.3775019,41.998427]},"n1820939749":{"id":"n1820939749","loc":[-85.0993571,42.0909178]},"n1820939750":{"id":"n1820939750","loc":[-85.1308503,42.0669339]},"n1820939751":{"id":"n1820939751","loc":[-85.4802566,41.9856659]},"n1820939752":{"id":"n1820939752","loc":[-85.2543563,42.0108804]},"n1820939753":{"id":"n1820939753","loc":[-85.1041033,42.0878815]},"n1820939755":{"id":"n1820939755","loc":[-85.4000969,42.0071651]},"n1820939757":{"id":"n1820939757","loc":[-85.3858275,42.0022381]},"n1820939758":{"id":"n1820939758","loc":[-85.3653998,41.996609]},"n1820939759":{"id":"n1820939759","loc":[-85.2432949,42.0202305]},"n1820939760":{"id":"n1820939760","loc":[-85.3878874,42.0042472]},"n1820939761":{"id":"n1820939761","loc":[-85.2516741,42.0114145]},"n1820939762":{"id":"n1820939762","loc":[-85.2788825,41.9865142]},"n1820939763":{"id":"n1820939763","loc":[-85.0009147,42.0886686]},"n1820939764":{"id":"n1820939764","loc":[-85.3918142,42.003434]},"n1820939765":{"id":"n1820939765","loc":[-85.5532832,41.9696848]},"n1820939766":{"id":"n1820939766","loc":[-85.5545063,41.969254]},"n1820939768":{"id":"n1820939768","loc":[-85.1327989,42.0704769]},"n1820939770":{"id":"n1820939770","loc":[-85.0588558,42.1047696]},"n1820939772":{"id":"n1820939772","loc":[-85.555798,41.9713017]},"n1820939773":{"id":"n1820939773","loc":[-85.0565853,42.1023589]},"n1820939774":{"id":"n1820939774","loc":[-85.2582941,41.9992765]},"n1820939775":{"id":"n1820939775","loc":[-85.3007264,41.9727642]},"n1820939776":{"id":"n1820939776","loc":[-85.2477045,42.0082652]},"n1820939777":{"id":"n1820939777","loc":[-85.2415247,42.0104973]},"n1821006698":{"id":"n1821006698","loc":[-85.6345227,41.9382009]},"n1821006700":{"id":"n1821006700","loc":[-85.6344894,41.938975]},"n1821006704":{"id":"n1821006704","loc":[-85.6351181,41.9370157]},"n1821006706":{"id":"n1821006706","loc":[-85.6357554,41.9361657]},"n1821006708":{"id":"n1821006708","loc":[-85.6351235,41.9368481]},"n1821006710":{"id":"n1821006710","loc":[-85.6352844,41.9364211]},"n1821006712":{"id":"n1821006712","loc":[-85.6351503,41.937307]},"n1821006716":{"id":"n1821006716","loc":[-85.6350366,41.9379774]},"n1821006725":{"id":"n1821006725","loc":[-85.6352147,41.9375903]},"n1821137607":{"id":"n1821137607","loc":[-85.5297057,41.9669915]},"n1821137608":{"id":"n1821137608","loc":[-85.5288598,41.9673094]},"n1821139530":{"id":"n1821139530","loc":[-85.4832228,41.9881686]},"n1821139531":{"id":"n1821139531","loc":[-85.4812101,41.9851258]},"n1821139532":{"id":"n1821139532","loc":[-85.4799127,41.9860244]},"n1821139533":{"id":"n1821139533","loc":[-85.4800313,41.9865555]},"n1841425201":{"id":"n1841425201","loc":[-85.4334577,42.0063713]},"n1841425222":{"id":"n1841425222","loc":[-85.4382449,42.0055785]},"n1914861007":{"id":"n1914861007","loc":[-85.394959,42.0057472]},"n1914861057":{"id":"n1914861057","loc":[-85.3967185,42.0049695]},"n1914861112":{"id":"n1914861112","loc":[-85.394179,42.0056906]},"n1914861306":{"id":"n1914861306","loc":[-85.3900226,42.0028488]},"n2114807565":{"id":"n2114807565","loc":[-85.6385979,41.9577824]},"n2114807568":{"id":"n2114807568","loc":[-85.6325097,41.9775713]},"n2114807572":{"id":"n2114807572","loc":[-85.6328996,41.9980965]},"n2114807578":{"id":"n2114807578","loc":[-85.6344818,41.9696956]},"n2114807583":{"id":"n2114807583","loc":[-85.6326289,41.9757853]},"n2114807593":{"id":"n2114807593","loc":[-85.6360828,41.9650674]},"n2130304159":{"id":"n2130304159","loc":[-85.6352537,41.9450015],"tags":{"railway":"level_crossing"}},"n2139795852":{"id":"n2139795852","loc":[-85.6374708,41.9311633]},"n2139858882":{"id":"n2139858882","loc":[-85.635178,41.9356158]},"n2139858883":{"id":"n2139858883","loc":[-85.63533,41.9355886]},"n2139858884":{"id":"n2139858884","loc":[-85.6353819,41.93556]},"n2139858885":{"id":"n2139858885","loc":[-85.6353665,41.9355157]},"n2139858886":{"id":"n2139858886","loc":[-85.6353165,41.9354971]},"n2139858887":{"id":"n2139858887","loc":[-85.6352454,41.9355328]},"n2139858888":{"id":"n2139858888","loc":[-85.6350184,41.9357846]},"n2139858889":{"id":"n2139858889","loc":[-85.634978,41.9359448]},"n2139858890":{"id":"n2139858890","loc":[-85.6347723,41.9361523]},"n2139858891":{"id":"n2139858891","loc":[-85.6347165,41.9362667]},"n2139858892":{"id":"n2139858892","loc":[-85.6346992,41.9364312]},"n2139858893":{"id":"n2139858893","loc":[-85.634603,41.9366329]},"n2139858894":{"id":"n2139858894","loc":[-85.6345973,41.9367488]},"n2139858895":{"id":"n2139858895","loc":[-85.6345127,41.9369734]},"n2139858896":{"id":"n2139858896","loc":[-85.634478,41.9371923]},"n2139858897":{"id":"n2139858897","loc":[-85.6344838,41.9373768]},"n2139858898":{"id":"n2139858898","loc":[-85.6346242,41.9375299]},"n2139858899":{"id":"n2139858899","loc":[-85.6347723,41.9376357]},"n2139858900":{"id":"n2139858900","loc":[-85.6347607,41.9377788]},"n2139858901":{"id":"n2139858901","loc":[-85.6346204,41.9379533]},"n2139858902":{"id":"n2139858902","loc":[-85.6344184,41.9380105]},"n2139858903":{"id":"n2139858903","loc":[-85.6341627,41.9380406]},"n2139858904":{"id":"n2139858904","loc":[-85.634005,41.9381679]},"n2139858905":{"id":"n2139858905","loc":[-85.63393,41.9383353]},"n2139858906":{"id":"n2139858906","loc":[-85.6338588,41.9384597]},"n2139858907":{"id":"n2139858907","loc":[-85.6336627,41.9387759]},"n2139858908":{"id":"n2139858908","loc":[-85.6335127,41.9389361]},"n2139858933":{"id":"n2139858933","loc":[-85.6353118,41.9432646]},"n2139858934":{"id":"n2139858934","loc":[-85.6353952,41.9433002]},"n2139858935":{"id":"n2139858935","loc":[-85.6356496,41.9433255]},"n2139858936":{"id":"n2139858936","loc":[-85.6363128,41.9433373]},"n2139858937":{"id":"n2139858937","loc":[-85.6365467,41.9433779]},"n2139858938":{"id":"n2139858938","loc":[-85.6368692,41.9435265]},"n2139858939":{"id":"n2139858939","loc":[-85.6370986,41.9437039]},"n2139858940":{"id":"n2139858940","loc":[-85.6372371,41.9437732]},"n2139858941":{"id":"n2139858941","loc":[-85.6374756,41.9438171]},"n2139858942":{"id":"n2139858942","loc":[-85.6376164,41.9439286]},"n2139858943":{"id":"n2139858943","loc":[-85.6377504,41.944138]},"n2139858944":{"id":"n2139858944","loc":[-85.6384204,41.9443137]},"n2139858945":{"id":"n2139858945","loc":[-85.6385726,41.9444506]},"n2139858946":{"id":"n2139858946","loc":[-85.638702,41.9445739]},"n2139858947":{"id":"n2139858947","loc":[-85.6387179,41.9446516]},"n2139858948":{"id":"n2139858948","loc":[-85.6387088,41.9447985]},"n2139858949":{"id":"n2139858949","loc":[-85.6387656,41.9449877]},"n2139858950":{"id":"n2139858950","loc":[-85.638777,41.9451448]},"n2139858951":{"id":"n2139858951","loc":[-85.6387088,41.9452631]},"n2139858964":{"id":"n2139858964","loc":[-85.6383346,41.9442912]},"n2139858966":{"id":"n2139858966","loc":[-85.6384724,41.9443605]},"n2139858967":{"id":"n2139858967","loc":[-85.6354078,41.9434285]},"n2139858968":{"id":"n2139858968","loc":[-85.635271,41.943654]},"n2139858969":{"id":"n2139858969","loc":[-85.6352657,41.9437437]},"n2139858970":{"id":"n2139858970","loc":[-85.635271,41.9438195]},"n2139858971":{"id":"n2139858971","loc":[-85.6351563,41.9438906]},"n2139858972":{"id":"n2139858972","loc":[-85.6351384,41.9438882]},"n2139858973":{"id":"n2139858973","loc":[-85.6351514,41.9438034]},"n2139858974":{"id":"n2139858974","loc":[-85.6351237,41.9436641]},"n2139858975":{"id":"n2139858975","loc":[-85.6351498,41.9436108]},"n2139858976":{"id":"n2139858976","loc":[-85.6351058,41.9435345]},"n2139858977":{"id":"n2139858977","loc":[-85.6349641,41.9432051]},"n2139858986":{"id":"n2139858986","loc":[-85.6341205,41.9380746]},"n2139858990":{"id":"n2139858990","loc":[-85.6345671,41.9381816]},"n2139858995":{"id":"n2139858995","loc":[-85.6339783,41.9382273]},"n2139859003":{"id":"n2139859003","loc":[-85.6340477,41.9373489]},"n2139859004":{"id":"n2139859004","loc":[-85.6339784,41.9374752]},"n2139870406":{"id":"n2139870406","loc":[-85.6342265,41.9432605]},"n2139877106":{"id":"n2139877106","loc":[-85.6346323,41.9438746]},"n2139982399":{"id":"n2139982399","loc":[-85.6324055,41.9408537]},"n2139982400":{"id":"n2139982400","loc":[-85.632488,41.941063],"tags":{"leisure":"slipway"}},"n2139982401":{"id":"n2139982401","loc":[-85.6327261,41.9415366]},"n2139982402":{"id":"n2139982402","loc":[-85.6326391,41.9413598]},"n2139982403":{"id":"n2139982403","loc":[-85.6327041,41.9414391]},"n2139982405":{"id":"n2139982405","loc":[-85.6322891,41.9406009]},"n2139982406":{"id":"n2139982406","loc":[-85.6325412,41.9425257]},"n2139989333":{"id":"n2139989333","loc":[-85.6340584,41.9431731]},"n2140006331":{"id":"n2140006331","loc":[-85.6361751,41.9459744]},"n2140006334":{"id":"n2140006334","loc":[-85.636528,41.9459751]},"n2140006336":{"id":"n2140006336","loc":[-85.6370918,41.9458926]},"n2140006338":{"id":"n2140006338","loc":[-85.6378806,41.9456474]},"n2140006340":{"id":"n2140006340","loc":[-85.6385831,41.9454343]},"n2140006342":{"id":"n2140006342","loc":[-85.639341,41.945157]},"n2140006344":{"id":"n2140006344","loc":[-85.6393497,41.9450232]},"n2140006346":{"id":"n2140006346","loc":[-85.6388245,41.9450145]},"n2140006348":{"id":"n2140006348","loc":[-85.6388167,41.9441739]},"n2140006351":{"id":"n2140006351","loc":[-85.6382915,41.9441797]},"n2140006353":{"id":"n2140006353","loc":[-85.63828,41.9438109]},"n2140006355":{"id":"n2140006355","loc":[-85.6381949,41.9436009]},"n2140006357":{"id":"n2140006357","loc":[-85.6371904,41.9435918]},"n2140006359":{"id":"n2140006359","loc":[-85.6366966,41.9432727]},"n2140006361":{"id":"n2140006361","loc":[-85.6353755,41.9432744]},"n2140006365":{"id":"n2140006365","loc":[-85.6350906,41.9435472]},"n2140006366":{"id":"n2140006366","loc":[-85.6343461,41.9441573]},"n2140006395":{"id":"n2140006395","loc":[-85.6351171,41.9437175]},"n2140006397":{"id":"n2140006397","loc":[-85.635352,41.9450206]},"n2140006399":{"id":"n2140006399","loc":[-85.6358194,41.9454937]},"n2140006401":{"id":"n2140006401","loc":[-85.6348693,41.9445739]},"n2140006431":{"id":"n2140006431","loc":[-85.6376737,41.9438023]},"n2140006437":{"id":"n2140006437","loc":[-85.6382631,41.9442724]},"n2189123379":{"id":"n2189123379","loc":[-85.6342671,41.9352665]},"w203974076":{"id":"w203974076","tags":{"highway":"footway"},"nodes":["n2139870442","n2139870457","n2139870458","n2139870459","n2139870460","n2139870452"]},"w170989131":{"id":"w170989131","tags":{"name":"St Joseph River","waterway":"river"},"nodes":["n1820938225","n1820938712","n1820937596","n1820937574","n1820938515","n1820938330","n1820938678","n1820938240","n1820938950","n1820939226","n1820939575","n1820937913","n1820938223","n1820937668","n1820938545","n1820937584","n1820939742","n1820939727","n1820937578","n1820938149","n1820938124","n1820938888","n1820938898","n1820937922","n1820939543","n1820939370","n1820939401","n1820939647","n1820938345","n1820939644","n1820938333","n1820938370","n1820938624","n1820938493","n1820939559","n1820939763","n1820939237","n1820939416","n1820937810","n1820938317","n1820938324","n1820937558","n1820939556","n1820938298","n1820939348","n1820939125","n1820939081","n1820938859","n1820939126","n1820938881","n1820939439","n1820939324","n1820939128","n1820938101","n1820937706","n1820938382","n1820938776","n1820937815","n1820939177","n1820939688","n1820938952","n1820938216","n1820938387","n1820939333","n1820938243","n1820938248","n1820937666","n1820939051","n1820938332","n1820939438","n1820939407","n1820939361","n1820937517","n1820938770","n1820939591","n1820937857","n1820938491","n1820937993","n1820938125","n1820938166","n1820937746","n1820939028","n1820937638","n1820938676","n1820938843","n1820938844","n1820937978","n1820938730","n1820939544","n1820938304","n1820939123","n1820939494","n1820939450","n1820939555","n1820938133","n1820938129","n1820938871","n1820939408","n1820938669","n1820938260","n1820939740","n1820937625","n1820938631","n1820939651","n1820939613","n1820937850","n1820938325","n1820937736","n1820938804","n1820938837","n1820938014","n1820938991","n1820938722","n1820938935","n1820937870","n1820938432","n1820937986","n1820938756","n1820938966","n1820939159","n1820937744","n1820938334","n1820937645","n1820939394","n1820937656","n1820938392","n1820939703","n1820938385","n1820938947","n1820938854","n1820938428","n1820938488","n1820938269","n1820938668","n1820938268","n1820938707","n1820937732","n1820939144","n1820938481","n1820938771","n1820938686","n1820938948","n1820937997","n1820937769","n1820939003","n1820938083","n1820939011","n1820938803","n1820938700","n1820939723","n1820938808","n1820938262","n1820938081","n1820938926","n1820938326","n1820938102","n1820938508","n1820939590","n1820939199","n1820938084","n1820938870","n1820938895","n1820937611","n1820938918","n1820938514","n1820939610","n1820938910","n1820937523","n1820938127","n1820939108","n1820937981","n1820938938","n1820938715","n1820939016","n1820938237","n1820939623","n1820939602","n1820937734","n1820938977","n1820939633","n1820939156","n1820939406","n1820938279","n1820938301","n1820937678","n1820937671","n1820939163","n1820938356","n1820939372","n1820937568","n1820937626","n1820939710","n1820939004","n1820938253","n1820938571","n1820937513","n1820939412","n1820939701","n1820939509","n1820938839","n1820939731","n1820937798","n1820939676","n1820939724","n1820939243","n1820939704","n1820937814","n1820937599","n1820938199","n1820938995","n1820938445","n1820938069","n1820938470","n1820939074","n1820938193","n1820938740","n1820938047","n1820939507","n1820939441","n1820939160","n1820937849","n1820937840","n1820938052","n1820938988","n1820938796","n1820937724","n1820937620","n1820939304","n1820938343","n1820939649","n1820938875","n1820939686","n1820938476","n1820937801","n1820937737","n1820938264","n1820939609","n1820939496","n1820939593","n1820939566","n1820939661","n1820937782","n1820938912","n1820939173","n1820937733","n1820938953","n1820939603","n1820937607","n1820938468","n1820939601","n1820939694","n1820939133","n1820938897","n1820938893","n1820937831","n1820937730","n1820938820","n1820938046","n1820938426","n1820938347","n1820937582","n1820938954","n1820938033","n1820938104","n1820938680","n1820939563","n1820939404","n1820939714","n1820939000","n1820937992","n1820938168","n1820939510","n1820939500","n1820937509","n1820938865","n1820939773","n1820938138","n1820938905","n1820937623","n1820939418","n1820937946","n1820939577","n1820937615","n1820939687","n1820939119","n1820937988","n1820938337","n1820937750","n1820938703","n1820938339","n1820939044","n1820939770","n1820938913","n1820937672","n1820939722","n1820937768","n1820939597","n1820939612","n1820937699","n1820937682","n1820937669","n1820937657","n1820939363","n1820937800","n1820938265","n1820937760","n1820938207","n1820938115","n1820939130","n1820939716","n1820938338","n1820938239","n1820939040","n1820938064","n1820938855","n1820939015","n1820938258","n1820939042","n1820939043","n1820938443","n1820939725","n1820937675","n1820938568","n1820938280","n1820937705","n1820938775","n1820938636","n1820938626","n1820937859","n1820938096","n1820937852","n1820939039","n1820938247","n1820938585","n1820937707","n1820938117","n1820938909","n1820939115","n1820939335","n1820938805","n1820937935","n1820937876","n1820938699","n1820937869","n1820938603","n1820938100","n1820938500","n1820938283","n1820938275","n1820938923","n1820938365","n1820938349","n1820937804","n1820937903","n1820937608","n1820938688","n1820939671","n1820938092","n1820937820","n1820938753","n1820938922","n1820937990","n1820939682","n1820939738","n1820939600","n1820938167","n1820937726","n1820939702","n1820938209","n1820939456","n1820937837","n1820938222","n1820938902","n1820939162","n1820938965","n1820938461","n1820937681","n1820937514","n1820937764","n1820939719","n1820939697","n1820938899","n1820939093","n1820938702","n1820939595","n1820938749","n1820938348","n1820937606","n1820938675","n1820938830","n1820938737","n1820938758","n1820938716","n1820939107","n1820937863","n1820939033","n1820938163","n1820937867","n1820938819","n1820938034","n1820938252","n1820937563","n1820937868","n1820939032","n1820938632","n1820937982","n1820937943","n1820939568","n1820939541","n1820938215","n1820939097","n1820938812","n1820937518","n1820937952","n1820938711","n1820938736","n1820939066","n1820937591","n1820938082","n1820938108","n1820938496","n1820939410","n1820938949","n1820938327","n1820937708","n1820939023","n1820937772","n1820938256","n1820939083","n1820938378","n1820938961","n1820937610","n1820939717","n1820938695","n1820938590","n1820939655","n1820938341","n1820939054","n1820939157","n1820939674","n1820939684","n1820939511","n1820937631","n1820939458","n1820937830","n1820937709","n1820937779","n1820939749","n1820938880","n1820938856","n1820938557","n1820939557","n1820938249","n1820938818","n1820937594","n1820939114","n1820938416","n1820937508","n1820938990","n1820938201","n1820937759","n1820937987","n1820939164","n1820939753","n1820938187","n1820939067","n1820937586","n1820937941","n1820938121","n1820937807","n1820938521","n1820939726","n1820938244","n1820939014","n1820938741","n1820937629","n1820938664","n1820938747","n1820939082","n1820938709","n1820938320","n1820938270","n1820937619","n1820937777","n1820937718","n1820939138","n1820938056","n1820938155","n1820938596","n1820937775","n1820938437","n1820938128","n1820939581","n1820939145","n1820938546","n1820938184","n1820937601","n1820937794","n1820938539","n1820939645","n1820938438","n1820938436","n1820939025","n1820938915","n1820938534","n1820937605","n1820939607","n1820939101","n1820939580","n1820939268","n1820939134","n1820938849","n1820938754","n1820938079","n1820937842","n1820938781","n1820938873","n1820938495","n1820938381","n1820938503","n1820939436","n1820938502","n1820939087","n1820938996","n1820938449","n1820938907","n1820937979","n1820937780","n1820937546","n1820939699","n1820937677","n1820938957","n1820938946","n1820937776","n1820937717","n1820938718","n1820937637","n1820938510","n1820937663","n1820938941","n1820939151","n1820937603","n1820938250","n1820937951","n1820938630","n1820938821","n1820938779","n1820938497","n1820938159","n1820939536","n1820938409","n1820938386","n1820939116","n1820938340","n1820939117","n1820938291","n1820938435","n1820937819","n1820938242","n1820939078","n1820938877","n1820939104","n1820939445","n1820938367","n1820938903","n1820939420","n1820938517","n1820939508","n1820939542","n1820939326","n1820938210","n1820939020","n1820938815","n1820937832","n1820939513","n1820937818","n1820939005","n1820938717","n1820939135","n1820938384","n1820937587","n1820939024","n1820939504","n1820939120","n1820939026","n1820938015","n1820938998","n1820937648","n1820939137","n1820937761","n1820938195","n1820938535","n1820939550","n1820938725","n1820938282","n1820937781","n1820937792","n1820939705","n1820937788","n1820939707","n1820937882","n1820939632","n1820938427","n1820938276","n1820939617","n1820939013","n1820939035","n1820937543","n1820939365","n1820937752","n1820937802","n1820939183","n1820939670","n1820938450","n1820939375","n1820937813","n1820937673","n1820937783","n1820939029","n1820939768","n1820939377","n1820937974","n1820939244","n1820939642","n1820937864","n1820938255","n1820938528","n1820939666","n1820938120","n1820937812","n1820938928","n1820939750","n1820939099","n1820938073","n1820938714","n1820939140","n1820938192","n1820937844","n1820938635","n1820938742","n1820939583","n1820937887","n1820938318","n1820938816","n1820939698","n1820938273","n1820939181","n1820937652","n1820938748","n1820937651","n1820938519","n1820938019","n1820938752","n1820938235","n1820939118","n1820938562","n1820939314","n1820939570","n1820938190","n1820938342","n1820938533","n1820937977","n1820939089","n1820939146","n1820938622","n1820938297","n1820938524","n1820939283","n1820938874","n1820938832","n1820937550","n1820937843","n1820938638","n1820938116","n1820938206","n1820938319","n1820939053","n1820937845","n1820938093","n1820939217","n1820938997","n1820939355","n1820938861","n1820938726","n1820938057","n1820939373","n1820937862","n1820938518","n1820939072","n1820939680","n1820938444","n1820938217","n1820938506","n1820938393","n1820938492","n1820938852","n1820938221","n1820938773","n1820937684","n1820939060","n1820938224","n1820938203","n1820938840","n1820937525","n1820938147","n1820938433","n1820938188","n1820939359","n1820938750","n1820938016","n1820938768","n1820937621","n1820937799","n1820938951","n1820938721","n1820939037","n1820937866","n1820939715","n1820938063","n1820938446","n1820937627","n1820939624","n1820938431","n1820939721","n1820939622","n1820939239","n1820939263","n1820939648","n1820939640","n1820938867","n1820938757","n1820938439","n1820939352","n1820937740","n1820939329","n1820938229","n1820937583","n1820938180","n1820938366","n1820937767","n1820937758","n1820939374","n1820938869","n1820938292","n1820938400","n1820938399","n1820939734","n1820939289","n1820938944","n1820937755","n1820938759","n1820938434","n1820937600","n1820937825","n1820937670","n1820937793","n1820938011","n1820938246","n1820938956","n1820937770","n1820937757","n1820938059","n1820937860","n1820937569","n1820939266","n1820939685","n1820939672","n1820938606","n1820938772","n1820939038","n1820938211","n1820938359","n1820939619","n1820938708","n1820939512","n1820938065","n1820939233","n1820939739","n1820938786","n1820938879","n1820939147","n1820938563","n1820939148","n1820937839","n1820937659","n1820937786","n1820938419","n1820939565","n1820939402","n1820937710","n1820938254","n1820938271","n1820938390","n1820937680","n1820938140","n1820937817","n1820938218","n1820937985","n1820939235","n1820938441","n1820938401","n1820938719","n1820937795","n1820938971","n1820938460","n1820939759","n1820937972","n1820937841","n1820938462","n1820939320","n1820938978","n1820938360","n1820939713","n1820937676","n1820939712","n1820937939","n1820938080","n1820937754","n1820937753","n1820938530","n1820937886","n1820939689","n1820939124","n1820938697","n1820938789","n1820939105","n1820938860","n1820938853","n1820939400","n1820937561","n1820938404","n1820938774","n1820939316","n1820937696","n1820938782","n1820938975","n1820937564","n1820939730","n1820938257","n1820937853","n1820938487","n1820938848","n1820938906","n1820939230","n1820938424","n1820938051","n1820937771","n1820939587","n1820939149","n1820938792","n1820939041","n1820938934","n1820939777","n1820937515","n1820939058","n1820938312","n1820939264","n1820939631","n1820939109","n1820939403","n1820939664","n1820938724","n1820938929","n1820939399","n1820939776","n1820939369","n1820939185","n1820937701","n1820938126","n1820938336","n1820938219","n1820939080","n1820938642","n1820938043","n1820937725","n1820938548","n1820938552","n1820938035","n1820938684","n1820937778","n1820938764","n1820939021","n1820939346","n1820937712","n1820939761","n1820938397","n1820937747","n1820938566","n1820939161","n1820939090","n1820939752","n1820939271","n1820938878","n1820938110","n1820938346","n1820938499","n1820938151","n1820939538","n1820938281","n1820939153","n1820938551","n1820939285","n1820938197","n1820938408","n1820938482","n1820939036","n1820939579","n1820938489","n1820938483","n1820938189","n1820938123","n1820938087","n1820937741","n1820938485","n1820937590","n1820938972","n1820937773","n1820937520","n1820938872","n1820938131","n1820938452","n1820938328","n1820939620","n1820937641","n1820938353","n1820939693","n1820938705","n1820937640","n1820939189","n1820938144","n1820939774","n1820938694","n1820938238","n1820939397","n1820937917","n1820938454","n1820938567","n1820938979","n1820938060","n1820938204","n1820937828","n1820939232","n1820938806","n1820938857","n1820938078","n1820938105","n1820939228","n1820938604","n1820937763","n1820937854","n1820938289","n1820939736","n1820937937","n1820937714","n1820938278","n1820938058","n1820938706","n1820938989","n1820938313","n1820938520","n1820938288","n1820937689","n1820939537","n1820939531","n1820939019","n1820937527","n1820938455","n1820938814","n1820938045","n1820939627","n1820938213","n1820938161","n1820938331","n1820938024","n1820938220","n1820938062","n1820938178","n1820937796","n1820937644","n1820938490","n1820937589","n1820937879","n1820939614","n1820938882","n1820938039","n1820938538","n1820937667","n1820937719","n1820938561","n1820939658","n1820938783","n1820938601","n1820938198","n1820938388","n1820938969","n1820937687","n1820939086","n1820939665","n1820939187","n1820938498","n1820938261","n1820937983","n1820938068","n1820938136","n1820939061","n1820938137","n1820938186","n1820939071","n1820937592","n1820939669","n1820937553","n1820939357","n1820938727","n1820939371","n1820939112","n1820939079","n1820938743","n1820938467","n1820938834","n1820938022","n1820938537","n1820938122","n1820938516","n1820937614","n1820937612","n1820939469","n1820939636","n1820939050","n1820939552","n1820938157","n1820938663","n1820938955","n1820939091","n1820938430","n1820938471","n1820937809","n1820938074","n1820938208","n1820938914","n1820938858","n1820938417","n1820937531","n1820938107","n1820939100","n1820938751","n1820937711","n1820938824","n1820939745","n1820937572","n1820938602","n1820938212","n1820938097","n1820937921","n1820938090","n1820938511","n1820938876","n1820939762","n1820938234","n1820938048","n1820937774","n1820937856","n1820937749","n1820937765","n1820938286","n1820939095","n1820938480","n1820939229","n1820938277","n1820937617","n1820938311","n1820937622","n1820939196","n1820937690","n1820939006","n1820939287","n1820939131","n1820938106","n1820938784","n1820938335","n1820938095","n1820938182","n1820937715","n1820937683","n1820938070","n1820939605","n1820938527","n1820938763","n1820938398","n1820937686","n1820939621","n1820937664","n1820939277","n1820938565","n1820939539","n1820938099","n1820939646","n1820938556","n1820937548","n1820938729","n1820939336","n1820938259","n1820938728","n1820938361","n1820937643","n1820938644","n1820939007","n1820939690","n1820939227","n1820937635","n1820937950","n1820938682","n1820939150","n1820939012","n1820939261","n1820939111","n1820937805","n1820939691","n1820939677","n1820937628","n1820937811","n1820938790","n1820938251","n1820938226","n1820938942","n1820937633","n1820937984","n1820937751","n1820939673","n1820938970","n1820938415","n1820938597","n1820938309","n1820938111","n1820938472","n1820938894","n1820938402","n1820937593","n1820938570","n1820939102","n1820939775","n1820937948","n1820939121","n1820937511","n1820938787","n1820939720","n1820939075","n1820937880","n1820937742","n1820937721","n1820939535","n1820938486","n1820938354","n1820937632","n1820939010","n1820938885","n1820938089","n1820937613","n1820938442","n1820938245","n1820938272","n1820937566","n1820938295","n1820938532","n1820938883","n1820937713","n1820937674","n1820939635","n1820938448","n1820938355","n1820938587","n1820938559","n1820937787","n1820939301","n1820937723","n1820939056","n1820937560","n1820938323","n1820938230","n1820938453","n1820938377","n1820938357","n1820939637","n1820938017","n1820939540","n1820939376","n1820937639","n1820937642","n1820938075","n1820938351","n1820938766","n1820937897","n1820938973","n1820938066","n1820939547","n1820939652","n1820937944","n1820937748","n1820939234","n1820939193","n1820937891","n1820938785","n1820939132","n1820938523","n1820938884","n1820938411","n1820939554","n1820938791","n1820937655","n1820938368","n1820939152","n1820938030","n1820938447","n1820937580","n1820939628","n1820937588","n1820937894","n1820939201","n1820938086","n1820937650","n1820938379","n1820939008","n1820938999","n1820937524","n1820937872","n1820938389","n1820939197","n1820938422","n1820938936","n1820939262","n1820937634","n1820938583","n1820939589","n1820937901","n1820939034","n1820939065","n1820938290","n1820939195","n1820938228","n1820937884","n1820938797","n1820938191","n1820939191","n1820939198","n1820937892","n1820939679","n1820938507","n1820937647","n1820937909","n1820938542","n1820939598","n1820937851","n1820939084","n1820939728","n1820937688","n1820938263","n1820938670","n1820937762","n1820939310","n1820938925","n1820938862","n1820938822","n1820938547","n1820937731","n1820938594","n1820938592","n1820938214","n1820938284","n1820937835","n1820938599","n1820939437","n1820937834","n1820937576","n1820937692","n1820939586","n1820939546","n1820938403","n1820937970","n1820939561","n1820938098","n1820938851","n1820938477","n1820938892","n1820939045","n1820939758","n1820939350","n1820938321","n1820938440","n1820938595","n1820938364","n1820938962","n1820938118","n1820939678","n1820938406","n1820938549","n1820937555","n1820938823","n1820937521","n1820939471","n1820939487","n1820938799","n1820938605","n1820937928","n1820938373","n1820939747","n1820939629","n1820937557","n1820937526","n1820938958","n1820938833","n1820937636","n1820938967","n1820938760","n1820938842","n1820938067","n1820939077","n1820939224","n1820938185","n1820939110","n1820938372","n1820939757","n1820939063","n1820939660","n1820938813","n1820937528","n1820938369","n1820938896","n1820939551","n1820939683","n1820937660","n1820937873","n1820938810","n1820938478","n1820939662","n1820937595","n1820939052","n1820938113","n1820939070","n1820938733","n1820937878","n1820938300","n1820939760","n1820939718","n1820937646","n1820939057","n1820939443","n1914861306","n1820938013","n1820937529","n1820939764","n1820938826","n1820937885","n1820939588","n1820937865","n1820937833","n1914861112","n1820938761","n1914861007","n1820937905","n1820938541","n1820939092","n1914861057","n1820938153","n1820938267","n1820939265","n1820938085","n1820939018","n1820939755","n1820938474","n1820939027","n1820938593","n1820938202","n1820939599","n1820939695","n1820938077","n1820938012","n1820939545","n1820939596","n1820939337","n1820938227","n1820937698","n1820938475","n1820939465","n1820938165","n1820938698","n1820938525","n1820938529","n1820938553","n1820938940","n1820939498","n1820938501","n1820939533","n1820938924","n1820939634","n1820939220","n1820939657","n1820938887","n1820938838","n1820938114","n1820937823","n1820938778","n1820938801","n1820939096","n1820938981","n1820937953","n1820938732","n1820938980","n1820938960","n1820937949","n1820938026","n1820939273","n1841425201","n1820938629","n1820938864","n1820938554","n1820938088","n1820937685","n1841425222","n1820939729","n1820937665","n1820937838","n1820937739","n1820938780","n1820937821","n1820938825","n1820939055","n1820939485","n1820938041","n1820938746","n1820939562","n1820938459","n1820939489","n1820938050","n1820937980","n1820937695","n1820938413","n1820938555","n1820937703","n1820938536","n1820938196","n1820938287","n1820938169","n1820939279","n1820938531","n1820938959","n1820939741","n1820938665","n1820938963","n1820939611","n1820937653","n1820939618","n1820939492","n1820938600","n1820938628","n1820939312","n1820939616","n1820937738","n1820939001","n1820939062","n1820938794","n1820938558","n1820937822","n1820937532","n1820939073","n1820938200","n1820938241","n1820938968","n1820938927","n1820938306","n1820937630","n1820938456","n1820937694","n1820938908","n1820939076","n1820937522","n1820939659","n1820938522","n1820939318","n1820938932","n1820938841","n1820937579","n1820937540","n1820938560","n1821139530","n1820938964","n1820937662","n1820939281","n1821139533","n1820937797","n1821139532","n1820939751","n1821139531","n1820939291","n1820938420","n1820939696","n1820938904","n1820938484","n1820939448","n1820939009","n1820938735","n1820938986","n1820938937","n1820939030","n1820938734","n1820938745","n1820939106","n1820938987","n1820937858","n1820938673","n1820938620","n1820937808","n1820937700","n1820939573","n1820938540","n1820937661","n1820937570","n1820938396","n1820937875","n1820939048","n1820938233","n1820938793","n1820939584","n1820938412","n1820938394","n1820937846","n1820938800","n1820938690","n1820939331","n1820939630","n1820938762","n1820938710","n1820939322","n1820938992","n1821137608","n1821137607","n1820937924","n1820939139","n1820939463","n1820939574","n1820938294","n1820938071","n1820938307","n1820938061","n1820939260","n1820937899","n1820938310","n1820938983","n1820937530","n1820938993","n1820938890","n1820937915","n1820938231","n1820938040","n1820938920","n1820939585","n1820938135","n1820939700","n1820937824","n1820939667","n1820937930","n1820938134","n1820937551","n1820939405","n1820938232","n1820937716","n1820937848","n1820939765","n1820939068","n1820939766","n1820937933","n1820937720","n1820939222","n1820939772","n1820939022","n1820939732","n1820937702","n1820937691","n1820938945","n1820937756","n1820938451","n1820938410","n1820938798","n1820937945","n1820937654","n1820938598","n1820938836","n1820937571","n1820937556","n1820938994","n1820938919","n1820938863","n1820939064","n1820938018","n1820937658","n1820937537","n1820938142","n1820938666","n1820937535","n1820939571","n1820938465","n1820939638","n1820937533","n1820939656","n1820939422","n1820938109","n1820938405","n1820938028","n1820937649","n1820938829","n1820939031","n1820939155","n1820938350","n1820938463","n1820938425","n1820939047","n1820938831","n1820938494","n1820937697","n1820938504","n1820938900","n1820937784","n1820938414","n1820938076","n1820938723","n1820937722","n1820938739","n1820937791","n1820938985","n1820938352","n1820938293","n1820938274","n1820939692","n1820937871","n1820939059","n1820938868","n1820937877","n1820937743","n1820938429","n1820937545","n1820937575","n1820938302","n1820938505","n1820938916","n1820938374","n1820938329","n1820937790","n1820939735","n1820938930","n1820937995","n1820938512","n1820938130","n1820938194","n1820938671","n1820938802","n1820937542","n1820937602","n1820939069","n1820938901","n1820939654","n1820937727","n1820939569","n1820938375","n1820939306","n1820938479","n1820938376","n1820938667","n1820937766","n1820939467","n1820939567","n1820937806","n1820938943","n1820938931","n1820937745","n1820939452","n1820938738","n1820938053","n1820939653","n1820938640","n1820937604","n1820937536","n1820938701","n1820939625","n1820939744","n1820939572","n1820937577","n1820937541","n1820938891","n1820937597","n1820938469","n1820939194","n1820937539","n1820938911","n1820939017","n1820939650","n1820939103","n1820939578","n1820938132","n1820937549","n1820938634","n1820939743","n1820937544","n1820937826","n1820937598","n1820937547","n1820938032","n1820939142"]},"w17963021":{"id":"w17963021","tags":{"highway":"residential"},"nodes":["n185948706","n185948708","n185948710"]},"w203974069":{"id":"w203974069","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139870431","n2139870432","n2139870433","n2139870434","n2139870431"]},"w209816575":{"id":"w209816575","tags":{"area":"yes","building":"yes"},"nodes":["n2199856288","n2199856289","n2199856290","n2199856291","n2199856292","n2199856293","n2199856294","n2199856295","n2199856296","n2199856297","n2199856298","n2199856299","n2199856300","n2199856301","n2199856302","n2199856303","n2199856288"]},"w203841838":{"id":"w203841838","tags":{"area":"yes","natural":"water"},"nodes":["n2138493826","n2138493827","n2138493828","n2138493829","n2138493830","n2138493831","n2138493833","n2138493832","n2138493826"]},"w203972937":{"id":"w203972937","tags":{"highway":"path","name":"Riverwalk Trail","surface":"asphalt","width":"3"},"nodes":["n2139858882","n2139858883","n2139858884","n2139858885","n2139858886","n2139858887","n2139858882","n2139858888","n2139858889","n2139858890","n2139858891","n2139858892","n2139858893","n2139858894","n2139858895","n2139858896","n2139858897","n2139858898","n2139858899","n2139858900","n2139858901","n2139858902","n2139858903","n2139858986","n2139858904","n2139858995","n2139858905","n2139858906","n2139858907","n2139858908","n2139858909","n2139858910","n2139858911","n2139858912","n2139858913","n2139858914","n2139858915","n2139858916","n2139858917","n2139858918","n2139858919","n2139858920","n2139858921","n2139858922","n2139858923","n2139858924","n2139858925","n2139858926","n2139858927","n2139858982","n2139858928","n2139858929","n2139858930","n2139858931","n2139858932","n2139858981","n2139858933","n2139858934","n2139858935","n2139858936","n2139858937","n2139858938","n2139858939","n2139858940","n2139858941","n2139858942","n2139858943","n2140006437","n2139858964","n2139858944","n2139858966","n2139858945","n2139858946","n2139858947","n2139858948","n2139858949","n2139858950","n2139858951"]},"w17964015":{"id":"w17964015","tags":{"highway":"residential"},"nodes":["n185954680","n185954683","n185954685","n185954687","n185954689","n185954690","n185954691","n2139870379","n2139870456","n185954692","n185954693","n185954695"]},"w17967315":{"id":"w17967315","tags":{"highway":"residential","name":"South Andrews Street"},"nodes":["n185981999","n185974477","n185964963"]},"w203974071":{"id":"w203974071","tags":{"highway":"footway"},"nodes":["n2139870439","n2139870440","n2139870441","n2139870442","n2139870443","n2139870444","n2139870445","n2139870446","n2139870447","n2139870448","n2139870449"]},"w170848824":{"id":"w170848824","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819858503","n1819858531","n1819858526","n1819858518","n1819858505","n1819858508","n1819858512","n1819858514","n1819858528","n1819858509","n1819858511","n1819858507","n1819858521"]},"w203986458":{"id":"w203986458","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139989357","n2139989359","n2139989360","n2139989362","n2139989357"]},"w170844917":{"id":"w170844917","tags":{"waterway":"riverbank"},"nodes":["n1819805911","n1819805690","n1819805812","n1819805766","n1819805802","n1819805885","n1819805626","n1819805842","n1819805715","n1819805694","n1819805618","n1819805629","n1819805731","n1819805636","n1819805878","n1819805718","n1819805798","n1819849057","n1819805666","n1819805852","n1819805805","n1819805789","n1819805868","n1819805680","n1819805918","n1819848888","n1819805762","n2139989328","n1819805907","n2139989330","n1819805915","n1819858521","n1819805854","n1819805876","n1819805864","n1819805922","n2139859004","n1819805702","n2139859003","n1819805614","n1819805792","n1819805786","n1819805777","n1819805645","n1819805838","n1819805889","n1819805795","n1819805707","n1819805774","n1819805808","n1819805810","n1819805724","n1819805676","n1819805728","n1819805783","n1819805687","n1819805727","n2189123379","n1819805632","n1819805641","n1819805760","n1819805887","n1819805861","n1819805722","n1819805880","n2139982405","n2139982399","n2139982400","n1819805770","n2139982402","n2139982403","n2139982401","n1819805780","n1819805834","n2139982406","n1819805698","n1819805647","n1819805870","n1819805683","n1819805622","n1819805639","n1819805858","n1819805643","n1819805673","n1819805925","n1819805849","n1819805711","n1819805846","n1819805669","n1819805883","n1819805814","n1819805873","n1819805911"]},"w17967326":{"id":"w17967326","tags":{"highway":"residential","name":"North Constantine Street"},"nodes":["n185985217","n185985219","n185985221","n185985222","n185985223","n185985225","n2140006431","n185985227","n185985229","n185985231","n185985233","n185985235","n185985238","n185985240","n2140018998","n185964965"]},"w134150789":{"id":"w134150789","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964971","n2139870406","n185964972"]},"w17966400":{"id":"w17966400","tags":{"highway":"tertiary","name":"South Constantine Street"},"nodes":["n185958672","n185964965"]},"w203974066":{"id":"w203974066","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139870417","n2139870418","n2139870420","n2139870419"]},"w17965998":{"id":"w17965998","tags":{"name":"Conrail Railroad","railway":"rail"},"nodes":["n185972775","n185972777","n185972779","n185972781","n185972783","n185972785","n185972787","n185972788","n185972789","n185972790","n185972791","n185972793","n185972795","n185972797","n185972798","n185972800","n185972802","n185972805","n185972807","n185972809","n185972811","n185972813","n185972814","n185972815","n185972816","n185972817","n185972819","n185972821","n185972824","n185972826","n185972830","n185972832","n185972834","n185972835","n185972836","n185972839","n185990434","n2114807572","n2114807568","n185972845","n2114807583","n185972847","n185972849","n185972851","n2114807578","n1475293254","n2114807593","n1475293226","n185972862","n2114807565","n185951869","n1475293234","n1475293252","n185972868","n1475293264","n1475293222","n185972878","n1475293261","n185972882","n185972885","n1475293260","n1475293240","n185972891","n185972895","n185972897","n185972899","n2130304159","n1475284023","n185972903"]},"w134150795":{"id":"w134150795","tags":{"bridge":"yes","highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964970","n185964971"]},"w203974067":{"id":"w203974067","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139870420","n2139870421"]},"w170995908":{"id":"w170995908","tags":{"highway":"residential","name":"Thomas Street"},"nodes":["n1821006702","n1821006700","n1821006698","n2139858990","n1821006716","n1821006725","n1821006712","n1821006704","n1821006708","n1821006710","n1821006706"]},"w17965834":{"id":"w17965834","tags":{"highway":"residential","name":"Spring Street"},"nodes":["n185971361","n185971364","n185971366","n185971368","n185954695","n185964968"]},"w203974070":{"id":"w203974070","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139870435","n2139870436","n2139870437","n2139870438","n2139870435"]},"w203989879":{"id":"w203989879","tags":{"highway":"service"},"nodes":["n2140018998","n2140018999","n2140019000"]},"w203974062":{"id":"w203974062","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870387","n2139870388","n2139870389","n2139870390","n2139870391","n2139870392","n2139870397","n2139870393","n2139870396","n2139870395","n2139870394","n2139870387"]},"w203974061":{"id":"w203974061","tags":{"bridge":"yes","highway":"footway"},"nodes":["n2139870382","n2139870383"]},"w203049587":{"id":"w203049587","tags":{"area":"yes","name":"Scidmore Park Petting Zoo","tourism":"zoo","zoo":"petting_zoo"},"nodes":["n2130304133","n2130304136","n2130304138","n2130304140","n2130304142","n2130304144","n2130304146","n2130304147","n2130304148","n2130304149","n2130304150","n2130304151","n2130304133"]},"w203972941":{"id":"w203972941","tags":{"highway":"path"},"nodes":["n2139858982","n2139858983","n2139858984","n2139858985","n2139858927"]},"w203974065":{"id":"w203974065","tags":{"highway":"service"},"nodes":["n2139870406","n2139870407","n2139870408","n2139870417","n2139870409","n2139870410","n2139870411","n2139870412","n2139870426","n2139870413","n2139870414","n2139870415","n2139870419","n2139870416","n2139870421","n2139870408"]},"w203972940":{"id":"w203972940","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858934","n2139858967","n2139858968","n2139858969","n2139858970","n2139858971","n2139858972","n2139858973","n2139858974","n2139858975","n2139858976","n2139858977","n2139858978","n2139858979","n2139858980","n2139858981"]},"w203974072":{"id":"w203974072","tags":{"highway":"footway"},"nodes":["n2139858925","n2139870450","n2139870453","n2139870451","n2139870452","n2139870441"]},"w203974074":{"id":"w203974074","tags":{"highway":"footway"},"nodes":["n2139870454","n2139870456","n2139870429"]},"w203974060":{"id":"w203974060","tags":{"highway":"footway"},"nodes":["n2139870383","n2139870384","n2139870422","n2139870385","n2139870386","n2139870388"]},"w203841837":{"id":"w203841837","tags":{"area":"yes","natural":"water"},"nodes":["n2138493807","n2138493808","n2138493809","n2138493810","n2138493811","n2138493812","n2138493813","n2138493814","n2138493815","n2138493816","n2138493825","n2138493817","n2138493824","n2138493818","n2138493819","n2138493820","n2138493821","n2138493822","n2138493823","n2138493807"]},"w134150845":{"id":"w134150845","tags":{"bridge":"yes","name":"Conrail Railroad","railway":"rail"},"nodes":["n185972903","n185972905"]},"w203974059":{"id":"w203974059","tags":{"highway":"footway"},"nodes":["n2139870430","n2139870439","n2139870429","n2139870428","n2139870379","n2139870455","n2139870380","n2139870381","n2139858925","n2139870382"]},"w203986457":{"id":"w203986457","tags":{"area":"yes","leisure":"park","name":"Scidmore Park"},"nodes":["n2139989333","n2139989335","n2139989337","n2139989339","n1819805762","n2139989328","n1819805907","n2139989330","n1819805915","n2139989341","n2139989344","n2139989346","n2139989348","n2139989350","n2139989351","n2139989353","n2139989355","n2139989333"]},"w170848331":{"id":"w170848331","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819848937","n1819849104","n1819849076","n1819849183","n1819848928","n1819848972","n1819848948","n1819848971","n1819848859","n1819849008","n1819848889","n1819849026","n1819849094","n1819849083","n1819849079","n1819849187","n1819848992","n1819849060","n1819849056","n1819849071","n1819849067","n1819849048","n1819849036","n1819849150","n1819849075","n1819849051","n1819849062","n1819848926","n1819849035","n1819848987","n1819849012","n1819848933","n1819848996","n1819848990","n1819849005","n1819849021","n1819848892","n1819849092","n1819848863","n1819848922","n1819848858","n1819848855","n1819848974","n1819848953","n1819849019","n1819849049","n1819848979","n1819849140","n1819849193","n1819849147","n1819849151","n1819849163","n1819849023","n1819848878","n1819849004","n1819848857","n1819848879","n1819849041","n1819849165","n1819849107","n1819849156","n1819848934","n1819848914","n1819848955","n1819848931","n1819848927","n1819849084","n1819849169","n1819849045","n1819848945","n1819849095","n1819848924","n1819849171","n1819849141","n1819849046","n1819849197","n1819849011","n1819849108","n1819849158","n1819849160","n1819848870","n1819849006","n1819849157","n1819848993","n1819848970","n1819849202","n1819848903","n1819848975","n1819848849","n1819849025","n1819849105","n1819849033","n1819849176","n1819849099","n1819849086","n1819848960","n1819848961","n1819849001","n1819848980","n1819849038","n1819848854","n1819849127","n1819849170","n1819849139","n1819848873","n1819848929","n1819849201","n1819849121","n1819849031","n1819849131","n1819848875","n1819849080","n1819849066","n1819849081","n1819849096","n1819849172","n1819849114","n1819849182","n1819848905","n1819849054","n1819848920","n1819848851","n1819848968","n1819848917","n1819849111","n1819849119","n1819849074","n1819848893","n1819849129","n1819848850","n1819848956","n1819849154","n1819848877","n1819848986","n1819849191","n1819848952","n1819848954","n1819848942","n1819849028","n1819849195","n1819848938","n1819848962","n1819849070","n1819849034","n1819849052","n1819849059","n1819848916","n1819849162","n1819849167","n1819849093","n1819849030","n1819849002","n1819849161","n1819848886","n1819848958","n1819849064","n1819849112","n1819849148","n1819848856","n1819848976","n1819848977","n1819849144","n1819848918","n1819849200","n1819848919","n1819849042","n1819849166","n1819849186","n1819849152","n1819849058","n1819849185","n1819849199","n1819849053","n1819849194","n1819849068","n1819849146","n1819849174","n1819848967","n1819848932","n1819849155","n1819849198","n1819848964","n1819848894","n1819848969","n1819849184","n1819849055","n1819849179","n1819848865","n1819848860","n1819849082","n1819848966","n1819849040","n1819849069","n1819849078","n1819849077","n1819848904","n1819848959","n1819849133","n1819849089","n1819849000","n1819849124","n1819849032","n1819849097","n1819848939","n1819849072","n1819848915","n1819849196","n1819848946","n1819849047","n1819849029","n1819849164","n1819848994","n1819849022","n1819858513","n1819849126","n1819849063","n1819848941","n1819849085","n1819848871","n1819848943","n1819849192","n1819858501","n1819849159","n1819858523","n1819848901","n1819849189","n1819858503","n1819849065","n2139877106","n1819848909","n1819848930","n1819848888"]},"w17967397":{"id":"w17967397","tags":{"highway":"residential","name":"North Andrews Street"},"nodes":["n185964963","n185985217"]},"w17964497":{"id":"w17964497","tags":{"highway":"tertiary","name":"Constantine St"},"nodes":["n185958643","n185958645","n2139795852","n185958647","n185958649","n185958651","n185958653","n185958656","n185958658","n185958660","n185958662","n185958664","n185958666","n185958668","n185958670","n185948710","n185958672"]},"w203974068":{"id":"w203974068","tags":{"highway":"footway"},"nodes":["n2139870422","n2139870423","n2139870424","n2139870425","n2139870426","n2139870427"]},"w203974063":{"id":"w203974063","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870398","n2139870399","n2139870400","n2139870401","n2139870398"]},"w203986459":{"id":"w203986459","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139989364","n2139989366","n2139989368","n2139989370","n2139989364"]},"w203988286":{"id":"w203988286","tags":{"area":"yes","leisure":"park","name":"Memory Isle Park"},"nodes":["n2140006331","n2140006334","n2140006336","n2140006338","n2140006340","n2140006342","n2140006344","n2140006346","n2140006348","n2140006351","n2140006353","n2140006355","n2140006357","n2140006359","n2140006361","n2140006363","n2140006364","n2140006365","n2140006395","n2140006366","n2140006401","n2140006397","n2140006399","n2140006331"]},"w203974073":{"id":"w203974073","tags":{"highway":"footway"},"nodes":["n2139870453","n2139870454","n2139870455"]},"w203974064":{"id":"w203974064","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139870402","n2139870403","n2139870404","n2139870405","n2139870402"]},"n185966959":{"id":"n185966959","loc":[-85.642185,41.946411]},"n1475283980":{"id":"n1475283980","loc":[-85.6398249,41.9451425]},"n1475284013":{"id":"n1475284013","loc":[-85.6396448,41.9451666]},"n1475284042":{"id":"n1475284042","loc":[-85.6386382,41.9454789]},"n185975925":{"id":"n185975925","loc":[-85.6393332,41.9452388]},"n185975919":{"id":"n185975919","loc":[-85.6391279,41.9453044]},"n185975917":{"id":"n185975917","loc":[-85.6389034,41.9453872]},"n2140006369":{"id":"n2140006369","loc":[-85.6386163,41.9451631]},"n2140006370":{"id":"n2140006370","loc":[-85.6385144,41.9449357]},"n2140006417":{"id":"n2140006417","loc":[-85.6385785,41.9450299]},"n2140006419":{"id":"n2140006419","loc":[-85.6385781,41.9452152]},"n2189123361":{"id":"n2189123361","loc":[-85.6404948,41.947015]},"n2189123363":{"id":"n2189123363","loc":[-85.6395765,41.946495]},"n2189123365":{"id":"n2189123365","loc":[-85.6389347,41.9460875]},"n185966962":{"id":"n185966962","loc":[-85.644417,41.946364]},"n185975911":{"id":"n185975911","loc":[-85.637532,41.9458276]},"n185975913":{"id":"n185975913","loc":[-85.6376323,41.9457936]},"n185975915":{"id":"n185975915","loc":[-85.6383596,41.9455425]},"n185975932":{"id":"n185975932","loc":[-85.644403,41.945088]},"n185975934":{"id":"n185975934","loc":[-85.645486,41.945084]},"n185979974":{"id":"n185979974","loc":[-85.644381,41.943831]},"n2139795809":{"id":"n2139795809","loc":[-85.6464756,41.9450813]},"n2139795810":{"id":"n2139795810","loc":[-85.6466646,41.945174]},"n2139858952":{"id":"n2139858952","loc":[-85.6383567,41.9454039]},"n2139858953":{"id":"n2139858953","loc":[-85.6380506,41.9455301]},"n2139858954":{"id":"n2139858954","loc":[-85.6377321,41.9455546]},"n2139858955":{"id":"n2139858955","loc":[-85.6376571,41.9455245]},"n2139858956":{"id":"n2139858956","loc":[-85.6375859,41.9454544]},"n2139858957":{"id":"n2139858957","loc":[-85.6376686,41.9453185]},"n2139858958":{"id":"n2139858958","loc":[-85.6378936,41.9451712]},"n2139858959":{"id":"n2139858959","loc":[-85.6379225,41.9450825]},"n2139858960":{"id":"n2139858960","loc":[-85.6379302,41.9447564]},"n2139858961":{"id":"n2139858961","loc":[-85.6379763,41.9446963]},"n2139858962":{"id":"n2139858962","loc":[-85.6380436,41.9446706]},"n2139858963":{"id":"n2139858963","loc":[-85.6381286,41.9445969]},"n2139858965":{"id":"n2139858965","loc":[-85.6382523,41.9444134]},"n2140006367":{"id":"n2140006367","loc":[-85.6380923,41.9454418]},"n2140006368":{"id":"n2140006368","loc":[-85.6384089,41.9453146]},"n2140006372":{"id":"n2140006372","loc":[-85.6383252,41.9447706]},"n2140006374":{"id":"n2140006374","loc":[-85.6381033,41.9447436]},"n2140006376":{"id":"n2140006376","loc":[-85.6379759,41.9447815]},"n2140006378":{"id":"n2140006378","loc":[-85.6379832,41.9448654]},"n2140006380":{"id":"n2140006380","loc":[-85.6380632,41.9450738]},"n2140006382":{"id":"n2140006382","loc":[-85.6380414,41.9452064]},"n2140006389":{"id":"n2140006389","loc":[-85.6379068,41.9453092]},"n2140006391":{"id":"n2140006391","loc":[-85.637925,41.9453904]},"n2140006393":{"id":"n2140006393","loc":[-85.6379977,41.94545]},"n2189123275":{"id":"n2189123275","loc":[-85.6371346,41.9462544]},"n2189123278":{"id":"n2189123278","loc":[-85.6368371,41.9466153]},"n2189123280":{"id":"n2189123280","loc":[-85.6379537,41.9489088]},"n2189123282":{"id":"n2189123282","loc":[-85.6383816,41.9497858]},"n2189123285":{"id":"n2189123285","loc":[-85.6393673,41.9512417]},"n2189123287":{"id":"n2189123287","loc":[-85.640554,41.9517766]},"n2189123289":{"id":"n2189123289","loc":[-85.6411,41.9522344]},"n2189123291":{"id":"n2189123291","loc":[-85.6417418,41.9526574]},"n2189123293":{"id":"n2189123293","loc":[-85.642321,41.9529407]},"n2189123295":{"id":"n2189123295","loc":[-85.6427697,41.9532278]},"n2189123297":{"id":"n2189123297","loc":[-85.6433332,41.9538254]},"n2189123300":{"id":"n2189123300","loc":[-85.6435785,41.9543648]},"n2189123301":{"id":"n2189123301","loc":[-85.6444394,41.9541048]},"n2189123303":{"id":"n2189123303","loc":[-85.6450603,41.954]},"n2189123312":{"id":"n2189123312","loc":[-85.6454829,41.9539108]},"n2189123314":{"id":"n2189123314","loc":[-85.6460464,41.9538526]},"n2189123315":{"id":"n2189123315","loc":[-85.6463178,41.9537167]},"n2189123316":{"id":"n2189123316","loc":[-85.646276,41.9534141]},"n2189123317":{"id":"n2189123317","loc":[-85.6459995,41.9531541]},"n2189123318":{"id":"n2189123318","loc":[-85.645222,41.9531929]},"n2189123319":{"id":"n2189123319","loc":[-85.6447316,41.9531813]},"n2189123320":{"id":"n2189123320","loc":[-85.6440637,41.9532977]},"n2189123321":{"id":"n2189123321","loc":[-85.6438185,41.9531774]},"n2189123322":{"id":"n2189123322","loc":[-85.6440011,41.9528398]},"n2189123323":{"id":"n2189123323","loc":[-85.6442672,41.9525914]},"n2189123324":{"id":"n2189123324","loc":[-85.6442881,41.9523276]},"n2189123326":{"id":"n2189123326","loc":[-85.644262,41.952153]},"n2189123328":{"id":"n2189123328","loc":[-85.6441681,41.9520404]},"n2189123330":{"id":"n2189123330","loc":[-85.6442098,41.9517494]},"n2189123333":{"id":"n2189123333","loc":[-85.6438498,41.9515864]},"n2189123336":{"id":"n2189123336","loc":[-85.6435889,41.9513225]},"n2189123339":{"id":"n2189123339","loc":[-85.6425349,41.9510315]},"n2189123342":{"id":"n2189123342","loc":[-85.6422688,41.9508802]},"n2189123345":{"id":"n2189123345","loc":[-85.6418775,41.9508142]},"n2189123348":{"id":"n2189123348","loc":[-85.6415488,41.9508064]},"n2189123351":{"id":"n2189123351","loc":[-85.6411027,41.9505488]},"n2189123353":{"id":"n2189123353","loc":[-85.6410374,41.9498208]},"n2189123355":{"id":"n2189123355","loc":[-85.6410061,41.9494327]},"n2189123357":{"id":"n2189123357","loc":[-85.6411522,41.9482569]},"n2189123359":{"id":"n2189123359","loc":[-85.6410548,41.9473036]},"n2189123368":{"id":"n2189123368","loc":[-85.6380216,41.9458974]},"n2189123370":{"id":"n2189123370","loc":[-85.6386721,41.9507782]},"w17968193":{"id":"w17968193","tags":{"highway":"residential","name":"French St"},"nodes":["n185970906","n185982877","n185967774","n185985823","n185979974"]},"w203972939":{"id":"w203972939","tags":{"highway":"path"},"nodes":["n2139858965","n2139858966"]},"w203988289":{"id":"w203988289","tags":{"area":"yes","natural":"water"},"nodes":["n2140006367","n2140006368","n2140006419","n2140006369","n2140006417","n2140006370","n2140006372","n2140006374","n2140006376","n2140006378","n2140006380","n2140006382","n2140006389","n2140006391","n2140006393","n2140006367"]},"w208640157":{"id":"w208640157","tags":{"area":"yes","natural":"wetland"},"nodes":["n1819849029","n2189123275","n2189123278","n2189123280","n2189123282","n2189123370","n2189123285","n2189123287","n2189123289","n2189123291","n2189123293","n2189123295","n2189123297","n2189123300","n2189123301","n2189123303","n2189123312","n2189123314","n2189123315","n2189123316","n2189123317","n2189123318","n2189123319","n2189123320","n2189123321","n2189123322","n2189123323","n2189123324","n2189123326","n2189123328","n2189123330","n2189123333","n2189123336","n2189123339","n2189123342","n2189123345","n2189123348","n2189123351","n2189123353","n2189123355","n2189123357","n2189123359","n2189123361","n2189123363","n2189123365","n2189123368","n1819849029"]},"w17966281":{"id":"w17966281","tags":{"highway":"residential","name":"Pealer St"},"nodes":["n185975911","n185975913","n185975915","n1475284042","n185975917","n185975919","n185975925","n185970909","n1475284013","n1475283980","n185975928","n185967775","n185975930","n185975932","n185975934","n2139795809","n2139795810"]},"w17965353":{"id":"w17965353","tags":{"highway":"residential","name":"Yauney St"},"nodes":["n185966958","n185966959","n185966960","n185966962"]},"w203972938":{"id":"w203972938","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858964","n2139858965","n2139858963","n2139858962","n2139858961","n2139858960","n2139858959","n2139858958","n2139858957","n2139858956","n2139858955","n2139858954","n2139858953","n2139858952","n2139858951"]},"n354002665":{"id":"n354002665","loc":[-85.6366599,41.9444923],"tags":{"name":"Memory Isle","place":"island"}},"n354031301":{"id":"n354031301","loc":[-85.635,41.9463889],"tags":{"amenity":"post_office","name":"Three Rivers Post Office"}},"n185963454":{"id":"n185963454","loc":[-85.633686,41.946072]},"n185963455":{"id":"n185963455","loc":[-85.633815,41.946131]},"n185963456":{"id":"n185963456","loc":[-85.633951,41.946174]},"n185978375":{"id":"n185978375","loc":[-85.634385,41.94559]},"n185978377":{"id":"n185978377","loc":[-85.634544,41.945725]},"n185978379":{"id":"n185978379","loc":[-85.634573,41.945764]},"n185978381":{"id":"n185978381","loc":[-85.634616,41.945849]},"n185978383":{"id":"n185978383","loc":[-85.634629,41.945893]},"n185984011":{"id":"n185984011","loc":[-85.636058,41.946201]},"n185984013":{"id":"n185984013","loc":[-85.636112,41.946366]},"n185984015":{"id":"n185984015","loc":[-85.636143,41.946551]},"n185988237":{"id":"n185988237","loc":[-85.6354162,41.946044]},"n185988969":{"id":"n185988969","loc":[-85.635374,41.945325]},"n185988971":{"id":"n185988971","loc":[-85.635643,41.945585]},"n185988972":{"id":"n185988972","loc":[-85.635853,41.94586]},"n1475283992":{"id":"n1475283992","loc":[-85.6372968,41.9459007]},"n1475284011":{"id":"n1475284011","loc":[-85.6359415,41.9459797]},"n1475284019":{"id":"n1475284019","loc":[-85.6364433,41.9460423]},"n185984009":{"id":"n185984009","loc":[-85.6360524,41.9460485]},"n185988239":{"id":"n185988239","loc":[-85.6358187,41.9460423]},"n185988243":{"id":"n185988243","loc":[-85.6366156,41.9460282]},"n185988244":{"id":"n185988244","loc":[-85.6368316,41.9460046]},"n185988245":{"id":"n185988245","loc":[-85.6370133,41.9459704]},"n185988241":{"id":"n185988241","loc":[-85.636291,41.9460461]},"n185964976":{"id":"n185964976","loc":[-85.633923,41.9434157]},"n185964980":{"id":"n185964980","loc":[-85.6333656,41.9437293]},"n185978388":{"id":"n185978388","loc":[-85.6346449,41.9460571]},"n1819858504":{"id":"n1819858504","loc":[-85.6365343,41.9447926]},"n1819858506":{"id":"n1819858506","loc":[-85.6370546,41.9451882]},"n1819858516":{"id":"n1819858516","loc":[-85.6358369,41.9444654]},"n1819858519":{"id":"n1819858519","loc":[-85.6361534,41.9446176]},"n1819858525":{"id":"n1819858525","loc":[-85.6368025,41.9449442]},"n1819858527":{"id":"n1819858527","loc":[-85.6334199,41.9457495]},"n185963452":{"id":"n185963452","loc":[-85.633564,41.9458519]},"n185963453":{"id":"n185963453","loc":[-85.6336152,41.9459804]},"n185963451":{"id":"n185963451","loc":[-85.6332888,41.9456871]},"n2130304152":{"id":"n2130304152","loc":[-85.6359466,41.9454599]},"n2130304153":{"id":"n2130304153","loc":[-85.6362773,41.9452683]},"n2130304154":{"id":"n2130304154","loc":[-85.6352028,41.9442868]},"n2130304155":{"id":"n2130304155","loc":[-85.6348756,41.9444769]},"n2130304156":{"id":"n2130304156","loc":[-85.6349723,41.9444207]},"n2130304157":{"id":"n2130304157","loc":[-85.6338698,41.9434443]},"n2130304158":{"id":"n2130304158","loc":[-85.635094,41.9451026]},"n2130304160":{"id":"n2130304160","loc":[-85.6353716,41.9449322]},"n2130304162":{"id":"n2130304162","loc":[-85.6365942,41.9459352]},"n2130304163":{"id":"n2130304163","loc":[-85.6369006,41.9457469]},"n2130304164":{"id":"n2130304164","loc":[-85.6363292,41.9452278]},"n2130304165":{"id":"n2130304165","loc":[-85.6360248,41.9454175]},"n2139824683":{"id":"n2139824683","loc":[-85.6339825,41.9446441]},"n2139824689":{"id":"n2139824689","loc":[-85.6340437,41.9446925]},"n2139824702":{"id":"n2139824702","loc":[-85.6340961,41.9447551]},"n2139824705":{"id":"n2139824705","loc":[-85.6337467,41.944809]},"n2139824707":{"id":"n2139824707","loc":[-85.6341598,41.9448129]},"n2139824710":{"id":"n2139824710","loc":[-85.6342771,41.9448223]},"n2139824712":{"id":"n2139824712","loc":[-85.6346058,41.944841]},"n2139824713":{"id":"n2139824713","loc":[-85.633808,41.9448574]},"n2139824714":{"id":"n2139824714","loc":[-85.6340889,41.9448589]},"n2139824716":{"id":"n2139824716","loc":[-85.6343335,41.944871]},"n2139824717":{"id":"n2139824717","loc":[-85.6343341,41.9448717]},"n2139824720":{"id":"n2139824720","loc":[-85.6338757,41.9449069]},"n2139824721":{"id":"n2139824721","loc":[-85.6341445,41.9449071]},"n2139824724":{"id":"n2139824724","loc":[-85.6334787,41.9449262]},"n2139824726":{"id":"n2139824726","loc":[-85.6347119,41.9449332]},"n2139824727":{"id":"n2139824727","loc":[-85.6347175,41.9449418]},"n2139824728":{"id":"n2139824728","loc":[-85.6344284,41.9449538]},"n2139824729":{"id":"n2139824729","loc":[-85.6339339,41.9449573]},"n2139824730":{"id":"n2139824730","loc":[-85.6339179,41.9449682]},"n2139824732":{"id":"n2139824732","loc":[-85.6335472,41.9449895]},"n2139824733":{"id":"n2139824733","loc":[-85.6339736,41.9450164]},"n2139824735":{"id":"n2139824735","loc":[-85.6336034,41.9450415]},"n2139824736":{"id":"n2139824736","loc":[-85.6348317,41.945043]},"n2139824737":{"id":"n2139824737","loc":[-85.63403,41.9450651]},"n2139824738":{"id":"n2139824738","loc":[-85.6336611,41.9450949]},"n2139824740":{"id":"n2139824740","loc":[-85.6336582,41.9450966]},"n2139824744":{"id":"n2139824744","loc":[-85.6331702,41.9451107]},"n2139824745":{"id":"n2139824745","loc":[-85.6333388,41.9451142]},"n2139824746":{"id":"n2139824746","loc":[-85.6337131,41.9451341]},"n2139824747":{"id":"n2139824747","loc":[-85.6337021,41.9451372]},"n2139824748":{"id":"n2139824748","loc":[-85.6341244,41.9451472]},"n2139824749":{"id":"n2139824749","loc":[-85.6333952,41.945166]},"n2139824750":{"id":"n2139824750","loc":[-85.633395,41.9451661]},"n2139824751":{"id":"n2139824751","loc":[-85.6346258,41.9451725]},"n2139824752":{"id":"n2139824752","loc":[-85.6332387,41.9451741]},"n2139824753":{"id":"n2139824753","loc":[-85.6346901,41.9451853]},"n2139824754":{"id":"n2139824754","loc":[-85.6346611,41.9452035]},"n2139824755":{"id":"n2139824755","loc":[-85.6346574,41.9452059]},"n2139824756":{"id":"n2139824756","loc":[-85.6345611,41.9452133]},"n2139824757":{"id":"n2139824757","loc":[-85.633453,41.9452194]},"n2139824758":{"id":"n2139824758","loc":[-85.6335508,41.9452283]},"n2139824759":{"id":"n2139824759","loc":[-85.6347424,41.9452312]},"n2139824760":{"id":"n2139824760","loc":[-85.6342305,41.9452395]},"n2139824761":{"id":"n2139824761","loc":[-85.6342319,41.9452449]},"n2139824762":{"id":"n2139824762","loc":[-85.6334969,41.94526]},"n2139824763":{"id":"n2139824763","loc":[-85.63468,41.9452706]},"n2139824764":{"id":"n2139824764","loc":[-85.6346772,41.9452724]},"n2139824765":{"id":"n2139824765","loc":[-85.6338611,41.9452763]},"n2139824766":{"id":"n2139824766","loc":[-85.6347811,41.9452939]},"n2139824767":{"id":"n2139824767","loc":[-85.6347375,41.9453211]},"n2139824768":{"id":"n2139824768","loc":[-85.6339171,41.9453301]},"n2139824769":{"id":"n2139824769","loc":[-85.6348307,41.9453377]},"n2139824770":{"id":"n2139824770","loc":[-85.6347067,41.9453405]},"n2139824771":{"id":"n2139824771","loc":[-85.6343461,41.9453461]},"n2139824772":{"id":"n2139824772","loc":[-85.6343481,41.9453475]},"n2139824773":{"id":"n2139824773","loc":[-85.634805,41.9453538]},"n2139824774":{"id":"n2139824774","loc":[-85.6336997,41.9453692]},"n2139824775":{"id":"n2139824775","loc":[-85.6339709,41.9453818]},"n2139824776":{"id":"n2139824776","loc":[-85.6336229,41.9454134]},"n2139824777":{"id":"n2139824777","loc":[-85.6349022,41.9454141]},"n2139824778":{"id":"n2139824778","loc":[-85.6348854,41.9454246]},"n2139824779":{"id":"n2139824779","loc":[-85.6340286,41.9454373]},"n2139824780":{"id":"n2139824780","loc":[-85.6336963,41.9454572]},"n2139824781":{"id":"n2139824781","loc":[-85.6336789,41.9454672]},"n2139824782":{"id":"n2139824782","loc":[-85.6344933,41.945475]},"n2139824783":{"id":"n2139824783","loc":[-85.6340854,41.9454918]},"n2139824784":{"id":"n2139824784","loc":[-85.6350036,41.9455034]},"n2139824785":{"id":"n2139824785","loc":[-85.6337501,41.9455089]},"n2139824786":{"id":"n2139824786","loc":[-85.6337497,41.9455091]},"n2139824787":{"id":"n2139824787","loc":[-85.6345425,41.9455186]},"n2139824788":{"id":"n2139824788","loc":[-85.6341459,41.9455372]},"n2139824789":{"id":"n2139824789","loc":[-85.6341376,41.945542]},"n2139824790":{"id":"n2139824790","loc":[-85.6338394,41.9455462]},"n2139824791":{"id":"n2139824791","loc":[-85.6349171,41.9455588]},"n2139824792":{"id":"n2139824792","loc":[-85.6338074,41.9455646]},"n2139824793":{"id":"n2139824793","loc":[-85.6346229,41.9455894]},"n2139824794":{"id":"n2139824794","loc":[-85.6338983,41.9455995]},"n2139824795":{"id":"n2139824795","loc":[-85.6338962,41.9456007]},"n2139824796":{"id":"n2139824796","loc":[-85.6342475,41.9456348]},"n2139824797":{"id":"n2139824797","loc":[-85.6339505,41.9456497]},"n2139824798":{"id":"n2139824798","loc":[-85.6347243,41.9456788]},"n2139824799":{"id":"n2139824799","loc":[-85.635057,41.9456831]},"n2139824800":{"id":"n2139824800","loc":[-85.635287,41.9457056]},"n2139824801":{"id":"n2139824801","loc":[-85.6350753,41.9457068]},"n2139824802":{"id":"n2139824802","loc":[-85.6347753,41.9457252]},"n2139824803":{"id":"n2139824803","loc":[-85.6340521,41.9457473]},"n2139824804":{"id":"n2139824804","loc":[-85.6352875,41.9457611]},"n2139824805":{"id":"n2139824805","loc":[-85.6352941,41.9457611]},"n2139824806":{"id":"n2139824806","loc":[-85.6350758,41.9457623]},"n2139824807":{"id":"n2139824807","loc":[-85.6348194,41.9457638]},"n2139824808":{"id":"n2139824808","loc":[-85.635296,41.9459428]},"n2139824809":{"id":"n2139824809","loc":[-85.6348212,41.9459455]},"n2139832635":{"id":"n2139832635","loc":[-85.6354612,41.9448791]},"n2139832636":{"id":"n2139832636","loc":[-85.6360241,41.9453844]},"n2139832637":{"id":"n2139832637","loc":[-85.6361452,41.9453121]},"n2139832639":{"id":"n2139832639","loc":[-85.6355997,41.944797]},"n2139832641":{"id":"n2139832641","loc":[-85.6351346,41.9443541]},"n2139832647":{"id":"n2139832647","loc":[-85.6329883,41.9453692]},"n2139832653":{"id":"n2139832653","loc":[-85.6333643,41.9456293]},"n2139832663":{"id":"n2139832663","loc":[-85.6335394,41.9455339]},"n2139832665":{"id":"n2139832665","loc":[-85.6332375,41.9452476]},"n2139832667":{"id":"n2139832667","loc":[-85.6331664,41.9452161]},"n2139832669":{"id":"n2139832669","loc":[-85.6331144,41.9451875]},"n2139832671":{"id":"n2139832671","loc":[-85.6330779,41.9451274]},"n2139832673":{"id":"n2139832673","loc":[-85.6330664,41.9450802]},"n2139832678":{"id":"n2139832678","loc":[-85.6332218,41.9453585]},"n2139832686":{"id":"n2139832686","loc":[-85.6334246,41.945541]},"n2139832691":{"id":"n2139832691","loc":[-85.6329898,41.9454997]},"n2139832693":{"id":"n2139832693","loc":[-85.6343554,41.9443274]},"n2139832694":{"id":"n2139832694","loc":[-85.6336339,41.9437089]},"n2139832696":{"id":"n2139832696","loc":[-85.633532,41.9437708]},"n2139832697":{"id":"n2139832697","loc":[-85.6338316,41.9440868]},"n2139832698":{"id":"n2139832698","loc":[-85.6342258,41.9444141]},"n2139832699":{"id":"n2139832699","loc":[-85.6339164,41.9442166]},"n2139832700":{"id":"n2139832700","loc":[-85.6341389,41.944384]},"n2139832701":{"id":"n2139832701","loc":[-85.634235,41.9443259]},"n2139832702":{"id":"n2139832702","loc":[-85.633613,41.9437875]},"n2139832703":{"id":"n2139832703","loc":[-85.633915,41.9436132]},"n2139832704":{"id":"n2139832704","loc":[-85.6340019,41.9435613]},"n2139832706":{"id":"n2139832706","loc":[-85.6343197,41.9438427]},"n2139832708":{"id":"n2139832708","loc":[-85.6342361,41.9438936]},"n2139832709":{"id":"n2139832709","loc":[-85.6353839,41.9460401]},"n2139832710":{"id":"n2139832710","loc":[-85.6354032,41.9456763]},"n2139832711":{"id":"n2139832711","loc":[-85.6356839,41.9459252]},"n2139832712":{"id":"n2139832712","loc":[-85.6356109,41.945735]},"n2139832713":{"id":"n2139832713","loc":[-85.6353997,41.9457421]},"n2139832714":{"id":"n2139832714","loc":[-85.6353895,41.9459347]},"n2139832715":{"id":"n2139832715","loc":[-85.6334777,41.9436628]},"n2139832716":{"id":"n2139832716","loc":[-85.6333137,41.9435382]},"n2139832717":{"id":"n2139832717","loc":[-85.6330938,41.9435406]},"n2139832721":{"id":"n2139832721","loc":[-85.6333023,41.9434922]},"n2139832722":{"id":"n2139832722","loc":[-85.6330466,41.943623]},"n2139832723":{"id":"n2139832723","loc":[-85.6332746,41.9435624]},"n2139832724":{"id":"n2139832724","loc":[-85.6333511,41.9435176]},"n2139832725":{"id":"n2139832725","loc":[-85.6332241,41.9434001]},"n2139832726":{"id":"n2139832726","loc":[-85.6332355,41.9433686]},"n2139870373":{"id":"n2139870373","loc":[-85.6351783,41.9439117]},"n2139870374":{"id":"n2139870374","loc":[-85.6351431,41.9439217]},"n2139870375":{"id":"n2139870375","loc":[-85.6348853,41.9439117]},"n2139870376":{"id":"n2139870376","loc":[-85.6348317,41.9439105]},"n2139870377":{"id":"n2139870377","loc":[-85.6346384,41.944007]},"n2139870378":{"id":"n2139870378","loc":[-85.6345563,41.9440523]},"n2140006403":{"id":"n2140006403","loc":[-85.6359942,41.9450097]},"n2140006405":{"id":"n2140006405","loc":[-85.6363884,41.9446079]},"n2140006407":{"id":"n2140006407","loc":[-85.6362148,41.9447874]},"n2140006409":{"id":"n2140006409","loc":[-85.6379476,41.9445869]},"n2140006411":{"id":"n2140006411","loc":[-85.6378485,41.9445674]},"n2140006413":{"id":"n2140006413","loc":[-85.6378952,41.9444547]},"n2140006415":{"id":"n2140006415","loc":[-85.6379962,41.944477]},"n2140006421":{"id":"n2140006421","loc":[-85.6355248,41.9433702]},"n2140006423":{"id":"n2140006423","loc":[-85.6378471,41.9439233]},"n2140006425":{"id":"n2140006425","loc":[-85.6378913,41.9441238]},"n2140006426":{"id":"n2140006426","loc":[-85.6381674,41.9442289]},"n2140006427":{"id":"n2140006427","loc":[-85.6382359,41.9440975]},"n2140006428":{"id":"n2140006428","loc":[-85.6382071,41.9440252]},"n2140006429":{"id":"n2140006429","loc":[-85.6381409,41.9439973]},"n2140006430":{"id":"n2140006430","loc":[-85.6380569,41.9440153]},"n2140006433":{"id":"n2140006433","loc":[-85.6379071,41.9442467]},"n2140006435":{"id":"n2140006435","loc":[-85.6381634,41.9443125]},"n2140006436":{"id":"n2140006436","loc":[-85.6382407,41.944301]},"n2140006438":{"id":"n2140006438","loc":[-85.6382761,41.9442188]},"n2140006439":{"id":"n2140006439","loc":[-85.6382429,41.9441761]},"n2140006440":{"id":"n2140006440","loc":[-85.6382016,41.9441632]},"n2140006441":{"id":"n2140006441","loc":[-85.6378185,41.9439835]},"n2166205688":{"id":"n2166205688","loc":[-85.6349963,41.9444392]},"n2168544780":{"id":"n2168544780","loc":[-85.633944,41.945807]},"n2168544781":{"id":"n2168544781","loc":[-85.6340783,41.9458621]},"n2168544782":{"id":"n2168544782","loc":[-85.6338184,41.9457548]},"n2168544783":{"id":"n2168544783","loc":[-85.6339925,41.9459777]},"n2168544784":{"id":"n2168544784","loc":[-85.6337317,41.9458698]},"n2168544785":{"id":"n2168544785","loc":[-85.6337297,41.9460042]},"n2168544786":{"id":"n2168544786","loc":[-85.633919,41.9460797]},"n2168544787":{"id":"n2168544787","loc":[-85.6338672,41.9459263]},"n2168544788":{"id":"n2168544788","loc":[-85.6338246,41.9459853]},"n2168544789":{"id":"n2168544789","loc":[-85.6337615,41.9459601]},"n2168544790":{"id":"n2168544790","loc":[-85.6342079,41.9460399]},"n2168544791":{"id":"n2168544791","loc":[-85.6343346,41.9458503]},"n2168544792":{"id":"n2168544792","loc":[-85.6343759,41.9458116]},"n2168544793":{"id":"n2168544793","loc":[-85.6344394,41.9458109]},"n2168544795":{"id":"n2168544795","loc":[-85.6344827,41.945851]},"n2168544797":{"id":"n2168544797","loc":[-85.6344807,41.945969]},"n2168544798":{"id":"n2168544798","loc":[-85.6344404,41.9459697]},"n2168544799":{"id":"n2168544799","loc":[-85.6344413,41.9460333]},"n2168544800":{"id":"n2168544800","loc":[-85.6342173,41.9460705]},"n2168544801":{"id":"n2168544801","loc":[-85.6342162,41.9460392]},"n2168544802":{"id":"n2168544802","loc":[-85.6344251,41.9460351]},"n2168544805":{"id":"n2168544805","loc":[-85.6344257,41.9460507]},"n2168544807":{"id":"n2168544807","loc":[-85.6344721,41.9460498]},"n2168544809":{"id":"n2168544809","loc":[-85.6344754,41.9461427]},"n2168544811":{"id":"n2168544811","loc":[-85.6344311,41.9461435]},"n2168544813":{"id":"n2168544813","loc":[-85.6344317,41.9461592]},"n2168544815":{"id":"n2168544815","loc":[-85.6343708,41.9461604]},"n2168544817":{"id":"n2168544817","loc":[-85.6343715,41.9461786]},"n2168544819":{"id":"n2168544819","loc":[-85.6343229,41.9461795]},"n2168544821":{"id":"n2168544821","loc":[-85.6343222,41.9461606]},"n2168544823":{"id":"n2168544823","loc":[-85.6342476,41.9461621]},"n2168544825":{"id":"n2168544825","loc":[-85.6342444,41.94607]},"n2168544827":{"id":"n2168544827","loc":[-85.634138,41.9461632]},"n2168544829":{"id":"n2168544829","loc":[-85.6342016,41.9460703]},"n2168544830":{"id":"n2168544830","loc":[-85.6332929,41.9463092]},"n2168544831":{"id":"n2168544831","loc":[-85.633122,41.946239]},"n2168544832":{"id":"n2168544832","loc":[-85.6332954,41.9460055]},"n2168544833":{"id":"n2168544833","loc":[-85.6333954,41.9460466]},"n2168544834":{"id":"n2168544834","loc":[-85.6334044,41.9460345]},"n2168544835":{"id":"n2168544835","loc":[-85.6334594,41.9460571]},"n2168544836":{"id":"n2168544836","loc":[-85.6333871,41.9461544]},"n2168544837":{"id":"n2168544837","loc":[-85.633403,41.9461609]},"n2168544838":{"id":"n2168544838","loc":[-85.6341683,41.9464167]},"n2168544839":{"id":"n2168544839","loc":[-85.6341711,41.9463411]},"n2168544840":{"id":"n2168544840","loc":[-85.6344471,41.9463469]},"n2168544841":{"id":"n2168544841","loc":[-85.6344441,41.9464243]},"n2168544842":{"id":"n2168544842","loc":[-85.6343622,41.9464226]},"n2168544843":{"id":"n2168544843","loc":[-85.6343593,41.9464989]},"n2168544844":{"id":"n2168544844","loc":[-85.6342812,41.9464973]},"n2168544845":{"id":"n2168544845","loc":[-85.634283,41.9464504]},"n2168544846":{"id":"n2168544846","loc":[-85.6342609,41.9464499]},"n2168544847":{"id":"n2168544847","loc":[-85.6342621,41.9464187]},"n2168544848":{"id":"n2168544848","loc":[-85.6348414,41.9463396]},"n2168544849":{"id":"n2168544849","loc":[-85.6348387,41.9461872]},"n2168544850":{"id":"n2168544850","loc":[-85.6351186,41.9461844]},"n2168544851":{"id":"n2168544851","loc":[-85.635119,41.9462112]},"n2168544852":{"id":"n2168544852","loc":[-85.6351918,41.9462104]},"n2168544853":{"id":"n2168544853","loc":[-85.6351944,41.9463515]},"n2168544854":{"id":"n2168544854","loc":[-85.6351049,41.9463524]},"n2168544855":{"id":"n2168544855","loc":[-85.6351046,41.946337]},"n2189153180":{"id":"n2189153180","loc":[-85.6340369,41.9469572]},"n2189153181":{"id":"n2189153181","loc":[-85.6342531,41.946953]},"n2189153183":{"id":"n2189153183","loc":[-85.6348115,41.9465468]},"n2189153184":{"id":"n2189153184","loc":[-85.6348105,41.9464569]},"n2189153185":{"id":"n2189153185","loc":[-85.6351431,41.9464549]},"n2189153186":{"id":"n2189153186","loc":[-85.6351441,41.9465448]},"n2189153187":{"id":"n2189153187","loc":[-85.6350077,41.9465456]},"n2189153188":{"id":"n2189153188","loc":[-85.635008,41.9465721]},"n2189153189":{"id":"n2189153189","loc":[-85.6348965,41.9465727]},"n2189153190":{"id":"n2189153190","loc":[-85.6348962,41.9465463]},"n2189153191":{"id":"n2189153191","loc":[-85.6348963,41.9471586]},"n2189153192":{"id":"n2189153192","loc":[-85.6348944,41.947032]},"n2189153193":{"id":"n2189153193","loc":[-85.6350241,41.947031]},"n2189153194":{"id":"n2189153194","loc":[-85.635026,41.9471575]},"n2189153195":{"id":"n2189153195","loc":[-85.6352328,41.9471053]},"n2189153196":{"id":"n2189153196","loc":[-85.6352359,41.9469906]},"n2189153197":{"id":"n2189153197","loc":[-85.6353694,41.9469925]},"n2189153198":{"id":"n2189153198","loc":[-85.6353664,41.9471072]},"n2189153199":{"id":"n2189153199","loc":[-85.6348241,41.9469287]},"n2189153200":{"id":"n2189153200","loc":[-85.6348248,41.9468185]},"n2189153201":{"id":"n2189153201","loc":[-85.6351199,41.9468195]},"n2189153202":{"id":"n2189153202","loc":[-85.6351192,41.9469298]},"n2189153203":{"id":"n2189153203","loc":[-85.6347965,41.9468057]},"n2189153204":{"id":"n2189153204","loc":[-85.634792,41.9466044]},"n2189153205":{"id":"n2189153205","loc":[-85.6349483,41.9466025]},"n2189153206":{"id":"n2189153206","loc":[-85.6349493,41.9466448]},"n2189153207":{"id":"n2189153207","loc":[-85.6349753,41.9466445]},"n2189153208":{"id":"n2189153208","loc":[-85.6349743,41.9465995]},"n2189153209":{"id":"n2189153209","loc":[-85.6351173,41.9465977]},"n2189153210":{"id":"n2189153210","loc":[-85.6351219,41.9468015]},"n2189153211":{"id":"n2189153211","loc":[-85.6349806,41.9468032]},"n2189153212":{"id":"n2189153212","loc":[-85.6349794,41.9467519]},"n2189153213":{"id":"n2189153213","loc":[-85.6349521,41.9467523]},"n2189153214":{"id":"n2189153214","loc":[-85.6349532,41.9468037]},"n2189153215":{"id":"n2189153215","loc":[-85.6346302,41.9468381]},"n2189153216":{"id":"n2189153216","loc":[-85.6343028,41.9468449]},"n2189153217":{"id":"n2189153217","loc":[-85.6342006,41.9468297]},"n2189153218":{"id":"n2189153218","loc":[-85.6336698,41.9465918]},"n2189153219":{"id":"n2189153219","loc":[-85.6344663,41.9466639]},"n2189153220":{"id":"n2189153220","loc":[-85.6344639,41.9466015]},"n2189153221":{"id":"n2189153221","loc":[-85.6342283,41.9466065]},"n2189153222":{"id":"n2189153222","loc":[-85.6342303,41.9466587]},"n2189153223":{"id":"n2189153223","loc":[-85.6342843,41.9466575]},"n2189153224":{"id":"n2189153224","loc":[-85.6342851,41.9466794]},"n2189153225":{"id":"n2189153225","loc":[-85.6343475,41.9466781]},"n2189153226":{"id":"n2189153226","loc":[-85.634347,41.9466664]},"n2189153227":{"id":"n2189153227","loc":[-85.6354428,41.9470148]},"n2189153228":{"id":"n2189153228","loc":[-85.6354432,41.9468005]},"n2189153229":{"id":"n2189153229","loc":[-85.6360277,41.9468011]},"n2189153230":{"id":"n2189153230","loc":[-85.6360273,41.9470154]},"n2189153231":{"id":"n2189153231","loc":[-85.6354565,41.9465823]},"n2189153232":{"id":"n2189153232","loc":[-85.6354496,41.946218]},"n2189153233":{"id":"n2189153233","loc":[-85.6356355,41.9465788]},"n2189153234":{"id":"n2189153234","loc":[-85.6357155,41.9468008]},"n2189153235":{"id":"n2189153235","loc":[-85.6359539,41.9467969]},"n2189153236":{"id":"n2189153236","loc":[-85.6359561,41.9463036]},"n2189153237":{"id":"n2189153237","loc":[-85.6360129,41.9464793]},"n2189153238":{"id":"n2189153238","loc":[-85.6360152,41.9463898]},"n2189153239":{"id":"n2189153239","loc":[-85.6359607,41.9464928]},"n2189153240":{"id":"n2189153240","loc":[-85.6356903,41.9462227]},"n2189153242":{"id":"n2189153242","loc":[-85.6354163,41.946142]},"n2189153243":{"id":"n2189153243","loc":[-85.6357546,41.9462214]},"n2189153244":{"id":"n2189153244","loc":[-85.6357937,41.9462542]},"n2189153245":{"id":"n2189153245","loc":[-85.6358723,41.9467048]},"n2189153246":{"id":"n2189153246","loc":[-85.6361494,41.946757]},"n2189153247":{"id":"n2189153247","loc":[-85.6354173,41.9469082]},"n2189153248":{"id":"n2189153248","loc":[-85.635443,41.9469079]},"n2189153249":{"id":"n2189153249","loc":[-85.6360275,41.9469093]},"n2189153250":{"id":"n2189153250","loc":[-85.6361542,41.946915]},"n2189153251":{"id":"n2189153251","loc":[-85.6358654,41.9464843]},"n2189153252":{"id":"n2189153252","loc":[-85.6359549,41.9467499]},"n2189153253":{"id":"n2189153253","loc":[-85.6357172,41.9466335]},"n2189153254":{"id":"n2189153254","loc":[-85.6355644,41.9461768]},"n2189153255":{"id":"n2189153255","loc":[-85.6355655,41.946528]},"n2189153256":{"id":"n2189153256","loc":[-85.6357055,41.9465971]},"n2189153257":{"id":"n2189153257","loc":[-85.635869,41.9465971]},"n2189153259":{"id":"n2189153259","loc":[-85.6354561,41.9470278]},"n2189153260":{"id":"n2189153260","loc":[-85.6357961,41.9470233]},"n2189153261":{"id":"n2189153261","loc":[-85.6357977,41.9470907]},"n2189153262":{"id":"n2189153262","loc":[-85.6357297,41.9470916]},"n2189153263":{"id":"n2189153263","loc":[-85.635733,41.947233]},"n2189153264":{"id":"n2189153264","loc":[-85.6362674,41.9468637]},"n2189153265":{"id":"n2189153265","loc":[-85.6362646,41.9467047]},"n2189153266":{"id":"n2189153266","loc":[-85.6363267,41.9467047]},"n2189153267":{"id":"n2189153267","loc":[-85.6362633,41.9465848]},"n2189153268":{"id":"n2189153268","loc":[-85.6363805,41.9465468]},"n2189153269":{"id":"n2189153269","loc":[-85.6364604,41.9466842]},"n2189153270":{"id":"n2189153270","loc":[-85.6364604,41.9468647]},"n2199109756":{"id":"n2199109756","loc":[-85.6337134,41.9471841]},"n2199109757":{"id":"n2199109757","loc":[-85.6336514,41.94716]},"n2199109758":{"id":"n2199109758","loc":[-85.6337043,41.9470847]},"n2199109759":{"id":"n2199109759","loc":[-85.6335997,41.9470441]},"n2199109760":{"id":"n2199109760","loc":[-85.6335064,41.9471771]},"n185960195":{"id":"n185960195","loc":[-85.6295992,41.9524346]},"n185960796":{"id":"n185960796","loc":[-85.634723,41.953681]},"n185961396":{"id":"n185961396","loc":[-85.634767,41.959009]},"n185962625":{"id":"n185962625","loc":[-85.635175,41.97201]},"n185964982":{"id":"n185964982","loc":[-85.632799,41.9440543]},"n185965289":{"id":"n185965289","loc":[-85.634621,41.947323]},"n185965291":{"id":"n185965291","loc":[-85.636166,41.947296]},"n185965399":{"id":"n185965399","loc":[-85.634776,41.959834]},"n185966937":{"id":"n185966937","loc":[-85.633183,41.947315]},"n185966948":{"id":"n185966948","loc":[-85.626406,41.957188]},"n185967422":{"id":"n185967422","loc":[-85.6320229,41.9490123]},"n185967917":{"id":"n185967917","loc":[-85.634763,41.958292]},"n185967918":{"id":"n185967918","loc":[-85.636271,41.958311]},"n185968100":{"id":"n185968100","loc":[-85.630835,41.950656]},"n185970515":{"id":"n185970515","loc":[-85.634832,41.963866]},"n185971578":{"id":"n185971578","loc":[-85.634641,41.948627]},"n185971580":{"id":"n185971580","loc":[-85.6361818,41.9486135]},"n185971631":{"id":"n185971631","loc":[-85.634729,41.954667]},"n185971632":{"id":"n185971632","loc":[-85.636236,41.954656]},"n185972155":{"id":"n185972155","loc":[-85.623333,41.961987]},"n185974583":{"id":"n185974583","loc":[-85.634686,41.951158]},"n185974585":{"id":"n185974585","loc":[-85.6362059,41.9511457]},"n185975064":{"id":"n185975064","loc":[-85.636218,41.953667]},"n185975735":{"id":"n185975735","loc":[-85.634923,41.969269]},"n185978390":{"id":"n185978390","loc":[-85.634668,41.949875]},"n185978392":{"id":"n185978392","loc":[-85.634686,41.952415]},"n185978394":{"id":"n185978394","loc":[-85.634726,41.955921]},"n185978399":{"id":"n185978399","loc":[-85.6347861,41.9606613]},"n185978402":{"id":"n185978402","loc":[-85.634806,41.961485]},"n185978406":{"id":"n185978406","loc":[-85.6348298,41.964783]},"n185978410":{"id":"n185978410","loc":[-85.6348766,41.9677088]},"n185978414":{"id":"n185978414","loc":[-85.634938,41.971566]},"n185978415":{"id":"n185978415","loc":[-85.634942,41.971611]},"n185978417":{"id":"n185978417","loc":[-85.634952,41.971655]},"n185978419":{"id":"n185978419","loc":[-85.634989,41.971741]},"n185978420":{"id":"n185978420","loc":[-85.635063,41.971864]},"n185978787":{"id":"n185978787","loc":[-85.627936,41.954693]},"n185978790":{"id":"n185978790","loc":[-85.626832,41.954677]},"n185978967":{"id":"n185978967","loc":[-85.632278,41.948613]},"n185980735":{"id":"n185980735","loc":[-85.628639,41.953725]},"n185982163":{"id":"n185982163","loc":[-85.636233,41.952398]},"n185982193":{"id":"n185982193","loc":[-85.6313855,41.9499125]},"n185982195":{"id":"n185982195","loc":[-85.6304857,41.9511945]},"n185982196":{"id":"n185982196","loc":[-85.626336,41.957291]},"n185982197":{"id":"n185982197","loc":[-85.625578,41.958664]},"n185982198":{"id":"n185982198","loc":[-85.624619,41.960145]},"n185982200":{"id":"n185982200","loc":[-85.624494,41.960338]},"n185984017":{"id":"n185984017","loc":[-85.636163,41.947382]},"n185984020":{"id":"n185984020","loc":[-85.636188,41.9498803]},"n185984022":{"id":"n185984022","loc":[-85.636276,41.955919]},"n185984024":{"id":"n185984024","loc":[-85.636279,41.956901]},"n185988036":{"id":"n185988036","loc":[-85.631422,41.948294]},"n185988867":{"id":"n185988867","loc":[-85.63102,41.948805]},"n185988869":{"id":"n185988869","loc":[-85.630773,41.949209]},"n185988871":{"id":"n185988871","loc":[-85.63005,41.95016]},"n185988872":{"id":"n185988872","loc":[-85.629423,41.951016]},"n185988873":{"id":"n185988873","loc":[-85.629252,41.951256]},"n185988875":{"id":"n185988875","loc":[-85.629126,41.951489]},"n185988877":{"id":"n185988877","loc":[-85.628991,41.951704]},"n185988878":{"id":"n185988878","loc":[-85.628689,41.952112]},"n185988879":{"id":"n185988879","loc":[-85.628313,41.952666]},"n185988880":{"id":"n185988880","loc":[-85.627687,41.953529]},"n185988882":{"id":"n185988882","loc":[-85.627394,41.953947]},"n185988884":{"id":"n185988884","loc":[-85.627287,41.954128]},"n1819858502":{"id":"n1819858502","loc":[-85.6328435,41.9455473]},"n1819858510":{"id":"n1819858510","loc":[-85.6324841,41.9453438]},"n1819858515":{"id":"n1819858515","loc":[-85.6318511,41.9446409]},"n1819858520":{"id":"n1819858520","loc":[-85.6326558,41.9454708]},"n1819858522":{"id":"n1819858522","loc":[-85.6319048,41.9447407]},"n1819858524":{"id":"n1819858524","loc":[-85.6317718,41.9443666]},"n1819858530":{"id":"n1819858530","loc":[-85.632055,41.9449128]},"n2139795768":{"id":"n2139795768","loc":[-85.6243023,41.9606102]},"n2139832645":{"id":"n2139832645","loc":[-85.6324455,41.9448607]},"n2139832649":{"id":"n2139832649","loc":[-85.6328043,41.9454773]},"n2139832651":{"id":"n2139832651","loc":[-85.6322547,41.9449621]},"n2139832675":{"id":"n2139832675","loc":[-85.6327356,41.944757]},"n2139832677":{"id":"n2139832677","loc":[-85.6325433,41.9448599]},"n2139832680":{"id":"n2139832680","loc":[-85.6328885,41.9455614]},"n2139832682":{"id":"n2139832682","loc":[-85.6320913,41.9449492]},"n2139832684":{"id":"n2139832684","loc":[-85.6325366,41.9447133]},"n2139832688":{"id":"n2139832688","loc":[-85.6322786,41.94485]},"n2139832718":{"id":"n2139832718","loc":[-85.6327486,41.9432475]},"n2139832719":{"id":"n2139832719","loc":[-85.6327926,41.9431773]},"n2139832720":{"id":"n2139832720","loc":[-85.6329033,41.943153]},"n2139832727":{"id":"n2139832727","loc":[-85.6328975,41.9430783]},"n2139844839":{"id":"n2139844839","loc":[-85.6326261,41.9432308]},"n2189015992":{"id":"n2189015992","loc":[-85.6347706,41.9593383]},"n2189153179":{"id":"n2189153179","loc":[-85.6340476,41.9472565]},"n2189153182":{"id":"n2189153182","loc":[-85.6342638,41.9472522]},"n2189153241":{"id":"n2189153241","loc":[-85.6354184,41.9473091]},"n2189153258":{"id":"n2189153258","loc":[-85.6354611,41.9472366]},"n2189153277":{"id":"n2189153277","loc":[-85.6328948,41.9462374]},"n2199109755":{"id":"n2199109755","loc":[-85.6336729,41.9472417]},"w203970139":{"id":"w203970139","tags":{"building":"yes"},"nodes":["n2139824793","n2139824787","n2139824773","n2139824778","n2139824793"]},"w203970098":{"id":"w203970098","tags":{"building":"yes"},"nodes":["n2139824748","n2139824712","n2139824726","n2139824760","n2139824748"]},"w208643132":{"id":"w208643132","tags":{"area":"yes","building":"yes"},"nodes":["n2189153195","n2189153196","n2189153197","n2189153198","n2189153195"]},"w203970094":{"id":"w203970094","tags":{"building":"yes"},"nodes":["n2139824755","n2139824753","n2139824759","n2139824764","n2139824763","n2139824767","n2139824770","n2139824782","n2139824772","n2139824756","n2139824751","n2139824754","n2139824755"]},"w208643138":{"id":"w208643138","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153231","n2189153232","n2189153240","n2189153244","n2189153236","n2189153238","n2189153237","n2189153239","n2189153252","n2189153235","n2189153234","n2189153253","n2189153233","n2189153231"]},"w203970125":{"id":"w203970125","tags":{"building":"yes"},"nodes":["n2139824735","n2139824738","n2139824757","n2139824749","n2139824735"]},"w170848823":{"id":"w170848823","tags":{"name":"Rocky River","waterway":"river"},"nodes":["n1819849189","n1819858516","n1819858519","n1819858504","n1819858525","n1819858506","n1819858513"]},"w203970898":{"id":"w203970898","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832645","n2139832647","n2139832649","n2139832651","n2139832645"]},"w203970134":{"id":"w203970134","tags":{"building":"yes"},"nodes":["n2139824796","n2139824803","n2139824797","n2139824788","n2139824796"]},"w203970104":{"id":"w203970104","tags":{"building":"yes"},"nodes":["n2139824733","n2139824730","n2139824714","n2139824721","n2139824733"]},"w206805245":{"id":"w206805245","tags":{"area":"yes","building":"yes"},"nodes":["n2168544780","n2168544781","n2139824796","n2139824803","n2168544780"]},"w206805252":{"id":"w206805252","tags":{"area":"yes","building":"yes"},"nodes":["n2168544838","n2168544839","n2168544840","n2168544841","n2168544842","n2168544843","n2168544844","n2168544845","n2168544846","n2168544847","n2168544838"]},"w203970099":{"id":"w203970099","tags":{"building":"yes"},"nodes":["n2139824783","n2139824795","n2139824790","n2139824779","n2139824783"]},"w17967730":{"id":"w17967730","tags":{"highway":"residential","name":"Water St"},"nodes":["n185963451","n2189153277","n185988036","n185988867","n185988869","n185988871","n185988872","n185988873","n185988875","n185988877","n185988878","n185988879","n185988880","n185988882","n185988884","n185978790"]},"w208643133":{"id":"w208643133","tags":{"area":"yes","building":"yes"},"nodes":["n2189153199","n2189153200","n2189153201","n2189153202","n2189153199"]},"w203970127":{"id":"w203970127","tags":{"building":"yes"},"nodes":["n2139824794","n2139824783","n2139824789","n2139824797","n2139824794"]},"w208643139":{"id":"w208643139","tags":{"highway":"service"},"nodes":["n185988237","n2189153242","n2189153247","n2189153241"]},"w203988297":{"id":"w203988297","tags":{"amenity":"parking","area":"yes"},"nodes":["n2140006423","n2140006441","n2140006425","n2140006426","n2140006440","n2140006427","n2140006428","n2140006429","n2140006430","n2140006423"]},"w206805250":{"id":"w206805250","tags":{"area":"yes","building":"yes"},"nodes":["n2168544827","n2168544823","n2168544825","n2168544800","n2168544829","n2168544827"]},"w208643140":{"id":"w208643140","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153242","n2189153254","n2189153243","n2189153244","n2189153251","n2189153257","n2189153245","n2189153252","n2189153246"]},"w203974055":{"id":"w203974055","tags":{"bridge":"yes","highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870376","n2139870377"]},"w206805247":{"id":"w206805247","tags":{"area":"yes","building":"yes"},"nodes":["n2168544785","n2168544786","n2168544783","n2168544787","n2168544788","n2168544789","n2168544785"]},"w17964996":{"id":"w17964996","tags":{"highway":"residential","name":"Foster St"},"nodes":["n1819858524","n1819858515","n1819858522","n1819858530","n2139832682","n1819858510","n1819858520","n1819858502","n2139832680","n185963451","n1819858527","n185963452","n185963453","n185963454","n185963455","n185963456"]},"w208643144":{"id":"w208643144","tags":{"area":"yes","building":"yes"},"nodes":["n2189153264","n2189153265","n2189153266","n2189153267","n2189153268","n2189153269","n2189153270","n2189153264"]},"w203970914":{"id":"w203970914","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832722","n2139832723","n2139832724","n2139832725","n2139832726","n2139832727","n2139844839","n2139832722"]},"w208643143":{"id":"w208643143","tags":{"area":"yes","building":"yes"},"nodes":["n2189153258","n2189153259","n2189153260","n2189153261","n2189153262","n2189153263","n2189153258"]},"w203049590":{"id":"w203049590","tags":{"amenity":"parking","area":"yes"},"nodes":["n2130304152","n2130304153","n2140006403","n2130304154","n2130304156","n2130304155","n2130304160","n2130304152"]},"w203974054":{"id":"w203974054","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139858971","n2139870373","n2139870374"]},"w203049595":{"id":"w203049595","tags":{"highway":"service"},"nodes":["n2130304158","n2130304159","n2130304160","n2139832635","n2139832639"]},"w203970913":{"id":"w203970913","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832715","n2139832716","n2139832717","n2139832718","n2139832719","n2139832720","n2139832721","n2139832716"]},"w208643134":{"id":"w208643134","tags":{"area":"yes","building":"yes"},"nodes":["n2189153203","n2189153204","n2189153205","n2189153206","n2189153207","n2189153208","n2189153209","n2189153210","n2189153211","n2189153212","n2189153213","n2189153214","n2189153203"]},"w134150808":{"id":"w134150808","tags":{"bridge":"yes","highway":"residential","name":"Moore St"},"nodes":["n185988239","n185984009","n185988241","n1475284019"]},"w203970115":{"id":"w203970115","tags":{"building":"yes"},"nodes":["n2139824761","n2139824727","n2139824736","n2139824771","n2139824761"]},"w208643130":{"id":"w208643130","tags":{"area":"yes","building":"yes"},"nodes":["n2189153183","n2189153184","n2189153185","n2189153186","n2189153187","n2189153188","n2189153189","n2189153190","n2189153183"]},"w206805246":{"id":"w206805246","tags":{"area":"yes","building":"yes"},"nodes":["n2168544782","n2168544780","n2168544781","n2168544783","n2168544787","n2168544784","n2168544782"]},"w203970138":{"id":"w203970138","tags":{"building":"yes"},"nodes":["n2139824729","n2139824720","n2139824702","n2139824707","n2139824729"]},"w203970133":{"id":"w203970133","tags":{"building":"yes"},"nodes":["n2139824748","n2139824737","n2139824717","n2139824728","n2139824748"]},"w203970907":{"id":"w203970907","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832700","n2139832701","n2139832702"]},"w203974056":{"id":"w203974056","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870377","n2139870378"]},"w203970897":{"id":"w203970897","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2130304156","n2166205688","n2139832635","n2139832636","n2139832637","n2139832639","n2139832641","n2166205688"]},"w203974057":{"id":"w203974057","tags":{"highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870375","n2139870376"]},"w203049594":{"id":"w203049594","tags":{"highway":"service"},"nodes":["n2130304156","n2139870378","n2139832706","n2139832704","n2130304157"]},"w203970122":{"id":"w203970122","tags":{"building":"yes"},"nodes":["n2139824757","n2139824740","n2139824747","n2139824762","n2139824757"]},"w208643136":{"id":"w208643136","tags":{"area":"yes","building":"yes"},"nodes":["n2189153219","n2189153220","n2189153221","n2189153222","n2189153223","n2189153224","n2189153225","n2189153226","n2189153219"]},"w203970128":{"id":"w203970128","tags":{"building":"yes"},"nodes":["n2139824732","n2139824752","n2139824744","n2139824724","n2139824732"]},"w203970097":{"id":"w203970097","tags":{"building":"yes"},"nodes":["n2139824737","n2139824733","n2139824710","n2139824716","n2139824737"]},"w203970137":{"id":"w203970137","tags":{"building":"yes"},"nodes":["n2139824765","n2139824774","n2139824758","n2139824746","n2139824765"]},"w134150840":{"id":"w134150840","tags":{"highway":"residential","name":"Moore St"},"nodes":["n1475284019","n185988243","n185988244","n185988245"]},"w17967628":{"id":"w17967628","tags":{"highway":"residential","name":"Moore St"},"nodes":["n185978388","n2139832709","n185988237","n185988239"]},"w203988292":{"id":"w203988292","tags":{"bridge":"yes","highway":"footway"},"nodes":["n2140006407","n2140006405"]},"w203970118":{"id":"w203970118","tags":{"building":"yes"},"nodes":["n2139824775","n2139824785","n2139824780","n2139824768","n2139824775"]},"w203970121":{"id":"w203970121","tags":{"building":"yes"},"nodes":["n2139824768","n2139824781","n2139824776","n2139824765","n2139824768"]},"w17967752":{"id":"w17967752","tags":{"highway":"residential","name":"Railroad Drive"},"nodes":["n185964980","n2139832699","n2139832700","n2130304158","n185988969","n185988971","n185988972","n1475284011"]},"w203970136":{"id":"w203970136","tags":{"building":"yes"},"nodes":["n2139824798","n2139824793","n2139824777","n2139824784","n2139824798"]},"w203970142":{"id":"w203970142","tags":{"building":"yes"},"nodes":["n2139824808","n2139824809","n2139824807","n2139824806","n2139824801","n2139824800","n2139824804","n2139824805","n2139824808"]},"w208643137":{"id":"w208643137","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153227","n2189153248","n2189153228","n2189153234","n2189153235","n2189153229","n2189153249","n2189153230","n2189153227"]},"w208643129":{"id":"w208643129","tags":{"area":"yes","building":"yes"},"nodes":["n2189153179","n2189153180","n2189153181","n2189153182","n2189153179"]},"w203970909":{"id":"w203970909","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832703","n2139832704","n2139832706","n2139832708","n2139832703"]},"w203970905":{"id":"w203970905","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832688","n2139832691"]},"w203988298":{"id":"w203988298","tags":{"highway":"service"},"nodes":["n2140006431","n2140006433","n2140006435","n2140006436","n2140006437","n2140006438","n2140006439","n2140006440"]},"w203970106":{"id":"w203970106","tags":{"building":"yes"},"nodes":["n2139824798","n2139824791","n2139824799","n2139824802","n2139824798"]},"w203970129":{"id":"w203970129","tags":{"building":"yes"},"nodes":["n2139824787","n2139824782","n2139824766","n2139824769","n2139824787"]},"w208643131":{"id":"w208643131","tags":{"area":"yes","building":"yes"},"nodes":["n2189153191","n2189153192","n2189153193","n2189153194","n2189153191"]},"w206805249":{"id":"w206805249","tags":{"area":"yes","building":"yes"},"nodes":["n2168544800","n2168544801","n2168544802","n2168544805","n2168544807","n2168544809","n2168544811","n2168544813","n2168544815","n2168544817","n2168544819","n2168544821","n2168544823","n2168544825","n2168544800"]},"w134150800":{"id":"w134150800","tags":{"bridge":"yes","highway":"primary","name":"W Michigan Ave","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964972","n185964976"]},"w17966984":{"id":"w17966984","tags":{"highway":"residential","name":"Portage Avenue"},"nodes":["n185978375","n185963456","n2189153218","n185966937","n185978967","n185967422","n185982193","n185968100","n185982195","n185960195","n185980735","n185978787","n185966948","n185982196","n185982197","n185982198","n185982200","n2139795768","n185972155"]},"w203988294":{"id":"w203988294","tags":{"amenity":"shelter","area":"yes","building":"yes","shelter_type":"picnic_shelter"},"nodes":["n2140006409","n2140006411","n2140006413","n2140006415","n2140006409"]},"w203970912":{"id":"w203970912","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832711","n2139832712","n2139832713","n2139832714","n2139832711"]},"w203970119":{"id":"w203970119","tags":{"building":"yes"},"nodes":["n2139824713","n2139824705","n2139824683","n2139824689","n2139824713"]},"w203970114":{"id":"w203970114","tags":{"building":"yes"},"nodes":["n2139824735","n2139824750","n2139824745","n2139824732","n2139824735"]},"w208643142":{"id":"w208643142","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153254","n2189153255","n2189153256","n2189153257"]},"w206805253":{"id":"w206805253","tags":{"area":"yes","building":"yes"},"nodes":["n2168544848","n2168544849","n2168544850","n2168544851","n2168544852","n2168544853","n2168544854","n2168544855","n2168544848"]},"w143497377":{"id":"w143497377","tags":{"highway":"primary","name":"North Main Street","old_ref":"US 131","ref":"US 131 Business"},"nodes":["n185962625","n185978420","n185978419","n185978417","n185978415","n185978414","n185975735","n1475293254","n185978410","n185978406","n185970515","n185978402","n185978399","n185965399","n2189015992","n185961396","n185967917","n185978394","n185971631","n185960796","n185978392","n185974583","n185978390","n185971578","n185965289","n2189153215","n185978388","n185978383","n185978381","n185978379","n185978377","n185978375","n185964982"]},"w134150811":{"id":"w134150811","tags":{"highway":"primary","name":"West Michigan Avenue","old_ref":"US 131","ref":"US 131 Business;M 60"},"nodes":["n185964976","n2130304157","n1475284023","n2139832715","n185964980","n185964982"]},"w208643135":{"id":"w208643135","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153215","n2189153216","n2189153217","n2189153218"]},"w17967183":{"id":"w17967183","tags":{"highway":"residential","name":"West Street"},"nodes":["n1475284011","n185984011","n185984013","n185984015","n2189153246","n2189153250","n185965291","n185984017","n185971580","n185984020","n185974585","n185982163","n185975064","n185971632","n185984022","n185984024","n185967918"]},"w134150778":{"id":"w134150778","tags":{"bridge":"yes","highway":"residential","name":"Moore St"},"nodes":["n185988245","n1475283992","n185975911"]},"w206805248":{"id":"w206805248","tags":{"area":"yes","building":"yes"},"nodes":["n2168544790","n2168544791","n2168544792","n2168544793","n2168544795","n2168544797","n2168544798","n2168544799","n2168544802","n2168544801","n2168544790"]},"w203974058":{"id":"w203974058","tags":{"bridge":"yes","highway":"path","name":"Riverwalk Trail"},"nodes":["n2139870374","n2139870375"]},"w203970902":{"id":"w203970902","tags":{"highway":"service"},"nodes":["n2139832678","n2139832691","n2139832680"]},"w203988296":{"id":"w203988296","tags":{"highway":"path"},"nodes":["n2139858967","n2140006421","n2139858935"]},"w206805251":{"id":"w206805251","tags":{"area":"yes","building":"yes"},"nodes":["n2168544830","n2168544831","n2168544832","n2168544833","n2168544834","n2168544835","n2168544836","n2168544837","n2168544830"]},"w203970906":{"id":"w203970906","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832693","n2139832694","n2139832696","n2139832697","n2139832698","n2139832693"]},"w203049598":{"id":"w203049598","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2130304162","n2130304163","n2130304164","n2130304165","n2130304162"]},"w203970911":{"id":"w203970911","tags":{"highway":"service"},"nodes":["n2139832709","n2139832714","n2139832713","n2139832710","n185988971"]},"w203970105":{"id":"w203970105","tags":{"building":"yes"},"nodes":["n2139824779","n2139824792","n2139824786","n2139824775","n2139824779"]},"w203988290":{"id":"w203988290","tags":{"highway":"footway"},"nodes":["n2140006403","n2140006407"]},"w203970900":{"id":"w203970900","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139832653","n2139832663","n2139832665","n2139832667","n2139832669","n2139832671","n2139832673","n2139832675","n2139832677","n2139832653"]},"w209717048":{"id":"w209717048","tags":{"area":"yes","building":"yes"},"nodes":["n2199109755","n2199109756","n2199109757","n2199109758","n2199109759","n2199109760","n2199109755"]},"w208643141":{"id":"w208643141","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153247","n2189153248","n2189153249","n2189153250"]},"w203970903":{"id":"w203970903","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2139832682","n2139832688","n2139832684","n2139832678","n2139832686"]},"n354002527":{"id":"n354002527","loc":[-85.6236039,41.9458813],"tags":{"amenity":"school","name":"Barrows School"}},"n185963396":{"id":"n185963396","loc":[-85.627401,41.943496]},"n185963397":{"id":"n185963397","loc":[-85.627403,41.943625]},"n185965101":{"id":"n185965101","loc":[-85.626409,41.943215]},"n185971474":{"id":"n185971474","loc":[-85.624884,41.943508]},"n185971475":{"id":"n185971475","loc":[-85.625191,41.943509]},"n185971482":{"id":"n185971482","loc":[-85.624882,41.94382]},"n185983135":{"id":"n185983135","loc":[-85.624893,41.945616]},"n185983137":{"id":"n185983137","loc":[-85.624912,41.946524]},"n185988027":{"id":"n185988027","loc":[-85.622721,41.946535]},"n185963398":{"id":"n185963398","loc":[-85.6273993,41.9446899]},"n185983238":{"id":"n185983238","loc":[-85.6227157,41.9456321]},"n185980374":{"id":"n185980374","loc":[-85.6248856,41.9447242]},"n185980373":{"id":"n185980373","loc":[-85.6226744,41.9447371]},"n2196831342":{"id":"n2196831342","loc":[-85.6250924,41.945063]},"n2196831343":{"id":"n2196831343","loc":[-85.6252335,41.9450636]},"n2196831344":{"id":"n2196831344","loc":[-85.6252286,41.9448707]},"n2196831345":{"id":"n2196831345","loc":[-85.6250661,41.9448707]},"n2196831346":{"id":"n2196831346","loc":[-85.6250243,41.9449012]},"n2196831347":{"id":"n2196831347","loc":[-85.6250251,41.9449244]},"n2196831348":{"id":"n2196831348","loc":[-85.6250867,41.9449257]},"n2196831349":{"id":"n2196831349","loc":[-85.625349,41.9445058]},"n2196831350":{"id":"n2196831350","loc":[-85.6253471,41.9443882]},"n2196831351":{"id":"n2196831351","loc":[-85.6251516,41.94439]},"n2196831352":{"id":"n2196831352","loc":[-85.6251522,41.9444308]},"n2196831353":{"id":"n2196831353","loc":[-85.6251344,41.9444309]},"n2196831354":{"id":"n2196831354","loc":[-85.6251356,41.9445077]},"n2196831355":{"id":"n2196831355","loc":[-85.6232357,41.9463406]},"n2196831356":{"id":"n2196831356","loc":[-85.6232409,41.9460668]},"n2196831357":{"id":"n2196831357","loc":[-85.6232072,41.9460665]},"n2196831358":{"id":"n2196831358","loc":[-85.6232117,41.9458272]},"n2196831359":{"id":"n2196831359","loc":[-85.6229808,41.9458248]},"n2196831360":{"id":"n2196831360","loc":[-85.6229763,41.9460627]},"n2196831361":{"id":"n2196831361","loc":[-85.623006,41.946063]},"n2196831362":{"id":"n2196831362","loc":[-85.6230023,41.9462557]},"n2196831363":{"id":"n2196831363","loc":[-85.6230755,41.9462565]},"n2196831364":{"id":"n2196831364","loc":[-85.6230739,41.9463389]},"n185947349":{"id":"n185947349","loc":[-85.618327,41.945607]},"n185947359":{"id":"n185947359","loc":[-85.615453,41.945597]},"n185947378":{"id":"n185947378","loc":[-85.617231,41.945603]},"n185947474":{"id":"n185947474","loc":[-85.616136,41.945602]},"n185948972":{"id":"n185948972","loc":[-85.615273,41.945637]},"n185955019":{"id":"n185955019","loc":[-85.620172,41.945627]},"n185960682":{"id":"n185960682","loc":[-85.622759,41.951845]},"n185961369":{"id":"n185961369","loc":[-85.622758,41.947444]},"n185961371":{"id":"n185961371","loc":[-85.624908,41.947416]},"n185963392":{"id":"n185963392","loc":[-85.6270462,41.9409953]},"n185963393":{"id":"n185963393","loc":[-85.627295,41.941304]},"n185963394":{"id":"n185963394","loc":[-85.627352,41.94148]},"n185963395":{"id":"n185963395","loc":[-85.62737,41.942261]},"n185965099":{"id":"n185965099","loc":[-85.6264,41.942263]},"n185965108":{"id":"n185965108","loc":[-85.622769,41.949224]},"n185965110":{"id":"n185965110","loc":[-85.624937,41.949237]},"n185966295":{"id":"n185966295","loc":[-85.6299942,41.9446689]},"n185966342":{"id":"n185966342","loc":[-85.624873,41.942022]},"n185970222":{"id":"n185970222","loc":[-85.622761,41.948357]},"n185970224":{"id":"n185970224","loc":[-85.624924,41.9483338]},"n185971477":{"id":"n185971477","loc":[-85.620051,41.94383]},"n185971478":{"id":"n185971478","loc":[-85.621219,41.943801]},"n185971481":{"id":"n185971481","loc":[-85.621812,41.943807]},"n185973866":{"id":"n185973866","loc":[-85.627629,41.946498]},"n185974699":{"id":"n185974699","loc":[-85.6227688,41.950119]},"n185978800":{"id":"n185978800","loc":[-85.623953,41.954684]},"n185980372":{"id":"n185980372","loc":[-85.621459,41.944756]},"n185980378":{"id":"n185980378","loc":[-85.6286375,41.9446764]},"n185980380":{"id":"n185980380","loc":[-85.630139,41.944661]},"n185980382":{"id":"n185980382","loc":[-85.630298,41.944635]},"n185980384":{"id":"n185980384","loc":[-85.630759,41.94454]},"n185980386":{"id":"n185980386","loc":[-85.6312369,41.9444548]},"n185983133":{"id":"n185983133","loc":[-85.6248672,41.9415903]},"n185983139":{"id":"n185983139","loc":[-85.624951,41.950239]},"n185983140":{"id":"n185983140","loc":[-85.624934,41.950681]},"n185983141":{"id":"n185983141","loc":[-85.624813,41.950983]},"n185983143":{"id":"n185983143","loc":[-85.6246225,41.951591]},"n185983144":{"id":"n185983144","loc":[-85.623908,41.9539165]},"n185983145":{"id":"n185983145","loc":[-85.6238903,41.9540956]},"n185983146":{"id":"n185983146","loc":[-85.623898,41.95431]},"n185983236":{"id":"n185983236","loc":[-85.628481,41.945611]},"n185985914":{"id":"n185985914","loc":[-85.620072,41.946538]},"n185986812":{"id":"n185986812","loc":[-85.6227785,41.9510005]},"n185988028":{"id":"n185988028","loc":[-85.6281401,41.9469632]},"n185988030":{"id":"n185988030","loc":[-85.6282451,41.9470314]},"n185988032":{"id":"n185988032","loc":[-85.6283312,41.9470656]},"w17964989":{"id":"w17964989","tags":{"highway":"residential","name":"Middle St"},"nodes":["n185963392","n185963393","n185963394","n185963395","n185963396","n185963397","n185963398"]},"w17965158":{"id":"w17965158","tags":{"access":"private","highway":"service","name":"Battle St"},"nodes":["n185965099","n185965101"]},"w41074896":{"id":"w41074896","tags":{"highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980372","n185980373","n185980374","n185963398","n185980378","n185966295","n185980380","n185980382","n185980384","n185980386"]},"w17965846":{"id":"w17965846","tags":{"highway":"residential","name":"2nd Ave"},"nodes":["n185971477","n185971478","n185971481","n185971482"]},"w209470306":{"id":"w209470306","tags":{"area":"yes","building":"yes"},"nodes":["n2196831349","n2196831350","n2196831351","n2196831352","n2196831353","n2196831354","n2196831349"]},"w17965845":{"id":"w17965845","tags":{"highway":"residential","name":"2nd Ave"},"nodes":["n185971474","n185971475","n185963396"]},"w209470307":{"id":"w209470307","tags":{"area":"yes","building":"yes"},"nodes":["n2196831355","n2196831356","n2196831357","n2196831358","n2196831359","n2196831360","n2196831361","n2196831362","n2196831363","n2196831364","n2196831355"]},"w17968192":{"id":"w17968192","tags":{"highway":"residential","name":"Washington St"},"nodes":["n185980373","n185983238","n185988027","n185961369","n185970222","n185965108","n185974699","n185986812","n185960682"]},"w17967603":{"id":"w17967603","tags":{"highway":"residential","name":"5th Ave"},"nodes":["n185985914","n185988027","n185983137","n185973866","n185988028","n185988030","n185988032"]},"w209470305":{"id":"w209470305","tags":{"area":"yes","building":"yes"},"nodes":["n2196831342","n2196831343","n2196831344","n2196831345","n2196831346","n2196831347","n2196831348","n2196831342"]},"w17967092":{"id":"w17967092","tags":{"highway":"residential","name":"Wood St"},"nodes":["n185983133","n185966342","n185971474","n185971482","n185980374","n185983135","n185983137","n185961371","n185970224","n185965110","n185983139","n185983140","n185983141","n185983143","n185983144","n185983145","n185983146","n185978800"]},"w17967107":{"id":"w17967107","tags":{"highway":"residential","name":"4th Ave"},"nodes":["n185983236","n185983135","n185983238","n185955019","n185947349","n185947378","n185947474","n185947359","n185948972"]},"n354030330":{"id":"n354030330","loc":[-85.6297222,41.9444444],"tags":{"leisure":"park","name":"Scouter Park"}},"n185966296":{"id":"n185966296","loc":[-85.629998,41.944078]},"n185966298":{"id":"n185966298","loc":[-85.629972,41.943927]},"n185966300":{"id":"n185966300","loc":[-85.629948,41.943783]},"n185980391":{"id":"n185980391","loc":[-85.6322992,41.9442766]},"n185980393":{"id":"n185980393","loc":[-85.6324925,41.9442136]},"n185980389":{"id":"n185980389","loc":[-85.6320272,41.9443281]},"n185980388":{"id":"n185980388","loc":[-85.6315778,41.9443959]},"n354031320":{"id":"n354031320","loc":[-85.6280556,41.9447222],"tags":{"amenity":"place_of_worship","name":"Riverside Church","religion":"christian"}},"n185987309":{"id":"n185987309","loc":[-85.6286497,41.9453531]},"n185987311":{"id":"n185987311","loc":[-85.6285942,41.9454805]},"n185988034":{"id":"n185988034","loc":[-85.6285815,41.9471692]},"n185988896":{"id":"n185988896","loc":[-85.6318433,41.9437929]},"n185977764":{"id":"n185977764","loc":[-85.6322988,41.943472]},"n1819848852":{"id":"n1819848852","loc":[-85.6315188,41.9448808]},"n1819848912":{"id":"n1819848912","loc":[-85.6284289,41.9472189]},"n1819848925":{"id":"n1819848925","loc":[-85.6314501,41.9451617]},"n1819848949":{"id":"n1819848949","loc":[-85.6309394,41.9455192]},"n1819848951":{"id":"n1819848951","loc":[-85.6290297,41.9457187]},"n1819848963":{"id":"n1819848963","loc":[-85.630521,41.9455591]},"n1819848981":{"id":"n1819848981","loc":[-85.6292936,41.9455846]},"n1819848989":{"id":"n1819848989","loc":[-85.6298451,41.9455431]},"n1819848998":{"id":"n1819848998","loc":[-85.6314973,41.9446254]},"n1819849018":{"id":"n1819849018","loc":[-85.6302807,41.9455527]},"n1819849043":{"id":"n1819849043","loc":[-85.6285533,41.9469731]},"n1819849087":{"id":"n1819849087","loc":[-85.6314501,41.9453532]},"n1819849090":{"id":"n1819849090","loc":[-85.628843,41.9461033]},"n1819849109":{"id":"n1819849109","loc":[-85.6311926,41.9454729]},"n1819849116":{"id":"n1819849116","loc":[-85.6288967,41.9459437]},"n1819849177":{"id":"n1819849177","loc":[-85.6287894,41.9464544]},"n1819858529":{"id":"n1819858529","loc":[-85.6325485,41.9445625]},"n2189112797":{"id":"n2189112797","loc":[-85.6275271,41.944555]},"n2189112798":{"id":"n2189112798","loc":[-85.6275196,41.9437258]},"n2189112799":{"id":"n2189112799","loc":[-85.6278937,41.943723]},"n2189112800":{"id":"n2189112800","loc":[-85.6278969,41.9439191]},"n2189112801":{"id":"n2189112801","loc":[-85.6279907,41.9439345]},"n2189112802":{"id":"n2189112802","loc":[-85.6280817,41.9439663]},"n2189112803":{"id":"n2189112803","loc":[-85.6281768,41.9440145]},"n2189112804":{"id":"n2189112804","loc":[-85.6281933,41.9440483]},"n2189112805":{"id":"n2189112805","loc":[-85.6281671,41.9440535]},"n2189112806":{"id":"n2189112806","loc":[-85.6281933,41.9440935]},"n2189112807":{"id":"n2189112807","loc":[-85.6282126,41.9441437]},"n2189112808":{"id":"n2189112808","loc":[-85.628214,41.9441991]},"n2189112809":{"id":"n2189112809","loc":[-85.6283298,41.944196]},"n2189112810":{"id":"n2189112810","loc":[-85.6283285,41.9442616]},"n2189112811":{"id":"n2189112811","loc":[-85.6281727,41.9442616]},"n2189112812":{"id":"n2189112812","loc":[-85.6281713,41.9442934]},"n2189112813":{"id":"n2189112813","loc":[-85.6280386,41.9442963]},"n2189112814":{"id":"n2189112814","loc":[-85.6280405,41.9443292]},"n2189112815":{"id":"n2189112815","loc":[-85.627829,41.9443349]},"n2189112816":{"id":"n2189112816","loc":[-85.6278347,41.9445495]},"n2189153271":{"id":"n2189153271","loc":[-85.6321053,41.9460342]},"n2189153272":{"id":"n2189153272","loc":[-85.632278,41.9457841]},"n2189153273":{"id":"n2189153273","loc":[-85.632823,41.9459936]},"n2189153274":{"id":"n2189153274","loc":[-85.6326845,41.9461963]},"n2189153275":{"id":"n2189153275","loc":[-85.6325664,41.9461507]},"n2189153276":{"id":"n2189153276","loc":[-85.6325323,41.946198]},"n2189153278":{"id":"n2189153278","loc":[-85.6321916,41.9459733]},"n2189153279":{"id":"n2189153279","loc":[-85.6322598,41.9458703]},"n2189153280":{"id":"n2189153280","loc":[-85.6327208,41.9460358]},"n2189153281":{"id":"n2189153281","loc":[-85.6326413,41.9461422]},"n185959079":{"id":"n185959079","loc":[-85.6293702,41.9474668]},"n185966301":{"id":"n185966301","loc":[-85.629692,41.943136]},"n185966304":{"id":"n185966304","loc":[-85.629565,41.942916]},"n185966308":{"id":"n185966308","loc":[-85.629501,41.942751]},"n185966315":{"id":"n185966315","loc":[-85.629472,41.942578]},"n185966319":{"id":"n185966319","loc":[-85.629444,41.942414]},"n185966321":{"id":"n185966321","loc":[-85.629391,41.94205]},"n185966323":{"id":"n185966323","loc":[-85.629369,41.941858]},"n185966327":{"id":"n185966327","loc":[-85.629297,41.941604]},"n185966331":{"id":"n185966331","loc":[-85.629233,41.941549]},"n185966336":{"id":"n185966336","loc":[-85.628504,41.941364]},"n185966338":{"id":"n185966338","loc":[-85.628275,41.941303]},"n185966340":{"id":"n185966340","loc":[-85.6269038,41.9410983]},"n185973867":{"id":"n185973867","loc":[-85.626843,41.947333]},"n185977762":{"id":"n185977762","loc":[-85.6318441,41.9429453]},"n1819848853":{"id":"n1819848853","loc":[-85.625854,41.9492218]},"n1819848861":{"id":"n1819848861","loc":[-85.6251459,41.9552376]},"n1819848874":{"id":"n1819848874","loc":[-85.6267445,41.9482961]},"n1819848882":{"id":"n1819848882","loc":[-85.6257209,41.9552396]},"n1819848883":{"id":"n1819848883","loc":[-85.624706,41.9523173]},"n1819848907":{"id":"n1819848907","loc":[-85.62609,41.9561471]},"n1819848908":{"id":"n1819848908","loc":[-85.6244013,41.9549284]},"n1819848911":{"id":"n1819848911","loc":[-85.6265578,41.9553672]},"n1819848923":{"id":"n1819848923","loc":[-85.6246802,41.9550959]},"n1819848936":{"id":"n1819848936","loc":[-85.6241588,41.9539291]},"n1819848940":{"id":"n1819848940","loc":[-85.62506,41.9511129]},"n1819848944":{"id":"n1819848944","loc":[-85.624942,41.9515912]},"n1819848950":{"id":"n1819848950","loc":[-85.6273989,41.9475461]},"n1819848957":{"id":"n1819848957","loc":[-85.627695,41.947404]},"n1819849009":{"id":"n1819849009","loc":[-85.6259248,41.94896]},"n1819849037":{"id":"n1819849037","loc":[-85.6257252,41.9502112]},"n1819849061":{"id":"n1819849061","loc":[-85.6270084,41.9479626]},"n1819849073":{"id":"n1819849073","loc":[-85.6243734,41.9534583]},"n1819849091":{"id":"n1819849091","loc":[-85.6241373,41.9543918]},"n1819849130":{"id":"n1819849130","loc":[-85.6282572,41.9473067]},"n1819849143":{"id":"n1819849143","loc":[-85.625281,41.9506596]},"n1819849153":{"id":"n1819849153","loc":[-85.6258647,41.9498043]},"n1819849168":{"id":"n1819849168","loc":[-85.6265084,41.9559317]},"n1819849173":{"id":"n1819849173","loc":[-85.6263325,41.9552156]},"n1819849175":{"id":"n1819849175","loc":[-85.6266372,41.9556764]},"n1819849178":{"id":"n1819849178","loc":[-85.6242232,41.9545993]},"n1819849181":{"id":"n1819849181","loc":[-85.6262187,41.9486712]},"n1819849188":{"id":"n1819849188","loc":[-85.6245558,41.9530434]},"n1819849190":{"id":"n1819849190","loc":[-85.6255982,41.9563017]},"n2168544738":{"id":"n2168544738","loc":[-85.6245707,41.9529711]},"w208643145":{"id":"w208643145","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189153271","n2189153272","n2189153273","n2189153274","n2189153275","n2189153276","n2189153271"]},"w17967561":{"id":"w17967561","tags":{"highway":"residential","name":"Garden St"},"nodes":["n185980378","n185987309","n185987311","n185983236","n185973866"]},"w134150802":{"id":"w134150802","tags":{"bridge":"yes","highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980386","n185980388"]},"w208639462":{"id":"w208639462","tags":{"area":"yes","building":"yes"},"nodes":["n2189112797","n2189112798","n2189112799","n2189112800","n2189112801","n2189112802","n2189112803","n2189112804","n2189112805","n2189112806","n2189112807","n2189112808","n2189112809","n2189112810","n2189112811","n2189112812","n2189112813","n2189112814","n2189112815","n2189112816","n2189112797"]},"w134150830":{"id":"w134150830","tags":{"bridge":"yes","highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977762","n185977764"]},"w134150801":{"id":"w134150801","tags":{"highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977764","n185964982"]},"w208643146":{"id":"w208643146","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2189153277","n2189153281","n2189153278","n2189153279","n2189153280","n2189153281"]},"w17966061":{"id":"w17966061","tags":{"highway":"residential","name":"John Glenn Ct"},"nodes":["n185973866","n185973867"]},"w134150772":{"id":"w134150772","tags":{"highway":"residential","name":"5th Ave"},"nodes":["n185988034","n185959079","n185988036","n185978967"]},"w134150836":{"id":"w134150836","tags":{"highway":"secondary","name":"East Michigan Avenue","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185980388","n1819858524","n185980389","n185980391","n185980393","n185964982"]},"w17967734":{"id":"w17967734","tags":{"highway":"residential","name":"Water Street"},"nodes":["n185988896","n185980391","n1819858529"]},"w17965305":{"id":"w17965305","tags":{"highway":"residential","name":"River Dr"},"nodes":["n185966295","n185966296","n185966298","n185966300","n185966301","n185966304","n185966308","n185966315","n185966319","n185966321","n185966323","n185966327","n185966331","n185966336","n185966338","n185963392","n185966340","n185966342"]},"w134150826":{"id":"w134150826","tags":{"bridge":"yes","highway":"residential","name":"5th Ave"},"nodes":["n185988032","n185988034"]},"w170848330":{"id":"w170848330","tags":{"name":"Portage River","waterway":"river"},"nodes":["n1819849190","n1819848907","n1819849168","n1819849175","n1819848911","n1819849173","n1819848882","n1819848861","n1819848923","n1819848908","n1819849178","n1819849091","n1819848936","n1819849073","n1819849188","n2168544738","n1819848883","n1819848944","n1819848940","n1819849143","n1819849037","n1819849153","n1819848853","n1819849009","n1819849181","n1819848874","n1819849061","n1819848950","n1819848957","n1819849130","n1819848912","n1819849043","n1819849177","n1819849090","n1819849116","n1819848951","n1819848981","n1819848989","n1819849018","n1819848963","n1819848949","n1819849109","n1819849087","n1819848925","n1819848852","n1819848998","n1819849057"]},"r270264":{"id":"r270264","tags":{"network":"US:MI","ref":"86","route":"road","state_id":"MI","type":"route","url":"http://en.wikipedia.org/wiki/M-86_%28Michigan_highway%29"},"members":[{"id":"w17737723","type":"way","role":""},{"id":"w17735949","type":"way","role":""},{"id":"w17740404","type":"way","role":""},{"id":"w17966273","type":"way","role":""},{"id":"w17964745","type":"way","role":""},{"id":"w151538068","type":"way","role":""},{"id":"w151538067","type":"way","role":""},{"id":"w17964960","type":"way","role":""},{"id":"w17966099","type":"way","role":""},{"id":"w17968009","type":"way","role":""},{"id":"w41259499","type":"way","role":""},{"id":"w151540401","type":"way","role":""},{"id":"w151540418","type":"way","role":""},{"id":"w17967997","type":"way","role":""},{"id":"w17966029","type":"way","role":""},{"id":"w17964801","type":"way","role":""},{"id":"w41259496","type":"way","role":""},{"id":"w151540399","type":"way","role":""},{"id":"w17968004","type":"way","role":""},{"id":"w17966462","type":"way","role":""},{"id":"w134150830","type":"way","role":""},{"id":"w134150801","type":"way","role":""},{"id":"w17732295","type":"way","role":""}]},"n185980093":{"id":"n185980093","loc":[-85.6271414,41.9407274]},"n185964330":{"id":"n185964330","loc":[-85.6235688,41.9399111]},"n185964328":{"id":"n185964328","loc":[-85.6235609,41.9391301]},"n185958034":{"id":"n185958034","loc":[-85.627102,41.939125]},"n185964331":{"id":"n185964331","loc":[-85.623571,41.940124]},"n185964329":{"id":"n185964329","loc":[-85.623562,41.9392411]},"n185972756":{"id":"n185972756","loc":[-85.623802,41.939102]},"n185972757":{"id":"n185972757","loc":[-85.623584,41.93913]},"n185975325":{"id":"n185975325","loc":[-85.624835,41.939318]},"n185975326":{"id":"n185975326","loc":[-85.624811,41.939435]},"n185975327":{"id":"n185975327","loc":[-85.624635,41.939703]},"n185975328":{"id":"n185975328","loc":[-85.624366,41.940055]},"n185975330":{"id":"n185975330","loc":[-85.624287,41.940113]},"n185975332":{"id":"n185975332","loc":[-85.624215,41.940134]},"n185980088":{"id":"n185980088","loc":[-85.627127,41.940086]},"n185988943":{"id":"n185988943","loc":[-85.622643,41.940128]},"n185988961":{"id":"n185988961","loc":[-85.627263,41.940082]},"n185990192":{"id":"n185990192","loc":[-85.622933,41.939224]},"n185990194":{"id":"n185990194","loc":[-85.621976,41.939203]},"n185991378":{"id":"n185991378","loc":[-85.622643,41.940635]},"n1475283999":{"id":"n1475283999","loc":[-85.6271165,41.9408429]},"n185980090":{"id":"n185980090","loc":[-85.6271315,41.9402001]},"n185958036":{"id":"n185958036","loc":[-85.6248366,41.9391615]},"n1819800188":{"id":"n1819800188","loc":[-85.6246947,41.9401644]},"n1819800199":{"id":"n1819800199","loc":[-85.6233686,41.9430896]},"n1819800204":{"id":"n1819800204","loc":[-85.6223236,41.9408587]},"n1819800213":{"id":"n1819800213","loc":[-85.6247526,41.9414138]},"n1819800216":{"id":"n1819800216","loc":[-85.6230961,41.9407151]},"n1819800218":{"id":"n1819800218","loc":[-85.621991,41.9429336]},"n1819800221":{"id":"n1819800221","loc":[-85.6246088,41.9424708]},"n1819800227":{"id":"n1819800227","loc":[-85.6241368,41.9403081]},"n1819800230":{"id":"n1819800230","loc":[-85.6226776,41.9431012]},"n1819800231":{"id":"n1819800231","loc":[-85.6243728,41.9401644]},"n1819800232":{"id":"n1819800232","loc":[-85.6249629,41.9408907]},"n1819800248":{"id":"n1819800248","loc":[-85.6238685,41.9405555]},"n1819800266":{"id":"n1819800266","loc":[-85.6246882,41.9418367]},"n1819800271":{"id":"n1819800271","loc":[-85.62492,41.9413695]},"n1819800294":{"id":"n1819800294","loc":[-85.6243556,41.9427465]},"n1819800304":{"id":"n1819800304","loc":[-85.6251453,41.94117]},"n1819800325":{"id":"n1819800325","loc":[-85.6248234,41.9405714]},"n1819800362":{"id":"n1819800362","loc":[-85.6239544,41.9429416]},"n1819800368":{"id":"n1819800368","loc":[-85.6243406,41.9402283]},"n1819800375":{"id":"n1819800375","loc":[-85.6226562,41.940755]},"n1819800377":{"id":"n1819800377","loc":[-85.6232033,41.9406512]},"n185945133":{"id":"n185945133","loc":[-85.623501,41.933232]},"n185945135":{"id":"n185945135","loc":[-85.624776,41.933205]},"n185945395":{"id":"n185945395","loc":[-85.624741,41.93019]},"n185952239":{"id":"n185952239","loc":[-85.615166,41.9382]},"n185954490":{"id":"n185954490","loc":[-85.624721,41.929278]},"n185957831":{"id":"n185957831","loc":[-85.625041,41.938662]},"n185958030":{"id":"n185958030","loc":[-85.629033,41.93913]},"n185958032":{"id":"n185958032","loc":[-85.628429,41.939143]},"n185958498":{"id":"n185958498","loc":[-85.621605,41.940143]},"n185961186":{"id":"n185961186","loc":[-85.624792,41.935214]},"n185963099":{"id":"n185963099","loc":[-85.6204461,41.9401485]},"n185963698":{"id":"n185963698","loc":[-85.6297342,41.9400783]},"n185964320":{"id":"n185964320","loc":[-85.623511,41.934216]},"n185964322":{"id":"n185964322","loc":[-85.6235312,41.9362084]},"n185964324":{"id":"n185964324","loc":[-85.6235488,41.9371726]},"n185964326":{"id":"n185964326","loc":[-85.6235512,41.9381718]},"n185967077":{"id":"n185967077","loc":[-85.617359,41.940161]},"n185967634":{"id":"n185967634","loc":[-85.6248039,41.9362012]},"n185970833":{"id":"n185970833","loc":[-85.6248019,41.9381684]},"n185972752":{"id":"n185972752","loc":[-85.624582,41.938848]},"n185972754":{"id":"n185972754","loc":[-85.6242,41.939008]},"n185973251":{"id":"n185973251","loc":[-85.602727,41.936012]},"n185974509":{"id":"n185974509","loc":[-85.62478,41.93217]},"n185975315":{"id":"n185975315","loc":[-85.624703,41.925597]},"n185975316":{"id":"n185975316","loc":[-85.624716,41.927359]},"n185975317":{"id":"n185975317","loc":[-85.62475,41.93119]},"n185975318":{"id":"n185975318","loc":[-85.624782,41.934218]},"n185975320":{"id":"n185975320","loc":[-85.6247949,41.9371708]},"n185977754":{"id":"n185977754","loc":[-85.6276,41.937412]},"n185980075":{"id":"n185980075","loc":[-85.627451,41.937549]},"n185980077":{"id":"n185980077","loc":[-85.627375,41.937618]},"n185980078":{"id":"n185980078","loc":[-85.627278,41.937728]},"n185980079":{"id":"n185980079","loc":[-85.627199,41.937842]},"n185980081":{"id":"n185980081","loc":[-85.627141,41.937981]},"n185980083":{"id":"n185980083","loc":[-85.627109,41.938153]},"n185980085":{"id":"n185980085","loc":[-85.627101,41.938699]},"n185981173":{"id":"n185981173","loc":[-85.61433,41.940167]},"n185987021":{"id":"n185987021","loc":[-85.628311,41.942261]},"n185988963":{"id":"n185988963","loc":[-85.628439,41.940086]},"n185990195":{"id":"n185990195","loc":[-85.621225,41.939143]},"n185990196":{"id":"n185990196","loc":[-85.620576,41.939033]},"n185990198":{"id":"n185990198","loc":[-85.619081,41.938804]},"n185990200":{"id":"n185990200","loc":[-85.617593,41.938552]},"n185990202":{"id":"n185990202","loc":[-85.617372,41.938535]},"n185990204":{"id":"n185990204","loc":[-85.616087,41.93832]},"n185990206":{"id":"n185990206","loc":[-85.615754,41.938289]},"n185990209":{"id":"n185990209","loc":[-85.615438,41.938251]},"n185990211":{"id":"n185990211","loc":[-85.613469,41.937867]},"n185990212":{"id":"n185990212","loc":[-85.610172,41.937298]},"n185990213":{"id":"n185990213","loc":[-85.605537,41.936497]},"n185990214":{"id":"n185990214","loc":[-85.604014,41.936234]},"n1819800180":{"id":"n1819800180","loc":[-85.588775,41.9455032]},"n1819800181":{"id":"n1819800181","loc":[-85.6074212,41.9408827]},"n1819800182":{"id":"n1819800182","loc":[-85.6131397,41.9427022]},"n1819800183":{"id":"n1819800183","loc":[-85.6171523,41.9416807]},"n1819800184":{"id":"n1819800184","loc":[-85.602465,41.9397415]},"n1819800185":{"id":"n1819800185","loc":[-85.6109296,41.9410583]},"n1819800186":{"id":"n1819800186","loc":[-85.6165729,41.9418004]},"n1819800189":{"id":"n1819800189","loc":[-85.5866293,41.9458224]},"n1819800191":{"id":"n1819800191","loc":[-85.5853311,41.9466603]},"n1819800201":{"id":"n1819800201","loc":[-85.6101142,41.9433406]},"n1819800202":{"id":"n1819800202","loc":[-85.600963,41.9428618]},"n1819800206":{"id":"n1819800206","loc":[-85.6154357,41.9427501]},"n1819800207":{"id":"n1819800207","loc":[-85.6040309,41.9414094]},"n1819800209":{"id":"n1819800209","loc":[-85.6113694,41.943189]},"n1819800211":{"id":"n1819800211","loc":[-85.618032,41.9416408]},"n1819800214":{"id":"n1819800214","loc":[-85.5959419,41.9402602]},"n1819800219":{"id":"n1819800219","loc":[-85.5972117,41.9420043]},"n1819800223":{"id":"n1819800223","loc":[-85.6117171,41.9430019]},"n1819800224":{"id":"n1819800224","loc":[-85.5977873,41.9395579]},"n1819800226":{"id":"n1819800226","loc":[-85.5917362,41.9432209]},"n1819800228":{"id":"n1819800228","loc":[-85.6055759,41.9419122]},"n1819800229":{"id":"n1819800229","loc":[-85.6203395,41.9425595]},"n1819800233":{"id":"n1819800233","loc":[-85.6107579,41.9433007]},"n1819800234":{"id":"n1819800234","loc":[-85.6039773,41.9412498]},"n1819800235":{"id":"n1819800235","loc":[-85.6000977,41.9412861]},"n1819800236":{"id":"n1819800236","loc":[-85.6026689,41.9407231]},"n1819800237":{"id":"n1819800237","loc":[-85.615161,41.9428662]},"n1819800238":{"id":"n1819800238","loc":[-85.5878953,41.9454314]},"n1819800239":{"id":"n1819800239","loc":[-85.6035267,41.941569]},"n1819800240":{"id":"n1819800240","loc":[-85.5929738,41.9450208]},"n1819800241":{"id":"n1819800241","loc":[-85.6186329,41.9416488]},"n1819800242":{"id":"n1819800242","loc":[-85.5881136,41.9483963]},"n1819800243":{"id":"n1819800243","loc":[-85.5909208,41.9466922]},"n1819800244":{"id":"n1819800244","loc":[-85.5997721,41.9394941]},"n1819800245":{"id":"n1819800245","loc":[-85.6202064,41.9425712]},"n1819800246":{"id":"n1819800246","loc":[-85.591071,41.9448808]},"n1819800247":{"id":"n1819800247","loc":[-85.5866078,41.9490622]},"n1819800250":{"id":"n1819800250","loc":[-85.602383,41.9420841]},"n1819800251":{"id":"n1819800251","loc":[-85.5957418,41.9426906]},"n1819800255":{"id":"n1819800255","loc":[-85.6157039,41.9416727]},"n1819800256":{"id":"n1819800256","loc":[-85.6080328,41.9410982]},"n1819800258":{"id":"n1819800258","loc":[-85.6192551,41.9414892]},"n1819800260":{"id":"n1819800260","loc":[-85.6104253,41.94117]},"n1819800261":{"id":"n1819800261","loc":[-85.6204503,41.9425709]},"n1819800263":{"id":"n1819800263","loc":[-85.5872194,41.9455431]},"n1819800264":{"id":"n1819800264","loc":[-85.616176,41.9418244]},"n1819800268":{"id":"n1819800268","loc":[-85.6120883,41.9426703]},"n1819800269":{"id":"n1819800269","loc":[-85.5894547,41.9474946]},"n1819800272":{"id":"n1819800272","loc":[-85.6209181,41.9425027]},"n1819800274":{"id":"n1819800274","loc":[-85.6122814,41.9412817]},"n1819800276":{"id":"n1819800276","loc":[-85.5895153,41.9452798]},"n1819800277":{"id":"n1819800277","loc":[-85.5884317,41.9455272]},"n1819800279":{"id":"n1819800279","loc":[-85.5884103,41.9480966]},"n1819800287":{"id":"n1819800287","loc":[-85.5904917,41.9453915]},"n1819800288":{"id":"n1819800288","loc":[-85.6212292,41.9412977]},"n1819800289":{"id":"n1819800289","loc":[-85.5954377,41.9406832]},"n1819800290":{"id":"n1819800290","loc":[-85.593721,41.9420957]},"n1819800291":{"id":"n1819800291","loc":[-85.6162832,41.9427102]},"n1819800292":{"id":"n1819800292","loc":[-85.605018,41.9401804]},"n1819800293":{"id":"n1819800293","loc":[-85.6086443,41.941146]},"n1819800296":{"id":"n1819800296","loc":[-85.6204675,41.9413775]},"n1819800297":{"id":"n1819800297","loc":[-85.612496,41.9424947]},"n1819800299":{"id":"n1819800299","loc":[-85.6065629,41.9423431]},"n1819800301":{"id":"n1819800301","loc":[-85.6036125,41.9398452]},"n1819800303":{"id":"n1819800303","loc":[-85.6114767,41.94117]},"n1819800306":{"id":"n1819800306","loc":[-85.592616,41.9428139]},"n1819800308":{"id":"n1819800308","loc":[-85.6023041,41.9419521]},"n1819800310":{"id":"n1819800310","loc":[-85.6218944,41.9411061]},"n1819800311":{"id":"n1819800311","loc":[-85.6097816,41.941162]},"n1819800312":{"id":"n1819800312","loc":[-85.5922549,41.9457869]},"n1819800313":{"id":"n1819800313","loc":[-85.5986027,41.9417206]},"n1819800314":{"id":"n1819800314","loc":[-85.5918687,41.946138]},"n1819800315":{"id":"n1819800315","loc":[-85.5872875,41.948883]},"n1819800316":{"id":"n1819800316","loc":[-85.594272,41.9436642]},"n1819800317":{"id":"n1819800317","loc":[-85.6176351,41.941577]},"n1819800318":{"id":"n1819800318","loc":[-85.6137834,41.9430853]},"n1819800319":{"id":"n1819800319","loc":[-85.6195383,41.942622],"tags":{"leisure":"slipway"}},"n1819800320":{"id":"n1819800320","loc":[-85.5971006,41.9398053]},"n1819800321":{"id":"n1819800321","loc":[-85.601714,41.9406752]},"n1819800322":{"id":"n1819800322","loc":[-85.5908028,41.9453117]},"n1819800323":{"id":"n1819800323","loc":[-85.6062732,41.9404597]},"n1819800324":{"id":"n1819800324","loc":[-85.62124,41.9425905]},"n1819800327":{"id":"n1819800327","loc":[-85.6008664,41.942766]},"n1819800328":{"id":"n1819800328","loc":[-85.6179355,41.9428538]},"n1819800330":{"id":"n1819800330","loc":[-85.6045566,41.9415131]},"n1819800331":{"id":"n1819800331","loc":[-85.5944935,41.9414653]},"n1819800333":{"id":"n1819800333","loc":[-85.6088911,41.943181]},"n1819800334":{"id":"n1819800334","loc":[-85.5946367,41.943369]},"n1819800336":{"id":"n1819800336","loc":[-85.6150494,41.9429656]},"n1819800343":{"id":"n1819800343","loc":[-85.6096099,41.9433326]},"n1819800345":{"id":"n1819800345","loc":[-85.5915216,41.9435401]},"n1819800347":{"id":"n1819800347","loc":[-85.607786,41.9428698]},"n1819800349":{"id":"n1819800349","loc":[-85.6187616,41.9426623]},"n1819800350":{"id":"n1819800350","loc":[-85.6012527,41.9426064]},"n1819800352":{"id":"n1819800352","loc":[-85.6214867,41.9428379]},"n1819800354":{"id":"n1819800354","loc":[-85.61338,41.94293]},"n1819800355":{"id":"n1819800355","loc":[-85.5923156,41.9428139]},"n1819800357":{"id":"n1819800357","loc":[-85.5901591,41.9453197]},"n1819800359":{"id":"n1819800359","loc":[-85.6033979,41.9408827]},"n1819800360":{"id":"n1819800360","loc":[-85.6186543,41.9414653]},"n1819800363":{"id":"n1819800363","loc":[-85.6128607,41.9425665]},"n1819800365":{"id":"n1819800365","loc":[-85.614234,41.9412977]},"n1819800367":{"id":"n1819800367","loc":[-85.6089662,41.9410902]},"n1819800369":{"id":"n1819800369","loc":[-85.6197379,41.9413695]},"n1819800370":{"id":"n1819800370","loc":[-85.6037348,41.941733]},"n1819800371":{"id":"n1819800371","loc":[-85.5993467,41.9415654]},"n1819800372":{"id":"n1819800372","loc":[-85.598077,41.94196]},"n1819800373":{"id":"n1819800373","loc":[-85.5984203,41.9394781]},"n1819800374":{"id":"n1819800374","loc":[-85.6013315,41.9427066]},"n1819800376":{"id":"n1819800376","loc":[-85.5934673,41.944167]},"n1819800378":{"id":"n1819800378","loc":[-85.6011062,41.9407753]},"n1819800379":{"id":"n1819800379","loc":[-85.6150602,41.9415131]},"n1819800380":{"id":"n1819800380","loc":[-85.6132148,41.9412338]},"n1819800381":{"id":"n1819800381","loc":[-85.5889038,41.9453835]},"n2139966621":{"id":"n2139966621","loc":[-85.6198719,41.9426184]},"n2139966622":{"id":"n2139966622","loc":[-85.6197551,41.9426123]},"n2139966623":{"id":"n2139966623","loc":[-85.6196467,41.9426279]},"n2139966624":{"id":"n2139966624","loc":[-85.6191519,41.9426221]},"n2139966625":{"id":"n2139966625","loc":[-85.6194153,41.9426256]},"n2139966626":{"id":"n2139966626","loc":[-85.6200497,41.9425812]},"n2139966629":{"id":"n2139966629","loc":[-85.6192123,41.9426229]},"n2203933101":{"id":"n2203933101","loc":[-85.6030009,41.9360592]},"w17967539":{"id":"w17967539","tags":{"highway":"residential","name":"1st Ave"},"nodes":["n185965099","n185963395","n185987021"]},"w17967751":{"id":"w17967751","tags":{"highway":"residential","name":"River St"},"nodes":["n185980088","n185988961","n185988963","n185963698"]},"w17965088":{"id":"w17965088","tags":{"highway":"residential","name":"9th St"},"nodes":["n185945133","n185964320","n185964322","n185964324","n185964326","n185964328","n185964329","n185964330","n185964331"]},"w17964467":{"id":"w17964467","tags":{"highway":"residential","name":"Mechanic St"},"nodes":["n185958030","n185958032","n185958034","n185958036"]},"w134150842":{"id":"w134150842","tags":{"bridge":"yes","highway":"residential","name":"6th St"},"nodes":["n185980090","n185980093"]},"w17966740":{"id":"w17966740","tags":{"highway":"residential","name":"6th St"},"nodes":["n185977754","n185980075","n185980077","n185980078","n185980079","n185980081","n185980083","n185980085","n185958034","n185980088","n185980090"]},"w170844765":{"id":"w170844765","tags":{"waterway":"dam"},"nodes":["n1819800304","n1819800232","n1819800325","n1819800188"]},"w17967745":{"id":"w17967745","tags":{"highway":"residential","name":"River St"},"nodes":["n185981173","n185967077","n185963099","n185958498","n185988943","n185964331","n185975332"]},"w17968113":{"id":"w17968113","tags":{"highway":"residential","name":"Green St"},"nodes":["n185988943","n185991378"]},"w134150833":{"id":"w134150833","tags":{"highway":"residential","name":"6th St"},"nodes":["n185980093","n1475283999","n185963392"]},"w17967935":{"id":"w17967935","tags":{"name":"Michigan Central Railroad","railway":"abandoned"},"nodes":["n185972757","n185990192","n185990194","n185990195","n185990196","n185990198","n185990200","n185990202","n185990204","n185990206","n185990209","n185952239","n185990211","n185990212","n185990213","n185990214","n2203933101","n185973251"]},"w17965993":{"id":"w17965993","tags":{"name":"Conrail Railroad","railway":"abandoned"},"nodes":["n185957831","n185972752","n185972754","n185972756","n185972757"]},"w17966211":{"id":"w17966211","tags":{"highway":"residential","name":"8th St"},"nodes":["n185975315","n185975316","n185954490","n185945395","n185975317","n185974509","n185945135","n185975318","n185961186","n185967634","n185975320","n185970833","n185958036","n185975325","n185975326","n185975327","n185975328","n185975330","n185975332"]},"w170844766":{"id":"w170844766","tags":{"waterway":"riverbank"},"nodes":["n1819800229","n1819800245","n2139966626","n2139966621","n2139966622","n2139966623","n1819800319","n2139966625","n2139966629","n2139966624","n1819800349","n1819800328","n1819800291","n1819800206","n1819800237","n1819800336","n1819800318","n1819800354","n1819800182","n1819800363","n1819800297","n1819800268","n1819800223","n1819800209","n1819800233","n1819800201","n1819800343","n1819800333","n1819800347","n1819800299","n1819800228","n1819800330","n1819800370","n1819800250","n1819800374","n1819800202","n1819800327","n1819800350","n1819800308","n1819800239","n1819800207","n1819800234","n1819800359","n1819800236","n1819800321","n1819800378","n1819800235","n1819800371","n1819800313","n1819800372","n1819800219","n1819800251","n1819800334","n1819800316","n1819800376","n1819800240","n1819800312","n1819800314","n1819800243","n1819800269","n1819800279","n1819800242","n1819800315","n1819800247","n1819800191","n1819800189","n1819800263","n1819800238","n1819800277","n1819800180","n1819800381","n1819800276","n1819800357","n1819800287","n1819800322","n1819800246","n1819800345","n1819800226","n1819800355","n1819800306","n1819800290","n1819800331","n1819800289","n1819800214","n1819800320","n1819800224","n1819800373","n1819800244","n1819800184","n1819800301","n1819800292","n1819800323","n1819800181","n1819800256","n1819800293","n1819800367","n1819800311","n1819800260","n1819800185","n1819800303","n1819800274","n1819800380","n1819800365","n1819800379","n1819800255","n1819800264","n1819800186","n1819800183","n1819800317","n1819800211","n1819800241","n1819800360","n1819800258","n1819800369","n1819800296","n1819800288","n1819800310","n1819800204","n1819800375","n1819800216","n1819800377","n1819800248","n1819800227","n1819800368","n1819800231","n1819800188","n1819800325","n1819800232","n1819800304","n1819800271","n1819800213","n1819800266","n1819800221","n1819800294","n1819800362","n1819800199","n1819800230","n1819800218","n1819800352","n1819800324","n1819800272","n1819800261","n1819800229"]},"n1875654132":{"id":"n1875654132","loc":[-85.6297439,41.939808]},"n1475293263":{"id":"n1475293263","loc":[-85.6296235,41.939922]},"n185947850":{"id":"n185947850","loc":[-85.631594,41.942613]},"n185952745":{"id":"n185952745","loc":[-85.630986,41.941786]},"n185972907":{"id":"n185972907","loc":[-85.631797,41.9420055]},"n185972911":{"id":"n185972911","loc":[-85.6309723,41.9411623]},"n185972915":{"id":"n185972915","loc":[-85.6295971,41.939267]},"n1475293223":{"id":"n1475293223","loc":[-85.6313962,41.9416114],"tags":{"railway":"level_crossing"}},"n1475293231":{"id":"n1475293231","loc":[-85.6318779,41.9415447]},"n1475293241":{"id":"n1475293241","loc":[-85.6304613,41.9405499]},"n1475293246":{"id":"n1475293246","loc":[-85.6297512,41.9395393],"tags":{"railway":"level_crossing"}},"n1475293251":{"id":"n1475293251","loc":[-85.6316633,41.9415128]},"n2139982404":{"id":"n2139982404","loc":[-85.6313283,41.9413748]},"n2139982407":{"id":"n2139982407","loc":[-85.6325545,41.9417787]},"n2139982408":{"id":"n2139982408","loc":[-85.6324499,41.9417693]},"n2139982409":{"id":"n2139982409","loc":[-85.6324753,41.9416444]},"n2139982410":{"id":"n2139982410","loc":[-85.6325814,41.9416538]},"n2139982411":{"id":"n2139982411","loc":[-85.6319572,41.9413515]},"n2139982412":{"id":"n2139982412","loc":[-85.6322925,41.941139]},"n2139982413":{"id":"n2139982413","loc":[-85.6323153,41.941153]},"n2139982414":{"id":"n2139982414","loc":[-85.6323019,41.9412617]},"n2139982415":{"id":"n2139982415","loc":[-85.6323703,41.9412667]},"n2139982416":{"id":"n2139982416","loc":[-85.6323555,41.941538]},"n2139982417":{"id":"n2139982417","loc":[-85.6321343,41.9416777]},"n2139982418":{"id":"n2139982418","loc":[-85.6319425,41.9416866]},"n2139982419":{"id":"n2139982419","loc":[-85.6320303,41.9416941]},"n2139982420":{"id":"n2139982420","loc":[-85.6321665,41.9415554]},"n2139982421":{"id":"n2139982421","loc":[-85.632412,41.9414164]},"n2139982422":{"id":"n2139982422","loc":[-85.6324801,41.9413421]},"n2139982423":{"id":"n2139982423","loc":[-85.6325023,41.9412585]},"n2139982424":{"id":"n2139982424","loc":[-85.6324532,41.9411607]},"n2139982425":{"id":"n2139982425","loc":[-85.6323502,41.941103]},"n2139982426":{"id":"n2139982426","loc":[-85.6322362,41.9411183]},"n2139982427":{"id":"n2139982427","loc":[-85.6318941,41.9413551]},"n2139982428":{"id":"n2139982428","loc":[-85.6318592,41.9414105]},"n2139982429":{"id":"n2139982429","loc":[-85.6320111,41.9415866]},"n2139982430":{"id":"n2139982430","loc":[-85.632446,41.9413792]},"n2139982431":{"id":"n2139982431","loc":[-85.6325112,41.941416]},"n2139982432":{"id":"n2139982432","loc":[-85.6325449,41.9416345]},"n2139982433":{"id":"n2139982433","loc":[-85.6326122,41.94164]},"n2139982434":{"id":"n2139982434","loc":[-85.6325954,41.9421966]},"n2139982435":{"id":"n2139982435","loc":[-85.6325655,41.9422411]},"n2139982436":{"id":"n2139982436","loc":[-85.632515,41.9422564]},"n2139982437":{"id":"n2139982437","loc":[-85.6324495,41.94223]},"n2139982438":{"id":"n2139982438","loc":[-85.6324009,41.9421743]},"n2139982439":{"id":"n2139982439","loc":[-85.6323915,41.9421145]},"n2139982440":{"id":"n2139982440","loc":[-85.6320287,41.9418585]},"n2139982441":{"id":"n2139982441","loc":[-85.6318285,41.9416387]},"n1475293258":{"id":"n1475293258","loc":[-85.6318289,41.9415077]},"n2168544754":{"id":"n2168544754","loc":[-85.6312814,41.9431198]},"n2168544755":{"id":"n2168544755","loc":[-85.6314212,41.9430646]},"n2168544756":{"id":"n2168544756","loc":[-85.6313387,41.942949]},"n2168544757":{"id":"n2168544757","loc":[-85.6311989,41.9430041]},"n2168544758":{"id":"n2168544758","loc":[-85.6311024,41.9429313]},"n2168544759":{"id":"n2168544759","loc":[-85.6310087,41.9428087]},"n2168544760":{"id":"n2168544760","loc":[-85.6313831,41.9426504]},"n2168544761":{"id":"n2168544761","loc":[-85.6314768,41.9427729]},"n2168544762":{"id":"n2168544762","loc":[-85.6306376,41.942809]},"n2168544763":{"id":"n2168544763","loc":[-85.6307378,41.9429427]},"n2168544764":{"id":"n2168544764","loc":[-85.630841,41.9428998]},"n2168544765":{"id":"n2168544765","loc":[-85.6307408,41.9427662]},"n2168544766":{"id":"n2168544766","loc":[-85.6305404,41.9426029]},"n2168544767":{"id":"n2168544767","loc":[-85.6304976,41.9426194]},"n2168544768":{"id":"n2168544768","loc":[-85.6305673,41.9427184]},"n2168544769":{"id":"n2168544769","loc":[-85.6306164,41.9426984]},"n2168544770":{"id":"n2168544770","loc":[-85.6306418,41.9427302]},"n2168544771":{"id":"n2168544771","loc":[-85.6306861,41.9427137]},"n2168544772":{"id":"n2168544772","loc":[-85.6307146,41.9427537]},"n2168544773":{"id":"n2168544773","loc":[-85.6308999,41.9426807]},"n2168544774":{"id":"n2168544774","loc":[-85.6308429,41.9426053]},"n2168544775":{"id":"n2168544775","loc":[-85.6308999,41.9425806]},"n2168544776":{"id":"n2168544776","loc":[-85.6308318,41.9424875]},"n2168544777":{"id":"n2168544777","loc":[-85.6307732,41.9425087]},"n2168544778":{"id":"n2168544778","loc":[-85.6307178,41.9424357]},"n2168544779":{"id":"n2168544779","loc":[-85.630485,41.942524]},"n2189099387":{"id":"n2189099387","loc":[-85.631203,41.9393371]},"n2189099404":{"id":"n2189099404","loc":[-85.6301963,41.9391363]},"n2189099405":{"id":"n2189099405","loc":[-85.6304447,41.9391352]},"n2189099406":{"id":"n2189099406","loc":[-85.6304463,41.9393391]},"n2189099407":{"id":"n2189099407","loc":[-85.6308435,41.9393373]},"n2189099408":{"id":"n2189099408","loc":[-85.6308418,41.9391251]},"n2189099409":{"id":"n2189099409","loc":[-85.6310929,41.939124]},"n2189099410":{"id":"n2189099410","loc":[-85.6310946,41.9393376]},"n2189112720":{"id":"n2189112720","loc":[-85.6314677,41.9412327]},"n2189112721":{"id":"n2189112721","loc":[-85.6313337,41.9411397]},"n2189112722":{"id":"n2189112722","loc":[-85.6320521,41.9405678]},"n2189112723":{"id":"n2189112723","loc":[-85.6321899,41.9406633]},"n2189112724":{"id":"n2189112724","loc":[-85.6313229,41.9408344]},"n2189112725":{"id":"n2189112725","loc":[-85.6311223,41.9410018]},"n2189112726":{"id":"n2189112726","loc":[-85.6313205,41.9411333]},"n2189112727":{"id":"n2189112727","loc":[-85.6315211,41.9409659]},"n2189112728":{"id":"n2189112728","loc":[-85.6311035,41.9402529]},"n2189112729":{"id":"n2189112729","loc":[-85.631226,41.9402107]},"n2189112730":{"id":"n2189112730","loc":[-85.6315966,41.9408051]},"n2189112731":{"id":"n2189112731","loc":[-85.6314741,41.9408473]},"n2189112732":{"id":"n2189112732","loc":[-85.6318114,41.940534]},"n2189112733":{"id":"n2189112733","loc":[-85.631588,41.94061]},"n2189112734":{"id":"n2189112734","loc":[-85.6314379,41.940366]},"n2189112735":{"id":"n2189112735","loc":[-85.6316613,41.94029]},"n2189112736":{"id":"n2189112736","loc":[-85.6306214,41.9400415]},"n2189112737":{"id":"n2189112737","loc":[-85.6304362,41.9397728]},"n2189112738":{"id":"n2189112738","loc":[-85.6305899,41.9397142]},"n2189112739":{"id":"n2189112739","loc":[-85.6307751,41.9399828]},"n2189112740":{"id":"n2189112740","loc":[-85.6304695,41.9401673]},"n2189112741":{"id":"n2189112741","loc":[-85.6301298,41.9396855]},"n2189112742":{"id":"n2189112742","loc":[-85.6303016,41.9396184]},"n2189112743":{"id":"n2189112743","loc":[-85.6306413,41.9401003]},"n2189112744":{"id":"n2189112744","loc":[-85.6309656,41.9406189]},"n2189112745":{"id":"n2189112745","loc":[-85.6308738,41.940493]},"n2189112746":{"id":"n2189112746","loc":[-85.6309333,41.940469]},"n2189112747":{"id":"n2189112747","loc":[-85.6307634,41.9402358]},"n2189112748":{"id":"n2189112748","loc":[-85.6308798,41.9401889]},"n2189112749":{"id":"n2189112749","loc":[-85.6311416,41.940548]},"n2189112750":{"id":"n2189112750","loc":[-85.6309577,41.9408708]},"n2189112751":{"id":"n2189112751","loc":[-85.630874,41.9407777]},"n2189112752":{"id":"n2189112752","loc":[-85.6310622,41.9406841]},"n2189112753":{"id":"n2189112753","loc":[-85.6311459,41.9407772]},"n2189112754":{"id":"n2189112754","loc":[-85.6320308,41.9405747]},"n2189112755":{"id":"n2189112755","loc":[-85.6317769,41.9401857]},"n2189112756":{"id":"n2189112756","loc":[-85.6313462,41.9401785]},"n2189112757":{"id":"n2189112757","loc":[-85.6313423,41.9401199]},"n2189112758":{"id":"n2189112758","loc":[-85.6318308,41.9401184]},"n2189112759":{"id":"n2189112759","loc":[-85.6321154,41.9405433]},"n2189112760":{"id":"n2189112760","loc":[-85.6310307,41.941683]},"n2189112761":{"id":"n2189112761","loc":[-85.6309808,41.9416078]},"n2189112762":{"id":"n2189112762","loc":[-85.6312094,41.9415156]},"n2189112763":{"id":"n2189112763","loc":[-85.6312636,41.9415865]},"n2189112764":{"id":"n2189112764","loc":[-85.6309384,41.94155]},"n2189112765":{"id":"n2189112765","loc":[-85.631156,41.9414619]},"n2189112766":{"id":"n2189112766","loc":[-85.6311968,41.94152]},"n2189112767":{"id":"n2189112767","loc":[-85.6308946,41.9414851]},"n2189112768":{"id":"n2189112768","loc":[-85.6308237,41.9413888]},"n2189112769":{"id":"n2189112769","loc":[-85.6309858,41.9413228]},"n2189112770":{"id":"n2189112770","loc":[-85.6310567,41.9414192]},"n2189112771":{"id":"n2189112771","loc":[-85.6307774,41.9413276]},"n2189112772":{"id":"n2189112772","loc":[-85.6309068,41.9412735]},"n2189112773":{"id":"n2189112773","loc":[-85.6309531,41.9413347]},"n2189112774":{"id":"n2189112774","loc":[-85.6307975,41.9412466]},"n2189112775":{"id":"n2189112775","loc":[-85.6307006,41.9411699]},"n2189112776":{"id":"n2189112776","loc":[-85.6308289,41.941113]},"n2189112777":{"id":"n2189112777","loc":[-85.6308997,41.9412012]},"n2189112778":{"id":"n2189112778","loc":[-85.630765,41.9412062]},"n2189112779":{"id":"n2189112779","loc":[-85.630739,41.9412177]},"n2189112780":{"id":"n2189112780","loc":[-85.6305822,41.9410391]},"n2189112781":{"id":"n2189112781","loc":[-85.6304117,41.9408177]},"n2189112782":{"id":"n2189112782","loc":[-85.6305086,41.9407769]},"n2189112783":{"id":"n2189112783","loc":[-85.6306779,41.9410044]},"n2189112784":{"id":"n2189112784","loc":[-85.6307734,41.9421663]},"n2189112785":{"id":"n2189112785","loc":[-85.630708,41.9420741]},"n2189112786":{"id":"n2189112786","loc":[-85.630863,41.9420133]},"n2189112787":{"id":"n2189112787","loc":[-85.6309285,41.9421055]},"n2189112788":{"id":"n2189112788","loc":[-85.6307014,41.9420263]},"n2189112789":{"id":"n2189112789","loc":[-85.6306648,41.941971]},"n2189112790":{"id":"n2189112790","loc":[-85.6307927,41.9419178]},"n2189112791":{"id":"n2189112791","loc":[-85.6308366,41.9419696]},"n2189112792":{"id":"n2189112792","loc":[-85.6307574,41.9418708]},"n2189112793":{"id":"n2189112793","loc":[-85.6306288,41.9419231]},"n2189112794":{"id":"n2189112794","loc":[-85.6306943,41.9417835]},"n2189112795":{"id":"n2189112795","loc":[-85.6305344,41.9418474]},"n2189112796":{"id":"n2189112796","loc":[-85.6305981,41.9419355]},"n2189123410":{"id":"n2189123410","loc":[-85.6315476,41.9393801]},"n2189123412":{"id":"n2189123412","loc":[-85.6315247,41.9399188]},"n2189123415":{"id":"n2189123415","loc":[-85.6316484,41.9400433]},"n185945138":{"id":"n185945138","loc":[-85.627073,41.93319]},"n185945142":{"id":"n185945142","loc":[-85.6296891,41.9331674]},"n185945401":{"id":"n185945401","loc":[-85.6269,41.930199]},"n185945405":{"id":"n185945405","loc":[-85.6296598,41.9301676]},"n185956891":{"id":"n185956891","loc":[-85.6251617,41.9255049]},"n185959979":{"id":"n185959979","loc":[-85.626333,41.928347]},"n185959983":{"id":"n185959983","loc":[-85.6296419,41.9283288]},"n185961192":{"id":"n185961192","loc":[-85.627053,41.9352031]},"n185961200":{"id":"n185961200","loc":[-85.6297088,41.9351902]},"n185963655":{"id":"n185963655","loc":[-85.6296112,41.9273948]},"n185963665":{"id":"n185963665","loc":[-85.626047,41.92737]},"n185963688":{"id":"n185963688","loc":[-85.6296503,41.9292199]},"n185963689":{"id":"n185963689","loc":[-85.6296694,41.931157]},"n185963690":{"id":"n185963690","loc":[-85.6296791,41.9321485]},"n185963691":{"id":"n185963691","loc":[-85.6296991,41.9341973]},"n185967638":{"id":"n185967638","loc":[-85.627089,41.9361884]},"n185972917":{"id":"n185972917","loc":[-85.6293759,41.9388605]},"n185972919":{"id":"n185972919","loc":[-85.6290337,41.9380234]},"n185972921":{"id":"n185972921","loc":[-85.628424,41.936212]},"n185972923":{"id":"n185972923","loc":[-85.628367,41.936029]},"n185974511":{"id":"n185974511","loc":[-85.627064,41.932169]},"n185977728":{"id":"n185977728","loc":[-85.625605,41.925842]},"n185977729":{"id":"n185977729","loc":[-85.625685,41.926163]},"n185977731":{"id":"n185977731","loc":[-85.6257845,41.9264872]},"n185977733":{"id":"n185977733","loc":[-85.62663,41.929251]},"n185977734":{"id":"n185977734","loc":[-85.627008,41.930642]},"n185977736":{"id":"n185977736","loc":[-85.627029,41.930775]},"n185977738":{"id":"n185977738","loc":[-85.627041,41.930946]},"n185977739":{"id":"n185977739","loc":[-85.6270379,41.9311746]},"n185977742":{"id":"n185977742","loc":[-85.627055,41.934206]},"n185977744":{"id":"n185977744","loc":[-85.627084,41.936804]},"n185977746":{"id":"n185977746","loc":[-85.627104,41.936914]},"n185977748":{"id":"n185977748","loc":[-85.627156,41.937026]},"n185977750":{"id":"n185977750","loc":[-85.6272406,41.9371672]},"n185977752":{"id":"n185977752","loc":[-85.627317,41.93723]},"n185977753":{"id":"n185977753","loc":[-85.627422,41.937312]},"n185977755":{"id":"n185977755","loc":[-85.627754,41.937504]},"n185977757":{"id":"n185977757","loc":[-85.627883,41.937623]},"n185977761":{"id":"n185977761","loc":[-85.627984,41.93773]},"n1475283996":{"id":"n1475283996","loc":[-85.6270514,41.9317122],"tags":{"railway":"level_crossing"}},"n1475284004":{"id":"n1475284004","loc":[-85.6278177,41.9342117],"tags":{"railway":"level_crossing"}},"n1475284014":{"id":"n1475284014","loc":[-85.6251877,41.9255913],"tags":{"railway":"level_crossing"}},"n1475284017":{"id":"n1475284017","loc":[-85.6274992,41.9331816],"tags":{"railway":"level_crossing"}},"n1475284021":{"id":"n1475284021","loc":[-85.6297108,41.9353939],"tags":{"railway":"level_crossing"}},"n1475284027":{"id":"n1475284027","loc":[-85.62811,41.935198],"tags":{"railway":"level_crossing"}},"n1475284035":{"id":"n1475284035","loc":[-85.626888,41.9311757],"tags":{"railway":"level_crossing"}},"n1475293245":{"id":"n1475293245","loc":[-85.6286047,41.9367881]},"n1875654302":{"id":"n1875654302","loc":[-85.6296367,41.927491]},"n2189099388":{"id":"n2189099388","loc":[-85.6312007,41.9389988]},"n2189099389":{"id":"n2189099389","loc":[-85.6311003,41.9389992]},"n2189099390":{"id":"n2189099390","loc":[-85.6310988,41.9387847]},"n2189099391":{"id":"n2189099391","loc":[-85.6312165,41.9387843]},"n2189099392":{"id":"n2189099392","loc":[-85.6312152,41.9385857]},"n2189099393":{"id":"n2189099393","loc":[-85.6310877,41.9385862]},"n2189099394":{"id":"n2189099394","loc":[-85.6310858,41.9383161]},"n2189099395":{"id":"n2189099395","loc":[-85.6302002,41.9383196]},"n2189099396":{"id":"n2189099396","loc":[-85.6302011,41.9384472]},"n2189099397":{"id":"n2189099397","loc":[-85.6301018,41.9384476]},"n2189099398":{"id":"n2189099398","loc":[-85.6301025,41.9385419]},"n2189099399":{"id":"n2189099399","loc":[-85.6299275,41.9385427]},"n2189099400":{"id":"n2189099400","loc":[-85.62993,41.9388653]},"n2189099401":{"id":"n2189099401","loc":[-85.630107,41.9388645]},"n2189099402":{"id":"n2189099402","loc":[-85.6301079,41.9389908]},"n2189099403":{"id":"n2189099403","loc":[-85.6301951,41.9389904]},"n2189123382":{"id":"n2189123382","loc":[-85.6336279,41.9354365]},"n2189123384":{"id":"n2189123384","loc":[-85.6328492,41.9355177]},"n2189123387":{"id":"n2189123387","loc":[-85.6323762,41.9357396]},"n2189123388":{"id":"n2189123388","loc":[-85.6315174,41.9358966]},"n2189123389":{"id":"n2189123389","loc":[-85.6304331,41.936124]},"n2189123390":{"id":"n2189123390","loc":[-85.6302075,41.9364271]},"n2189123391":{"id":"n2189123391","loc":[-85.6303458,41.9367953]},"n2189123392":{"id":"n2189123392","loc":[-85.6299601,41.9369739]},"n2189123393":{"id":"n2189123393","loc":[-85.6299164,41.9374882]},"n2189123394":{"id":"n2189123394","loc":[-85.6299455,41.9378022]},"n2189123395":{"id":"n2189123395","loc":[-85.6299771,41.9379053]},"n2189123396":{"id":"n2189123396","loc":[-85.6301597,41.9379091]},"n2189123397":{"id":"n2189123397","loc":[-85.6308042,41.9377913]},"n2189123398":{"id":"n2189123398","loc":[-85.6316885,41.9378082]},"n2189123399":{"id":"n2189123399","loc":[-85.6316848,41.9380079]},"n2189123400":{"id":"n2189123400","loc":[-85.6318449,41.9381161]},"n2189123401":{"id":"n2189123401","loc":[-85.6320705,41.9381811]},"n2189123402":{"id":"n2189123402","loc":[-85.6321433,41.9383706]},"n2189123404":{"id":"n2189123404","loc":[-85.632056,41.9384355]},"n2189123406":{"id":"n2189123406","loc":[-85.6317867,41.9384572]},"n2189123409":{"id":"n2189123409","loc":[-85.6316572,41.9387281]},"n2189123417":{"id":"n2189123417","loc":[-85.6315946,41.93775]},"n2189123419":{"id":"n2189123419","loc":[-85.6302641,41.9378393]},"w208640158":{"id":"w208640158","tags":{"area":"yes","natural":"wetland"},"nodes":["n2189123379","n2189123382","n2189123384","n2189123387","n2189123388","n2189123389","n2189123390","n2189123391","n2189123392","n2189123393","n2189123394","n2189123395","n2189123396","n2189123419","n2189123397","n2189123417","n2189123398","n2189123399","n2189123400","n2189123401","n2189123402","n2189123404","n2189123406","n2189123409","n2189123410","n2189123412","n2189123415","n1819805722","n1819805861","n1819805887","n1819805760","n1819805641","n1819805632","n2189123379"]},"w134150787":{"id":"w134150787","tags":{"name":"Conrail Railroad","railway":"rail"},"nodes":["n185972905","n185972907","n1475293223","n185972911","n1475293241","n1475293246","n185972915","n185972917","n185972919","n1475293245","n185972921","n185972923","n1475284027","n1475284004","n1475284017","n1475283996","n1475284035","n1475284014","n185956891"]},"w208639443":{"id":"w208639443","tags":{"area":"yes","building":"yes"},"nodes":["n2189112720","n2189112721","n2189112722","n2189112723","n2189112720"]},"w17966462":{"id":"w17966462","tags":{"highway":"secondary","name":"South Main Street","old_ref":"US 131","ref":"M 86"},"nodes":["n185977728","n185977729","n185977731","n185963665","n185959979","n185977733","n185945401","n185977734","n185977736","n185977738","n185977739","n1475283996","n185974511","n185945138","n185977742","n185961192","n185967638","n185977744","n185977746","n185977748","n185977750","n185977752","n185977753","n185977754","n185977755","n185977757","n185977761","n185958030","n1475293263","n185963698","n185952745","n185947850","n185977762"]},"w203985741":{"id":"w203985741","tags":{"area":"yes","leisure":"park","name":"Conservation Park"},"nodes":["n2139982404","n2139982405","n2139982399","n2139982400","n1819805770","n2139982402","n2139982403","n2139982401","n1819805780","n1819805834","n2139982406","n2139982404"]},"w17963676":{"id":"w17963676","tags":{"highway":"service"},"nodes":["n1475293258","n2139982428","n2139982427","n2139982426","n2139982425","n2139982424","n2139982423","n2139982422","n2139982430","n2139982421","n2139982420","n2139982429","n1475293231","n1475293258","n1475293251","n1475293223","n185952745"]},"w203985745":{"id":"w203985745","tags":{"highway":"footway"},"nodes":["n2139982430","n2139982431","n2139982432","n2139982433","n2139982434","n2139982435","n2139982436","n2139982437","n2139982438","n2139982439","n2139982440","n2139982441","n1475293231"]},"w208639451":{"id":"w208639451","tags":{"area":"yes","building":"yes"},"nodes":["n2189112754","n2189112755","n2189112756","n2189112757","n2189112758","n2189112759","n2189112754"]},"w208639452":{"id":"w208639452","tags":{"area":"yes","building":"yes"},"nodes":["n2189112760","n2189112761","n2189112766","n2189112762","n2189112763","n2189112760"]},"w206805244":{"id":"w206805244","tags":{"area":"yes","building":"yes"},"nodes":["n2168544766","n2168544767","n2168544768","n2168544769","n2168544770","n2168544771","n2168544772","n2168544773","n2168544774","n2168544775","n2168544776","n2168544777","n2168544778","n2168544779","n2168544766"]},"w208639444":{"id":"w208639444","tags":{"area":"yes","building":"yes"},"nodes":["n2189112724","n2189112725","n2189112726","n2189112727","n2189112724"]},"w208639450":{"id":"w208639450","tags":{"area":"yes","building":"yes"},"nodes":["n2189112750","n2189112751","n2189112752","n2189112753","n2189112750"]},"w208639448":{"id":"w208639448","tags":{"area":"yes","building":"yes"},"nodes":["n2189112740","n2189112741","n2189112742","n2189112743","n2189112740"]},"w208637859":{"id":"w208637859","tags":{"area":"yes","building":"yes"},"nodes":["n2189099387","n2189099388","n2189099389","n2189099390","n2189099391","n2189099392","n2189099393","n2189099394","n2189099395","n2189099396","n2189099397","n2189099398","n2189099399","n2189099400","n2189099401","n2189099402","n2189099403","n2189099404","n2189099405","n2189099406","n2189099407","n2189099408","n2189099409","n2189099410","n2189099387"]},"w208639453":{"id":"w208639453","tags":{"area":"yes","building":"yes"},"nodes":["n2189112764","n2189112765","n2189112766","n2189112761","n2189112764"]},"w208639456":{"id":"w208639456","tags":{"area":"yes","building":"yes"},"nodes":["n2189112774","n2189112778","n2189112779","n2189112775","n2189112776","n2189112777","n2189112774"]},"w208639445":{"id":"w208639445","tags":{"area":"yes","building":"yes"},"nodes":["n2189112728","n2189112729","n2189112730","n2189112731","n2189112728"]},"w17967776":{"id":"w17967776","tags":{"highway":"residential","name":"5th St"},"nodes":["n185958032","n185988963"]},"w208639461":{"id":"w208639461","tags":{"area":"yes","building":"yes"},"nodes":["n2189112792","n2189112794","n2189112795","n2189112796","n2189112793","n2189112792"]},"w206805241":{"id":"w206805241","tags":{"area":"yes","building":"yes"},"nodes":["n2168544754","n2168544755","n2168544756","n2168544757","n2168544754"]},"w208639449":{"id":"w208639449","tags":{"area":"yes","building":"yes"},"nodes":["n2189112744","n2189112745","n2189112746","n2189112747","n2189112748","n2189112749","n2189112744"]},"w208639455":{"id":"w208639455","tags":{"area":"yes","building":"yes"},"nodes":["n2189112771","n2189112772","n2189112773","n2189112768","n2189112771"]},"w208639457":{"id":"w208639457","tags":{"area":"yes","building":"yes"},"nodes":["n2189112780","n2189112781","n2189112782","n2189112783","n2189112780"]},"w208639446":{"id":"w208639446","tags":{"area":"yes","building":"yes"},"nodes":["n2189112732","n2189112733","n2189112734","n2189112735","n2189112732"]},"w208639454":{"id":"w208639454","tags":{"area":"yes","building":"yes"},"nodes":["n2189112767","n2189112768","n2189112773","n2189112769","n2189112770","n2189112767"]},"w203985743":{"id":"w203985743","tags":{"amenity":"parking","area":"yes"},"nodes":["n2139982411","n2139982412","n2139982413","n2139982414","n2139982415","n2139982416","n2139982417","n2139982419","n2139982418","n2139982411"]},"w17965023":{"id":"w17965023","tags":{"highway":"residential","name":"4th St"},"nodes":["n185963655","n1875654302","n185959983","n185963688","n185945405","n185963689","n185963690","n185945142","n185963691","n185961200","n1475284021","n1475293246","n1875654132","n1475293263"]},"w206805242":{"id":"w206805242","tags":{"area":"yes","building":"yes"},"nodes":["n2168544758","n2168544759","n2168544760","n2168544761","n2168544758"]},"w208639460":{"id":"w208639460","tags":{"area":"yes","building":"yes"},"nodes":["n2189112792","n2189112793","n2189112789","n2189112790","n2189112792"]},"w208639447":{"id":"w208639447","tags":{"area":"yes","building":"yes"},"nodes":["n2189112736","n2189112737","n2189112738","n2189112739","n2189112736"]},"w208639458":{"id":"w208639458","tags":{"area":"yes","building":"yes"},"nodes":["n2189112784","n2189112785","n2189112786","n2189112787","n2189112784"]},"w203985744":{"id":"w203985744","tags":{"highway":"service"},"nodes":["n2139982425","n2139982400"]},"w208639459":{"id":"w208639459","tags":{"area":"yes","building":"yes"},"nodes":["n2189112788","n2189112789","n2189112790","n2189112791","n2189112788"]},"w203985742":{"id":"w203985742","tags":{"amenity":"shelter","area":"yes","shelter_type":"picnic_shelter"},"nodes":["n2139982407","n2139982408","n2139982409","n2139982410","n2139982407"]},"w206805243":{"id":"w206805243","tags":{"area":"yes","building":"yes"},"nodes":["n2168544762","n2168544763","n2168544764","n2168544765","n2168544762"]},"n185959081":{"id":"n185959081","loc":[-85.628469,41.948674]},"n185967427":{"id":"n185967427","loc":[-85.632054,41.951174]},"n185967424":{"id":"n185967424","loc":[-85.6320391,41.9499109]},"n185968101":{"id":"n185968101","loc":[-85.6308395,41.9511969]},"n185960792":{"id":"n185960792","loc":[-85.632074,41.953707]},"n185961389":{"id":"n185961389","loc":[-85.630935,41.959037]},"n185961391":{"id":"n185961391","loc":[-85.632169,41.959025]},"n185965395":{"id":"n185965395","loc":[-85.63216,41.959859]},"n185966953":{"id":"n185966953","loc":[-85.630894,41.957428]},"n185966955":{"id":"n185966955","loc":[-85.632122,41.957427]},"n185967430":{"id":"n185967430","loc":[-85.632077,41.952453]},"n185967432":{"id":"n185967432","loc":[-85.632095,41.954685]},"n185967434":{"id":"n185967434","loc":[-85.632121,41.955914]},"n185967436":{"id":"n185967436","loc":[-85.632128,41.9583]},"n185967438":{"id":"n185967438","loc":[-85.632187,41.960681]},"n185967440":{"id":"n185967440","loc":[-85.632182,41.961493]},"n185968102":{"id":"n185968102","loc":[-85.630855,41.952452]},"n185968104":{"id":"n185968104","loc":[-85.630887,41.953714]},"n185968106":{"id":"n185968106","loc":[-85.630883,41.954692]},"n185968108":{"id":"n185968108","loc":[-85.630904,41.955913]},"n185968110":{"id":"n185968110","loc":[-85.630904,41.958058]},"n185968112":{"id":"n185968112","loc":[-85.630952,41.960667]},"n185968114":{"id":"n185968114","loc":[-85.630972,41.961495]},"n185968116":{"id":"n185968116","loc":[-85.630962,41.961967]},"n185978969":{"id":"n185978969","loc":[-85.633214,41.948618]},"n185985812":{"id":"n185985812","loc":[-85.633274,41.951159]},"n185986155":{"id":"n185986155","loc":[-85.633258,41.949893]},"n2208608826":{"id":"n2208608826","loc":[-85.6339222,41.9486225]},"w17964531":{"id":"w17964531","tags":{"highway":"residential","name":"Willow Dr"},"nodes":["n185959079","n185959081"]},"w17967386":{"id":"w17967386","tags":{"highway":"residential","name":"East Armitage Street"},"nodes":["n185982195","n185968101","n185967427","n185985812","n185974583"]},"w17965502":{"id":"w17965502","tags":{"highway":"residential","name":"Elm Street"},"nodes":["n185968100","n185968101","n185968102","n185968104","n185968106","n185968108","n185966953","n185968110","n185961389","n185968112","n185968114","n185968116"]},"w17967844":{"id":"w17967844","tags":{"highway":"residential","name":"East Bennett Street"},"nodes":["n185982193","n185967424","n185986155","n185978390"]},"w17966581":{"id":"w17966581","tags":{"highway":"residential","name":"E Kelsey St"},"nodes":["n185978967","n185978969","n2208608826","n185971578"]},"w17965402":{"id":"w17965402","tags":{"highway":"residential","name":"Walnut Street"},"nodes":["n185967422","n185967424","n185967427","n185967430","n185960792","n185967432","n185967434","n185966955","n185967436","n185961391","n185965395","n185967438","n185967440"]},"n2199093506":{"id":"n2199093506","loc":[-85.6251879,41.9478322]},"n2199093505":{"id":"n2199093505","loc":[-85.6252076,41.9477749]},"n2199093504":{"id":"n2199093504","loc":[-85.6252289,41.9477602]},"n2199093503":{"id":"n2199093503","loc":[-85.625201,41.9477492]},"n2199093502":{"id":"n2199093502","loc":[-85.6251682,41.9477066]},"n2199093501":{"id":"n2199093501","loc":[-85.6251715,41.947609]},"n2199093500":{"id":"n2199093500","loc":[-85.6252125,41.9475639]},"n2199093499":{"id":"n2199093499","loc":[-85.6252896,41.9475602]},"n2199093498":{"id":"n2199093498","loc":[-85.6253027,41.9475334]},"n2199093497":{"id":"n2199093497","loc":[-85.6253437,41.9474822]},"n2199093496":{"id":"n2199093496","loc":[-85.6254421,41.9474675]},"n2199093495":{"id":"n2199093495","loc":[-85.6256503,41.9474944]},"n2199093494":{"id":"n2199093494","loc":[-85.6257257,41.9476127]},"n2199093493":{"id":"n2199093493","loc":[-85.6257028,41.9477285]},"n2199093492":{"id":"n2199093492","loc":[-85.6255339,41.9478102]},"n2199093491":{"id":"n2199093491","loc":[-85.6253912,41.9478224]},"n2199093490":{"id":"n2199093490","loc":[-85.6253043,41.947859]},"n2199093489":{"id":"n2199093489","loc":[-85.6252027,41.9478846]},"n2199093458":{"id":"n2199093458","loc":[-85.6246876,41.9486617]},"n2199093457":{"id":"n2199093457","loc":[-85.6243127,41.9486583]},"n2199093456":{"id":"n2199093456","loc":[-85.624306,41.9490569]},"n2199093455":{"id":"n2199093455","loc":[-85.624681,41.9490603]},"n2199093514":{"id":"n2199093514","loc":[-85.6236228,41.9496059]},"n2199093513":{"id":"n2199093513","loc":[-85.6236231,41.9496997]},"n2199093512":{"id":"n2199093512","loc":[-85.623357,41.9497002]},"n2199093511":{"id":"n2199093511","loc":[-85.6233567,41.9496136]},"n2199093508":{"id":"n2199093508","loc":[-85.6239735,41.9494287]},"n2199093507":{"id":"n2199093507","loc":[-85.6239741,41.9496052]},"n2199093488":{"id":"n2199093488","loc":[-85.624497,41.9512286]},"n2199093487":{"id":"n2199093487","loc":[-85.6244966,41.9511259]},"n2199093486":{"id":"n2199093486","loc":[-85.6243151,41.9511263]},"n2199093485":{"id":"n2199093485","loc":[-85.6243154,41.951229]},"n2199093484":{"id":"n2199093484","loc":[-85.6241205,41.9508665]},"n2199093483":{"id":"n2199093483","loc":[-85.624115,41.9505249]},"n2199093482":{"id":"n2199093482","loc":[-85.6243149,41.9505231]},"n2199093481":{"id":"n2199093481","loc":[-85.6243203,41.9508648]},"n2199093480":{"id":"n2199093480","loc":[-85.624393,41.9508668]},"n2199093479":{"id":"n2199093479","loc":[-85.6243904,41.9505956]},"n2199093478":{"id":"n2199093478","loc":[-85.6246727,41.950594]},"n2199093477":{"id":"n2199093477","loc":[-85.624675,41.9508203]},"n2199093476":{"id":"n2199093476","loc":[-85.6245097,41.9508212]},"n2199093475":{"id":"n2199093475","loc":[-85.6245101,41.9508662]},"n2199093474":{"id":"n2199093474","loc":[-85.6241008,41.9493459]},"n2199093473":{"id":"n2199093473","loc":[-85.6242442,41.9493459]},"n2199093472":{"id":"n2199093472","loc":[-85.6242442,41.9493681]},"n2199093471":{"id":"n2199093471","loc":[-85.6243397,41.9493681]},"n2199093470":{"id":"n2199093470","loc":[-85.6243417,41.9493511]},"n2199093469":{"id":"n2199093469","loc":[-85.6247251,41.9493485]},"n2199093468":{"id":"n2199093468","loc":[-85.6247548,41.9504949]},"n2199093467":{"id":"n2199093467","loc":[-85.6241214,41.9505017]},"n2199093466":{"id":"n2199093466","loc":[-85.6254398,41.950174]},"n2199093465":{"id":"n2199093465","loc":[-85.6254412,41.9499872]},"n2199093464":{"id":"n2199093464","loc":[-85.6255363,41.9499876]},"n2199093463":{"id":"n2199093463","loc":[-85.6255374,41.9498439]},"n2199093462":{"id":"n2199093462","loc":[-85.6255638,41.949844]},"n2199093461":{"id":"n2199093461","loc":[-85.6255652,41.9496672]},"n2199093460":{"id":"n2199093460","loc":[-85.6251823,41.9496656]},"n2199093459":{"id":"n2199093459","loc":[-85.6251785,41.9501729]},"n2199093510":{"id":"n2199093510","loc":[-85.6229922,41.9496143]},"n2199093509":{"id":"n2199093509","loc":[-85.6229915,41.9494306]},"n185948903":{"id":"n185948903","loc":[-85.616514,41.947449]},"n185955120":{"id":"n185955120","loc":[-85.620103,41.951]},"n185955143":{"id":"n185955143","loc":[-85.619784,41.94746]},"n185960124":{"id":"n185960124","loc":[-85.615238,41.947468]},"n185961362":{"id":"n185961362","loc":[-85.617437,41.947451]},"n185961364":{"id":"n185961364","loc":[-85.61861,41.947456]},"n185961367":{"id":"n185961367","loc":[-85.620088,41.947458]},"n185965105":{"id":"n185965105","loc":[-85.620087,41.94924]},"n185970220":{"id":"n185970220","loc":[-85.62156,41.948333]},"n185974697":{"id":"n185974697","loc":[-85.6201059,41.950132]},"n2138420778":{"id":"n2138420778","loc":[-85.616948,41.9474499]},"w17967535":{"id":"w17967535","tags":{"highway":"residential","name":"10th Ave"},"nodes":["n185955120","n185986812","n185983141"]},"w209716130":{"id":"w209716130","tags":{"area":"yes","building":"yes"},"nodes":["n2199093485","n2199093486","n2199093487","n2199093488","n2199093485"]},"w17964788":{"id":"w17964788","tags":{"highway":"residential","name":"6th Ave"},"nodes":["n185960124","n185948903","n2138420778","n185961362","n185961364","n185955143","n185961367","n185961369","n185961371"]},"w17965159":{"id":"w17965159","tags":{"highway":"residential","name":"8th Ave"},"nodes":["n185965105","n185965108","n185965110"]},"w209716125":{"id":"w209716125","tags":{"area":"yes","building":"yes"},"nodes":["n2199093459","n2199093460","n2199093461","n2199093462","n2199093463","n2199093464","n2199093465","n2199093466","n2199093459"]},"w17965699":{"id":"w17965699","tags":{"highway":"residential","name":"7th Ave"},"nodes":["n185970220","n185970222","n185970224"]},"w209716132":{"id":"w209716132","tags":{"area":"yes","building":"yes"},"nodes":["n2199093507","n2199093508","n2199093509","n2199093510","n2199093511","n2199093512","n2199093513","n2199093514","n2199093507"]},"w17966129":{"id":"w17966129","tags":{"highway":"residential","name":"9th Ave"},"nodes":["n185974697","n185974699"]},"w209716127":{"id":"w209716127","tags":{"area":"yes","building":"yes"},"nodes":["n2199093475","n2199093476","n2199093477","n2199093478","n2199093479","n2199093480","n2199093475"]},"w209716131":{"id":"w209716131","tags":{"area":"yes","natural":"water","water":"pond"},"nodes":["n2199093489","n2199093490","n2199093491","n2199093492","n2199093493","n2199093494","n2199093495","n2199093496","n2199093497","n2199093498","n2199093499","n2199093500","n2199093501","n2199093502","n2199093503","n2199093504","n2199093505","n2199093506","n2199093489"]},"w209716126":{"id":"w209716126","tags":{"area":"yes","building":"yes"},"nodes":["n2199093467","n2199093468","n2199093469","n2199093470","n2199093471","n2199093472","n2199093473","n2199093474","n2199093467"]},"w209716124":{"id":"w209716124","tags":{"area":"yes","building":"yes"},"nodes":["n2199093455","n2199093456","n2199093457","n2199093458","n2199093455"]},"w209716128":{"id":"w209716128","tags":{"area":"yes","building":"yes"},"nodes":["n2199093481","n2199093482","n2199093483","n2199093484","n2199093481"]},"n185949872":{"id":"n185949872","loc":[-85.643009,41.949264]},"n185949875":{"id":"n185949875","loc":[-85.642598,41.94929]},"n185949877":{"id":"n185949877","loc":[-85.642127,41.949382]},"n185949881":{"id":"n185949881","loc":[-85.64169,41.949936]},"n185988165":{"id":"n185988165","loc":[-85.642167,41.947657]},"n185988167":{"id":"n185988167","loc":[-85.642347,41.947662]},"n185988169":{"id":"n185988169","loc":[-85.642621,41.947659]},"n185965019":{"id":"n185965019","loc":[-85.6385084,41.951127]},"n1475293248":{"id":"n1475293248","loc":[-85.6386095,41.9512214]},"n185962639":{"id":"n185962639","loc":[-85.649669,41.949161]},"n185962810":{"id":"n185962810","loc":[-85.649907,41.949157]},"n185964355":{"id":"n185964355","loc":[-85.637412,41.9511359]},"n185965021":{"id":"n185965021","loc":[-85.638661,41.952386]},"n185965023":{"id":"n185965023","loc":[-85.638654,41.953665]},"n185965025":{"id":"n185965025","loc":[-85.638694,41.954649]},"n185965027":{"id":"n185965027","loc":[-85.638724,41.955913]},"n185971415":{"id":"n185971415","loc":[-85.644466,41.949246]},"n185971417":{"id":"n185971417","loc":[-85.647021,41.949193]},"n185971420":{"id":"n185971420","loc":[-85.648476,41.949169]},"n185979975":{"id":"n185979975","loc":[-85.644429,41.947633]},"n185988171":{"id":"n185988171","loc":[-85.645377,41.947622]},"w17963211":{"id":"w17963211","tags":{"highway":"residential"},"nodes":["n185949870","n185949872","n185949875","n185949877","n185949881"]},"w17965839":{"id":"w17965839","tags":{"highway":"residential","name":"Arnold St"},"nodes":["n185949870","n185971415","n185971417","n185971420","n185962639","n185962810"]},"w17967618":{"id":"w17967618","tags":{"highway":"residential","name":"Pierson St"},"nodes":["n185967777","n185988165","n185988167","n185988169","n185985824","n185979975","n185988171"]},"w17965149":{"id":"w17965149","tags":{"highway":"residential","name":"Oak St"},"nodes":["n185965019","n1475293248","n185965021","n185965023","n185965025","n185965027"]},"w17966118":{"id":"w17966118","tags":{"highway":"residential","name":"West Armitage Street"},"nodes":["n185974583","n185974585","n185964355","n185965019"]},"n2208608800":{"id":"n2208608800","loc":[-85.6354294,41.9486201]},"n2199109806":{"id":"n2199109806","loc":[-85.6350474,41.9477884]},"n2199109804":{"id":"n2199109804","loc":[-85.6350476,41.9477962]},"n2199109802":{"id":"n2199109802","loc":[-85.635002,41.9477969]},"n2199109799":{"id":"n2199109799","loc":[-85.6350018,41.9477883]},"n2199109797":{"id":"n2199109797","loc":[-85.6349141,41.9477897]},"n2199109795":{"id":"n2199109795","loc":[-85.6349131,41.9477535]},"n2199109793":{"id":"n2199109793","loc":[-85.6349395,41.9477531]},"n2199109791":{"id":"n2199109791","loc":[-85.6349382,41.9477077]},"n2199109789":{"id":"n2199109789","loc":[-85.6351236,41.9477049]},"n2199109787":{"id":"n2199109787","loc":[-85.6351259,41.9477872]},"n2199109785":{"id":"n2199109785","loc":[-85.634972,41.9475992]},"n2199109783":{"id":"n2199109783","loc":[-85.6349206,41.9475997]},"n2199109770":{"id":"n2199109770","loc":[-85.6348499,41.9475461]},"n2199109768":{"id":"n2199109768","loc":[-85.6348499,41.9475084]},"n2199109765":{"id":"n2199109765","loc":[-85.6349241,41.9474569]},"n2199109763":{"id":"n2199109763","loc":[-85.634967,41.9474564]},"n2199109762":{"id":"n2199109762","loc":[-85.6350405,41.9475121]},"n2199109761":{"id":"n2199109761","loc":[-85.6350405,41.9475419]},"n2199109753":{"id":"n2199109753","loc":[-85.6342443,41.9478391]},"n2199109751":{"id":"n2199109751","loc":[-85.6342427,41.9477927]},"n2199109745":{"id":"n2199109745","loc":[-85.6342439,41.9476859]},"n2199109743":{"id":"n2199109743","loc":[-85.6342429,41.9476575]},"n2199109741":{"id":"n2199109741","loc":[-85.6344615,41.9476533]},"n2199109739":{"id":"n2199109739","loc":[-85.6344678,41.9478348]},"n2199109737":{"id":"n2199109737","loc":[-85.634416,41.9480059]},"n2199109735":{"id":"n2199109735","loc":[-85.6344145,41.9478983]},"n2199109733":{"id":"n2199109733","loc":[-85.6342749,41.9478993]},"n2199109731":{"id":"n2199109731","loc":[-85.6342753,41.9479272]},"n2199109729":{"id":"n2199109729","loc":[-85.6342498,41.9479274]},"n2199109727":{"id":"n2199109727","loc":[-85.6342505,41.9479762]},"n2199109725":{"id":"n2199109725","loc":[-85.6342743,41.947976]},"n2199109723":{"id":"n2199109723","loc":[-85.6342747,41.948007]},"n2199109721":{"id":"n2199109721","loc":[-85.6343415,41.9476355]},"n2199109719":{"id":"n2199109719","loc":[-85.6343391,41.9474973]},"n2199109717":{"id":"n2199109717","loc":[-85.6343133,41.9474798]},"n2199109715":{"id":"n2199109715","loc":[-85.6342874,41.9474737]},"n2199109709":{"id":"n2199109709","loc":[-85.6349804,41.94815]},"n2199109707":{"id":"n2199109707","loc":[-85.6348915,41.9481505]},"n2199109705":{"id":"n2199109705","loc":[-85.6348917,41.9481692]},"n2199109702":{"id":"n2199109702","loc":[-85.6348522,41.9481694]},"n2199109700":{"id":"n2199109700","loc":[-85.6348532,41.9482679]},"n2199109698":{"id":"n2199109698","loc":[-85.6348315,41.948268]},"n2199109696":{"id":"n2199109696","loc":[-85.6348318,41.9482955]},"n2199109694":{"id":"n2199109694","loc":[-85.6349653,41.9482946]},"n2199109692":{"id":"n2199109692","loc":[-85.6349656,41.9483211]},"n2199109690":{"id":"n2199109690","loc":[-85.634999,41.9483209]},"n2199109688":{"id":"n2199109688","loc":[-85.6349987,41.9482947]},"n2199109686":{"id":"n2199109686","loc":[-85.6351753,41.9482935]},"n2199109684":{"id":"n2199109684","loc":[-85.6351749,41.9482617]},"n2199109682":{"id":"n2199109682","loc":[-85.6351588,41.9482618]},"n2199109680":{"id":"n2199109680","loc":[-85.6351575,41.9481518]},"n2199109678":{"id":"n2199109678","loc":[-85.6350671,41.9481524]},"n2199109676":{"id":"n2199109676","loc":[-85.6350649,41.9479659]},"n2199109674":{"id":"n2199109674","loc":[-85.6349785,41.9479665]},"n2199109671":{"id":"n2199109671","loc":[-85.6343069,41.9483263]},"n2199109669":{"id":"n2199109669","loc":[-85.6343052,41.9482981]},"n2199109658":{"id":"n2199109658","loc":[-85.6343314,41.9480549]},"n2199109656":{"id":"n2199109656","loc":[-85.6343305,41.9480461]},"n2199109654":{"id":"n2199109654","loc":[-85.634435,41.9480468]},"n2199109652":{"id":"n2199109652","loc":[-85.6344342,41.9483746]},"n2199109650":{"id":"n2199109650","loc":[-85.6344629,41.9483727]},"n2199109648":{"id":"n2199109648","loc":[-85.6344637,41.9484561]},"n2199109645":{"id":"n2199109645","loc":[-85.63443,41.9484567]},"n2199109642":{"id":"n2199109642","loc":[-85.6344317,41.948505]},"n185964352":{"id":"n185964352","loc":[-85.6373958,41.9489943]},"n185964351":{"id":"n185964351","loc":[-85.637113,41.9486]},"n2208608825":{"id":"n2208608825","loc":[-85.6354483,41.9494241]},"n2208608823":{"id":"n2208608823","loc":[-85.6360418,41.949416]},"n2208608821":{"id":"n2208608821","loc":[-85.6360458,41.9495802]},"n2208608811":{"id":"n2208608811","loc":[-85.6357458,41.9495843]},"n2208608808":{"id":"n2208608808","loc":[-85.6357508,41.9497835]},"n2208608806":{"id":"n2208608806","loc":[-85.6354573,41.9497875]},"n2208608795":{"id":"n2208608795","loc":[-85.6354595,41.9498778]},"n2199109638":{"id":"n2199109638","loc":[-85.6349605,41.949749]},"n2199109636":{"id":"n2199109636","loc":[-85.6349605,41.9497639]},"n2199109634":{"id":"n2199109634","loc":[-85.6349061,41.94971]},"n2199109632":{"id":"n2199109632","loc":[-85.6349048,41.9496569]},"n2199109630":{"id":"n2199109630","loc":[-85.6348835,41.9496571]},"n2199109628":{"id":"n2199109628","loc":[-85.6348829,41.9497103]},"n2199109626":{"id":"n2199109626","loc":[-85.635227,41.9497738]},"n2199109624":{"id":"n2199109624","loc":[-85.6352184,41.9497787]},"n2199109622":{"id":"n2199109622","loc":[-85.6351181,41.9497806]},"n2199109620":{"id":"n2199109620","loc":[-85.6351181,41.9497456]},"n2199109618":{"id":"n2199109618","loc":[-85.6348842,41.9497651]},"n2199109616":{"id":"n2199109616","loc":[-85.6348827,41.9496238]},"n2199109615":{"id":"n2199109615","loc":[-85.6351268,41.9496206]},"n2199109614":{"id":"n2199109614","loc":[-85.6351261,41.9495891]},"n2199109613":{"id":"n2199109613","loc":[-85.6351957,41.9495881]},"n2199109612":{"id":"n2199109612","loc":[-85.6351924,41.9494515]},"n2199109611":{"id":"n2199109611","loc":[-85.6353997,41.9494488]},"n2199109610":{"id":"n2199109610","loc":[-85.6354074,41.9497715]},"n2189015681":{"id":"n2189015681","loc":[-85.6344229,41.9509639]},"n2189015677":{"id":"n2189015677","loc":[-85.634424,41.9507396]},"n2138493843":{"id":"n2138493843","loc":[-85.6343935,41.9502836]},"n2138493840":{"id":"n2138493840","loc":[-85.634398,41.9506264]},"n354002838":{"id":"n354002838","loc":[-85.6345197,41.9510631]},"n2114807590":{"id":"n2114807590","loc":[-85.634511,41.9499767]},"n185964353":{"id":"n185964353","loc":[-85.6374092,41.9498755]},"n1819849180":{"id":"n1819849180","loc":[-85.6348236,41.94996]},"n1819849115":{"id":"n1819849115","loc":[-85.6354372,41.9499538]},"n1819848921":{"id":"n1819848921","loc":[-85.6348439,41.951064]},"n1819848885":{"id":"n1819848885","loc":[-85.6354575,41.9510578]},"n185984281":{"id":"n185984281","loc":[-85.638075,41.949872]},"n2208608827":{"id":"n2208608827","loc":[-85.6339169,41.9473191]},"n2199109749":{"id":"n2199109749","loc":[-85.6342082,41.9477934]},"n2199109747":{"id":"n2199109747","loc":[-85.6342045,41.9476867]},"n2199109713":{"id":"n2199109713","loc":[-85.6342404,41.9474746]},"n2199109711":{"id":"n2199109711","loc":[-85.6342404,41.9476355]},"n2199109673":{"id":"n2199109673","loc":[-85.6340886,41.9483282]},"n2199109667":{"id":"n2199109667","loc":[-85.6342403,41.9482988]},"n2199109665":{"id":"n2199109665","loc":[-85.6342386,41.9482116]},"n2199109662":{"id":"n2199109662","loc":[-85.6340861,41.9482135]},"n2199109660":{"id":"n2199109660","loc":[-85.6340802,41.9480562]},"n2199109640":{"id":"n2199109640","loc":[-85.6340928,41.9485063]},"n354031366":{"id":"n354031366","loc":[-85.6341667,41.9477778],"tags":{"amenity":"place_of_worship","name":"Faith Tabernacle Church","religion":"christian"}},"n2189015686":{"id":"n2189015686","loc":[-85.6337798,41.95099]},"n2189015684":{"id":"n2189015684","loc":[-85.6337794,41.9509674]},"n2189015673":{"id":"n2189015673","loc":[-85.6337501,41.9507457]},"n2189015669":{"id":"n2189015669","loc":[-85.6337501,41.9506974]},"n2189015665":{"id":"n2189015665","loc":[-85.6339034,41.9506959]},"n2189015662":{"id":"n2189015662","loc":[-85.6339015,41.950436]},"n2189015658":{"id":"n2189015658","loc":[-85.6334916,41.9504376]},"n2189015655":{"id":"n2189015655","loc":[-85.6334939,41.9507558]},"n2189015650":{"id":"n2189015650","loc":[-85.6334543,41.950756]},"n2189015649":{"id":"n2189015649","loc":[-85.633456,41.9509915]},"n2138493842":{"id":"n2138493842","loc":[-85.6339937,41.9502836]},"n2138493841":{"id":"n2138493841","loc":[-85.6339983,41.9506281]},"n2114807579":{"id":"n2114807579","loc":[-85.6333644,41.9510682]},"n2114807573":{"id":"n2114807573","loc":[-85.6333557,41.9499819]},"n354031330":{"id":"n354031330","loc":[-85.6341667,41.9497222],"tags":{"amenity":"place_of_worship","name":"Trinity Episcopal Church","religion":"christian"}},"n185960794":{"id":"n185960794","loc":[-85.633307,41.9537]},"n185964357":{"id":"n185964357","loc":[-85.637432,41.952399]},"n185964358":{"id":"n185964358","loc":[-85.637452,41.953665]},"n185964359":{"id":"n185964359","loc":[-85.63746,41.954658]},"n185964360":{"id":"n185964360","loc":[-85.637473,41.95592]},"n185964361":{"id":"n185964361","loc":[-85.637468,41.956906]},"n185964362":{"id":"n185964362","loc":[-85.637483,41.958313]},"n185966957":{"id":"n185966957","loc":[-85.633361,41.957422]},"n185975351":{"id":"n185975351","loc":[-85.63334,41.9559]},"n185978784":{"id":"n185978784","loc":[-85.633311,41.954679]},"n185986157":{"id":"n185986157","loc":[-85.633287,41.952426]},"n185986158":{"id":"n185986158","loc":[-85.6333607,41.9582301],"tags":{"highway":"turning_circle"}},"w17965182":{"id":"w17965182","tags":{"highway":"residential","name":"W Prutzman St"},"nodes":["n185965289","n2189153241","n185965291"]},"w208627205":{"id":"w208627205","tags":{"area":"yes","building":"yes"},"nodes":["n2189015649","n2189015650","n2189015655","n2189015658","n2189015662","n2189015665","n2189015669","n2189015673","n2189015677","n2189015681","n2189015684","n2189015686","n2189015649"]},"w209717042":{"id":"w209717042","tags":{"amenity":"place_of_worship","area":"yes","building":"yes","denomination":"presbyterian","name":"First Presbyterian Church","religion":"christian"},"nodes":["n2199109610","n2199109611","n2199109612","n2199109613","n2199109614","n2199109615","n2199109616","n2199109630","n2199109632","n2199109634","n2199109628","n2199109618","n2199109636","n2199109638","n2199109620","n2199109622","n2199109624","n2199109626","n2199109610"]},"w209717045":{"id":"w209717045","tags":{"area":"yes","building":"yes"},"nodes":["n2199109711","n2199109713","n2199109715","n2199109717","n2199109719","n2199109721","n2199109711"]},"w209717047":{"id":"w209717047","tags":{"area":"yes","building":"yes"},"nodes":["n2199109739","n2199109741","n2199109743","n2199109745","n2199109747","n2199109749","n2199109751","n2199109753","n2199109739"]},"w209717044":{"id":"w209717044","tags":{"area":"yes","building":"yes"},"nodes":["n2199109674","n2199109676","n2199109678","n2199109680","n2199109682","n2199109684","n2199109686","n2199109688","n2199109690","n2199109692","n2199109694","n2199109696","n2199109698","n2199109700","n2199109702","n2199109705","n2199109707","n2199109709","n2199109674"]},"w210822776":{"id":"w210822776","tags":{"highway":"service","service":"alley","surface":"unpaved"},"nodes":["n2208608795","n2208608806","n2208608825","n2208608800","n2189153241"]},"w210822778":{"id":"w210822778","tags":{"highway":"service","service":"alley"},"nodes":["n2208608826","n2208608827"]},"w209717050":{"id":"w209717050","tags":{"area":"yes","building":"yes"},"nodes":["n2199109787","n2199109789","n2199109791","n2199109793","n2199109795","n2199109797","n2199109799","n2199109802","n2199109804","n2199109806","n2199109787"]},"w17965097":{"id":"w17965097","tags":{"highway":"residential","name":"Maple Street"},"nodes":["n185964351","n185964352","n185964353","n185964355","n185964357","n185964358","n185964359","n185964360","n185964361","n185964362"]},"w17965856":{"id":"w17965856","tags":{"highway":"residential","name":"W Kelsey St"},"nodes":["n185971578","n2208608800","n185971580","n185964351"]},"w17967444":{"id":"w17967444","tags":{"highway":"residential","name":"East Street"},"nodes":["n185966937","n185978969","n185986155","n185985812","n185986157","n185960794","n185978784","n185975351","n185966957","n185986158"]},"w17967764":{"id":"w17967764","tags":{"highway":"residential","name":"Rock River Ave"},"nodes":["n185984017","n185964351"]},"w170848329":{"id":"w170848329","tags":{"leisure":"park","name":"LaFayette Park"},"nodes":["n1819849180","n1819849115","n1819848885","n1819848921","n1819849180"]},"w17967208":{"id":"w17967208","tags":{"highway":"residential","name":"West Bennett Street"},"nodes":["n185978390","n2208608795","n185984020","n185964353","n185984281"]},"w17965349":{"id":"w17965349","tags":{"highway":"residential","name":"E Prutzman St"},"nodes":["n185966937","n2208608827","n185965289"]},"w209717049":{"id":"w209717049","tags":{"area":"yes","building":"yes"},"nodes":["n2199109761","n2199109762","n2199109763","n2199109765","n2199109768","n2199109770","n2199109783","n2199109785","n2199109761"]},"w203841840":{"id":"w203841840","tags":{"area":"yes","leisure":"playground"},"nodes":["n2138493840","n2138493841","n2138493842","n2138493843","n2138493840"]},"w209717043":{"id":"w209717043","tags":{"amenity":"place_of_worship","area":"yes","building":"church","denomination":"methodist","name":"First United Methodist Church","religion":"christian"},"nodes":["n2199109640","n2199109642","n2199109645","n2199109648","n2199109650","n2199109652","n2199109654","n2199109656","n2199109658","n2199109660","n2199109662","n2199109665","n2199109667","n2199109669","n2199109671","n2199109673","n2199109640"]},"w201484341":{"id":"w201484341","tags":{"amenity":"school","name":"Hoppin School"},"nodes":["n354002838","n2114807579","n2114807573","n2114807590","n354002838"]},"w209717046":{"id":"w209717046","tags":{"area":"yes","building":"yes"},"nodes":["n2199109723","n2199109725","n2199109727","n2199109729","n2199109731","n2199109733","n2199109735","n2199109737","n2199109723"]},"w210822777":{"id":"w210822777","tags":{"amenity":"parking","area":"yes"},"nodes":["n2208608806","n2208608808","n2208608811","n2208608821","n2208608823","n2208608825","n2208608806"]},"n185954965":{"id":"n185954965","loc":[-85.6191189,41.9441922]},"n185954968":{"id":"n185954968","loc":[-85.6194384,41.9442405]},"n185954970":{"id":"n185954970","loc":[-85.6196543,41.9443252]},"n185954972":{"id":"n185954972","loc":[-85.6197862,41.9444539]},"n354002931":{"id":"n354002931","loc":[-85.6198991,41.9455269]},"n354030853":{"id":"n354030853","loc":[-85.6219444,41.9455556],"tags":{"amenity":"place_of_worship","name":"Grant Chapel","religion":"christian"}},"n367815963":{"id":"n367815963","loc":[-85.6202778,41.9461111],"tags":{"building":"yes","name":"George Washington Carver Community Center"}},"n185947331":{"id":"n185947331","loc":[-85.618779,41.943269]},"n185947333":{"id":"n185947333","loc":[-85.618795,41.943511]},"n185947336":{"id":"n185947336","loc":[-85.618711,41.94413]},"n185947338":{"id":"n185947338","loc":[-85.618704,41.944189]},"n185947339":{"id":"n185947339","loc":[-85.618597,41.944337]},"n185947340":{"id":"n185947340","loc":[-85.618485,41.944528]},"n185947343":{"id":"n185947343","loc":[-85.618442,41.944716]},"n185947345":{"id":"n185947345","loc":[-85.618457,41.945107]},"n185947347":{"id":"n185947347","loc":[-85.618296,41.945338]},"n185947374":{"id":"n185947374","loc":[-85.616748,41.944453]},"n185947375":{"id":"n185947375","loc":[-85.616813,41.944646]},"n185947376":{"id":"n185947376","loc":[-85.616859,41.945196]},"n185947377":{"id":"n185947377","loc":[-85.616941,41.945352]},"n185947406":{"id":"n185947406","loc":[-85.618184,41.944227]},"n185947409":{"id":"n185947409","loc":[-85.617911,41.943875]},"n185947410":{"id":"n185947410","loc":[-85.617579,41.943682]},"n185947411":{"id":"n185947411","loc":[-85.61713,41.943589]},"n185947412":{"id":"n185947412","loc":[-85.616549,41.943559]},"n185947414":{"id":"n185947414","loc":[-85.616482,41.943556]},"n185947464":{"id":"n185947464","loc":[-85.616526,41.943788]},"n185947466":{"id":"n185947466","loc":[-85.616504,41.944002]},"n185948863":{"id":"n185948863","loc":[-85.619017,41.943391]},"n185948865":{"id":"n185948865","loc":[-85.619059,41.943368]},"n185955022":{"id":"n185955022","loc":[-85.620088,41.945571]},"n185955025":{"id":"n185955025","loc":[-85.620051,41.945505]},"n185955028":{"id":"n185955028","loc":[-85.62001,41.94541]},"n185980371":{"id":"n185980371","loc":[-85.620982,41.944742]},"n185980398":{"id":"n185980398","loc":[-85.621305,41.944782]},"n185980401":{"id":"n185980401","loc":[-85.621174,41.944819]},"n185980403":{"id":"n185980403","loc":[-85.621029,41.944871]},"n185980405":{"id":"n185980405","loc":[-85.620741,41.945011]},"n185980407":{"id":"n185980407","loc":[-85.620616,41.945085]},"n185980409":{"id":"n185980409","loc":[-85.620506,41.945172]},"n185980411":{"id":"n185980411","loc":[-85.620394,41.945273]},"n185980413":{"id":"n185980413","loc":[-85.620316,41.94536]},"n185980415":{"id":"n185980415","loc":[-85.620257,41.945452]},"n185980417":{"id":"n185980417","loc":[-85.620212,41.945535]},"n185985910":{"id":"n185985910","loc":[-85.620101,41.945811]},"n185985912":{"id":"n185985912","loc":[-85.620081,41.945937]},"n1475283972":{"id":"n1475283972","loc":[-85.6198991,41.9437179]},"n1475283982":{"id":"n1475283982","loc":[-85.6195022,41.9433463]},"n1475284007":{"id":"n1475284007","loc":[-85.6193037,41.9433383]},"n1475284040":{"id":"n1475284040","loc":[-85.6197329,41.9434121]},"n1475284044":{"id":"n1475284044","loc":[-85.6198756,41.9435363]},"n1475284050":{"id":"n1475284050","loc":[-85.6199689,41.9432106]},"n1475284053":{"id":"n1475284053","loc":[-85.6198943,41.9432921]},"n185954974":{"id":"n185954974","loc":[-85.6198296,41.94473]},"n185954977":{"id":"n185954977","loc":[-85.6200474,41.9447384]},"n2196831365":{"id":"n2196831365","loc":[-85.6202259,41.9460883]},"n2196831366":{"id":"n2196831366","loc":[-85.6202245,41.9458642]},"n2196831367":{"id":"n2196831367","loc":[-85.6205184,41.9458631]},"n2196831368":{"id":"n2196831368","loc":[-85.6205189,41.9459437]},"n2196831369":{"id":"n2196831369","loc":[-85.6203879,41.9459441]},"n2196831370":{"id":"n2196831370","loc":[-85.6203888,41.9460878]},"n2196831371":{"id":"n2196831371","loc":[-85.6184046,41.9465663]},"n2196831372":{"id":"n2196831372","loc":[-85.6191563,41.9465618]},"n2196831373":{"id":"n2196831373","loc":[-85.6191536,41.946319]},"n2196831374":{"id":"n2196831374","loc":[-85.6187356,41.9463216]},"n2196831375":{"id":"n2196831375","loc":[-85.6187334,41.9461197]},"n2196831376":{"id":"n2196831376","loc":[-85.6193167,41.9461162]},"n2196831377":{"id":"n2196831377","loc":[-85.6193156,41.9460229]},"n2196831378":{"id":"n2196831378","loc":[-85.619622,41.946021]},"n2196831379":{"id":"n2196831379","loc":[-85.6196237,41.9461712]},"n2196831380":{"id":"n2196831380","loc":[-85.6197702,41.9461703]},"n2196831381":{"id":"n2196831381","loc":[-85.6197685,41.9460202]},"n2196831382":{"id":"n2196831382","loc":[-85.6197323,41.9460204]},"n2196831383":{"id":"n2196831383","loc":[-85.6197305,41.9458563]},"n2196831384":{"id":"n2196831384","loc":[-85.6196165,41.945857]},"n2196831385":{"id":"n2196831385","loc":[-85.6196156,41.9457764]},"n2196831386":{"id":"n2196831386","loc":[-85.6194472,41.9457775]},"n2196831387":{"id":"n2196831387","loc":[-85.6194151,41.9457777]},"n2196831388":{"id":"n2196831388","loc":[-85.6183779,41.9457883]},"n2196831389":{"id":"n2196831389","loc":[-85.6183842,41.9461317]},"n2196831390":{"id":"n2196831390","loc":[-85.6185026,41.9461304]},"n2196831391":{"id":"n2196831391","loc":[-85.6185061,41.9463194]},"n2196831392":{"id":"n2196831392","loc":[-85.6184001,41.9463205]},"n2196831393":{"id":"n2196831393","loc":[-85.6182482,41.9464163]},"n2196831394":{"id":"n2196831394","loc":[-85.6182467,41.9463193]},"n2196831395":{"id":"n2196831395","loc":[-85.6180389,41.946321]},"n2196831397":{"id":"n2196831397","loc":[-85.6180404,41.946418]},"n185947303":{"id":"n185947303","loc":[-85.611074,41.943389]},"n185947304":{"id":"n185947304","loc":[-85.611332,41.943267]},"n185947305":{"id":"n185947305","loc":[-85.611635,41.943218]},"n185947306":{"id":"n185947306","loc":[-85.612762,41.943311]},"n185947308":{"id":"n185947308","loc":[-85.613027,41.943327]},"n185947310":{"id":"n185947310","loc":[-85.615377,41.942996]},"n185947312":{"id":"n185947312","loc":[-85.615701,41.943007]},"n185947314":{"id":"n185947314","loc":[-85.61604,41.943067]},"n185947315":{"id":"n185947315","loc":[-85.61626,41.943083]},"n185947316":{"id":"n185947316","loc":[-85.616507,41.943048]},"n185947319":{"id":"n185947319","loc":[-85.616702,41.94299]},"n185947321":{"id":"n185947321","loc":[-85.617078,41.942918]},"n185947322":{"id":"n185947322","loc":[-85.617366,41.942973]},"n185947323":{"id":"n185947323","loc":[-85.617601,41.943033]},"n185947325":{"id":"n185947325","loc":[-85.617799,41.943027]},"n185947327":{"id":"n185947327","loc":[-85.618264,41.942961]},"n185947328":{"id":"n185947328","loc":[-85.618508,41.942972]},"n185947329":{"id":"n185947329","loc":[-85.618707,41.943076]},"n185947361":{"id":"n185947361","loc":[-85.615356,41.944922]},"n185947363":{"id":"n185947363","loc":[-85.61536,41.944893]},"n185947365":{"id":"n185947365","loc":[-85.615406,41.944547]},"n185947367":{"id":"n185947367","loc":[-85.61548,41.944351]},"n185947369":{"id":"n185947369","loc":[-85.615805,41.94419]},"n185947371":{"id":"n185947371","loc":[-85.616166,41.944156]},"n185947373":{"id":"n185947373","loc":[-85.616411,41.944197]},"n185947416":{"id":"n185947416","loc":[-85.616335,41.94343]},"n185947417":{"id":"n185947417","loc":[-85.616069,41.943293]},"n185947419":{"id":"n185947419","loc":[-85.615803,41.943249]},"n185947420":{"id":"n185947420","loc":[-85.615524,41.943342]},"n185947421":{"id":"n185947421","loc":[-85.615311,41.94353]},"n185947422":{"id":"n185947422","loc":[-85.614338,41.943558]},"n185947423":{"id":"n185947423","loc":[-85.61422,41.94369]},"n185947425":{"id":"n185947425","loc":[-85.614221,41.944224]},"n185947427":{"id":"n185947427","loc":[-85.614198,41.944888]},"n185947429":{"id":"n185947429","loc":[-85.614221,41.945439]},"n185947468":{"id":"n185947468","loc":[-85.615908,41.944756]},"n185947470":{"id":"n185947470","loc":[-85.615871,41.944888]},"n185947472":{"id":"n185947472","loc":[-85.615878,41.94507]},"n185955153":{"id":"n185955153","loc":[-85.620087,41.947701]},"n185960690":{"id":"n185960690","loc":[-85.620141,41.951901]},"n185978817":{"id":"n185978817","loc":[-85.617193,41.954706]},"n185985916":{"id":"n185985916","loc":[-85.620088,41.94758]},"n185985918":{"id":"n185985918","loc":[-85.620133,41.951538]},"n185985919":{"id":"n185985919","loc":[-85.62013,41.952104]},"n185985920":{"id":"n185985920","loc":[-85.620104,41.952305]},"n185985921":{"id":"n185985921","loc":[-85.620062,41.952499]},"n185985922":{"id":"n185985922","loc":[-85.619993,41.952702]},"n185985925":{"id":"n185985925","loc":[-85.619879,41.952986]},"n185985927":{"id":"n185985927","loc":[-85.619689,41.95329]},"n185985928":{"id":"n185985928","loc":[-85.619508,41.953521]},"n185985929":{"id":"n185985929","loc":[-85.619286,41.953728]},"n185985930":{"id":"n185985930","loc":[-85.618925,41.954007]},"n185985931":{"id":"n185985931","loc":[-85.618638,41.954189]},"n185985932":{"id":"n185985932","loc":[-85.61831,41.954358]},"n185985934":{"id":"n185985934","loc":[-85.618015,41.954485]},"n185985936":{"id":"n185985936","loc":[-85.617606,41.954611]},"n1475283975":{"id":"n1475283975","loc":[-85.6150935,41.9434118]},"n1475283979":{"id":"n1475283979","loc":[-85.6193367,41.9430252]},"n1475283989":{"id":"n1475283989","loc":[-85.6104771,41.9455269]},"n1475283990":{"id":"n1475283990","loc":[-85.6104771,41.9437179]},"n1475283994":{"id":"n1475283994","loc":[-85.6198042,41.9429763]},"n1475283998":{"id":"n1475283998","loc":[-85.6192101,41.9426716]},"n1475284000":{"id":"n1475284000","loc":[-85.6198622,41.942836]},"n1475284002":{"id":"n1475284002","loc":[-85.6163262,41.9427688]},"n1475284006":{"id":"n1475284006","loc":[-85.6179527,41.9429168]},"n1475284029":{"id":"n1475284029","loc":[-85.6197195,41.9427278]},"n1475284038":{"id":"n1475284038","loc":[-85.6194405,41.9427837]},"n1475284052":{"id":"n1475284052","loc":[-85.6153225,41.942841]},"n1475284055":{"id":"n1475284055","loc":[-85.6129233,41.9437179]},"n2139966627":{"id":"n2139966627","loc":[-85.61958,41.9427558]},"w17966773":{"id":"w17966773","tags":{"highway":"secondary","name":"E Michigan Ave","ref":"M 60"},"nodes":["n185980372","n185980398","n185980401","n185980403","n185980405","n185980407","n185980409","n185980411","n185980413","n185980415","n185980417","n185955019"]},"w17964043":{"id":"w17964043","tags":{"highway":"residential"},"nodes":["n185955019","n185955022","n185955025","n185955028","n185954977","n185971477","n1475284050","n1475284000","n1475284029","n2139966627","n1475284038"]},"w17962834":{"id":"w17962834","tags":{"highway":"service"},"nodes":["n185947316","n185947414","n185947464","n185947466","n185947373","n185947468","n185947470","n185947472","n185947474"]},"w209470310":{"id":"w209470310","tags":{"area":"yes","building":"yes"},"nodes":["n2196831393","n2196831394","n2196831395","n2196831397","n2196831393"]},"w17963058":{"id":"w17963058","tags":{"highway":"service"},"nodes":["n185947333","n185948863","n185948865","n1475284007","n1475283982","n1475284040","n1475284044"]},"w17962823":{"id":"w17962823","tags":{"highway":"service"},"nodes":["n185947359","n185947361","n185947363","n185947365","n185947367","n185947369","n185947371","n185947373","n185947374","n185947375","n185947376","n185947377","n185947378"]},"w17962821":{"id":"w17962821","tags":{"highway":"service"},"nodes":["n185947303","n185947304","n185947305","n185947306","n185947308","n185947310","n185947312","n185947314","n185947315","n185947316","n185947319","n185947321","n185947322","n185947323","n185947325","n185947327","n185947328","n185947329","n185947331","n185947333","n185947336","n185947338","n185947339","n185947340","n185947343","n185947345","n185947347","n185947349"]},"w134150798":{"id":"w134150798","tags":{"amenity":"grave_yard","name":"Riverside Cemetery"},"nodes":["n354002931","n1475283972","n1475284053","n1475283994","n1475283979","n1475283998","n1475284006","n1475284002","n1475284052","n1475283975","n1475284055","n1475283990","n1475283989","n354002931"]},"w17964040":{"id":"w17964040","tags":{"highway":"service"},"nodes":["n185947336","n185954965","n185954968","n185954970","n185954972","n185954974","n185954977"]},"w209470308":{"id":"w209470308","tags":{"area":"yes","building":"yes"},"nodes":["n2196831365","n2196831366","n2196831367","n2196831368","n2196831369","n2196831370","n2196831365"]},"w17962828":{"id":"w17962828","tags":{"highway":"service"},"nodes":["n185947340","n185947406","n185947409","n185947410","n185947411","n185947412","n185947414","n185947416","n185947417","n185947419","n185947420","n185947421","n185947422","n185947423","n185947425","n185947427","n185947429"]},"w209470309":{"id":"w209470309","tags":{"area":"yes","building":"yes"},"nodes":["n2196831371","n2196831372","n2196831373","n2196831374","n2196831375","n2196831376","n2196831377","n2196831378","n2196831379","n2196831380","n2196831381","n2196831382","n2196831383","n2196831384","n2196831385","n2196831386","n2196831387","n2196831388","n2196831389","n2196831390","n2196831391","n2196831392","n2196831371"]},"w17967415":{"id":"w17967415","tags":{"highway":"secondary","name":"Jefferson St","name_1":"State Highway 60","ref":"M 60"},"nodes":["n185955019","n185985910","n185985912","n185985914","n185961367","n185985916","n185955153","n185965105","n185974697","n185955120","n185985918","n185960690","n185985919","n185985920","n185985921","n185985922","n185985925","n185985927","n185985928","n185985929","n185985930","n185985931","n185985932","n185985934","n185985936","n185978817"]},"w17966772":{"id":"w17966772","tags":{"highway":"unclassified","name":"E Michigan Ave","name_1":"State Highway 60"},"nodes":["n185954977","n185980371","n185980372"]},"n185958500":{"id":"n185958500","loc":[-85.621591,41.941075]},"n185963110":{"id":"n185963110","loc":[-85.6204416,41.9408882]},"n2139966628":{"id":"n2139966628","loc":[-85.6196431,41.9426467],"tags":{"leisure":"fishing"}},"n2139966630":{"id":"n2139966630","loc":[-85.6199354,41.9429616]},"n2199127051":{"id":"n2199127051","loc":[-85.6170556,41.939696]},"n2199127052":{"id":"n2199127052","loc":[-85.6170536,41.9392909]},"n2199127053":{"id":"n2199127053","loc":[-85.6172067,41.9392905]},"n2199127054":{"id":"n2199127054","loc":[-85.6172061,41.9391853]},"n2199127055":{"id":"n2199127055","loc":[-85.6171481,41.9391854]},"n2199127060":{"id":"n2199127060","loc":[-85.6167389,41.9392896]},"n2199127061":{"id":"n2199127061","loc":[-85.6168728,41.9392892]},"n2199127062":{"id":"n2199127062","loc":[-85.6168747,41.9396965]},"n2199127071":{"id":"n2199127071","loc":[-85.620196,41.9399446]},"n2199127072":{"id":"n2199127072","loc":[-85.620193,41.9397316]},"n2199127073":{"id":"n2199127073","loc":[-85.6200381,41.9397328]},"n2199127074":{"id":"n2199127074","loc":[-85.6200412,41.9399458]},"n2199127075":{"id":"n2199127075","loc":[-85.6203606,41.9399939]},"n2199127076":{"id":"n2199127076","loc":[-85.6205527,41.9399922]},"n2199127077":{"id":"n2199127077","loc":[-85.6205482,41.9397115]},"n2199127078":{"id":"n2199127078","loc":[-85.6204132,41.9397124]},"n2199127079":{"id":"n2199127079","loc":[-85.6204144,41.9396341]},"n2199127080":{"id":"n2199127080","loc":[-85.6205699,41.9396324]},"n2199127081":{"id":"n2199127081","loc":[-85.6205722,41.939498]},"n2199127082":{"id":"n2199127082","loc":[-85.6204064,41.9394997]},"n2199127083":{"id":"n2199127083","loc":[-85.6204087,41.939561]},"n2199127084":{"id":"n2199127084","loc":[-85.6203103,41.9395618]},"n2199127085":{"id":"n2199127085","loc":[-85.620308,41.9396069]},"n2199127086":{"id":"n2199127086","loc":[-85.6200347,41.9396086]},"n2199127087":{"id":"n2199127087","loc":[-85.6200382,41.9397141]},"n2199127088":{"id":"n2199127088","loc":[-85.6202257,41.9397149]},"n2199127089":{"id":"n2199127089","loc":[-85.6202269,41.9399182]},"n2199127090":{"id":"n2199127090","loc":[-85.6203595,41.9399199]},"n2199127091":{"id":"n2199127091","loc":[-85.6212335,41.939688]},"n2199127092":{"id":"n2199127092","loc":[-85.6212328,41.939595]},"n2199127093":{"id":"n2199127093","loc":[-85.6208807,41.9395966]},"n2199127094":{"id":"n2199127094","loc":[-85.6208815,41.9396896]},"n2199127095":{"id":"n2199127095","loc":[-85.6208676,41.9396872]},"n2199127096":{"id":"n2199127096","loc":[-85.6208583,41.9393539]},"n2199127097":{"id":"n2199127097","loc":[-85.6207006,41.9393563]},"n2199127098":{"id":"n2199127098","loc":[-85.6207099,41.9396896]},"n185967054":{"id":"n185967054","loc":[-85.6173384,41.9356126]},"n185967063":{"id":"n185967063","loc":[-85.617371,41.936243]},"n185967065":{"id":"n185967065","loc":[-85.617337,41.936299]},"n185967068":{"id":"n185967068","loc":[-85.617321,41.936373]},"n185967070":{"id":"n185967070","loc":[-85.6173562,41.9366969]},"n185967074":{"id":"n185967074","loc":[-85.6173635,41.9377414]},"n185967075":{"id":"n185967075","loc":[-85.6173696,41.9381886]},"n185967076":{"id":"n185967076","loc":[-85.617372,41.938535]},"n2199127056":{"id":"n2199127056","loc":[-85.617147,41.9389616]},"n2199127057":{"id":"n2199127057","loc":[-85.6172136,41.9389614]},"n2199127058":{"id":"n2199127058","loc":[-85.6172123,41.9386909]},"n2199127059":{"id":"n2199127059","loc":[-85.616736,41.9386922]},"n2203921041":{"id":"n2203921041","loc":[-85.6173018,41.9346369]},"w203983952":{"id":"w203983952","tags":{"highway":"service"},"nodes":["n2139966627","n1819800319"]},"w209718301":{"id":"w209718301","tags":{"area":"yes","building":"yes"},"nodes":["n2199127051","n2199127052","n2199127053","n2199127054","n2199127055","n2199127056","n2199127057","n2199127058","n2199127059","n2199127060","n2199127061","n2199127062","n2199127051"]},"w209718304":{"id":"w209718304","tags":{"area":"yes","building":"yes"},"nodes":["n2199127071","n2199127072","n2199127073","n2199127074","n2199127071"]},"w17964961":{"id":"w17964961","tags":{"highway":"residential","name":"Whipple St"},"nodes":["n185963099","n185963110"]},"w17964489":{"id":"w17964489","tags":{"highway":"residential","name":"Jackson St"},"nodes":["n185958498","n185958500"]},"w203983953":{"id":"w203983953","tags":{"area":"yes","leisure":"park","name":"Marina Park"},"nodes":["n1475283994","n1475283979","n1475283998","n2139966629","n2139966625","n1819800319","n2139966623","n2139966622","n2139966621","n2139966630","n1475283994"]},"w17965366":{"id":"w17965366","tags":{"highway":"residential","name":"14th St"},"nodes":["n2203921041","n185967054","n185967063","n185967065","n185967068","n185967070","n185967074","n185967075","n185967076","n185967077"]},"w209718306":{"id":"w209718306","tags":{"area":"yes","building":"yes"},"nodes":["n2199127091","n2199127092","n2199127093","n2199127094","n2199127091"]},"w209718307":{"id":"w209718307","tags":{"area":"yes","building":"yes"},"nodes":["n2199127095","n2199127096","n2199127097","n2199127098","n2199127095"]},"w209718305":{"id":"w209718305","tags":{"area":"yes","building":"yes"},"nodes":["n2199127075","n2199127076","n2199127077","n2199127078","n2199127079","n2199127080","n2199127081","n2199127082","n2199127083","n2199127084","n2199127085","n2199127086","n2199127087","n2199127088","n2199127089","n2199127090","n2199127075"]},"n185960199":{"id":"n185960199","loc":[-85.62965,41.95469]},"n185980737":{"id":"n185980737","loc":[-85.629083,41.953725]},"n2114807561":{"id":"n2114807561","loc":[-85.6297681,41.9524688]},"n2114807597":{"id":"n2114807597","loc":[-85.6296517,41.952563]},"n185960197":{"id":"n185960197","loc":[-85.629676,41.9537314]},"n185978791":{"id":"n185978791","loc":[-85.6244542,41.9547066]},"w17967573":{"id":"w17967573","tags":{"highway":"residential","name":"E Wheeler St"},"nodes":["n185960195","n2114807561","n185968102","n185967430","n185986157","n185978392"]},"w17966553":{"id":"w17966553","tags":{"highway":"residential","name":"East Hoffman Street"},"nodes":["n185971631","n185978784","n185967432","n185968106","n185960199","n185978787","n185978790","n185978791"]},"w17966787":{"id":"w17966787","tags":{"highway":"residential","name":"East Cushman Street"},"nodes":["n185980735","n185980737","n185960197","n185968104","n185960792"]},"w17964723":{"id":"w17964723","tags":{"highway":"residential","name":"Cushman Street"},"nodes":["n185960792","n185960794","n185960796"]},"w17964654":{"id":"w17964654","tags":{"highway":"residential","name":"Pine Street"},"nodes":["n185960195","n2114807597","n185960197","n185960199"]},"n1819848862":{"id":"n1819848862","loc":[-85.6346087,41.9545845]},"n1819848935":{"id":"n1819848935","loc":[-85.6345948,41.9537717]},"n1819848973":{"id":"n1819848973","loc":[-85.6334247,41.9537827]},"n1819848997":{"id":"n1819848997","loc":[-85.6334386,41.9545956]},"n2189015861":{"id":"n2189015861","loc":[-85.6375906,41.954836]},"n2189015865":{"id":"n2189015865","loc":[-85.6383307,41.9548291]},"n2189015867":{"id":"n2189015867","loc":[-85.6383337,41.9550072]},"n2189015868":{"id":"n2189015868","loc":[-85.6380986,41.9550094]},"n2189015869":{"id":"n2189015869","loc":[-85.6381005,41.9551226]},"n2199109808":{"id":"n2199109808","loc":[-85.6372702,41.9522894]},"n2199109810":{"id":"n2199109810","loc":[-85.6372677,41.9521583]},"n2199109812":{"id":"n2199109812","loc":[-85.6369505,41.9521617]},"n2199109814":{"id":"n2199109814","loc":[-85.636953,41.9522927]},"n185952156":{"id":"n185952156","loc":[-85.640983,41.9546557]},"n185953423":{"id":"n185953423","loc":[-85.641871,41.954652]},"n185971637":{"id":"n185971637","loc":[-85.641583,41.95465]},"n185971639":{"id":"n185971639","loc":[-85.6421344,41.9546444]},"n185971642":{"id":"n185971642","loc":[-85.6428264,41.9545612]},"n185971648":{"id":"n185971648","loc":[-85.6436023,41.9544262]},"n185975066":{"id":"n185975066","loc":[-85.640532,41.953638]},"n185975067":{"id":"n185975067","loc":[-85.64079,41.953638]},"n185982166":{"id":"n185982166","loc":[-85.6399012,41.9523817]},"n2189015858":{"id":"n2189015858","loc":[-85.6376104,41.9560138]},"n2189015870":{"id":"n2189015870","loc":[-85.6386794,41.9551172]},"n2189015871":{"id":"n2189015871","loc":[-85.6386817,41.955256]},"n2189015873":{"id":"n2189015873","loc":[-85.6385437,41.9552573]},"n2189015876":{"id":"n2189015876","loc":[-85.638555,41.9559278]},"n2189015879":{"id":"n2189015879","loc":[-85.6384954,41.9559283]},"n2189015882":{"id":"n2189015882","loc":[-85.6384965,41.9559935]},"n2189015885":{"id":"n2189015885","loc":[-85.6383533,41.9559949]},"n2189015888":{"id":"n2189015888","loc":[-85.638351,41.9558607]},"n2189015891":{"id":"n2189015891","loc":[-85.6382178,41.9558619]},"n2189015894":{"id":"n2189015894","loc":[-85.6382203,41.956008]},"w208627223":{"id":"w208627223","tags":{"area":"yes","building":"yes"},"nodes":["n2189015858","n2189015861","n2189015865","n2189015867","n2189015868","n2189015869","n2189015870","n2189015871","n2189015873","n2189015876","n2189015879","n2189015882","n2189015885","n2189015888","n2189015891","n2189015894","n2189015858"]},"w170848328":{"id":"w170848328","tags":{"leisure":"park","name":"Bowman Park"},"nodes":["n1819848935","n1819848973","n1819848997","n1819848862","n1819848935"]},"w17965866":{"id":"w17965866","tags":{"highway":"residential","name":"West Hoffman Street"},"nodes":["n185971631","n185971632","n185964359","n185965025","n1475293264","n185952156","n185971637","n185953423","n185971639","n185971642","n185971648"]},"w209717051":{"id":"w209717051","tags":{"amenity":"place_of_worship","area":"yes","building":"yes","denomination":"baptist","name":"Calvary Missionary Baptist Church","religion":"christian"},"nodes":["n2199109808","n2199109810","n2199109812","n2199109814","n2199109808"]},"w17966172":{"id":"w17966172","tags":{"highway":"residential","name":"West Cushman Street"},"nodes":["n185960796","n185975064","n185964358","n185965023","n1475293222","n185975066","n185975067"]},"w17966975":{"id":"w17966975","tags":{"highway":"residential","name":"W Wheeler St"},"nodes":["n185978392","n185982163","n185964357","n185965021","n1475293261","n185982166"]},"n185960684":{"id":"n185960684","loc":[-85.622687,41.951885]},"n185960686":{"id":"n185960686","loc":[-85.622492,41.951901]},"n185978795":{"id":"n185978795","loc":[-85.6240991,41.954708]},"n185978803":{"id":"n185978803","loc":[-85.623348,41.954547]},"n185978806":{"id":"n185978806","loc":[-85.623123,41.954502]},"n185978808":{"id":"n185978808","loc":[-85.622923,41.954469]},"n185978810":{"id":"n185978810","loc":[-85.622787,41.954457]},"n185978811":{"id":"n185978811","loc":[-85.622612,41.954458]},"n185978813":{"id":"n185978813","loc":[-85.622368,41.954472]},"n1819790545":{"id":"n1819790545","loc":[-85.6240295,41.9548949]},"n1819790621":{"id":"n1819790621","loc":[-85.6235789,41.954855]},"n1819790664":{"id":"n1819790664","loc":[-85.6238363,41.9549507]},"n1819790683":{"id":"n1819790683","loc":[-85.6224727,41.9545921]},"n1819790730":{"id":"n1819790730","loc":[-85.6227527,41.9545795]},"n1819790740":{"id":"n1819790740","loc":[-85.6240402,41.9550784]},"n1819790831":{"id":"n1819790831","loc":[-85.624126,41.9549986]},"n1819790861":{"id":"n1819790861","loc":[-85.6231712,41.9546872]},"n1819790887":{"id":"n1819790887","loc":[-85.6242762,41.955206]},"n2168544739":{"id":"n2168544739","loc":[-85.6249102,41.952801]},"n2168544740":{"id":"n2168544740","loc":[-85.6251859,41.9527564]},"n2168544741":{"id":"n2168544741","loc":[-85.6255515,41.9527921]},"n2168544742":{"id":"n2168544742","loc":[-85.626001,41.9529481]},"n2168544743":{"id":"n2168544743","loc":[-85.6265284,41.9529838]},"n2168544744":{"id":"n2168544744","loc":[-85.626942,41.9528857]},"n2168544745":{"id":"n2168544745","loc":[-85.6270918,41.9526851]},"n2168544746":{"id":"n2168544746","loc":[-85.6272117,41.95244]},"n2168544747":{"id":"n2168544747","loc":[-85.6271578,41.952226]},"n2168544748":{"id":"n2168544748","loc":[-85.6270019,41.9519719]},"n2168544749":{"id":"n2168544749","loc":[-85.6268221,41.9518382]},"n2168544750":{"id":"n2168544750","loc":[-85.6265284,41.951807]},"n2168544751":{"id":"n2168544751","loc":[-85.6256534,41.9518516]},"n2168544752":{"id":"n2168544752","loc":[-85.6253477,41.9518338]},"n2168544753":{"id":"n2168544753","loc":[-85.6251139,41.9517669]},"n185955747":{"id":"n185955747","loc":[-85.620674,41.954709]},"n185960688":{"id":"n185960688","loc":[-85.621032,41.951913]},"n185972054":{"id":"n185972054","loc":[-85.6186728,41.9547335]},"n185978814":{"id":"n185978814","loc":[-85.6201708,41.9547403]},"n1819790532":{"id":"n1819790532","loc":[-85.6244908,41.9555731]},"n1819790536":{"id":"n1819790536","loc":[-85.6217925,41.9583135]},"n1819790538":{"id":"n1819790538","loc":[-85.6233954,41.9600014]},"n1819790539":{"id":"n1819790539","loc":[-85.6204611,41.9562117]},"n1819790546":{"id":"n1819790546","loc":[-85.6210898,41.9567657]},"n1819790548":{"id":"n1819790548","loc":[-85.6202465,41.9562237]},"n1819790550":{"id":"n1819790550","loc":[-85.6250165,41.9560677]},"n1819790551":{"id":"n1819790551","loc":[-85.6227946,41.9597023]},"n1819790553":{"id":"n1819790553","loc":[-85.6215726,41.9584571]},"n1819790556":{"id":"n1819790556","loc":[-85.6196306,41.9573002]},"n1819790557":{"id":"n1819790557","loc":[-85.6209503,41.9563109]},"n1819790558":{"id":"n1819790558","loc":[-85.6196939,41.9574085]},"n1819790561":{"id":"n1819790561","loc":[-85.621079,41.957751]},"n1819790562":{"id":"n1819790562","loc":[-85.6224255,41.9611417]},"n1819790565":{"id":"n1819790565","loc":[-85.6232506,41.9604841]},"n1819790566":{"id":"n1819790566","loc":[-85.6190835,41.9562909]},"n1819790567":{"id":"n1819790567","loc":[-85.622227,41.9593028]},"n1819790569":{"id":"n1819790569","loc":[-85.620976,41.9591039]},"n1819790571":{"id":"n1819790571","loc":[-85.6212078,41.9565303]},"n1819790572":{"id":"n1819790572","loc":[-85.6235306,41.9595102]},"n1819790581":{"id":"n1819790581","loc":[-85.6235563,41.9579351]},"n1819790584":{"id":"n1819790584","loc":[-85.6230371,41.9574598]},"n1819790586":{"id":"n1819790586","loc":[-85.6211748,41.9564272]},"n1819790588":{"id":"n1819790588","loc":[-85.6226508,41.9601086]},"n1819790591":{"id":"n1819790591","loc":[-85.6218032,41.9607468]},"n1819790593":{"id":"n1819790593","loc":[-85.6207915,41.9618735]},"n1819790596":{"id":"n1819790596","loc":[-85.6252955,41.9567858]},"n1819790598":{"id":"n1819790598","loc":[-85.6196618,41.9568939]},"n1819790600":{"id":"n1819790600","loc":[-85.6224416,41.9587084]},"n1819790602":{"id":"n1819790602","loc":[-85.6217442,41.9558641]},"n1819790603":{"id":"n1819790603","loc":[-85.6213355,41.9592116]},"n1819790604":{"id":"n1819790604","loc":[-85.622801,41.9573042]},"n1819790608":{"id":"n1819790608","loc":[-85.6199729,41.9574325]},"n1819790610":{"id":"n1819790610","loc":[-85.6195555,41.9557165]},"n1819790611":{"id":"n1819790611","loc":[-85.622978,41.9586007]},"n1819790613":{"id":"n1819790613","loc":[-85.6253963,41.9562636]},"n1819790614":{"id":"n1819790614","loc":[-85.6235252,41.9580342]},"n1819790616":{"id":"n1819790616","loc":[-85.6232988,41.9596305]},"n1819790617":{"id":"n1819790617","loc":[-85.6226776,41.9598732]},"n1819790619":{"id":"n1819790619","loc":[-85.625553,41.9561794]},"n1819790620":{"id":"n1819790620","loc":[-85.6235574,41.959231]},"n1819790624":{"id":"n1819790624","loc":[-85.6228429,41.9573726]},"n1819790626":{"id":"n1819790626","loc":[-85.6193785,41.9556766]},"n1819790628":{"id":"n1819790628","loc":[-85.620092,41.9554253]},"n1819790630":{"id":"n1819790630","loc":[-85.6226658,41.9604402]},"n1819790638":{"id":"n1819790638","loc":[-85.6219964,41.9602561]},"n1819790640":{"id":"n1819790640","loc":[-85.6232731,41.9599969]},"n1819790643":{"id":"n1819790643","loc":[-85.6247698,41.9568895]},"n1819790650":{"id":"n1819790650","loc":[-85.6216412,41.9550149]},"n1819790652":{"id":"n1819790652","loc":[-85.6224952,41.9603918]},"n1819790656":{"id":"n1819790656","loc":[-85.61918,41.9555649]},"n1819790661":{"id":"n1819790661","loc":[-85.6200169,41.955505]},"n1819790662":{"id":"n1819790662","loc":[-85.6217389,41.9563149]},"n1819790666":{"id":"n1819790666","loc":[-85.6229566,41.9598373]},"n1819790667":{"id":"n1819790667","loc":[-85.6209117,41.9609189]},"n1819790669":{"id":"n1819790669","loc":[-85.6252311,41.9562353]},"n1819790670":{"id":"n1819790670","loc":[-85.6209758,41.961868]},"n1819790672":{"id":"n1819790672","loc":[-85.6209557,41.9589078]},"n1819790673":{"id":"n1819790673","loc":[-85.6190352,41.9561393]},"n1819790675":{"id":"n1819790675","loc":[-85.6236432,41.9586685]},"n1819790676":{"id":"n1819790676","loc":[-85.6194901,41.9565389]},"n1819790678":{"id":"n1819790678","loc":[-85.6219266,41.9582417]},"n1819790680":{"id":"n1819790680","loc":[-85.6208258,41.9557211]},"n1819790681":{"id":"n1819790681","loc":[-85.6212024,41.9613212]},"n1819790682":{"id":"n1819790682","loc":[-85.624877,41.9559401]},"n1819790684":{"id":"n1819790684","loc":[-85.6206499,41.9583693]},"n1819790699":{"id":"n1819790699","loc":[-85.6215243,41.956279]},"n1819790701":{"id":"n1819790701","loc":[-85.6246625,41.9559321]},"n1819790703":{"id":"n1819790703","loc":[-85.6230478,41.9585089]},"n1819790708":{"id":"n1819790708","loc":[-85.6211102,41.9575402]},"n1819790710":{"id":"n1819790710","loc":[-85.6215082,41.9548468]},"n1819790711":{"id":"n1819790711","loc":[-85.6206552,41.9586007]},"n1819790713":{"id":"n1819790713","loc":[-85.6215404,41.9549705]},"n1819790715":{"id":"n1819790715","loc":[-85.6216906,41.955521]},"n1819790717":{"id":"n1819790717","loc":[-85.6215404,41.9547391]},"n1819790722":{"id":"n1819790722","loc":[-85.6219964,41.9599131]},"n1819790723":{"id":"n1819790723","loc":[-85.622286,41.9606989]},"n1819790725":{"id":"n1819790725","loc":[-85.6228439,41.9572005]},"n1819790727":{"id":"n1819790727","loc":[-85.6202518,41.9554458]},"n1819790728":{"id":"n1819790728","loc":[-85.623434,41.9575276]},"n1819790729":{"id":"n1819790729","loc":[-85.6234287,41.9568576]},"n1819790732":{"id":"n1819790732","loc":[-85.6229566,41.9571369]},"n1819790733":{"id":"n1819790733","loc":[-85.6225543,41.9590275]},"n1819790734":{"id":"n1819790734","loc":[-85.6232892,41.9583135]},"n1819790736":{"id":"n1819790736","loc":[-85.622977,41.9608551]},"n1819790737":{"id":"n1819790737","loc":[-85.624008,41.9569533]},"n1819790741":{"id":"n1819790741","loc":[-85.6212775,41.9608545]},"n1819790742":{"id":"n1819790742","loc":[-85.6231282,41.9569932]},"n1819790743":{"id":"n1819790743","loc":[-85.6224523,41.9591831]},"n1819790744":{"id":"n1819790744","loc":[-85.6210951,41.9610819]},"n1819790745":{"id":"n1819790745","loc":[-85.6220114,41.960544]},"n1819790755":{"id":"n1819790755","loc":[-85.6216369,41.9553854]},"n1819790757":{"id":"n1819790757","loc":[-85.6209986,41.9592709]},"n1819790758":{"id":"n1819790758","loc":[-85.6200437,41.9563468]},"n1819790764":{"id":"n1819790764","loc":[-85.6219363,41.9596823]},"n1819790765":{"id":"n1819790765","loc":[-85.6237612,41.9568496]},"n1819790769":{"id":"n1819790769","loc":[-85.6212389,41.9593433]},"n1819790771":{"id":"n1819790771","loc":[-85.6210726,41.9560123]},"n1819790772":{"id":"n1819790772","loc":[-85.6212711,41.9561838]},"n1819790776":{"id":"n1819790776","loc":[-85.6234437,41.9577795]},"n1819790777":{"id":"n1819790777","loc":[-85.6212502,41.9618599]},"n1819790783":{"id":"n1819790783","loc":[-85.6216895,41.9610585]},"n1819790784":{"id":"n1819790784","loc":[-85.6200115,41.9556367]},"n1819790785":{"id":"n1819790785","loc":[-85.6210576,41.9573002]},"n1819790786":{"id":"n1819790786","loc":[-85.621138,41.9576632]},"n1819790788":{"id":"n1819790788","loc":[-85.6207733,41.9578946]},"n1819790789":{"id":"n1819790789","loc":[-85.6200705,41.9571566]},"n1819790790":{"id":"n1819790790","loc":[-85.6245337,41.9558443]},"n1819790792":{"id":"n1819790792","loc":[-85.621932,41.9608066]},"n1819790793":{"id":"n1819790793","loc":[-85.6233578,41.9581385]},"n1819790794":{"id":"n1819790794","loc":[-85.6204557,41.9555136]},"n1819790797":{"id":"n1819790797","loc":[-85.6235038,41.9576074]},"n1819790800":{"id":"n1819790800","loc":[-85.6214438,41.9607508]},"n1819790801":{"id":"n1819790801","loc":[-85.623492,41.9602129]},"n1819790802":{"id":"n1819790802","loc":[-85.6216691,41.9546553]},"n1819790803":{"id":"n1819790803","loc":[-85.6231057,41.9586851]},"n1819790804":{"id":"n1819790804","loc":[-85.6209224,41.9578673]},"n1819790813":{"id":"n1819790813","loc":[-85.620092,41.9572962]},"n1819790814":{"id":"n1819790814","loc":[-85.6216691,41.9552218]},"n1819790816":{"id":"n1819790816","loc":[-85.6216144,41.9609668]},"n1819790818":{"id":"n1819790818","loc":[-85.6216906,41.9557324]},"n1819790820":{"id":"n1819790820","loc":[-85.6192069,41.9564186]},"n1819790823":{"id":"n1819790823","loc":[-85.6211155,41.9566027]},"n1819790825":{"id":"n1819790825","loc":[-85.6233106,41.9569294]},"n1819790839":{"id":"n1819790839","loc":[-85.625671,41.9564986]},"n1819790842":{"id":"n1819790842","loc":[-85.6235252,41.9567379]},"n1819790844":{"id":"n1819790844","loc":[-85.6253813,41.9566342]},"n1819790847":{"id":"n1819790847","loc":[-85.6200963,41.9567702]},"n1819790849":{"id":"n1819790849","loc":[-85.6238031,41.9587449]},"n1819790851":{"id":"n1819790851","loc":[-85.6234984,41.9584571]},"n1819790856":{"id":"n1819790856","loc":[-85.6242226,41.9570092]},"n1819790865":{"id":"n1819790865","loc":[-85.6200265,41.9569458]},"n1819790869":{"id":"n1819790869","loc":[-85.6230049,41.9601245]},"n1819790871":{"id":"n1819790871","loc":[-85.6190727,41.9558322]},"n1819790873":{"id":"n1819790873","loc":[-85.6217442,41.9550104]},"n1819790875":{"id":"n1819790875","loc":[-85.6208044,41.9587808]},"n1819790879":{"id":"n1819790879","loc":[-85.6198444,41.9574484]},"n1819790883":{"id":"n1819790883","loc":[-85.623713,41.9588719]},"n1819790885":{"id":"n1819790885","loc":[-85.6223289,41.9605075]},"n1819790889":{"id":"n1819790889","loc":[-85.6208044,41.9562437]},"n1819790893":{"id":"n1819790893","loc":[-85.6218183,41.9559684]},"n1819790906":{"id":"n1819790906","loc":[-85.6214052,41.958697]},"n1819790913":{"id":"n1819790913","loc":[-85.6209981,41.9609957]},"n1819790917":{"id":"n1819790917","loc":[-85.6216208,41.9604436]},"n1819790919":{"id":"n1819790919","loc":[-85.6209406,41.9616373]},"n1819790920":{"id":"n1819790920","loc":[-85.6221948,41.9583334]},"n1819790922":{"id":"n1819790922","loc":[-85.6216681,41.9615292]},"n1819790924":{"id":"n1819790924","loc":[-85.6210147,41.9570489]},"n1819790929":{"id":"n1819790929","loc":[-85.6193678,41.955521]},"w17964707":{"id":"w17964707","tags":{"highway":"residential","name":"11th Ave"},"nodes":["n185960682","n185960684","n185960686","n185960688","n185960690"]},"w201484345":{"id":"w201484345","tags":{"bridge":"yes","highway":"residential","name":"E Hoffman St"},"nodes":["n185978791","n185978795"]},"w201484348":{"id":"w201484348","tags":{"highway":"residential","name":"E Hoffman St"},"nodes":["n185978795","n185978800","n185978803","n185978806","n185978808","n185978810","n185978811","n185978813","n185955747","n185978814","n185972054","n185978817"]},"w170843845":{"id":"w170843845","tags":{"landuse":"reservoir","name":"Hoffman Pond","natural":"water"},"nodes":["n1819790732","n1819790742","n1819790825","n1819790729","n1819790842","n1819790765","n1819790737","n1819790856","n1819790643","n1819790596","n1819790844","n1819790839","n1819849190","n1819790619","n1819790613","n1819790669","n1819790550","n1819790682","n1819790701","n1819790790","n1819790532","n1819790887","n1819790740","n1819790831","n1819790545","n1819790664","n1819790621","n1819790861","n1819790730","n1819790683","n1819790802","n1819790717","n1819790710","n1819790713","n1819790650","n1819790873","n1819790814","n1819790755","n1819790715","n1819790818","n1819790602","n1819790893","n1819790662","n1819790699","n1819790772","n1819790771","n1819790680","n1819790794","n1819790727","n1819790628","n1819790661","n1819790784","n1819790610","n1819790626","n1819790929","n1819790656","n1819790871","n1819790673","n1819790566","n1819790820","n1819790676","n1819790598","n1819790556","n1819790558","n1819790879","n1819790608","n1819790813","n1819790789","n1819790865","n1819790847","n1819790758","n1819790548","n1819790539","n1819790889","n1819790557","n1819790586","n1819790571","n1819790823","n1819790546","n1819790924","n1819790785","n1819790708","n1819790786","n1819790561","n1819790804","n1819790788","n1819790684","n1819790711","n1819790875","n1819790672","n1819790569","n1819790757","n1819790769","n1819790603","n1819790906","n1819790553","n1819790536","n1819790678","n1819790920","n1819790600","n1819790733","n1819790743","n1819790567","n1819790764","n1819790722","n1819790638","n1819790917","n1819790800","n1819790741","n1819790667","n1819790913","n1819790744","n1819790816","n1819790591","n1819790745","n1819790885","n1819790652","n1819790588","n1819790617","n1819790551","n1819790666","n1819790869","n1819790630","n1819790723","n1819790792","n1819790783","n1819790681","n1819790919","n1819790593","n1819790670","n1819790777","n1819790922","n1819790562","n1819790736","n1819790565","n1819790801","n1819790538","n1819790640","n1819790616","n1819790572","n1819790620","n1819790883","n1819790849","n1819790675","n1819790851","n1819790803","n1819790611","n1819790703","n1819790734","n1819790793","n1819790614","n1819790581","n1819790776","n1819790797","n1819790728","n1819790584","n1819790624","n1819790604","n1819790725","n1819790732"]},"w206805240":{"id":"w206805240","tags":{"waterway":"river"},"nodes":["n2168544738","n2168544739","n2168544740","n2168544741","n2168544742","n2168544743","n2168544744","n2168544745","n2168544746","n2168544747","n2168544748","n2168544749","n2168544750","n2168544751","n2168544752","n2168544753","n1819848944"]},"n394490429":{"id":"n394490429","loc":[-85.643883,41.954365]},"n185953421":{"id":"n185953421","loc":[-85.641876,41.954946]},"n185953417":{"id":"n185953417","loc":[-85.6418306,41.9551597]},"n185977233":{"id":"n185977233","loc":[-85.642987,41.95486]},"n185977232":{"id":"n185977232","loc":[-85.642894,41.9547842]},"n1475293244":{"id":"n1475293244","loc":[-85.63974,41.9521543]},"n1819848890":{"id":"n1819848890","loc":[-85.6410004,41.9552822]},"n1819848965":{"id":"n1819848965","loc":[-85.6409795,41.9553892]},"n2189015846":{"id":"n2189015846","loc":[-85.6420457,41.9549528]},"n2189015849":{"id":"n2189015849","loc":[-85.6425867,41.9551392]},"n2189015852":{"id":"n2189015852","loc":[-85.6426877,41.9549771]},"n2199109816":{"id":"n2199109816","loc":[-85.6399215,41.9540925]},"n2199109818":{"id":"n2199109818","loc":[-85.6399182,41.9538236]},"n2199109820":{"id":"n2199109820","loc":[-85.6402201,41.9538216]},"n2199109822":{"id":"n2199109822","loc":[-85.640222,41.9539771]},"n2199109825":{"id":"n2199109825","loc":[-85.6402904,41.9539766]},"n2199109827":{"id":"n2199109827","loc":[-85.6402918,41.95409]},"n2199109829":{"id":"n2199109829","loc":[-85.6395845,41.9544626]},"n2199109831":{"id":"n2199109831","loc":[-85.6395792,41.9540671]},"n2199109833":{"id":"n2199109833","loc":[-85.6397173,41.9540661]},"n2199109835":{"id":"n2199109835","loc":[-85.6397226,41.9544616]},"n2199109837":{"id":"n2199109837","loc":[-85.6399641,41.9545058]},"n2199109839":{"id":"n2199109839","loc":[-85.6399637,41.9541859]},"n2199109841":{"id":"n2199109841","loc":[-85.6401098,41.9541858]},"n2199109843":{"id":"n2199109843","loc":[-85.64011,41.9543272]},"n2199109845":{"id":"n2199109845","loc":[-85.6400783,41.9543273]},"n2199109847":{"id":"n2199109847","loc":[-85.6400785,41.9545058]},"n2199109853":{"id":"n2199109853","loc":[-85.6396184,41.9554049]},"n2199109855":{"id":"n2199109855","loc":[-85.6396825,41.9553713]},"n185949745":{"id":"n185949745","loc":[-85.6442727,41.9553112]},"n185949748":{"id":"n185949748","loc":[-85.6448804,41.9555238]},"n185949755":{"id":"n185949755","loc":[-85.6420011,41.9603536]},"n185949763":{"id":"n185949763","loc":[-85.6408843,41.9555822]},"n185949765":{"id":"n185949765","loc":[-85.6414548,41.9557751]},"n185952158":{"id":"n185952158","loc":[-85.640066,41.956854]},"n185952160":{"id":"n185952160","loc":[-85.639848,41.957229]},"n185952161":{"id":"n185952161","loc":[-85.6396089,41.9576192]},"n185952163":{"id":"n185952163","loc":[-85.63892,41.957957]},"n185953413":{"id":"n185953413","loc":[-85.64162,41.955475]},"n185971651":{"id":"n185971651","loc":[-85.6440766,41.9543462]},"n185977234":{"id":"n185977234","loc":[-85.645044,41.955581]},"n394490395":{"id":"n394490395","loc":[-85.657336,41.936762]},"n394490396":{"id":"n394490396","loc":[-85.653896,41.936978]},"n394490397":{"id":"n394490397","loc":[-85.653732,41.937386]},"n394490398":{"id":"n394490398","loc":[-85.65182,41.937378]},"n394490399":{"id":"n394490399","loc":[-85.651843,41.938445]},"n394490400":{"id":"n394490400","loc":[-85.652536,41.938447]},"n394490401":{"id":"n394490401","loc":[-85.652533,41.938901]},"n394490402":{"id":"n394490402","loc":[-85.652084,41.9389]},"n394490403":{"id":"n394490403","loc":[-85.6521,41.939627]},"n394490404":{"id":"n394490404","loc":[-85.652301,41.939628]},"n394490405":{"id":"n394490405","loc":[-85.652302,41.939755]},"n394490406":{"id":"n394490406","loc":[-85.652783,41.939747]},"n394490407":{"id":"n394490407","loc":[-85.652835,41.94112]},"n394490408":{"id":"n394490408","loc":[-85.651968,41.941123]},"n394490409":{"id":"n394490409","loc":[-85.651983,41.941969]},"n394490410":{"id":"n394490410","loc":[-85.652908,41.941961]},"n394490411":{"id":"n394490411","loc":[-85.65292,41.94278]},"n394490412":{"id":"n394490412","loc":[-85.651698,41.942816]},"n394490413":{"id":"n394490413","loc":[-85.651509,41.942823]},"n394490414":{"id":"n394490414","loc":[-85.651272,41.942837]},"n394490415":{"id":"n394490415","loc":[-85.651272,41.943325]},"n394490416":{"id":"n394490416","loc":[-85.65122,41.944053]},"n394490417":{"id":"n394490417","loc":[-85.651193,41.944449]},"n394490418":{"id":"n394490418","loc":[-85.651088,41.944969]},"n394490419":{"id":"n394490419","loc":[-85.650949,41.945554]},"n394490420":{"id":"n394490420","loc":[-85.650907,41.945719]},"n394490421":{"id":"n394490421","loc":[-85.650808,41.946016]},"n394490422":{"id":"n394490422","loc":[-85.650712,41.946516]},"n394490423":{"id":"n394490423","loc":[-85.650493,41.947166]},"n394490424":{"id":"n394490424","loc":[-85.650626,41.947213]},"n394490425":{"id":"n394490425","loc":[-85.650201,41.948109]},"n394490426":{"id":"n394490426","loc":[-85.649868,41.948797]},"n394490427":{"id":"n394490427","loc":[-85.649669,41.949161]},"n394490428":{"id":"n394490428","loc":[-85.64659,41.954067]},"n394490430":{"id":"n394490430","loc":[-85.644034,41.95444]},"n394490431":{"id":"n394490431","loc":[-85.644248,41.954507]},"n394490432":{"id":"n394490432","loc":[-85.64491,41.954481]},"n394490433":{"id":"n394490433","loc":[-85.645213,41.954433]},"n394490434":{"id":"n394490434","loc":[-85.645426,41.954477]},"n394490435":{"id":"n394490435","loc":[-85.6458,41.954704]},"n394490436":{"id":"n394490436","loc":[-85.64605,41.954804]},"n394490437":{"id":"n394490437","loc":[-85.646125,41.954817]},"n394490438":{"id":"n394490438","loc":[-85.646002,41.954997]},"n394490439":{"id":"n394490439","loc":[-85.645764,41.955366]},"n394490440":{"id":"n394490440","loc":[-85.645525,41.955734]},"n394490441":{"id":"n394490441","loc":[-85.64443,41.957424]},"n394490442":{"id":"n394490442","loc":[-85.641712,41.961723]},"n394490443":{"id":"n394490443","loc":[-85.640747,41.963246]},"n394490444":{"id":"n394490444","loc":[-85.637803,41.967894]},"n394490445":{"id":"n394490445","loc":[-85.637673,41.967861]},"n394490446":{"id":"n394490446","loc":[-85.636637,41.969275]},"n394490447":{"id":"n394490447","loc":[-85.634923,41.969269]},"n394490448":{"id":"n394490448","loc":[-85.634893,41.968537]},"n394490449":{"id":"n394490449","loc":[-85.634544,41.96927]},"n394490450":{"id":"n394490450","loc":[-85.630835,41.969274]},"n394490451":{"id":"n394490451","loc":[-85.630834,41.968348]},"n394490452":{"id":"n394490452","loc":[-85.630857,41.968179]},"n394490453":{"id":"n394490453","loc":[-85.630924,41.968044]},"n394490454":{"id":"n394490454","loc":[-85.631004,41.967925]},"n394490455":{"id":"n394490455","loc":[-85.631143,41.967811]},"n394490456":{"id":"n394490456","loc":[-85.631311,41.967736]},"n394490457":{"id":"n394490457","loc":[-85.631595,41.967693]},"n394490458":{"id":"n394490458","loc":[-85.63325,41.967702]},"n394490459":{"id":"n394490459","loc":[-85.633247,41.967021]},"n394490460":{"id":"n394490460","loc":[-85.634858,41.967021]},"n394490461":{"id":"n394490461","loc":[-85.634865,41.967711]},"n394490462":{"id":"n394490462","loc":[-85.634884,41.968231]},"n394490463":{"id":"n394490463","loc":[-85.636559,41.963867]},"n394490464":{"id":"n394490464","loc":[-85.634832,41.963866]},"n394490465":{"id":"n394490465","loc":[-85.63481,41.961899]},"n394490466":{"id":"n394490466","loc":[-85.637219,41.961842]},"n394490467":{"id":"n394490467","loc":[-85.637837,41.960019]},"n394490468":{"id":"n394490468","loc":[-85.637459,41.960022]},"n394490469":{"id":"n394490469","loc":[-85.635295,41.959987]},"n394490470":{"id":"n394490470","loc":[-85.634783,41.959979]},"n394490471":{"id":"n394490471","loc":[-85.634776,41.959834]},"n394490472":{"id":"n394490472","loc":[-85.634767,41.959009]},"n394490473":{"id":"n394490473","loc":[-85.634763,41.958292]},"n394490474":{"id":"n394490474","loc":[-85.633346,41.958287]},"n394490475":{"id":"n394490475","loc":[-85.632128,41.9583]},"n394490476":{"id":"n394490476","loc":[-85.631414,41.958318]},"n394490477":{"id":"n394490477","loc":[-85.63137,41.959033]},"n394490478":{"id":"n394490478","loc":[-85.631325,41.959753]},"n394490479":{"id":"n394490479","loc":[-85.631494,41.95977]},"n394490480":{"id":"n394490480","loc":[-85.631456,41.960673]},"n394490481":{"id":"n394490481","loc":[-85.631421,41.961494]},"n394490482":{"id":"n394490482","loc":[-85.631404,41.961887]},"n394490483":{"id":"n394490483","loc":[-85.631401,41.961968]},"n394490484":{"id":"n394490484","loc":[-85.630962,41.961967]},"n394490485":{"id":"n394490485","loc":[-85.6299,41.961973]},"n394490486":{"id":"n394490486","loc":[-85.624929,41.962002]},"n394490487":{"id":"n394490487","loc":[-85.623333,41.961987]},"n394490488":{"id":"n394490488","loc":[-85.621894,41.963956]},"n394490489":{"id":"n394490489","loc":[-85.62131,41.963727]},"n394490490":{"id":"n394490490","loc":[-85.621216,41.963868]},"n394490491":{"id":"n394490491","loc":[-85.620356,41.965119]},"n394490492":{"id":"n394490492","loc":[-85.620848,41.965341]},"n394490493":{"id":"n394490493","loc":[-85.620684,41.965558]},"n394490494":{"id":"n394490494","loc":[-85.620621,41.965658]},"n394490495":{"id":"n394490495","loc":[-85.618165,41.965759]},"n394490496":{"id":"n394490496","loc":[-85.618071,41.965759]},"n394490497":{"id":"n394490497","loc":[-85.617986,41.965759]},"n394490498":{"id":"n394490498","loc":[-85.605673,41.965764]},"n394490499":{"id":"n394490499","loc":[-85.605668,41.963548]},"n394490500":{"id":"n394490500","loc":[-85.605664,41.962094]},"n394490501":{"id":"n394490501","loc":[-85.595828,41.962159]},"n394490502":{"id":"n394490502","loc":[-85.587869,41.962169]},"n394490503":{"id":"n394490503","loc":[-85.586289,41.962179]},"n394490504":{"id":"n394490504","loc":[-85.583774,41.962178]},"n394490505":{"id":"n394490505","loc":[-85.583774,41.961789]},"n394490506":{"id":"n394490506","loc":[-85.581303,41.961783]},"n394490507":{"id":"n394490507","loc":[-85.581304,41.961616]},"n394490508":{"id":"n394490508","loc":[-85.581292,41.961616]},"n394490509":{"id":"n394490509","loc":[-85.581247,41.959244]},"n394490510":{"id":"n394490510","loc":[-85.581245,41.958394]},"n394490511":{"id":"n394490511","loc":[-85.581276,41.958372]},"n394490512":{"id":"n394490512","loc":[-85.581302,41.958353]},"n394490513":{"id":"n394490513","loc":[-85.581376,41.9583]},"n394490514":{"id":"n394490514","loc":[-85.582256,41.957663]},"n394490515":{"id":"n394490515","loc":[-85.585299,41.955483]},"n394490516":{"id":"n394490516","loc":[-85.585588,41.955331]},"n394490517":{"id":"n394490517","loc":[-85.586053,41.955163]},"n394490518":{"id":"n394490518","loc":[-85.58632,41.955076]},"n394490519":{"id":"n394490519","loc":[-85.586478,41.955025]},"n394490520":{"id":"n394490520","loc":[-85.58692,41.954947]},"n394490521":{"id":"n394490521","loc":[-85.587327,41.954914]},"n394490522":{"id":"n394490522","loc":[-85.587345,41.954913]},"n394490523":{"id":"n394490523","loc":[-85.587358,41.954913]},"n394490524":{"id":"n394490524","loc":[-85.58963,41.954877]},"n394490525":{"id":"n394490525","loc":[-85.591077,41.954865]},"n394490526":{"id":"n394490526","loc":[-85.594824,41.954843]},"n394490527":{"id":"n394490527","loc":[-85.594804,41.95331]},"n394490528":{"id":"n394490528","loc":[-85.599336,41.95331]},"n394490529":{"id":"n394490529","loc":[-85.599336,41.954825]},"n394490530":{"id":"n394490530","loc":[-85.597828,41.954839]},"n394490531":{"id":"n394490531","loc":[-85.597833,41.95614]},"n394490532":{"id":"n394490532","loc":[-85.596586,41.956151]},"n394490533":{"id":"n394490533","loc":[-85.596586,41.956394]},"n394490534":{"id":"n394490534","loc":[-85.595933,41.956394]},"n394490535":{"id":"n394490535","loc":[-85.595933,41.958176]},"n394490536":{"id":"n394490536","loc":[-85.597635,41.958179]},"n394490537":{"id":"n394490537","loc":[-85.597717,41.958177]},"n394490538":{"id":"n394490538","loc":[-85.601671,41.958194]},"n394490539":{"id":"n394490539","loc":[-85.605619,41.958194]},"n394490540":{"id":"n394490540","loc":[-85.608054,41.958187]},"n394490542":{"id":"n394490542","loc":[-85.6080762,41.9547864]},"n394490545":{"id":"n394490545","loc":[-85.6104354,41.9548263]},"n394490546":{"id":"n394490546","loc":[-85.610274,41.951106]},"n394490547":{"id":"n394490547","loc":[-85.610278,41.950829]},"n394490548":{"id":"n394490548","loc":[-85.610309,41.948377]},"n394490549":{"id":"n394490549","loc":[-85.610314,41.947986]},"n394490550":{"id":"n394490550","loc":[-85.610464,41.947985]},"n394490551":{"id":"n394490551","loc":[-85.610447,41.947468]},"n394490552":{"id":"n394490552","loc":[-85.612469,41.947471]},"n394490553":{"id":"n394490553","loc":[-85.612494,41.945576]},"n394490554":{"id":"n394490554","loc":[-85.610292,41.94558]},"n394490555":{"id":"n394490555","loc":[-85.608412,41.945625]},"n394490556":{"id":"n394490556","loc":[-85.608412,41.943036]},"n394490557":{"id":"n394490557","loc":[-85.608702,41.943087]},"n394490558":{"id":"n394490558","loc":[-85.609196,41.943224]},"n394490559":{"id":"n394490559","loc":[-85.609571,41.943263]},"n394490560":{"id":"n394490560","loc":[-85.610116,41.943295]},"n394490561":{"id":"n394490561","loc":[-85.610273,41.943275]},"n394490562":{"id":"n394490562","loc":[-85.611339,41.943075]},"n394490563":{"id":"n394490563","loc":[-85.611575,41.942997]},"n394490564":{"id":"n394490564","loc":[-85.611847,41.942849]},"n394490565":{"id":"n394490565","loc":[-85.612164,41.942568]},"n394490566":{"id":"n394490566","loc":[-85.612341,41.942529]},"n394490567":{"id":"n394490567","loc":[-85.612562,41.942524]},"n394490568":{"id":"n394490568","loc":[-85.612768,41.942546]},"n394490569":{"id":"n394490569","loc":[-85.612938,41.942633]},"n394490570":{"id":"n394490570","loc":[-85.6131,41.942782]},"n394490571":{"id":"n394490571","loc":[-85.613299,41.942919]},"n394490572":{"id":"n394490572","loc":[-85.613498,41.942996]},"n394490573":{"id":"n394490573","loc":[-85.614698,41.942842]},"n394490574":{"id":"n394490574","loc":[-85.615288,41.942698]},"n394490575":{"id":"n394490575","loc":[-85.616054,41.942693]},"n394490576":{"id":"n394490576","loc":[-85.61603,41.942175]},"n394490577":{"id":"n394490577","loc":[-85.616004,41.941741]},"n394490578":{"id":"n394490578","loc":[-85.615994,41.940156]},"n394490579":{"id":"n394490579","loc":[-85.615144,41.940159]},"n394490580":{"id":"n394490580","loc":[-85.614915,41.940161]},"n394490582":{"id":"n394490582","loc":[-85.614875,41.938532]},"n394490583":{"id":"n394490583","loc":[-85.616167,41.938787]},"n394490585":{"id":"n394490585","loc":[-85.616176,41.938589]},"n394490586":{"id":"n394490586","loc":[-85.614537,41.938282]},"n394490588":{"id":"n394490588","loc":[-85.610141,41.937459]},"n394490589":{"id":"n394490589","loc":[-85.610172,41.937298]},"n394490590":{"id":"n394490590","loc":[-85.609918,41.935495]},"n394490592":{"id":"n394490592","loc":[-85.610092,41.935451]},"n394490594":{"id":"n394490594","loc":[-85.610681,41.935247]},"n394490595":{"id":"n394490595","loc":[-85.611446,41.934955]},"n394490596":{"id":"n394490596","loc":[-85.612057,41.934696]},"n394490598":{"id":"n394490598","loc":[-85.613256,41.934084]},"n394490599":{"id":"n394490599","loc":[-85.613948,41.933682]},"n394490601":{"id":"n394490601","loc":[-85.61436,41.933417]},"n394490602":{"id":"n394490602","loc":[-85.614638,41.933212]},"n394490604":{"id":"n394490604","loc":[-85.615249,41.9332]},"n394490605":{"id":"n394490605","loc":[-85.618218,41.933223]},"n394490607":{"id":"n394490607","loc":[-85.618241,41.933479]},"n394490608":{"id":"n394490608","loc":[-85.618257,41.93365]},"n394490609":{"id":"n394490609","loc":[-85.618298,41.935067]},"n394490611":{"id":"n394490611","loc":[-85.619791,41.935067]},"n394490612":{"id":"n394490612","loc":[-85.619794,41.933301]},"n394490613":{"id":"n394490613","loc":[-85.619795,41.932692]},"n394490614":{"id":"n394490614","loc":[-85.619729,41.929517]},"n394490615":{"id":"n394490615","loc":[-85.619801,41.929305]},"n394490616":{"id":"n394490616","loc":[-85.619809,41.927391]},"n394490617":{"id":"n394490617","loc":[-85.620883,41.927378]},"n394490618":{"id":"n394490618","loc":[-85.620988,41.927368]},"n394490619":{"id":"n394490619","loc":[-85.621076,41.927368]},"n394490620":{"id":"n394490620","loc":[-85.621156,41.927376]},"n394490621":{"id":"n394490621","loc":[-85.621685,41.92737]},"n394490622":{"id":"n394490622","loc":[-85.624716,41.927359]},"n394490623":{"id":"n394490623","loc":[-85.625308,41.92737]},"n394490624":{"id":"n394490624","loc":[-85.625655,41.927377]},"n394490625":{"id":"n394490625","loc":[-85.625093,41.925591]},"n394490626":{"id":"n394490626","loc":[-85.625174,41.92559]},"n394490627":{"id":"n394490627","loc":[-85.625249,41.925597]},"n394490628":{"id":"n394490628","loc":[-85.625532,41.925604]},"n394490629":{"id":"n394490629","loc":[-85.625761,41.925597]},"n394490630":{"id":"n394490630","loc":[-85.625955,41.926153]},"n394490631":{"id":"n394490631","loc":[-85.626209,41.926155]},"n394490632":{"id":"n394490632","loc":[-85.627757,41.926151]},"n394490633":{"id":"n394490633","loc":[-85.627825,41.926298]},"n394490634":{"id":"n394490634","loc":[-85.627994,41.926315]},"n394490635":{"id":"n394490635","loc":[-85.628049,41.927196]},"n394490636":{"id":"n394490636","loc":[-85.62949,41.927221]},"n394490637":{"id":"n394490637","loc":[-85.629602,41.927277]},"n394490638":{"id":"n394490638","loc":[-85.6297102,41.9273279]},"n394490639":{"id":"n394490639","loc":[-85.630958,41.927398]},"n394490699":{"id":"n394490699","loc":[-85.632741,41.927388]},"n394490700":{"id":"n394490700","loc":[-85.632997,41.927391]},"n394490701":{"id":"n394490701","loc":[-85.633149,41.927393]},"n394490702":{"id":"n394490702","loc":[-85.633334,41.927393]},"n394490703":{"id":"n394490703","loc":[-85.633468,41.927561]},"n394490704":{"id":"n394490704","loc":[-85.633563,41.927755]},"n394490705":{"id":"n394490705","loc":[-85.633662,41.928192]},"n394490706":{"id":"n394490706","loc":[-85.633679,41.928807]},"n394490707":{"id":"n394490707","loc":[-85.633687,41.929107]},"n394490708":{"id":"n394490708","loc":[-85.633927,41.929109]},"n394490709":{"id":"n394490709","loc":[-85.634126,41.929111]},"n394490710":{"id":"n394490710","loc":[-85.634207,41.92911]},"n394490711":{"id":"n394490711","loc":[-85.634323,41.929111]},"n394490712":{"id":"n394490712","loc":[-85.636712,41.929128]},"n394490713":{"id":"n394490713","loc":[-85.63808,41.9291]},"n394490714":{"id":"n394490714","loc":[-85.639213,41.929088]},"n394490715":{"id":"n394490715","loc":[-85.639189,41.92852]},"n394490716":{"id":"n394490716","loc":[-85.639204,41.925488]},"n394490717":{"id":"n394490717","loc":[-85.644204,41.925452]},"n394490718":{"id":"n394490718","loc":[-85.651425,41.925406]},"n394490719":{"id":"n394490719","loc":[-85.651449,41.926321]},"n394490720":{"id":"n394490720","loc":[-85.651451,41.926969]},"n394490721":{"id":"n394490721","loc":[-85.651458,41.928052]},"n394490722":{"id":"n394490722","loc":[-85.651446,41.928892]},"n394490723":{"id":"n394490723","loc":[-85.651456,41.929447]},"n394490724":{"id":"n394490724","loc":[-85.651707,41.929454]},"n394490725":{"id":"n394490725","loc":[-85.652369,41.929473]},"n394490726":{"id":"n394490726","loc":[-85.6525,41.929452]},"n394490727":{"id":"n394490727","loc":[-85.654066,41.92946]},"n394490728":{"id":"n394490728","loc":[-85.654816,41.92946]},"n394490729":{"id":"n394490729","loc":[-85.654816,41.930337]},"n394490730":{"id":"n394490730","loc":[-85.654587,41.930337]},"n394490731":{"id":"n394490731","loc":[-85.654548,41.931072]},"n394490732":{"id":"n394490732","loc":[-85.654538,41.931701]},"n394490733":{"id":"n394490733","loc":[-85.654898,41.931689]},"n394490734":{"id":"n394490734","loc":[-85.654898,41.932505]},"n394490735":{"id":"n394490735","loc":[-85.654854,41.932514]},"n394490736":{"id":"n394490736","loc":[-85.655497,41.932499]},"n394490737":{"id":"n394490737","loc":[-85.656405,41.932493]},"n394490738":{"id":"n394490738","loc":[-85.656422,41.933416]},"n394490739":{"id":"n394490739","loc":[-85.657322,41.933438]},"n1475293233":{"id":"n1475293233","loc":[-85.6385522,41.9585167]},"n1475293242":{"id":"n1475293242","loc":[-85.64609,41.9540815]},"n1475293249":{"id":"n1475293249","loc":[-85.6358079,41.9692721]},"n1475293256":{"id":"n1475293256","loc":[-85.6387369,41.9581583]},"n1475293259":{"id":"n1475293259","loc":[-85.6455882,41.9541138]},"n1475293266":{"id":"n1475293266","loc":[-85.6451008,41.9541821]},"n1819800253":{"id":"n1819800253","loc":[-85.6134286,41.9429692]},"n2114807558":{"id":"n2114807558","loc":[-85.6365609,41.963866],"tags":{"railway":"level_crossing"}},"n2189015728":{"id":"n2189015728","loc":[-85.6383956,41.9590576]},"n2189015838":{"id":"n2189015838","loc":[-85.6435144,41.9563705]},"n2189015842":{"id":"n2189015842","loc":[-85.6415782,41.9557035]},"n2189015855":{"id":"n2189015855","loc":[-85.6440829,41.9554577]},"n2199109849":{"id":"n2199109849","loc":[-85.6393434,41.9565591]},"n2199109851":{"id":"n2199109851","loc":[-85.6393208,41.9565002]},"n2199109857":{"id":"n2199109857","loc":[-85.6401986,41.955545]},"n2199109859":{"id":"n2199109859","loc":[-85.6402362,41.955587]},"n2199109861":{"id":"n2199109861","loc":[-85.6395958,41.9565675]},"n2199109863":{"id":"n2199109863","loc":[-85.639528,41.9566011]},"w209717053":{"id":"w209717053","tags":{"area":"yes","building":"yes"},"nodes":["n2199109829","n2199109831","n2199109833","n2199109835","n2199109829"]},"w17966415":{"id":"w17966415","tags":{"access":"private","highway":"service","name":"Manufacturing Way"},"nodes":["n185971642","n185977232","n185977233","n185949745","n185949748","n185977234"]},"w209717054":{"id":"w209717054","tags":{"area":"yes","building":"yes"},"nodes":["n2199109837","n2199109839","n2199109841","n2199109843","n2199109845","n2199109847","n2199109837"]},"w208627214":{"id":"w208627214","tags":{"highway":"service"},"nodes":["n185949755","n2189015728","n1475293233","n1475293256","n185952163","n185952161","n185952160","n185952158","n185949763","n1819848965","n1819848890","n185952156"]},"w17963817":{"id":"w17963817","tags":{"access":"private","highway":"service"},"nodes":["n185949765","n185953413","n185953417","n185953421","n185953423"]},"w34369809":{"id":"w34369809","tags":{"admin_level":"8","boundary":"administrative","landuse":"residential"},"nodes":["n394490395","n394490396","n394490397","n394490398","n394490399","n394490400","n394490401","n394490402","n394490403","n394490404","n394490405","n394490406","n394490407","n394490408","n394490409","n394490410","n394490411","n394490412","n394490413","n394490414","n394490415","n394490416","n394490417","n394490418","n394490419","n394490420","n394490421","n394490422","n394490423","n394490424","n394490425","n394490426","n394490427","n394490428","n1475293242","n1475293259","n1475293266","n394490429","n394490430","n394490431","n394490432","n394490433","n394490434","n394490435","n394490436","n394490437","n394490438","n394490439","n394490440","n394490441","n394490442","n394490443","n394490444","n394490445","n394490446","n1475293249","n394490447","n394490448","n394490449","n394490450","n394490451","n394490452","n394490453","n394490454","n394490455","n394490456","n394490457","n394490458","n394490459","n394490460","n394490461","n394490462","n2114807558","n394490463","n1475293226","n394490464","n394490465","n394490466","n394490467","n394490468","n394490469","n394490470","n394490471","n394490472","n394490473","n394490474","n394490475","n394490476","n394490477","n394490478","n394490479","n394490480","n394490481","n394490482","n394490483","n394490484","n394490485","n394490486","n394490487","n394490488","n394490489","n394490490","n394490491","n394490492","n394490493","n394490494","n394490495","n394490496","n394490497","n394490498","n394490499","n394490500","n394490501","n394490502","n394490503","n394490504","n394490505","n394490506","n394490507","n394490508","n394490509","n394490510","n394490511","n394490512","n394490513","n394490514","n394490515","n394490516","n394490517","n394490518","n394490519","n394490520","n394490521","n394490522","n394490523","n394490524","n394490525","n394490526","n394490527","n394490528","n394490529","n394490530","n394490531","n394490532","n394490533","n394490534","n394490535","n394490536","n394490537","n394490538","n394490539","n394490540","n394490542","n394490545","n394490546","n394490547","n394490548","n394490549","n394490550","n394490551","n394490552","n394490553","n394490554","n394490555","n394490556","n394490557","n394490558","n394490559","n394490560","n394490561","n394490562","n394490563","n394490564","n394490565","n394490566","n394490567","n394490568","n394490569","n394490570","n394490571","n1819800253","n394490572","n394490573","n394490574","n394490575","n394490576","n394490577","n394490578","n394490579","n394490580","n394490582","n394490583","n394490585","n394490586","n394490588","n394490589","n394490590","n394490592","n394490594","n394490595","n394490596","n394490598","n394490599","n394490601","n394490602","n394490604","n394490605","n394490607","n394490608","n394490609","n394490611","n394490612","n394490613","n394490614","n394490615","n394490616","n394490617","n394490618","n394490619","n394490620","n394490621","n394490622","n394490623","n394490624","n394490625","n394490626","n394490627","n394490628","n394490629","n394490630","n394490631","n394490632","n394490633","n394490634","n394490635","n394490636","n394490637","n394490638","n394490639","n394490699","n394490700","n394490701","n394490702","n394490703","n394490704","n394490705","n394490706","n394490707","n394490708","n394490709","n394490710","n394490711","n394490712","n394490713","n394490714","n394490715","n394490716","n394490717","n394490718","n394490719","n394490720","n394490721","n394490722","n394490723","n394490724","n394490725","n394490726","n394490727","n394490728","n394490729","n394490730","n394490731","n394490732","n394490733","n394490734","n394490735","n394490736","n394490737","n394490738","n394490739","n394490395"]},"w208627221":{"id":"w208627221","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189015838","n2189015842","n2189015846","n2189015849","n2189015852","n2189015855","n2189015838"]},"w209717052":{"id":"w209717052","tags":{"area":"yes","building":"yes"},"nodes":["n2199109816","n2199109818","n2199109820","n2199109822","n2199109825","n2199109827","n2199109816"]},"w134151784":{"id":"w134151784","tags":{"bridge":"yes","highway":"residential","name":"W Hoffman St"},"nodes":["n185971648","n185971651"]},"w209717055":{"id":"w209717055","tags":{"area":"yes","landuse":"basin"},"nodes":["n2199109849","n2199109851","n2199109853","n2199109855","n2199109857","n2199109859","n2199109861","n2199109863","n2199109849"]},"w17967763":{"id":"w17967763","tags":{"highway":"residential","name":"Rock River Ave"},"nodes":["n1475293244","n185982166","n185975067","n185971637"]},"r134949":{"id":"r134949","tags":{"admin_level":"8","border_type":"city","boundary":"administrative","name":"Three Rivers","place":"city","type":"boundary"},"members":[{"id":"w34369809","type":"way","role":"outer"},{"id":"w34369821","type":"way","role":"outer"},{"id":"w34369822","type":"way","role":"outer"},{"id":"w34369823","type":"way","role":"outer"},{"id":"w34369824","type":"way","role":"outer"},{"id":"w34369825","type":"way","role":"outer"},{"id":"w34369826","type":"way","role":"outer"},{"id":"w34369810","type":"way","role":"inner"},{"id":"w34369811","type":"way","role":"inner"},{"id":"w34369812","type":"way","role":"inner"},{"id":"w34367079","type":"way","role":"inner"},{"id":"w34369814","type":"way","role":"inner"},{"id":"w34367080","type":"way","role":"inner"},{"id":"w34369815","type":"way","role":"inner"},{"id":"w34369820","type":"way","role":"inner"}]},"n1819848881":{"id":"n1819848881","loc":[-85.638562,41.9569965]},"n1819848947":{"id":"n1819848947","loc":[-85.6384348,41.9576565]},"n1819849044":{"id":"n1819849044","loc":[-85.6385749,41.9573345]},"n2114807547":{"id":"n2114807547","loc":[-85.6384626,41.9583756]},"n2114807564":{"id":"n2114807564","loc":[-85.638535,41.9581283]},"n2189015691":{"id":"n2189015691","loc":[-85.6435584,41.9565243]},"n2189015696":{"id":"n2189015696","loc":[-85.6435805,41.9566049]},"n2189015722":{"id":"n2189015722","loc":[-85.6435035,41.9567438]},"n2189015744":{"id":"n2189015744","loc":[-85.6437991,41.9569582]},"n2189015747":{"id":"n2189015747","loc":[-85.6433042,41.9567742]},"n2189015750":{"id":"n2189015750","loc":[-85.6433827,41.9566844]},"n2189015753":{"id":"n2189015753","loc":[-85.6430447,41.9565588]},"n2189015756":{"id":"n2189015756","loc":[-85.6431111,41.956451]},"n2189015759":{"id":"n2189015759","loc":[-85.6420247,41.956083]},"n2189015760":{"id":"n2189015760","loc":[-85.6419945,41.9561369]},"n2189015764":{"id":"n2189015764","loc":[-85.6413729,41.9558945]},"n2189015766":{"id":"n2189015766","loc":[-85.6412884,41.9560606]},"n2189015770":{"id":"n2189015770","loc":[-85.6411798,41.9560112]},"n2189015771":{"id":"n2189015771","loc":[-85.6410651,41.9562132]},"n2189015774":{"id":"n2189015774","loc":[-85.6409504,41.9561728]},"n2189015778":{"id":"n2189015778","loc":[-85.6407996,41.9564241]},"n2189015781":{"id":"n2189015781","loc":[-85.6406889,41.9563892]},"n2189015785":{"id":"n2189015785","loc":[-85.6404857,41.9567024]},"n2189015789":{"id":"n2189015789","loc":[-85.6406909,41.9567877]},"n2189015793":{"id":"n2189015793","loc":[-85.6405642,41.9570165]},"n2189015796":{"id":"n2189015796","loc":[-85.6415359,41.9573711]},"n2189015800":{"id":"n2189015800","loc":[-85.6411738,41.9579501]},"n2189015804":{"id":"n2189015804","loc":[-85.6411119,41.957921]},"n2189015808":{"id":"n2189015808","loc":[-85.6403186,41.9591751]},"n2189015909":{"id":"n2189015909","loc":[-85.6389293,41.9564636]},"n2189015926":{"id":"n2189015926","loc":[-85.6385431,41.9564617]},"n2189015929":{"id":"n2189015929","loc":[-85.6385457,41.9561823]},"n2189015932":{"id":"n2189015932","loc":[-85.6389319,41.9561843]},"n2199109865":{"id":"n2199109865","loc":[-85.6400768,41.956776]},"n2199109867":{"id":"n2199109867","loc":[-85.639902,41.9567153]},"n2199109869":{"id":"n2199109869","loc":[-85.640004,41.956553]},"n2199109871":{"id":"n2199109871","loc":[-85.6401788,41.9566137]},"n2199109873":{"id":"n2199109873","loc":[-85.6399316,41.9564506],"tags":{"man_made":"water_tower"}},"n2199109876":{"id":"n2199109876","loc":[-85.6397689,41.9572354]},"n2199109878":{"id":"n2199109878","loc":[-85.6399229,41.9569826]},"n2199109880":{"id":"n2199109880","loc":[-85.639706,41.9569095]},"n2199109882":{"id":"n2199109882","loc":[-85.639552,41.9571623]},"n2199109884":{"id":"n2199109884","loc":[-85.6391028,41.9569517]},"n2199109886":{"id":"n2199109886","loc":[-85.6392876,41.956646]},"n2199109888":{"id":"n2199109888","loc":[-85.639484,41.9567117]},"n2199109889":{"id":"n2199109889","loc":[-85.6394322,41.9567973]},"n2199109890":{"id":"n2199109890","loc":[-85.6393718,41.9567771]},"n2199109891":{"id":"n2199109891","loc":[-85.6392387,41.9569972]},"n1819848900":{"id":"n1819848900","loc":[-85.638281,41.9576578]},"n1819848978":{"id":"n1819848978","loc":[-85.6377186,41.9580867]},"n1819849039":{"id":"n1819849039","loc":[-85.6384217,41.9573405]},"n1819849050":{"id":"n1819849050","loc":[-85.6377011,41.9570042]},"n1819849088":{"id":"n1819849088","loc":[-85.6382879,41.9580817]},"n2114807549":{"id":"n2114807549","loc":[-85.6362551,41.96473]},"n2114807587":{"id":"n2114807587","loc":[-85.6368694,41.9629829]},"n2189015725":{"id":"n2189015725","loc":[-85.644156,41.9569753]},"n2189015741":{"id":"n2189015741","loc":[-85.6419825,41.9597632]},"w208627217":{"id":"w208627217","tags":{"area":"yes","building":"yes"},"nodes":["n2189015741","n2189015744","n2189015747","n2189015750","n2189015753","n2189015756","n2189015759","n2189015760","n2189015764","n2189015766","n2189015770","n2189015771","n2189015774","n2189015778","n2189015781","n2189015785","n2189015789","n2189015793","n2189015796","n2189015800","n2189015804","n2189015808","n2189015741"]},"w208627212":{"id":"w208627212","tags":{"highway":"service"},"nodes":["n2189015691","n2189015696","n2189015722","n2189015725"]},"w209717057":{"id":"w209717057","tags":{"area":"yes","building":"yes"},"nodes":["n2199109876","n2199109878","n2199109880","n2199109882","n2199109876"]},"w209717056":{"id":"w209717056","tags":{"area":"yes","building":"yes"},"nodes":["n2199109865","n2199109867","n2199109869","n2199109871","n2199109865"]},"w208627231":{"id":"w208627231","tags":{"area":"yes","building":"yes"},"nodes":["n2189015909","n2189015926","n2189015929","n2189015932","n2189015909"]},"w170848326":{"id":"w170848326","tags":{"building":"yes"},"nodes":["n1819848881","n1819849050","n1819848978","n1819849088","n1819848900","n1819848947","n1819849039","n1819849044","n1819848881"]},"w17963182":{"id":"w17963182","tags":{"highway":"service"},"nodes":["n185949763","n185949765","n2189015691","n185949745"]},"w201484340":{"id":"w201484340","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807565","n2114807564","n2114807547","n2114807587","n2114807558","n2114807549","n2114807593"]},"w209717058":{"id":"w209717058","tags":{"area":"yes","building":"yes"},"nodes":["n2199109884","n2199109886","n2199109888","n2199109889","n2199109890","n2199109891","n2199109884"]},"n185954650":{"id":"n185954650","loc":[-85.627331,41.957439]},"n185966949":{"id":"n185966949","loc":[-85.626868,41.957314]},"n185989335":{"id":"n185989335","loc":[-85.62529,41.958568]},"n185989337":{"id":"n185989337","loc":[-85.624962,41.958453]},"n185989339":{"id":"n185989339","loc":[-85.624832,41.958399]},"n185989340":{"id":"n185989340","loc":[-85.624707,41.958325]},"n185989342":{"id":"n185989342","loc":[-85.624636,41.958251]},"n185989345":{"id":"n185989345","loc":[-85.624578,41.95818]},"n185989347":{"id":"n185989347","loc":[-85.624533,41.958099]},"n185989349":{"id":"n185989349","loc":[-85.624507,41.957985]},"n185989351":{"id":"n185989351","loc":[-85.624495,41.957807]},"n185989353":{"id":"n185989353","loc":[-85.624514,41.957663]},"n185989354":{"id":"n185989354","loc":[-85.624577,41.957593]},"n185989356":{"id":"n185989356","loc":[-85.624685,41.95754]},"n185989357":{"id":"n185989357","loc":[-85.624802,41.957523]},"n185989359":{"id":"n185989359","loc":[-85.624996,41.957524]},"n185989361":{"id":"n185989361","loc":[-85.625409,41.957515]},"n185989364":{"id":"n185989364","loc":[-85.625634,41.957496]},"n185989367":{"id":"n185989367","loc":[-85.625832,41.957453]},"n185989368":{"id":"n185989368","loc":[-85.626044,41.957394]},"n354031352":{"id":"n354031352","loc":[-85.6252778,41.9586111],"tags":{"amenity":"place_of_worship","denomination":"baptist","name":"First Baptist Church","religion":"christian"}},"n2199109892":{"id":"n2199109892","loc":[-85.6261578,41.9589963]},"n2199109893":{"id":"n2199109893","loc":[-85.6263191,41.9586865]},"n2199109894":{"id":"n2199109894","loc":[-85.6261186,41.9586288]},"n2199109895":{"id":"n2199109895","loc":[-85.6260644,41.9587329]},"n2199109896":{"id":"n2199109896","loc":[-85.6261547,41.9587589]},"n2199109898":{"id":"n2199109898","loc":[-85.6260476,41.9589646]},"n185966951":{"id":"n185966951","loc":[-85.628404,41.957438]},"w17965351":{"id":"w17965351","tags":{"highway":"residential","name":"Flower Street"},"nodes":["n185966948","n185966949","n185954650","n185966951","n185966953","n185966955","n185966957"]},"w17967809":{"id":"w17967809","tags":{"highway":"residential","name":"Azaleamum Drive"},"nodes":["n185982197","n185989335","n185989337","n185989339","n185989340","n185989342","n185989345","n185989347","n185989349","n185989351","n185989353","n185989354","n185989356","n185989357","n185989359","n185989361","n185989364","n185989367","n185989368","n185982196"]},"w209717059":{"id":"w209717059","tags":{"area":"yes","building":"yes"},"nodes":["n2199109892","n2199109893","n2199109894","n2199109895","n2199109896","n2199109898","n2199109892"]},"n185961390":{"id":"n185961390","loc":[-85.63137,41.959033]},"n185961393":{"id":"n185961393","loc":[-85.634315,41.959017]},"w17966214":{"id":"w17966214","tags":{"highway":"residential","name":"East Adams Street"},"nodes":["n185975351","n185967434","n185968108"]},"w17964793":{"id":"w17964793","tags":{"highway":"residential","name":"Morris Ave"},"nodes":["n185961389","n185961390","n185961391","n185961393","n185961396"]},"n185952166":{"id":"n185952166","loc":[-85.638174,41.95831]},"n2114807552":{"id":"n2114807552","loc":[-85.6383526,41.9593788]},"n2114807591":{"id":"n2114807591","loc":[-85.6383741,41.9593968]},"n2189015731":{"id":"n2189015731","loc":[-85.6368404,41.9592785]},"n2189015734":{"id":"n2189015734","loc":[-85.6368404,41.9585918]},"n2189015737":{"id":"n2189015737","loc":[-85.6376009,41.9585918]},"n2189015738":{"id":"n2189015738","loc":[-85.6376009,41.9592785]},"n2189015897":{"id":"n2189015897","loc":[-85.6376839,41.9566137]},"n2189015900":{"id":"n2189015900","loc":[-85.6376831,41.9564865]},"n2189015903":{"id":"n2189015903","loc":[-85.6381161,41.9564851]},"n2189015906":{"id":"n2189015906","loc":[-85.6381168,41.9566122]},"n2189015937":{"id":"n2189015937","loc":[-85.6364789,41.9590634]},"n2189015940":{"id":"n2189015940","loc":[-85.6361137,41.9590672]},"n2189015943":{"id":"n2189015943","loc":[-85.6361169,41.9594033]},"n2189015945":{"id":"n2189015945","loc":[-85.6363456,41.9594021]},"n2189015952":{"id":"n2189015952","loc":[-85.636112,41.958892]},"n2189015955":{"id":"n2189015955","loc":[-85.6364757,41.9588894]},"n2189015957":{"id":"n2189015957","loc":[-85.6364729,41.9586747]},"n2189015958":{"id":"n2189015958","loc":[-85.6361103,41.9586765]},"n2189015959":{"id":"n2189015959","loc":[-85.6364719,41.9585562]},"n2189015960":{"id":"n2189015960","loc":[-85.6361093,41.958558]},"n2189015961":{"id":"n2189015961","loc":[-85.6355494,41.9586403]},"n2189015962":{"id":"n2189015962","loc":[-85.635549,41.9584711]},"n2189015963":{"id":"n2189015963","loc":[-85.6351831,41.9584715]},"n2189015964":{"id":"n2189015964","loc":[-85.6351834,41.9586408]},"n2189015966":{"id":"n2189015966","loc":[-85.6359579,41.9586359]},"n2189015968":{"id":"n2189015968","loc":[-85.6359561,41.9585465]},"n2189015971":{"id":"n2189015971","loc":[-85.6355476,41.9585509]},"n2189015974":{"id":"n2189015974","loc":[-85.6359516,41.9592934]},"n2189015977":{"id":"n2189015977","loc":[-85.635949,41.9586697]},"n2189015980":{"id":"n2189015980","loc":[-85.6351329,41.9586716]},"n2189015983":{"id":"n2189015983","loc":[-85.6351318,41.9583949]},"n2189015986":{"id":"n2189015986","loc":[-85.6349148,41.9583954]},"n2189015989":{"id":"n2189015989","loc":[-85.6349186,41.9592958]},"n2189015995":{"id":"n2189015995","loc":[-85.6360173,41.9593286]},"n2189015998":{"id":"n2189015998","loc":[-85.6360278,41.9583079]},"n2114807550":{"id":"n2114807550","loc":[-85.6383392,41.9595404]},"n2114807551":{"id":"n2114807551","loc":[-85.6375855,41.9616107]},"n2114807559":{"id":"n2114807559","loc":[-85.6373978,41.9621273]},"n2114807562":{"id":"n2114807562","loc":[-85.6373361,41.9622609]},"n2114807563":{"id":"n2114807563","loc":[-85.6376472,41.9613953]},"n2114807574":{"id":"n2114807574","loc":[-85.636974,41.9627695]},"n2114807589":{"id":"n2114807589","loc":[-85.6383017,41.9595005]},"n2114807592":{"id":"n2114807592","loc":[-85.6377169,41.9613494]},"n2114807595":{"id":"n2114807595","loc":[-85.6371081,41.962574]},"n2189015934":{"id":"n2189015934","loc":[-85.6364855,41.9595098]},"n2189015949":{"id":"n2189015949","loc":[-85.6363466,41.9595105]},"w208627244":{"id":"w208627244","tags":{"highway":"service"},"nodes":["n2189015992","n2189015995","n2189015998"]},"w208627240":{"id":"w208627240","tags":{"area":"yes","building":"yes"},"nodes":["n2189015961","n2189015971","n2189015962","n2189015963","n2189015964","n2189015961"]},"w17967437":{"id":"w17967437","tags":{"highway":"residential","name":"Lyman St"},"nodes":["n185964361","n185984024"]},"w208627237":{"id":"w208627237","tags":{"area":"yes","building":"yes"},"nodes":["n2189015955","n2189015957","n2189015958","n2189015952","n2189015955"]},"w17967465":{"id":"w17967465","tags":{"highway":"residential","name":"W Adams St"},"nodes":["n185978394","n185984022","n185964360"]},"w208627228":{"id":"w208627228","tags":{"area":"yes","building":"yes"},"nodes":["n2189015897","n2189015900","n2189015903","n2189015906","n2189015897"]},"w201484351":{"id":"w201484351","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807587","n2114807574","n2114807595","n2114807562","n2114807559","n2114807551","n2114807563","n2114807589","n2114807552"]},"w208627239":{"id":"w208627239","tags":{"area":"yes","building":"yes"},"nodes":["n2189015957","n2189015959","n2189015960","n2189015958","n2189015957"]},"w208627233":{"id":"w208627233","tags":{"area":"yes","building":"yes"},"nodes":["n2189015934","n2189015937","n2189015940","n2189015943","n2189015945","n2189015949","n2189015934"]},"w208627241":{"id":"w208627241","tags":{"area":"yes","building":"yes"},"nodes":["n2189015961","n2189015966","n2189015968","n2189015971","n2189015961"]},"w17967970":{"id":"w17967970","tags":{"highway":"residential","name":"Adams St"},"nodes":["n185975351","n185978394"]},"w208627235":{"id":"w208627235","tags":{"area":"yes","building":"yes"},"nodes":["n2189015940","n2189015952","n2189015955","n2189015937","n2189015940"]},"w17965468":{"id":"w17965468","tags":{"highway":"residential","name":"Armstrong Blvd"},"nodes":["n185967917","n2189015998","n185967918","n185964362","n185952166"]},"w201484346":{"id":"w201484346","tags":{"railway":"rail","service":"siding"},"nodes":["n2114807551","n2114807592","n2114807550","n2114807591"]},"w208627242":{"id":"w208627242","tags":{"amenity":"parking","area":"yes"},"nodes":["n2189015974","n2189015977","n2189015980","n2189015983","n2189015986","n2189015989","n2189015974"]},"w208627216":{"id":"w208627216","tags":{"area":"yes","building":"yes"},"nodes":["n2189015731","n2189015734","n2189015737","n2189015738","n2189015731"]},"n185984309":{"id":"n185984309","loc":[-85.631421,41.961494]},"n185987987":{"id":"n185987987","loc":[-85.631456,41.960673]},"n185965397":{"id":"n185965397","loc":[-85.634603,41.959838]},"w17965196":{"id":"w17965196","tags":{"highway":"residential","name":"Burke Ave"},"nodes":["n185965395","n185965397","n185965399"]},"w17967215":{"id":"w17967215","tags":{"highway":"residential","name":"Kellogg Ave"},"nodes":["n185968114","n185984309","n185967440","n185978402"]},"w17967597":{"id":"w17967597","tags":{"highway":"residential","name":"Barnard Ave"},"nodes":["n185968112","n185987987","n185967438","n185978399"]},"n394490857":{"id":"n394490857","loc":[-85.633952,41.960664]},"n394490858":{"id":"n394490858","loc":[-85.633938,41.960227]},"n394490859":{"id":"n394490859","loc":[-85.634794,41.960212]},"n394490860":{"id":"n394490860","loc":[-85.634815,41.960662]},"n394490861":{"id":"n394490861","loc":[-85.634103,41.961268]},"n394490862":{"id":"n394490862","loc":[-85.634103,41.961001]},"n394490863":{"id":"n394490863","loc":[-85.634504,41.961003]},"n394490864":{"id":"n394490864","loc":[-85.634561,41.961269]},"n1057629869":{"id":"n1057629869","loc":[-85.6382599,41.9612134]},"n1057629937":{"id":"n1057629937","loc":[-85.6380035,41.9616137]},"n2189016014":{"id":"n2189016014","loc":[-85.6360365,41.9626496]},"n2189016017":{"id":"n2189016017","loc":[-85.6360374,41.9623228]},"n2189016020":{"id":"n2189016020","loc":[-85.6367557,41.9623239]},"n2189016022":{"id":"n2189016022","loc":[-85.6367566,41.9619919]},"n2189016025":{"id":"n2189016025","loc":[-85.6351794,41.9619893]},"n2189016028":{"id":"n2189016028","loc":[-85.6351788,41.9622011]},"n2189016031":{"id":"n2189016031","loc":[-85.6350855,41.9622009]},"n2189016034":{"id":"n2189016034","loc":[-85.6350845,41.962527]},"n2189016037":{"id":"n2189016037","loc":[-85.6352732,41.9625273]},"n2189016039":{"id":"n2189016039","loc":[-85.6352738,41.9623178]},"n2189016042":{"id":"n2189016042","loc":[-85.6357712,41.9623186]},"n2189016044":{"id":"n2189016044","loc":[-85.6357702,41.9626492]},"n1057629880":{"id":"n1057629880","loc":[-85.638817,41.9619017]},"n1057629923":{"id":"n1057629923","loc":[-85.6390733,41.9615014]},"w91092312":{"id":"w91092312","tags":{"power":"station"},"nodes":["n1057629923","n1057629869","n1057629937","n1057629880","n1057629923"]},"w34369826":{"id":"w34369826","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490861","n394490862","n394490863","n394490864","n394490861"]},"w34369825":{"id":"w34369825","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490857","n394490858","n394490859","n394490860","n394490857"]},"w208627248":{"id":"w208627248","tags":{"area":"yes","building":"yes"},"nodes":["n2189016014","n2189016017","n2189016020","n2189016022","n2189016025","n2189016028","n2189016031","n2189016034","n2189016037","n2189016039","n2189016042","n2189016044","n2189016014"]},"n394490766":{"id":"n394490766","loc":[-85.616777,41.955642]},"n394490768":{"id":"n394490768","loc":[-85.617239,41.955644]},"n394490792":{"id":"n394490792","loc":[-85.619034,41.95543]},"n185972055":{"id":"n185972055","loc":[-85.6185905,41.9568211]},"n185972057":{"id":"n185972057","loc":[-85.6186688,41.9570086]},"n185972059":{"id":"n185972059","loc":[-85.6186924,41.9581453]},"n185972060":{"id":"n185972060","loc":[-85.6187082,41.9588211],"tags":{"highway":"turning_circle"}},"n1819790724":{"id":"n1819790724","loc":[-85.6182155,41.9555703]},"n1819790735":{"id":"n1819790735","loc":[-85.6184059,41.9566188]},"n1819790799":{"id":"n1819790799","loc":[-85.6182372,41.9563771]},"n1819790896":{"id":"n1819790896","loc":[-85.6181431,41.9557227]},"n185971405":{"id":"n185971405","loc":[-85.6186766,41.9577468]},"n185971565":{"id":"n185971565","loc":[-85.6181613,41.9560879]},"n185967985":{"id":"n185967985","loc":[-85.6186798,41.9585791]},"n185955753":{"id":"n185955753","loc":[-85.620773,41.9555854]},"n185955755":{"id":"n185955755","loc":[-85.6212652,41.9559891]},"n185955748":{"id":"n185955748","loc":[-85.620722,41.954858]},"n185955751":{"id":"n185955751","loc":[-85.6206912,41.955367]},"n185967987":{"id":"n185967987","loc":[-85.6159351,41.9585809]},"n185971407":{"id":"n185971407","loc":[-85.6159142,41.9577578]},"n185971570":{"id":"n185971570","loc":[-85.6162248,41.95603]},"n185971572":{"id":"n185971572","loc":[-85.6160402,41.9560749]},"n185971574":{"id":"n185971574","loc":[-85.61593,41.956201]},"n185981301":{"id":"n185981301","loc":[-85.6158973,41.9581601]},"n394490762":{"id":"n394490762","loc":[-85.617193,41.954706]},"n394490764":{"id":"n394490764","loc":[-85.616773,41.954737]},"n394490787":{"id":"n394490787","loc":[-85.618972,41.954737]},"n394490790":{"id":"n394490790","loc":[-85.619046,41.954929]},"n394490794":{"id":"n394490794","loc":[-85.619922,41.955296]},"n394490796":{"id":"n394490796","loc":[-85.61991,41.95501]},"n394490798":{"id":"n394490798","loc":[-85.619974,41.954751]},"n1819790677":{"id":"n1819790677","loc":[-85.6187031,41.9550522]},"n1819790787":{"id":"n1819790787","loc":[-85.6186436,41.9552022]},"n1819790828":{"id":"n1819790828","loc":[-85.6185127,41.9553393]},"w17966857":{"id":"w17966857","tags":{"access":"private","highway":"service","name":"Sable River Rd"},"nodes":["n185972059","n185981301"]},"w34369814":{"id":"w34369814","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490787","n394490790","n394490792","n394490794","n394490796","n394490798","n394490787"]},"w17964176":{"id":"w17964176","tags":{"highway":"residential"},"nodes":["n185955747","n185955748","n185955751","n185955753","n185955755"]},"w17965838":{"id":"w17965838","tags":{"access":"private","highway":"service","name":"Pine River Rd"},"nodes":["n185971405","n185971407"]},"w17965476":{"id":"w17965476","tags":{"access":"private","highway":"service","name":"Raisin River Rd"},"nodes":["n185967985","n185967987"]},"w17965913":{"id":"w17965913","tags":{"access":"private","highway":"service","name":"Shiawassee River Rd"},"nodes":["n185972054","n1819790677","n1819790787","n1819790828","n1819790724","n1819790896","n185971565","n1819790799","n1819790735","n185972055","n185972057","n185971405","n185972059","n185967985","n185972060"]},"w34369811":{"id":"w34369811","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490762","n394490764","n394490766","n394490768","n394490762"]},"w17965854":{"id":"w17965854","tags":{"access":"private","highway":"service","name":"Sturgeon River Rd"},"nodes":["n185971565","n185971570","n185971572","n185971574"]},"n2139795769":{"id":"n2139795769","loc":[-85.6250804,41.9608796]},"n2139795770":{"id":"n2139795770","loc":[-85.6250315,41.9613684]},"n2139795771":{"id":"n2139795771","loc":[-85.6249671,41.9614362]},"n2139795772":{"id":"n2139795772","loc":[-85.6249698,41.961522]},"n2139795773":{"id":"n2139795773","loc":[-85.6250798,41.9615838]},"n2139795774":{"id":"n2139795774","loc":[-85.6252273,41.9615639]},"n2139795775":{"id":"n2139795775","loc":[-85.6252863,41.9614622]},"n2139795776":{"id":"n2139795776","loc":[-85.6252273,41.9613764]},"n2139795777":{"id":"n2139795777","loc":[-85.6251227,41.9613525]},"n2139795778":{"id":"n2139795778","loc":[-85.6249564,41.9612527]},"n2139795779":{"id":"n2139795779","loc":[-85.6249846,41.9610254]},"n2139795780":{"id":"n2139795780","loc":[-85.6266725,41.9599647]},"n2139795781":{"id":"n2139795781","loc":[-85.6259162,41.9599711]},"n2139795782":{"id":"n2139795782","loc":[-85.6257185,41.960019]},"n2139795783":{"id":"n2139795783","loc":[-85.6255509,41.9601213]},"n185963539":{"id":"n185963539","loc":[-85.615718,41.983893]},"n185964418":{"id":"n185964418","loc":[-85.616626,42.049512]},"n185966614":{"id":"n185966614","loc":[-85.615514,41.976603]},"n185966635":{"id":"n185966635","loc":[-85.616118,42.013017]},"n185969040":{"id":"n185969040","loc":[-85.615632,41.972357]},"n185969070":{"id":"n185969070","loc":[-85.619145,41.967648]},"n185972156":{"id":"n185972156","loc":[-85.621894,41.963956]},"n185972157":{"id":"n185972157","loc":[-85.621806,41.964077]},"n185972158":{"id":"n185972158","loc":[-85.620848,41.965341]},"n185972159":{"id":"n185972159","loc":[-85.620684,41.965558]},"n185972160":{"id":"n185972160","loc":[-85.620621,41.965658]},"n185972161":{"id":"n185972161","loc":[-85.617844,41.969359]},"n185972162":{"id":"n185972162","loc":[-85.616843,41.97068]},"n185972164":{"id":"n185972164","loc":[-85.616714,41.970839]},"n185972166":{"id":"n185972166","loc":[-85.615879,41.971969]},"n185972168":{"id":"n185972168","loc":[-85.615748,41.972159]},"n185972170":{"id":"n185972170","loc":[-85.615589,41.972502]},"n185972172":{"id":"n185972172","loc":[-85.615542,41.972733]},"n185972175":{"id":"n185972175","loc":[-85.615524,41.972947]},"n185972177":{"id":"n185972177","loc":[-85.615512,41.973715]},"n185972179":{"id":"n185972179","loc":[-85.615513,41.976496]},"n185972180":{"id":"n185972180","loc":[-85.615538,41.977246]},"n185972181":{"id":"n185972181","loc":[-85.61558,41.982139]},"n185972184":{"id":"n185972184","loc":[-85.61557,41.983317]},"n185972186":{"id":"n185972186","loc":[-85.615591,41.983463]},"n185972188":{"id":"n185972188","loc":[-85.615763,41.984146]},"n185972190":{"id":"n185972190","loc":[-85.615814,41.98435]},"n185972192":{"id":"n185972192","loc":[-85.615965,41.998453]},"n185972194":{"id":"n185972194","loc":[-85.615982,42.001237]},"n185972195":{"id":"n185972195","loc":[-85.616055,42.00555]},"n185972197":{"id":"n185972197","loc":[-85.616134,42.014887]},"n185972199":{"id":"n185972199","loc":[-85.616177,42.018465]},"n185972201":{"id":"n185972201","loc":[-85.616298,42.027627]},"n185972203":{"id":"n185972203","loc":[-85.616513,42.042212]},"w203968015":{"id":"w203968015","tags":{"highway":"residential"},"nodes":["n2139795768","n2139795769"]},"w17965932":{"id":"w17965932","tags":{"highway":"residential","name":"Buckhorn Road","name_1":"County Highway 122"},"nodes":["n185972155","n185972156","n185972157","n185972158","n185972159","n185972160","n185969070","n185972161","n185972162","n185972164","n185972166","n185972168","n185969040","n185972170","n185972172","n185972175","n185972177","n185972179","n185966614","n185972180","n185972181","n185972184","n185972186","n185963539","n185972188","n185972190","n185972192","n185972194","n185972195","n185966635","n185972197","n185972199","n185972201","n185972203","n185964418"]},"w203968016":{"id":"w203968016","tags":{"highway":"residential","name":"New Jersey Court"},"nodes":["n2139795770","n2139795771","n2139795772","n2139795773","n2139795774","n2139795775","n2139795776","n2139795777","n2139795770","n2139795778","n2139795779","n2139795769"]},"w203968017":{"id":"w203968017","tags":{"highway":"residential","name":"Oklahoma Drive"},"nodes":["n2139795780","n2139795781","n2139795782","n2139795783","n2139795769"]},"n1819790528":{"id":"n1819790528","loc":[-85.6184827,41.960025]},"n1819790530":{"id":"n1819790530","loc":[-85.6168626,41.9605834]},"n1819790534":{"id":"n1819790534","loc":[-85.6197379,41.9617163]},"n1819790541":{"id":"n1819790541","loc":[-85.6198881,41.9620833]},"n1819790543":{"id":"n1819790543","loc":[-85.619695,41.9619397]},"n1819790547":{"id":"n1819790547","loc":[-85.6190298,41.9609504]},"n1819790555":{"id":"n1819790555","loc":[-85.6180471,41.9609788]},"n1819790559":{"id":"n1819790559","loc":[-85.6203817,41.9605436]},"n1819790583":{"id":"n1819790583","loc":[-85.6201564,41.9603282]},"n1819790590":{"id":"n1819790590","loc":[-85.617045,41.9598894]},"n1819790609":{"id":"n1819790609","loc":[-85.6177638,41.9598495]},"n1819790618":{"id":"n1819790618","loc":[-85.6195234,41.9610143]},"n1819790642":{"id":"n1819790642","loc":[-85.6181179,41.9627933]},"n1819790659":{"id":"n1819790659","loc":[-85.6174634,41.962897]},"n1819790665":{"id":"n1819790665","loc":[-85.6170343,41.9630885]},"n1819790674":{"id":"n1819790674","loc":[-85.6194697,41.9601925]},"n1819790685":{"id":"n1819790685","loc":[-85.6207722,41.9610665]},"n1819790687":{"id":"n1819790687","loc":[-85.6202315,41.9622109]},"n1819790697":{"id":"n1819790697","loc":[-85.6184505,41.9624662]},"n1819790726":{"id":"n1819790726","loc":[-85.6178926,41.9628492]},"n1819790738":{"id":"n1819790738","loc":[-85.6173347,41.9598016]},"n1819790762":{"id":"n1819790762","loc":[-85.6186221,41.9609105]},"n1819790774":{"id":"n1819790774","loc":[-85.6175922,41.9608308]},"n1819790781":{"id":"n1819790781","loc":[-85.6167768,41.9633198]},"n1819790796":{"id":"n1819790796","loc":[-85.619856,41.961461]},"n1819790811":{"id":"n1819790811","loc":[-85.6208215,41.9620195]},"n1819790833":{"id":"n1819790833","loc":[-85.618311,41.9612536]},"n1819790854":{"id":"n1819790854","loc":[-85.6183646,41.9626417]},"n1819790863":{"id":"n1819790863","loc":[-85.6204997,41.9608547]},"n1819790867":{"id":"n1819790867","loc":[-85.6184934,41.9621391]},"n1819790877":{"id":"n1819790877","loc":[-85.6206928,41.9621152]},"n1819790881":{"id":"n1819790881","loc":[-85.6170879,41.960735]},"n1819790891":{"id":"n1819790891","loc":[-85.6168304,41.9601207]},"n1819790898":{"id":"n1819790898","loc":[-85.619813,41.9612297]},"n1819790909":{"id":"n1819790909","loc":[-85.6167982,41.960376]},"n1819790912":{"id":"n1819790912","loc":[-85.6205855,41.9610462]},"n1819790544":{"id":"n1819790544","loc":[-85.612968,41.9707781]},"n1819790549":{"id":"n1819790549","loc":[-85.614395,41.9697172]},"n1819790552":{"id":"n1819790552","loc":[-85.6180535,41.9655536]},"n1819790554":{"id":"n1819790554","loc":[-85.6111227,41.9703713]},"n1819790560":{"id":"n1819790560","loc":[-85.6112729,41.9701958]},"n1819790563":{"id":"n1819790563","loc":[-85.6137512,41.9689917]},"n1819790564":{"id":"n1819790564","loc":[-85.6181072,41.9659205]},"n1819790595":{"id":"n1819790595","loc":[-85.6170021,41.9666863]},"n1819790605":{"id":"n1819790605","loc":[-85.6168948,41.9644527]},"n1819790606":{"id":"n1819790606","loc":[-85.6128071,41.9701081]},"n1819790607":{"id":"n1819790607","loc":[-85.6129251,41.9704032]},"n1819790612":{"id":"n1819790612","loc":[-85.6177638,41.9663912]},"n1819790615":{"id":"n1819790615","loc":[-85.6152533,41.9670373]},"n1819790622":{"id":"n1819790622","loc":[-85.6146739,41.9673804]},"n1819790623":{"id":"n1819790623","loc":[-85.6180428,41.9661838]},"n1819790625":{"id":"n1819790625","loc":[-85.6172918,41.9646202]},"n1819790645":{"id":"n1819790645","loc":[-85.6178067,41.965043]},"n1819790647":{"id":"n1819790647","loc":[-85.6143306,41.9712488]},"n1819790649":{"id":"n1819790649","loc":[-85.6147383,41.9707702]},"n1819790654":{"id":"n1819790654","loc":[-85.6157361,41.9668459]},"n1819790657":{"id":"n1819790657","loc":[-85.6145666,41.9710733]},"n1819790668":{"id":"n1819790668","loc":[-85.6166909,41.9642692]},"n1819790671":{"id":"n1819790671","loc":[-85.6141482,41.9696538]},"n1819790679":{"id":"n1819790679","loc":[-85.6148349,41.9705388]},"n1819790686":{"id":"n1819790686","loc":[-85.6139551,41.9695501]},"n1819790696":{"id":"n1819790696","loc":[-85.6119703,41.9699087]},"n1819790704":{"id":"n1819790704","loc":[-85.6140731,41.9684174]},"n1819790706":{"id":"n1819790706","loc":[-85.6124745,41.9699246]},"n1819790718":{"id":"n1819790718","loc":[-85.6165407,41.9636868]},"n1819790720":{"id":"n1819790720","loc":[-85.61388,41.9687365]},"n1819790731":{"id":"n1819790731","loc":[-85.6165193,41.9639421]},"n1819790739":{"id":"n1819790739","loc":[-85.6146739,41.9699964]},"n1819790753":{"id":"n1819790753","loc":[-85.6173883,41.9665747]},"n1819790760":{"id":"n1819790760","loc":[-85.6133221,41.9712089]},"n1819790767":{"id":"n1819790767","loc":[-85.6116698,41.9699246]},"n1819790779":{"id":"n1819790779","loc":[-85.6130753,41.9710573]},"n1819790791":{"id":"n1819790791","loc":[-85.6137083,41.9692869]},"n1819790795":{"id":"n1819790795","loc":[-85.6141482,41.9679627]},"n1819790798":{"id":"n1819790798","loc":[-85.6137727,41.9694305]},"n1819790836":{"id":"n1819790836","loc":[-85.6143842,41.9676037]},"n1819790915":{"id":"n1819790915","loc":[-85.6148456,41.9702756]},"n1819790926":{"id":"n1819790926","loc":[-85.6138371,41.9713525]},"n1819790927":{"id":"n1819790927","loc":[-85.6141053,41.9713525]},"n1819790931":{"id":"n1819790931","loc":[-85.6162832,41.966814]},"n1821014625":{"id":"n1821014625","loc":[-85.5960611,41.9808498]},"n1821014627":{"id":"n1821014627","loc":[-85.5565843,42.010982]},"n1821014629":{"id":"n1821014629","loc":[-85.5971541,41.9805808]},"n1821014632":{"id":"n1821014632","loc":[-85.6061837,41.9725907]},"n1821014633":{"id":"n1821014633","loc":[-85.5247773,42.025766]},"n1821014635":{"id":"n1821014635","loc":[-85.5908938,41.9902384]},"n1821014636":{"id":"n1821014636","loc":[-85.5917682,41.9860637]},"n1821014637":{"id":"n1821014637","loc":[-85.5456556,42.0166797]},"n1821014638":{"id":"n1821014638","loc":[-85.5795749,42.0032352]},"n1821014639":{"id":"n1821014639","loc":[-85.6103988,41.9723456]},"n1821014642":{"id":"n1821014642","loc":[-85.5818816,42.0022466]},"n1821014643":{"id":"n1821014643","loc":[-85.5570604,42.0091586]},"n1821014644":{"id":"n1821014644","loc":[-85.5952886,41.9803792]},"n1821014645":{"id":"n1821014645","loc":[-85.5780366,42.0040343]},"n1821014646":{"id":"n1821014646","loc":[-85.6050505,41.9751971]},"n1821014647":{"id":"n1821014647","loc":[-85.5854435,41.9946162]},"n1821014648":{"id":"n1821014648","loc":[-85.5452278,42.0168768]},"n1821014649":{"id":"n1821014649","loc":[-85.6023254,41.9780166]},"n1821014651":{"id":"n1821014651","loc":[-85.5761899,42.0046783]},"n1821014653":{"id":"n1821014653","loc":[-85.5897351,41.9876707]},"n1821014657":{"id":"n1821014657","loc":[-85.5963601,41.9808998]},"n1821014658":{"id":"n1821014658","loc":[-85.5892952,41.9951983]},"n1821014660":{"id":"n1821014660","loc":[-85.5778328,42.0037194]},"n1821014661":{"id":"n1821014661","loc":[-85.5541475,42.0125705]},"n1821014663":{"id":"n1821014663","loc":[-85.5914047,41.9856469]},"n1821014664":{"id":"n1821014664","loc":[-85.6101681,41.9727723]},"n1821014665":{"id":"n1821014665","loc":[-85.5910172,41.9854696]},"n1821014666":{"id":"n1821014666","loc":[-85.5398688,42.0187699]},"n1821014667":{"id":"n1821014667","loc":[-85.5218752,42.0282884]},"n1821014668":{"id":"n1821014668","loc":[-85.5159582,42.0329384]},"n1821014669":{"id":"n1821014669","loc":[-85.5898102,41.9847319]},"n1821014670":{"id":"n1821014670","loc":[-85.5734809,42.0066235]},"n1821014671":{"id":"n1821014671","loc":[-85.5922939,41.980852]},"n1821014672":{"id":"n1821014672","loc":[-85.6023629,41.9781163]},"n1821014674":{"id":"n1821014674","loc":[-85.5409953,42.0191724]},"n1821014676":{"id":"n1821014676","loc":[-85.584435,41.9949909]},"n1821014677":{"id":"n1821014677","loc":[-85.5972399,41.9783835]},"n1821014678":{"id":"n1821014678","loc":[-85.5616738,42.0071337]},"n1821014681":{"id":"n1821014681","loc":[-85.5202994,42.0310755]},"n1821014682":{"id":"n1821014682","loc":[-85.5915912,41.9857767]},"n1821014684":{"id":"n1821014684","loc":[-85.6022288,41.977897]},"n1821014687":{"id":"n1821014687","loc":[-85.5933024,41.9846362]},"n1821014688":{"id":"n1821014688","loc":[-85.5846871,41.9956169]},"n1821014689":{"id":"n1821014689","loc":[-85.5898209,41.99037]},"n1821014691":{"id":"n1821014691","loc":[-85.5448939,42.0149261]},"n1821014692":{"id":"n1821014692","loc":[-85.5977763,41.9786348]},"n1821014694":{"id":"n1821014694","loc":[-85.5767706,42.0034523]},"n1821014695":{"id":"n1821014695","loc":[-85.6103559,41.9726766]},"n1821014697":{"id":"n1821014697","loc":[-85.5922134,41.9809876]},"n1821014698":{"id":"n1821014698","loc":[-85.5935277,41.9831728]},"n1821014700":{"id":"n1821014700","loc":[-85.5674674,42.0078273]},"n1821014703":{"id":"n1821014703","loc":[-85.6021,41.9778053]},"n1821014704":{"id":"n1821014704","loc":[-85.5756763,42.0053737]},"n1821014705":{"id":"n1821014705","loc":[-85.5887695,41.9895207]},"n1821014707":{"id":"n1821014707","loc":[-85.6061073,41.9746866]},"n1821014708":{"id":"n1821014708","loc":[-85.6033446,41.9751692]},"n1821014710":{"id":"n1821014710","loc":[-85.5180986,42.0322332]},"n1821014711":{"id":"n1821014711","loc":[-85.543365,42.0163569]},"n1821014712":{"id":"n1821014712","loc":[-85.6030656,41.9753646]},"n1821014713":{"id":"n1821014713","loc":[-85.6104417,41.9704792]},"n1821014714":{"id":"n1821014714","loc":[-85.5205716,42.030998]},"n1821014716":{"id":"n1821014716","loc":[-85.516382,42.032536]},"n1821014717":{"id":"n1821014717","loc":[-85.5932863,41.9820882]},"n1821014718":{"id":"n1821014718","loc":[-85.5361928,42.0194974]},"n1821014720":{"id":"n1821014720","loc":[-85.6011613,41.9773586]},"n1821014721":{"id":"n1821014721","loc":[-85.554287,42.0109124]},"n1821014722":{"id":"n1821014722","loc":[-85.5577524,42.0103425]},"n1821014725":{"id":"n1821014725","loc":[-85.5867256,41.9921004]},"n1821014726":{"id":"n1821014726","loc":[-85.5856045,41.9968807]},"n1821014727":{"id":"n1821014727","loc":[-85.5545445,42.0106454]},"n1821014728":{"id":"n1821014728","loc":[-85.5923797,41.9842534]},"n1821014729":{"id":"n1821014729","loc":[-85.5696346,42.0081462]},"n1821014730":{"id":"n1821014730","loc":[-85.5998322,41.9786884]},"n1821014735":{"id":"n1821014735","loc":[-85.5337426,42.0218266]},"n1821014736":{"id":"n1821014736","loc":[-85.5847944,41.994672]},"n1821014740":{"id":"n1821014740","loc":[-85.5315271,42.0238669]},"n1821014741":{"id":"n1821014741","loc":[-85.5248846,42.027085]},"n1821014742":{"id":"n1821014742","loc":[-85.5853376,41.997018]},"n1821014743":{"id":"n1821014743","loc":[-85.5894883,41.988811]},"n1821014745":{"id":"n1821014745","loc":[-85.6095311,41.9726226]},"n1821014746":{"id":"n1821014746","loc":[-85.5531511,42.0133416]},"n1821014747":{"id":"n1821014747","loc":[-85.5735882,42.007058]},"n1821014749":{"id":"n1821014749","loc":[-85.5428554,42.0164366]},"n1821014751":{"id":"n1821014751","loc":[-85.5395255,42.0186304]},"n1821014752":{"id":"n1821014752","loc":[-85.571378,42.0083176]},"n1821014754":{"id":"n1821014754","loc":[-85.5541918,42.0113925]},"n1821014755":{"id":"n1821014755","loc":[-85.5278029,42.0250806]},"n1821014756":{"id":"n1821014756","loc":[-85.5936725,41.9827102]},"n1821014757":{"id":"n1821014757","loc":[-85.5176266,42.0346677]},"n1821014758":{"id":"n1821014758","loc":[-85.6096692,41.9714245]},"n1821014759":{"id":"n1821014759","loc":[-85.5770321,42.0034266]},"n1821014761":{"id":"n1821014761","loc":[-85.5988921,41.9779369]},"n1821014762":{"id":"n1821014762","loc":[-85.5811788,42.0024499]},"n1821014763":{"id":"n1821014763","loc":[-85.5154003,42.0381101]},"n1821014764":{"id":"n1821014764","loc":[-85.5155827,42.0374089]},"n1821014765":{"id":"n1821014765","loc":[-85.5891249,41.9884978]},"n1821014766":{"id":"n1821014766","loc":[-85.5313863,42.0238293]},"n1821014768":{"id":"n1821014768","loc":[-85.593297,41.9833363]},"n1821014769":{"id":"n1821014769","loc":[-85.5849446,41.9957245]},"n1821014770":{"id":"n1821014770","loc":[-85.5537774,42.0130847]},"n1821014771":{"id":"n1821014771","loc":[-85.6111766,41.9706069]},"n1821014772":{"id":"n1821014772","loc":[-85.5585477,42.008989]},"n1821014774":{"id":"n1821014774","loc":[-85.5928142,41.9852623]},"n1821014777":{"id":"n1821014777","loc":[-85.5891933,41.9882608]},"n1821014778":{"id":"n1821014778","loc":[-85.5926909,41.9817532]},"n1821014779":{"id":"n1821014779","loc":[-85.5260272,42.0252201]},"n1821014781":{"id":"n1821014781","loc":[-85.5894615,41.9950468]},"n1821014782":{"id":"n1821014782","loc":[-85.5461063,42.0143242]},"n1821014783":{"id":"n1821014783","loc":[-85.5711527,42.0085886]},"n1821014784":{"id":"n1821014784","loc":[-85.5329379,42.0218624]},"n1821014786":{"id":"n1821014786","loc":[-85.583047,42.0020252]},"n1821014787":{"id":"n1821014787","loc":[-85.5758962,42.0054095]},"n1821014788":{"id":"n1821014788","loc":[-85.5626354,42.0077733]},"n1821014789":{"id":"n1821014789","loc":[-85.6029852,41.9755999]},"n1821014790":{"id":"n1821014790","loc":[-85.5892362,41.9886755]},"n1821014791":{"id":"n1821014791","loc":[-85.5157597,42.0372017]},"n1821014793":{"id":"n1821014793","loc":[-85.6054582,41.9751094]},"n1821014794":{"id":"n1821014794","loc":[-85.5986936,41.9778412]},"n1821014795":{"id":"n1821014795","loc":[-85.5880775,41.98976]},"n1821014796":{"id":"n1821014796","loc":[-85.5858727,41.9963624]},"n1821014798":{"id":"n1821014798","loc":[-85.5792543,42.0035958]},"n1821014799":{"id":"n1821014799","loc":[-85.5921665,41.9838326]},"n1821014801":{"id":"n1821014801","loc":[-85.599214,41.9782599]},"n1821014802":{"id":"n1821014802","loc":[-85.5571905,42.0090967]},"n1821014803":{"id":"n1821014803","loc":[-85.5426891,42.0173612]},"n1821014804":{"id":"n1821014804","loc":[-85.5889626,41.9896404]},"n1821014805":{"id":"n1821014805","loc":[-85.5491264,42.0141648]},"n1821014806":{"id":"n1821014806","loc":[-85.5618897,42.0072631]},"n1821014808":{"id":"n1821014808","loc":[-85.5573501,42.0109802]},"n1821014809":{"id":"n1821014809","loc":[-85.5983463,41.9778031]},"n1821014810":{"id":"n1821014810","loc":[-85.5885173,41.9895128]},"n1821014811":{"id":"n1821014811","loc":[-85.6084998,41.9721143]},"n1821014812":{"id":"n1821014812","loc":[-85.5737598,42.0056389]},"n1821014814":{"id":"n1821014814","loc":[-85.5542173,42.0118132]},"n1821014816":{"id":"n1821014816","loc":[-85.5277868,42.024451]},"n1821014817":{"id":"n1821014817","loc":[-85.5403999,42.0191724]},"n1821014819":{"id":"n1821014819","loc":[-85.5983879,41.9791452]},"n1821014820":{"id":"n1821014820","loc":[-85.5891302,41.9897578]},"n1821014822":{"id":"n1821014822","loc":[-85.5930731,41.9805108]},"n1821014824":{"id":"n1821014824","loc":[-85.515395,42.0378471]},"n1821014825":{"id":"n1821014825","loc":[-85.5352755,42.0205136]},"n1821014826":{"id":"n1821014826","loc":[-85.5502744,42.0133398]},"n1821014828":{"id":"n1821014828","loc":[-85.5701295,42.0088256]},"n1821014830":{"id":"n1821014830","loc":[-85.5888929,41.9953099]},"n1821014832":{"id":"n1821014832","loc":[-85.5880077,41.9901547]},"n1821014833":{"id":"n1821014833","loc":[-85.5451192,42.0157072]},"n1821014834":{"id":"n1821014834","loc":[-85.6096478,41.9711932]},"n1821014835":{"id":"n1821014835","loc":[-85.5806424,42.0026532]},"n1821014836":{"id":"n1821014836","loc":[-85.5911674,41.9868732]},"n1821014838":{"id":"n1821014838","loc":[-85.5930302,41.9836571]},"n1821014839":{"id":"n1821014839","loc":[-85.588925,41.9938148]},"n1821014840":{"id":"n1821014840","loc":[-85.6111874,41.9705311]},"n1821014841":{"id":"n1821014841","loc":[-85.5680843,42.0075842]},"n1821014842":{"id":"n1821014842","loc":[-85.6012793,41.9775062]},"n1821014843":{"id":"n1821014843","loc":[-85.5855562,41.9989777]},"n1821014844":{"id":"n1821014844","loc":[-85.5506137,42.0131662]},"n1821014845":{"id":"n1821014845","loc":[-85.5270049,42.025457]},"n1821014846":{"id":"n1821014846","loc":[-85.5257054,42.025244]},"n1821014847":{"id":"n1821014847","loc":[-85.6011184,41.9771832]},"n1821014848":{"id":"n1821014848","loc":[-85.515534,42.0389234]},"n1821014850":{"id":"n1821014850","loc":[-85.5847032,42.0010347]},"n1821014853":{"id":"n1821014853","loc":[-85.5361499,42.019063]},"n1821014854":{"id":"n1821014854","loc":[-85.5439176,42.0165721]},"n1821014855":{"id":"n1821014855","loc":[-85.5838825,42.0017284]},"n1821014857":{"id":"n1821014857","loc":[-85.5542173,42.0122317]},"n1821014859":{"id":"n1821014859","loc":[-85.5708201,42.0089195]},"n1821014860":{"id":"n1821014860","loc":[-85.5844833,41.9954415]},"n1821014862":{"id":"n1821014862","loc":[-85.5223204,42.0295396]},"n1821014863":{"id":"n1821014863","loc":[-85.5777898,42.0035918]},"n1821014864":{"id":"n1821014864","loc":[-85.591044,41.9898078]},"n1821014865":{"id":"n1821014865","loc":[-85.5973204,41.980182]},"n1821014866":{"id":"n1821014866","loc":[-85.5699578,42.0085825]},"n1821014867":{"id":"n1821014867","loc":[-85.5210598,42.0305278]},"n1821014868":{"id":"n1821014868","loc":[-85.5929108,41.9819008]},"n1821014869":{"id":"n1821014869","loc":[-85.5279799,42.0242995]},"n1821014870":{"id":"n1821014870","loc":[-85.5196114,42.0320539]},"n1821014871":{"id":"n1821014871","loc":[-85.5785449,42.0040883]},"n1821014872":{"id":"n1821014872","loc":[-85.588292,41.9895766]},"n1821014873":{"id":"n1821014873","loc":[-85.5160172,42.0331775]},"n1821014874":{"id":"n1821014874","loc":[-85.5688849,42.0077016]},"n1821014876":{"id":"n1821014876","loc":[-85.5857976,41.9996036]},"n1821014879":{"id":"n1821014879","loc":[-85.5990906,41.9780765]},"n1821014881":{"id":"n1821014881","loc":[-85.5483647,42.0144279]},"n1821014883":{"id":"n1821014883","loc":[-85.5691209,42.0077972]},"n1821014885":{"id":"n1821014885","loc":[-85.6076844,41.9721103]},"n1821014886":{"id":"n1821014886","loc":[-85.6015489,41.9766147]},"n1821014887":{"id":"n1821014887","loc":[-85.574822,42.0052802]},"n1821014888":{"id":"n1821014888","loc":[-85.5880024,41.9899593]},"n1821014890":{"id":"n1821014890","loc":[-85.5909421,41.9893772]},"n1821014892":{"id":"n1821014892","loc":[-85.5497326,42.0138141]},"n1821014893":{"id":"n1821014893","loc":[-85.5167106,42.0357811]},"n1821014895":{"id":"n1821014895","loc":[-85.5844404,41.9952501]},"n1821014896":{"id":"n1821014896","loc":[-85.5362465,42.0192662]},"n1821014898":{"id":"n1821014898","loc":[-85.5906095,41.9889147]},"n1821014899":{"id":"n1821014899","loc":[-85.5590667,42.0089354]},"n1821014900":{"id":"n1821014900","loc":[-85.5921598,41.9844209]},"n1821014902":{"id":"n1821014902","loc":[-85.5778971,42.0039266]},"n1821014903":{"id":"n1821014903","loc":[-85.603012,41.9761981]},"n1821014904":{"id":"n1821014904","loc":[-85.6108977,41.9706787]},"n1821014905":{"id":"n1821014905","loc":[-85.5685738,42.0076139]},"n1821014906":{"id":"n1821014906","loc":[-85.5392787,42.0186304]},"n1821014907":{"id":"n1821014907","loc":[-85.5227885,42.0274972]},"n1821014908":{"id":"n1821014908","loc":[-85.5857547,41.9961431]},"n1821014910":{"id":"n1821014910","loc":[-85.5610354,42.0072812]},"n1821014911":{"id":"n1821014911","loc":[-85.5209632,42.0308705]},"n1821014912":{"id":"n1821014912","loc":[-85.5709757,42.0087959]},"n1821014913":{"id":"n1821014913","loc":[-85.59231,41.9839344]},"n1821014914":{"id":"n1821014914","loc":[-85.5375245,42.0185865]},"n1821014916":{"id":"n1821014916","loc":[-85.5901548,41.9839841]},"n1821014917":{"id":"n1821014917","loc":[-85.5611213,42.0086405]},"n1821014918":{"id":"n1821014918","loc":[-85.5360426,42.0198122]},"n1821014919":{"id":"n1821014919","loc":[-85.5862817,41.9948691]},"n1821014921":{"id":"n1821014921","loc":[-85.5469807,42.0144438]},"n1821014922":{"id":"n1821014922","loc":[-85.5761309,42.0053838]},"n1821014924":{"id":"n1821014924","loc":[-85.516264,42.0332971]},"n1821014925":{"id":"n1821014925","loc":[-85.5277224,42.0246661]},"n1821014926":{"id":"n1821014926","loc":[-85.5980016,41.9798231]},"n1821014928":{"id":"n1821014928","loc":[-85.5924548,41.9806965]},"n1821014930":{"id":"n1821014930","loc":[-85.5899121,41.985023]},"n1821014931":{"id":"n1821014931","loc":[-85.5706015,42.0089492]},"n1821014932":{"id":"n1821014932","loc":[-85.515926,42.033046]},"n1821014933":{"id":"n1821014933","loc":[-85.5982377,41.9796796]},"n1821014936":{"id":"n1821014936","loc":[-85.5475721,42.0145253]},"n1821014938":{"id":"n1821014938","loc":[-85.5895701,41.9902323]},"n1821014939":{"id":"n1821014939","loc":[-85.6030495,41.9759947]},"n1821014942":{"id":"n1821014942","loc":[-85.6094721,41.9724989]},"n1821014944":{"id":"n1821014944","loc":[-85.5921973,41.9811112]},"n1821014945":{"id":"n1821014945","loc":[-85.5223526,42.0291332]},"n1821014946":{"id":"n1821014946","loc":[-85.5965103,41.9808998]},"n1821014948":{"id":"n1821014948","loc":[-85.517766,42.0349227]},"n1821014950":{"id":"n1821014950","loc":[-85.5889894,41.990996]},"n1821014951":{"id":"n1821014951","loc":[-85.5601932,42.0092902]},"n1821014954":{"id":"n1821014954","loc":[-85.6028135,41.9764055]},"n1821014955":{"id":"n1821014955","loc":[-85.5520621,42.0130666]},"n1821014956":{"id":"n1821014956","loc":[-85.593002,41.9839344]},"n1821014957":{"id":"n1821014957","loc":[-85.515926,42.0369666]},"n1821014960":{"id":"n1821014960","loc":[-85.5761255,42.003877]},"n1821014961":{"id":"n1821014961","loc":[-85.5716355,42.007911]},"n1821014962":{"id":"n1821014962","loc":[-85.5575378,42.0109045]},"n1821014963":{"id":"n1821014963","loc":[-85.5735667,42.0068188]},"n1821014964":{"id":"n1821014964","loc":[-85.5915214,41.9865861]},"n1821014965":{"id":"n1821014965","loc":[-85.5866344,41.9923157]},"n1821014967":{"id":"n1821014967","loc":[-85.5283138,42.0242256]},"n1821014968":{"id":"n1821014968","loc":[-85.5177875,42.0355801]},"n1821014969":{"id":"n1821014969","loc":[-85.548071,42.0144934]},"n1821014972":{"id":"n1821014972","loc":[-85.5611159,42.0088557]},"n1821014973":{"id":"n1821014973","loc":[-85.541686,42.0188757]},"n1821014974":{"id":"n1821014974","loc":[-85.5917628,41.9862631]},"n1821014975":{"id":"n1821014975","loc":[-85.5854864,41.9959478]},"n1821014977":{"id":"n1821014977","loc":[-85.609102,41.9722317]},"n1821014980":{"id":"n1821014980","loc":[-85.5761202,42.0042438]},"n1821014982":{"id":"n1821014982","loc":[-85.5465944,42.0143601]},"n1821014983":{"id":"n1821014983","loc":[-85.5173261,42.0342732]},"n1821014984":{"id":"n1821014984","loc":[-85.5897297,41.9888509]},"n1821014985":{"id":"n1821014985","loc":[-85.5856688,41.999181]},"n1821014986":{"id":"n1821014986","loc":[-85.5344011,42.0217251]},"n1821014987":{"id":"n1821014987","loc":[-85.601467,41.9768203]},"n1821014988":{"id":"n1821014988","loc":[-85.5457254,42.0165123]},"n1821014989":{"id":"n1821014989","loc":[-85.6023482,41.9784332]},"n1821014991":{"id":"n1821014991","loc":[-85.5361606,42.01823]},"n1821014992":{"id":"n1821014992","loc":[-85.5178465,42.0351139]},"n1821014995":{"id":"n1821014995","loc":[-85.5634293,42.0078092]},"n1821014996":{"id":"n1821014996","loc":[-85.573497,42.0072015]},"n1821014997":{"id":"n1821014997","loc":[-85.5976328,41.9799725]},"n1821014998":{"id":"n1821014998","loc":[-85.5210651,42.0303166]},"n1821015003":{"id":"n1821015003","loc":[-85.5222131,42.0288064]},"n1821015004":{"id":"n1821015004","loc":[-85.5897941,41.984405]},"n1821015005":{"id":"n1821015005","loc":[-85.5975725,41.9776099]},"n1821015006":{"id":"n1821015006","loc":[-85.5765708,42.0034903]},"n1821015007":{"id":"n1821015007","loc":[-85.5250187,42.026559]},"n1821015009":{"id":"n1821015009","loc":[-85.5426998,42.0166279]},"n1821015010":{"id":"n1821015010","loc":[-85.5957606,41.9806584]},"n1821015011":{"id":"n1821015011","loc":[-85.5262753,42.0252497]},"n1821015012":{"id":"n1821015012","loc":[-85.5266455,42.0253374]},"n1821015014":{"id":"n1821015014","loc":[-85.5515632,42.0130187]},"n1821015015":{"id":"n1821015015","loc":[-85.6024058,41.9765212]},"n1821015017":{"id":"n1821015017","loc":[-85.5175032,42.0357156]},"n1821015018":{"id":"n1821015018","loc":[-85.5302718,42.0236039]},"n1821015019":{"id":"n1821015019","loc":[-85.6024005,41.9782759]},"n1821015020":{"id":"n1821015020","loc":[-85.5907758,41.9890821]},"n1821015021":{"id":"n1821015021","loc":[-85.6019445,41.9777215]},"n1821015022":{"id":"n1821015022","loc":[-85.5942854,41.9800881]},"n1821015024":{"id":"n1821015024","loc":[-85.5325826,42.0222711]},"n1821015029":{"id":"n1821015029","loc":[-85.555093,42.0105316]},"n1821015033":{"id":"n1821015033","loc":[-85.5249704,42.0270372]},"n1821015034":{"id":"n1821015034","loc":[-85.5243965,42.0272205]},"n1821015038":{"id":"n1821015038","loc":[-85.5413426,42.0190749]},"n1821015039":{"id":"n1821015039","loc":[-85.5920431,41.9848175]},"n1821015041":{"id":"n1821015041","loc":[-85.5577685,42.0106015]},"n1821015042":{"id":"n1821015042","loc":[-85.5453606,42.0158866]},"n1821015045":{"id":"n1821015045","loc":[-85.5333228,42.0217889]},"n1821015046":{"id":"n1821015046","loc":[-85.5426891,42.0175924]},"n1821015048":{"id":"n1821015048","loc":[-85.5886836,41.9936474]},"n1821015050":{"id":"n1821015050","loc":[-85.6001152,41.9786467]},"n1821015051":{"id":"n1821015051","loc":[-85.6094064,41.9723655]},"n1821015053":{"id":"n1821015053","loc":[-85.605721,41.9749738]},"n1821015055":{"id":"n1821015055","loc":[-85.6106791,41.9705048]},"n1821015057":{"id":"n1821015057","loc":[-85.5210437,42.0307071]},"n1821015059":{"id":"n1821015059","loc":[-85.5995694,41.9786725]},"n1821015060":{"id":"n1821015060","loc":[-85.5371638,42.0182938]},"n1821015062":{"id":"n1821015062","loc":[-85.6111766,41.9704593]},"n1821015065":{"id":"n1821015065","loc":[-85.577704,42.0034921]},"n1821015067":{"id":"n1821015067","loc":[-85.5570067,42.0093699]},"n1821015068":{"id":"n1821015068","loc":[-85.5920364,41.9845525]},"n1821015069":{"id":"n1821015069","loc":[-85.5252065,42.0253954]},"n1821015072":{"id":"n1821015072","loc":[-85.5664159,42.0088517]},"n1821015073":{"id":"n1821015073","loc":[-85.5880399,41.991905]},"n1821015075":{"id":"n1821015075","loc":[-85.6099871,41.9727861]},"n1821015076":{"id":"n1821015076","loc":[-85.5319603,42.0231478]},"n1821015078":{"id":"n1821015078","loc":[-85.6036088,41.9751112]},"n1821015080":{"id":"n1821015080","loc":[-85.5983128,41.9789179]},"n1821015082":{"id":"n1821015082","loc":[-85.5614069,42.0071395]},"n1821015083":{"id":"n1821015083","loc":[-85.60968,41.9709738]},"n1821015086":{"id":"n1821015086","loc":[-85.5914195,41.9837351]},"n1821015087":{"id":"n1821015087","loc":[-85.5895473,41.9948036]},"n1821015090":{"id":"n1821015090","loc":[-85.5929913,41.9851905]},"n1821015093":{"id":"n1821015093","loc":[-85.5907396,41.9838485]},"n1821015095":{"id":"n1821015095","loc":[-85.5893864,41.9880176]},"n1821015096":{"id":"n1821015096","loc":[-85.5788024,42.0039807]},"n1821015097":{"id":"n1821015097","loc":[-85.5630592,42.0078411]},"n1821015098":{"id":"n1821015098","loc":[-85.5350609,42.0211274]},"n1821015099":{"id":"n1821015099","loc":[-85.5967195,41.9808679]},"n1821015100":{"id":"n1821015100","loc":[-85.5666734,42.0088119]},"n1821015101":{"id":"n1821015101","loc":[-85.564694,42.0077675]},"n1821015103":{"id":"n1821015103","loc":[-85.6066544,41.9726527]},"n1821015104":{"id":"n1821015104","loc":[-85.6011827,41.9769838]},"n1821015105":{"id":"n1821015105","loc":[-85.5972131,41.9776697]},"n1821015106":{"id":"n1821015106","loc":[-85.5880828,41.9903341]},"n1821015107":{"id":"n1821015107","loc":[-85.5510268,42.0130626]},"n1821015108":{"id":"n1821015108","loc":[-85.6102164,41.970543]},"n1821015109":{"id":"n1821015109","loc":[-85.5905344,41.9853899]},"n1821015111":{"id":"n1821015111","loc":[-85.5888821,41.9913429]},"n1821015112":{"id":"n1821015112","loc":[-85.606295,41.9741921]},"n1821015114":{"id":"n1821015114","loc":[-85.5969556,41.9807443]},"n1821015115":{"id":"n1821015115","loc":[-85.5882223,41.9934081]},"n1821015116":{"id":"n1821015116","loc":[-85.6104471,41.9724971]},"n1821015118":{"id":"n1821015118","loc":[-85.5406091,42.0192162]},"n1821015120":{"id":"n1821015120","loc":[-85.589955,41.9888429]},"n1821015121":{"id":"n1821015121","loc":[-85.5598821,42.0092304]},"n1821015122":{"id":"n1821015122","loc":[-85.545598,42.0144097]},"n1821015123":{"id":"n1821015123","loc":[-85.5649528,42.0079965]},"n1821015125":{"id":"n1821015125","loc":[-85.5883993,41.9917814]},"n1821015126":{"id":"n1821015126","loc":[-85.5295785,42.0239967]},"n1821015129":{"id":"n1821015129","loc":[-85.5648723,42.0078809]},"n1821015132":{"id":"n1821015132","loc":[-85.564989,42.0081103]},"n1821015133":{"id":"n1821015133","loc":[-85.5946127,41.9800841]},"n1821015134":{"id":"n1821015134","loc":[-85.583448,42.0019078]},"n1821015135":{"id":"n1821015135","loc":[-85.5905934,41.9871842]},"n1821015137":{"id":"n1821015137","loc":[-85.610608,41.9704752]},"n1821015138":{"id":"n1821015138","loc":[-85.5752257,42.0052939]},"n1821015139":{"id":"n1821015139","loc":[-85.5893864,41.9943491]},"n1821015140":{"id":"n1821015140","loc":[-85.5426247,42.0169866]},"n1821015141":{"id":"n1821015141","loc":[-85.562001,42.0074526]},"n1821015142":{"id":"n1821015142","loc":[-85.5212046,42.0301094]},"n1821015143":{"id":"n1821015143","loc":[-85.602214,41.9784531]},"n1821015144":{"id":"n1821015144","loc":[-85.5858687,41.9948293]},"n1821015145":{"id":"n1821015145","loc":[-85.5608477,42.0074805]},"n1821015146":{"id":"n1821015146","loc":[-85.5651607,42.0083614]},"n1821015147":{"id":"n1821015147","loc":[-85.5288288,42.0242495]},"n1821015149":{"id":"n1821015149","loc":[-85.5450334,42.0146989]},"n1821015151":{"id":"n1821015151","loc":[-85.5578275,42.0092304]},"n1821015154":{"id":"n1821015154","loc":[-85.6056634,41.9724511]},"n1821015155":{"id":"n1821015155","loc":[-85.5902179,41.9852742]},"n1821015156":{"id":"n1821015156","loc":[-85.5156256,42.0387157]},"n1821015157":{"id":"n1821015157","loc":[-85.5734433,42.0059459]},"n1821015158":{"id":"n1821015158","loc":[-85.6050773,41.9731273]},"n1821015160":{"id":"n1821015160","loc":[-85.5223419,42.0275233]},"n1821015163":{"id":"n1821015163","loc":[-85.6053562,41.972525]},"n1821015164":{"id":"n1821015164","loc":[-85.5850412,41.9946082]},"n1821015165":{"id":"n1821015165","loc":[-85.5359031,42.0186326]},"n1821015166":{"id":"n1821015166","loc":[-85.5608745,42.0077635]},"n1821015169":{"id":"n1821015169","loc":[-85.572876,42.0073189]},"n1821015171":{"id":"n1821015171","loc":[-85.5875424,41.9919188]},"n1821015172":{"id":"n1821015172","loc":[-85.5240116,42.0272581]},"n1821015173":{"id":"n1821015173","loc":[-85.5318369,42.0236818]},"n1821015174":{"id":"n1821015174","loc":[-85.566888,42.0086923]},"n1821015175":{"id":"n1821015175","loc":[-85.5931522,41.9850669]},"n1821015176":{"id":"n1821015176","loc":[-85.5604842,42.0093199]},"n1821015177":{"id":"n1821015177","loc":[-85.5868168,41.9927543]},"n1821015178":{"id":"n1821015178","loc":[-85.6052275,41.9732549]},"n1821015179":{"id":"n1821015179","loc":[-85.5910118,41.9900431]},"n1821015182":{"id":"n1821015182","loc":[-85.5610032,42.0082897]},"n1821015183":{"id":"n1821015183","loc":[-85.5425443,42.0179431]},"n1821015184":{"id":"n1821015184","loc":[-85.5843277,42.0014055]},"n1821015186":{"id":"n1821015186","loc":[-85.5733307,42.0063564]},"n1821015188":{"id":"n1821015188","loc":[-85.5277385,42.0248694]},"n1821015189":{"id":"n1821015189","loc":[-85.5558427,42.0108168]},"n1821015190":{"id":"n1821015190","loc":[-85.5650587,42.0082618]},"n1821015191":{"id":"n1821015191","loc":[-85.5660351,42.0088278]},"n1821015192":{"id":"n1821015192","loc":[-85.5849768,41.9980049]},"n1821015194":{"id":"n1821015194","loc":[-85.5359139,42.0188199]},"n1821015195":{"id":"n1821015195","loc":[-85.593238,41.9849194]},"n1821015197":{"id":"n1821015197","loc":[-85.5850841,41.9983239]},"n1821015199":{"id":"n1821015199","loc":[-85.5983396,41.9794283]},"n1821015204":{"id":"n1821015204","loc":[-85.5452801,42.0145355]},"n1821015205":{"id":"n1821015205","loc":[-85.5340685,42.0218407]},"n1821015207":{"id":"n1821015207","loc":[-85.5773272,42.0034186]},"n1821015209":{"id":"n1821015209","loc":[-85.5535212,42.0132419]},"n1821015211":{"id":"n1821015211","loc":[-85.6107703,41.9706045]},"n1821015212":{"id":"n1821015212","loc":[-85.6030066,41.9758193]},"n1821015213":{"id":"n1821015213","loc":[-85.5359943,42.0184213]},"n1821015214":{"id":"n1821015214","loc":[-85.5922993,41.9813305]},"n1821015215":{"id":"n1821015215","loc":[-85.5672689,42.0080465]},"n1821015217":{"id":"n1821015217","loc":[-85.5160494,42.0365682]},"n1821015218":{"id":"n1821015218","loc":[-85.5401142,42.0190351]},"n1821015219":{"id":"n1821015219","loc":[-85.5607632,42.0092282]},"n1821015220":{"id":"n1821015220","loc":[-85.5866197,41.9947894]},"n1821015221":{"id":"n1821015221","loc":[-85.6017889,41.9765132]},"n1821015222":{"id":"n1821015222","loc":[-85.5595978,42.009059]},"n1821015226":{"id":"n1821015226","loc":[-85.5871494,41.9929018]},"n1821015227":{"id":"n1821015227","loc":[-85.5857708,41.9998866]},"n1821015228":{"id":"n1821015228","loc":[-85.5317135,42.0238094]},"n1821015231":{"id":"n1821015231","loc":[-85.5733521,42.0061372]},"n1821015233":{"id":"n1821015233","loc":[-85.5855991,42.0001936]},"n1821015234":{"id":"n1821015234","loc":[-85.5213924,42.029962]},"n1821015235":{"id":"n1821015235","loc":[-85.6052221,41.9726567]},"n1821015236":{"id":"n1821015236","loc":[-85.5763723,42.0035422]},"n1821015237":{"id":"n1821015237","loc":[-85.5858512,41.9966215]},"n1821015238":{"id":"n1821015238","loc":[-85.567061,42.008439]},"n1821015239":{"id":"n1821015239","loc":[-85.5250563,42.0269057]},"n1821015240":{"id":"n1821015240","loc":[-85.5347551,42.0214263]},"n1821015241":{"id":"n1821015241","loc":[-85.6098463,41.9707066]},"n1821015242":{"id":"n1821015242","loc":[-85.5676927,42.0076519]},"n1821015243":{"id":"n1821015243","loc":[-85.516775,42.0322669]},"n1821015244":{"id":"n1821015244","loc":[-85.5762275,42.0036538]},"n1821015245":{"id":"n1821015245","loc":[-85.5583639,42.0090949]},"n1821015246":{"id":"n1821015246","loc":[-85.5554041,42.0106432]},"n1821015247":{"id":"n1821015247","loc":[-85.5973364,41.9776099]},"n1821015248":{"id":"n1821015248","loc":[-85.6098945,41.9717513]},"n1821015249":{"id":"n1821015249","loc":[-85.6045315,41.9751511]},"n1821015250":{"id":"n1821015250","loc":[-85.5579938,42.0092264]},"n1821015253":{"id":"n1821015253","loc":[-85.6058873,41.9724652]},"n1821015254":{"id":"n1821015254","loc":[-85.5869456,41.9947517]},"n1821015255":{"id":"n1821015255","loc":[-85.5936565,41.9823713]},"n1821015256":{"id":"n1821015256","loc":[-85.5218269,42.0278102]},"n1821015258":{"id":"n1821015258","loc":[-85.5887802,41.9905534]},"n1821015259":{"id":"n1821015259","loc":[-85.5901924,41.9904515]},"n1821015263":{"id":"n1821015263","loc":[-85.5249222,42.0255787]},"n1821015265":{"id":"n1821015265","loc":[-85.5175206,42.0321672]},"n1821015266":{"id":"n1821015266","loc":[-85.5275722,42.0254034]},"n1821015267":{"id":"n1821015267","loc":[-85.6016226,41.9765451]},"n1821015269":{"id":"n1821015269","loc":[-85.5569316,42.011032]},"n1821015271":{"id":"n1821015271","loc":[-85.6010714,41.9785209]},"n1821015272":{"id":"n1821015272","loc":[-85.6050666,41.9729917]},"n1821015273":{"id":"n1821015273","loc":[-85.5891235,41.99529]},"n1821015274":{"id":"n1821015274","loc":[-85.515454,42.0376439]},"n1821015276":{"id":"n1821015276","loc":[-85.5776021,42.0034443]},"n1821015277":{"id":"n1821015277","loc":[-85.6041707,41.9751453]},"n1821015278":{"id":"n1821015278","loc":[-85.5444701,42.0167435]},"n1821015280":{"id":"n1821015280","loc":[-85.5923274,41.9852202]},"n1821015283":{"id":"n1821015283","loc":[-85.5893649,41.9900271]},"n1821015284":{"id":"n1821015284","loc":[-85.5933453,41.9804412]},"n1821015285":{"id":"n1821015285","loc":[-85.5247237,42.026017]},"n1821015286":{"id":"n1821015286","loc":[-85.5286182,42.0242477]},"n1821015287":{"id":"n1821015287","loc":[-85.5904003,41.9888549]},"n1821015288":{"id":"n1821015288","loc":[-85.6062146,41.9739369]},"n1821015290":{"id":"n1821015290","loc":[-85.5762596,42.0052602]},"n1821015292":{"id":"n1821015292","loc":[-85.5849715,41.9975465]},"n1821015293":{"id":"n1821015293","loc":[-85.585229,42.0006241]},"n1821015294":{"id":"n1821015294","loc":[-85.5926922,41.9805946]},"n1821015295":{"id":"n1821015295","loc":[-85.5703387,42.0089133]},"n1821015299":{"id":"n1821015299","loc":[-85.5789955,42.0038611]},"n1821015301":{"id":"n1821015301","loc":[-85.6072888,41.9721918]},"n1821015302":{"id":"n1821015302","loc":[-85.5356349,42.0200992]},"n1821015304":{"id":"n1821015304","loc":[-85.5891772,41.994066]},"n1821015306":{"id":"n1821015306","loc":[-85.606295,41.9744952]},"n1821015307":{"id":"n1821015307","loc":[-85.538871,42.0186583]},"n1821015308":{"id":"n1821015308","loc":[-85.587997,41.994971]},"n1821015311":{"id":"n1821015311","loc":[-85.606869,41.9725809]},"n1821015312":{"id":"n1821015312","loc":[-85.5171974,42.0339943]},"n1821015314":{"id":"n1821015314","loc":[-85.5327435,42.0220479]},"n1821015315":{"id":"n1821015315","loc":[-85.5383439,42.0187282]},"n1821015316":{"id":"n1821015316","loc":[-85.5248095,42.0263119]},"n1821015318":{"id":"n1821015318","loc":[-85.5732502,42.0073051]},"n1821015319":{"id":"n1821015319","loc":[-85.5924226,41.9852663]},"n1821015321":{"id":"n1821015321","loc":[-85.5179001,42.0353052]},"n1821015322":{"id":"n1821015322","loc":[-85.5456771,42.0162413]},"n1821015323":{"id":"n1821015323","loc":[-85.5936618,41.9829096]},"n1821015325":{"id":"n1821015325","loc":[-85.5656931,42.0086582]},"n1821015326":{"id":"n1821015326","loc":[-85.5448456,42.0150975]},"n1821015327":{"id":"n1821015327","loc":[-85.5220039,42.027615]},"n1821015329":{"id":"n1821015329","loc":[-85.517884,42.0354885]},"n1821015330":{"id":"n1821015330","loc":[-85.5576666,42.0101671]},"n1821015332":{"id":"n1821015332","loc":[-85.5368754,42.0181402]},"n1821015333":{"id":"n1821015333","loc":[-85.5367078,42.0181145]},"n1821015334":{"id":"n1821015334","loc":[-85.5903909,41.9904316]},"n1821015335":{"id":"n1821015335","loc":[-85.5430767,42.0163587]},"n1821015336":{"id":"n1821015336","loc":[-85.5277492,42.0252878]},"n1821015337":{"id":"n1821015337","loc":[-85.5312146,42.0236898]},"n1821015338":{"id":"n1821015338","loc":[-85.5886568,41.991614]},"n1821015339":{"id":"n1821015339","loc":[-85.5782498,42.0040883]},"n1821015341":{"id":"n1821015341","loc":[-85.562233,42.0076457]},"n1821015342":{"id":"n1821015342","loc":[-85.588626,41.9952479]},"n1821015343":{"id":"n1821015343","loc":[-85.5762865,42.005033]},"n1821015344":{"id":"n1821015344","loc":[-85.5850841,41.9971478]},"n1821015346":{"id":"n1821015346","loc":[-85.5643144,42.0076936]},"n1821015347":{"id":"n1821015347","loc":[-85.5164893,42.0359467]},"n1821015348":{"id":"n1821015348","loc":[-85.5906846,41.9903541]},"n1821015349":{"id":"n1821015349","loc":[-85.557688,42.0107769]},"n1821015350":{"id":"n1821015350","loc":[-85.5363698,42.0181424]},"n1821015351":{"id":"n1821015351","loc":[-85.5939636,41.9801918]},"n1821015352":{"id":"n1821015352","loc":[-85.5524041,42.0131644]},"n1821015354":{"id":"n1821015354","loc":[-85.5308606,42.0236221]},"n1821015355":{"id":"n1821015355","loc":[-85.5877449,41.9932367]},"n1821015356":{"id":"n1821015356","loc":[-85.519885,42.0318586]},"n1821015357":{"id":"n1821015357","loc":[-85.5454035,42.0168431]},"n1821015358":{"id":"n1821015358","loc":[-85.5970629,41.9781881]},"n1821015359":{"id":"n1821015359","loc":[-85.5932541,41.9844767]},"n1821015360":{"id":"n1821015360","loc":[-85.5970736,41.9778252]},"n1821015361":{"id":"n1821015361","loc":[-85.537031,42.0181601]},"n1821015362":{"id":"n1821015362","loc":[-85.5548355,42.0105156]},"n1821015363":{"id":"n1821015363","loc":[-85.5168648,42.0336158]},"n1821015365":{"id":"n1821015365","loc":[-85.5870435,41.9919507]},"n1821015366":{"id":"n1821015366","loc":[-85.5719681,42.0075443]},"n1821015367":{"id":"n1821015367","loc":[-85.5969985,41.9780446]},"n1821015368":{"id":"n1821015368","loc":[-85.5926761,41.98528]},"n1821015369":{"id":"n1821015369","loc":[-85.5224009,42.0293444]},"n1821015371":{"id":"n1821015371","loc":[-85.518737,42.0322651]},"n1821015372":{"id":"n1821015372","loc":[-85.6064573,41.9726465]},"n1821015373":{"id":"n1821015373","loc":[-85.5201103,42.0313088]},"n1821015375":{"id":"n1821015375","loc":[-85.5378182,42.0186844]},"n1821015376":{"id":"n1821015376","loc":[-85.6109741,41.9706882]},"n1821015377":{"id":"n1821015377","loc":[-85.5993333,41.9785488]},"n1821015378":{"id":"n1821015378","loc":[-85.5889787,41.9907368]},"n1821015380":{"id":"n1821015380","loc":[-85.6060161,41.9737375]},"n1821015381":{"id":"n1821015381","loc":[-85.5743016,42.0053679]},"n1821015382":{"id":"n1821015382","loc":[-85.6014724,41.9776099]},"n1821015383":{"id":"n1821015383","loc":[-85.5574426,42.0091644]},"n1821015385":{"id":"n1821015385","loc":[-85.5208613,42.0309302]},"n1821015386":{"id":"n1821015386","loc":[-85.5919023,41.9837789]},"n1821015387":{"id":"n1821015387","loc":[-85.5455484,42.0160221]},"n1821015392":{"id":"n1821015392","loc":[-85.5801757,42.0028964]},"n1821015395":{"id":"n1821015395","loc":[-85.5493785,42.0139974]},"n1821015396":{"id":"n1821015396","loc":[-85.5449475,42.015488]},"n1821015398":{"id":"n1821015398","loc":[-85.611123,41.9706627]},"n1821015400":{"id":"n1821015400","loc":[-85.5935706,41.9822477]},"n1821015401":{"id":"n1821015401","loc":[-85.5724254,42.0073508]},"n1821015403":{"id":"n1821015403","loc":[-85.5486812,42.0143442]},"n1821015404":{"id":"n1821015404","loc":[-85.5161835,42.0327711]},"n1821015406":{"id":"n1821015406","loc":[-85.5921705,41.9851107]},"n1821015407":{"id":"n1821015407","loc":[-85.531912,42.0234069]},"n1821015410":{"id":"n1821015410","loc":[-85.5292566,42.024176]},"n1821015411":{"id":"n1821015411","loc":[-85.5845316,41.9948315]},"n1821015413":{"id":"n1821015413","loc":[-85.5217947,42.0280413]},"n1821015414":{"id":"n1821015414","loc":[-85.5527367,42.013272]},"n1821015415":{"id":"n1821015415","loc":[-85.5191179,42.0321973]},"n1821015416":{"id":"n1821015416","loc":[-85.5540241,42.0128655]},"n1821015418":{"id":"n1821015418","loc":[-85.5272892,42.0254849]},"n1821015419":{"id":"n1821015419","loc":[-85.5449744,42.016867]},"n1821015420":{"id":"n1821015420","loc":[-85.5852665,41.9986787]},"n1821015421":{"id":"n1821015421","loc":[-85.6102701,41.972186]},"n1821015423":{"id":"n1821015423","loc":[-85.6026365,41.9764972]},"n1821015427":{"id":"n1821015427","loc":[-85.5898692,41.9841498]},"n1821015429":{"id":"n1821015429","loc":[-85.5422546,42.0183855]},"n1821015430":{"id":"n1821015430","loc":[-85.5866505,41.9925549]},"n1821015431":{"id":"n1821015431","loc":[-85.5234376,42.0273577]},"n1821015432":{"id":"n1821015432","loc":[-85.6096746,41.9727284]},"n1821015433":{"id":"n1821015433","loc":[-85.5824891,42.0021567]},"n1821015434":{"id":"n1821015434","loc":[-85.5923905,41.9841139]},"n1821015435":{"id":"n1821015435","loc":[-85.5874565,41.9948014]},"n1821015437":{"id":"n1821015437","loc":[-85.6055279,41.9734423]},"n1821015438":{"id":"n1821015438","loc":[-85.5299379,42.0237376]},"n1821015439":{"id":"n1821015439","loc":[-85.5155022,42.0383651]},"n1821015442":{"id":"n1821015442","loc":[-85.527422,42.0254711]},"n1821015443":{"id":"n1821015443","loc":[-85.5920699,41.9849291]},"n1821015444":{"id":"n1821015444","loc":[-85.5639711,42.0077494]},"n1821015445":{"id":"n1821015445","loc":[-85.5162586,42.0361777]},"n1821015446":{"id":"n1821015446","loc":[-85.5220039,42.029695]},"n1821015448":{"id":"n1821015448","loc":[-85.5176641,42.0356956]},"n1821015449":{"id":"n1821015449","loc":[-85.5930556,41.9841577]},"n1821015451":{"id":"n1821015451","loc":[-85.5320783,42.0228848]},"n1821015452":{"id":"n1821015452","loc":[-85.5170096,42.0357235]},"n1821015453":{"id":"n1821015453","loc":[-85.5571355,42.009613]},"n1821015454":{"id":"n1821015454","loc":[-85.5609979,42.009059]},"n1821015455":{"id":"n1821015455","loc":[-85.6097336,41.9708342]},"n1821015456":{"id":"n1821015456","loc":[-85.5884476,41.9904218]},"w170843846":{"id":"w170843846","tags":{"waterway":"river"},"nodes":["n1819790555","n1819790762","n1819790547","n1819790618","n1819790898","n1819790796","n1819790534","n1819790543","n1819790541","n1819790687","n1819790877","n1819790811","n1819790670"]},"w209083541":{"id":"w209083541","tags":{"name":"Portage River","waterway":"river"},"nodes":["n1821014848","n1821015156","n1821015439","n1821014763","n1821014824","n1821015274","n1821014764","n1821014791","n1821014957","n1821015217","n1821015445","n1821015347","n1821014893","n1821015452","n1821015017","n1821015448","n1821014968","n1821015329","n1821015321","n1821014992","n1821014948","n1821014757","n1821014983","n1821015312","n1821015363","n1821014924","n1821014873","n1821014932","n1821014668","n1821015404","n1821014716","n1821015243","n1821015265","n1821014710","n1821015371","n1821015415","n1821014870","n1821015356","n1821015373","n1821014681","n1821014714","n1821015385","n1821014911","n1821015057","n1821014867","n1821014998","n1821015142","n1821015234","n1821015446","n1821014862","n1821015369","n1821014945","n1821015003","n1821014667","n1821015413","n1821015256","n1821015327","n1821015160","n1821014907","n1821015431","n1821015172","n1821015034","n1821014741","n1821015033","n1821015239","n1821015007","n1821015316","n1821015285","n1821014633","n1821015263","n1821015069","n1821014846","n1821014779","n1821015011","n1821015012","n1821014845","n1821015418","n1821015442","n1821015266","n1821015336","n1821014755","n1821015188","n1821014925","n1821014816","n1821014869","n1821014967","n1821015286","n1821015147","n1821015410","n1821015126","n1821015438","n1821015018","n1821015354","n1821015337","n1821014766","n1821014740","n1821015228","n1821015173","n1821015407","n1821015076","n1821015451","n1821015024","n1821015314","n1821014784","n1821015045","n1821014735","n1821015205","n1821014986","n1821015240","n1821015098","n1821014825","n1821015302","n1821014918","n1821014718","n1821014896","n1821014853","n1821015194","n1821015165","n1821015213","n1821014991","n1821015350","n1821015333","n1821015332","n1821015361","n1821015060","n1821014914","n1821015375","n1821015315","n1821015307","n1821014906","n1821014751","n1821014666","n1821015218","n1821014817","n1821015118","n1821014674","n1821015038","n1821014973","n1821015429","n1821015183","n1821015046","n1821014803","n1821015140","n1821015009","n1821014749","n1821015335","n1821014711","n1821014854","n1821015278","n1821015419","n1821014648","n1821015357","n1821014637","n1821014988","n1821015322","n1821015387","n1821015042","n1821014833","n1821015396","n1821015326","n1821014691","n1821015149","n1821015204","n1821015122","n1821014782","n1821014982","n1821014921","n1821014936","n1821014969","n1821014881","n1821015403","n1821014805","n1821015395","n1821014892","n1821014826","n1821014844","n1821015107","n1821015014","n1821014955","n1821015352","n1821015414","n1821014746","n1821015209","n1821014770","n1821015416","n1821014661","n1821014857","n1821014814","n1821014754","n1821014721","n1821014727","n1821015362","n1821015029","n1821015246","n1821015189","n1821014627","n1821015269","n1821014808","n1821014962","n1821015349","n1821015041","n1821014722","n1821015330","n1821015453","n1821015067","n1821014643","n1821014802","n1821015383","n1821015151","n1821015250","n1821015245","n1821014772","n1821014899","n1821015222","n1821015121","n1821014951","n1821015176","n1821015219","n1821015454","n1821014972","n1821014917","n1821015182","n1821015166","n1821015145","n1821014910","n1821015082","n1821014678","n1821014806","n1821015141","n1821015341","n1821014788","n1821015097","n1821014995","n1821015444","n1821015346","n1821015101","n1821015129","n1821015123","n1821015132","n1821015190","n1821015146","n1821015325","n1821015191","n1821015072","n1821015100","n1821015174","n1821015238","n1821015215","n1821014700","n1821015242","n1821014841","n1821014905","n1821014874","n1821014883","n1821014729","n1821014866","n1821014828","n1821015295","n1821014931","n1821014859","n1821014912","n1821014783","n1821014752","n1821014961","n1821015366","n1821015401","n1821015169","n1821015318","n1821014996","n1821014747","n1821014963","n1821014670","n1821015186","n1821015231","n1821015157","n1821014812","n1821015381","n1821014887","n1821015138","n1821014704","n1821014787","n1821014922","n1821015290","n1821015343","n1821014651","n1821014980","n1821014960","n1821015244","n1821015236","n1821015006","n1821014694","n1821014759","n1821015207","n1821015276","n1821015065","n1821014863","n1821014660","n1821014902","n1821014645","n1821015339","n1821014871","n1821015096","n1821015299","n1821014798","n1821014638","n1821015392","n1821014835","n1821014762","n1821014642","n1821015433","n1821014786","n1821015134","n1821014855","n1821015184","n1821014850","n1821015293","n1821015233","n1821015227","n1821014876","n1821014985","n1821014843","n1821015420","n1821015197","n1821015192","n1821015292","n1821015344","n1821014742","n1821014726","n1821015237","n1821014796","n1821014908","n1821014975","n1821014769","n1821014688","n1821014860","n1821014895","n1821014676","n1821015411","n1821014736","n1821015164","n1821014647","n1821015144","n1821014919","n1821015220","n1821015254","n1821015435","n1821015308","n1821015342","n1821014830","n1821015273","n1821014658","n1821014781","n1821015087","n1821015139","n1821015304","n1821014839","n1821015048","n1821015115","n1821015355","n1821015226","n1821015177","n1821015430","n1821014965","n1821014725","n1821015365","n1821015171","n1821015073","n1821015125","n1821015338","n1821015111","n1821014950","n1821015378","n1821015258","n1821015456","n1821015106","n1821014832","n1821014888","n1821014795","n1821014872","n1821014810","n1821014705","n1821014804","n1821014820","n1821015283","n1821014938","n1821014689","n1821015259","n1821015334","n1821015348","n1821014635","n1821015179","n1821014864","n1821014890","n1821015020","n1821014898","n1821015287","n1821015120","n1821014984","n1821014743","n1821014790","n1821014765","n1821014777","n1821015095","n1821014653","n1821015135","n1821014836","n1821014964","n1821014974","n1821014636","n1821014682","n1821014663","n1821014665","n1821015109","n1821015155","n1821014930","n1821014669","n1821015004","n1821015427","n1821014916","n1821015093","n1821015086","n1821015386","n1821014799","n1821014913","n1821015434","n1821014728","n1821014900","n1821015068","n1821015039","n1821015443","n1821015406","n1821015280","n1821015319","n1821015368","n1821014774","n1821015090","n1821015175","n1821015195","n1821014687","n1821015359","n1821015449","n1821014956","n1821014838","n1821014768","n1821014698","n1821015323","n1821014756","n1821015255","n1821015400","n1821014717","n1821014868","n1821014778","n1821015214","n1821014944","n1821014697","n1821014671","n1821014928","n1821015294","n1821014822","n1821015284","n1821015351","n1821015022","n1821015133","n1821014644","n1821015010","n1821014625","n1821014657","n1821014946","n1821015099","n1821015114","n1821014629","n1821014865","n1821014997","n1821014926","n1821014933","n1821015199","n1821014819","n1821015080","n1821014692","n1821014677","n1821015358","n1821015367","n1821015360","n1821015105","n1821015247","n1821015005","n1821014809","n1821014794","n1821014761","n1821014879","n1821014801","n1821015377","n1821015059","n1821014730","n1821015050","n1821015271","n1821015143","n1821014989","n1821015019","n1821014672","n1821014649","n1821014684","n1821014703","n1821015021","n1821015382","n1821014842","n1821014720","n1821014847","n1821015104","n1821014987","n1821014886","n1821015267","n1821015221","n1821015015","n1821015423","n1821014954","n1821014903","n1821014939","n1821015212","n1821014789","n1821014712","n1821014708","n1821015078","n1821015277","n1821015249","n1821014646","n1821014793","n1821015053","n1821014707","n1821015306","n1821015112","n1821015288","n1821015380","n1821015437","n1821015178","n1821015158","n1821015272","n1821015235","n1821015163","n1821015154","n1821015253","n1821014632","n1821015372","n1821015103","n1821015311","n1821015301","n1821014885","n1821014811","n1821014977","n1821015051","n1821014942","n1821014745","n1821015432","n1821015075","n1821014664","n1821014695","n1821015116","n1821014639","n1821015421","n1821015248","n1821014758","n1821014834","n1821015083","n1821015455","n1821015241","n1821015108","n1821014713","n1821015137","n1821015055","n1821015211","n1821014904","n1821015376","n1821015398","n1821014771","n1821014840","n1821015062","n1819790554","n1819790560","n1819790767","n1819790696","n1819790706","n1819790606","n1819790607","n1819790544","n1819790779","n1819790760","n1819790926","n1819790927","n1819790647","n1819790657","n1819790649","n1819790679","n1819790915","n1819790739","n1819790549","n1819790671","n1819790686","n1819790798","n1819790791","n1819790563","n1819790720","n1819790704","n1819790795","n1819790836","n1819790622","n1819790615","n1819790654","n1819790931","n1819790595","n1819790753","n1819790612","n1819790623","n1819790564","n1819790552","n1819790645","n1819790625","n1819790605","n1819790668","n1819790731","n1819790718","n1819790781","n1819790665","n1819790659","n1819790726","n1819790642","n1819790854","n1819790697","n1819790867","n1819790833","n1819790555","n1819790774","n1819790881","n1819790530","n1819790909","n1819790891","n1819790590","n1819790738","n1819790609","n1819790528","n1819790674","n1819790583","n1819790559","n1819790863","n1819790912","n1819790685","n1819790913"]},"n185955128":{"id":"n185955128","loc":[-85.6189367,41.9519432]},"n185948818":{"id":"n185948818","loc":[-85.616755,41.952231]},"n185978819":{"id":"n185978819","loc":[-85.616773,41.954737]},"n185978821":{"id":"n185978821","loc":[-85.616699,41.954742]},"n2138420714":{"id":"n2138420714","loc":[-85.6176304,41.9515154]},"n2138420715":{"id":"n2138420715","loc":[-85.6177355,41.9515717]},"n2138420716":{"id":"n2138420716","loc":[-85.6192901,41.951573]},"n2138420718":{"id":"n2138420718","loc":[-85.6171481,41.9513579]},"n2138420719":{"id":"n2138420719","loc":[-85.6165981,41.9519199]},"n2138420720":{"id":"n2138420720","loc":[-85.6165719,41.9519922]},"n2138420721":{"id":"n2138420721","loc":[-85.6165832,41.9520757]},"n2138420722":{"id":"n2138420722","loc":[-85.6166355,41.9521453]},"n2138420723":{"id":"n2138420723","loc":[-85.6169161,41.9522788]},"n2138420724":{"id":"n2138420724","loc":[-85.6170882,41.9522538]},"n2138420725":{"id":"n2138420725","loc":[-85.6189204,41.9514674]},"n2138420726":{"id":"n2138420726","loc":[-85.6180346,41.9514735]},"n2138420727":{"id":"n2138420727","loc":[-85.6180362,41.9515719]},"n2138420728":{"id":"n2138420728","loc":[-85.6189204,41.9515727]},"n2138420744":{"id":"n2138420744","loc":[-85.618919,41.9519571]},"n2138420745":{"id":"n2138420745","loc":[-85.6194575,41.9522374]},"n2138420746":{"id":"n2138420746","loc":[-85.6181777,41.9536179]},"n2138420747":{"id":"n2138420747","loc":[-85.6176582,41.9533658]},"n2138420748":{"id":"n2138420748","loc":[-85.6179871,41.9530242]},"n2138420749":{"id":"n2138420749","loc":[-85.618429,41.9532476]},"n2138420750":{"id":"n2138420750","loc":[-85.6185538,41.9531194]},"n2138420751":{"id":"n2138420751","loc":[-85.6180765,41.9528677]},"n2138420752":{"id":"n2138420752","loc":[-85.6180394,41.9528855]},"n2138420753":{"id":"n2138420753","loc":[-85.6193752,41.9521695]},"n2138420754":{"id":"n2138420754","loc":[-85.6181374,41.9535376]},"n2138420755":{"id":"n2138420755","loc":[-85.6179898,41.9535545]},"n2138420756":{"id":"n2138420756","loc":[-85.6177286,41.9534228]},"n2138420757":{"id":"n2138420757","loc":[-85.6181011,41.9530292]},"n2138420759":{"id":"n2138420759","loc":[-85.6185158,41.9531194]},"n2138420760":{"id":"n2138420760","loc":[-85.6191318,41.9520425]},"n2138420761":{"id":"n2138420761","loc":[-85.6182348,41.9529815]},"n2138420762":{"id":"n2138420762","loc":[-85.6184853,41.9524248]},"n2138420763":{"id":"n2138420763","loc":[-85.6186764,41.9525193]},"n2138420764":{"id":"n2138420764","loc":[-85.6189421,41.9526483]},"n2138420765":{"id":"n2138420765","loc":[-85.6182875,41.9531222]},"n2138420766":{"id":"n2138420766","loc":[-85.6179141,41.9535163]},"n2138420767":{"id":"n2138420767","loc":[-85.6178363,41.9535735]},"n185948824":{"id":"n185948824","loc":[-85.6165667,41.9529715]},"n2138420758":{"id":"n2138420758","loc":[-85.6184408,41.953201]},"n2138422349":{"id":"n2138422349","loc":[-85.6175136,41.9533346]},"n2138422350":{"id":"n2138422350","loc":[-85.6171867,41.9531679]},"n2138422351":{"id":"n2138422351","loc":[-85.61722,41.9531305]},"n2138422352":{"id":"n2138422352","loc":[-85.6171889,41.9531158]},"n2138422353":{"id":"n2138422353","loc":[-85.6171733,41.9531284]},"n2138422354":{"id":"n2138422354","loc":[-85.616765,41.9529207]},"n2138422355":{"id":"n2138422355","loc":[-85.6167565,41.9529355]},"n2138422356":{"id":"n2138422356","loc":[-85.6164772,41.9527911]},"n2138422357":{"id":"n2138422357","loc":[-85.6168227,41.9524261]},"n2138422358":{"id":"n2138422358","loc":[-85.6171913,41.9526158]},"n2138422359":{"id":"n2138422359","loc":[-85.6172403,41.9525589]},"n2138422360":{"id":"n2138422360","loc":[-85.6172097,41.952542]},"n2138422361":{"id":"n2138422361","loc":[-85.6173948,41.9523512]},"n2138422362":{"id":"n2138422362","loc":[-85.6174256,41.9523678]},"n2138422363":{"id":"n2138422363","loc":[-85.6174831,41.9523086]},"n2138422364":{"id":"n2138422364","loc":[-85.6173316,41.9522289]},"n2138422365":{"id":"n2138422365","loc":[-85.6174507,41.9521024]},"n2138422366":{"id":"n2138422366","loc":[-85.6174773,41.9521155]},"n2138422367":{"id":"n2138422367","loc":[-85.6176577,41.9519232]},"n2138422368":{"id":"n2138422368","loc":[-85.6176336,41.9519105]},"n2138422369":{"id":"n2138422369","loc":[-85.617747,41.9517861]},"n2138422370":{"id":"n2138422370","loc":[-85.6182675,41.9520559]},"n2138422371":{"id":"n2138422371","loc":[-85.6182105,41.9521219]},"n2138422372":{"id":"n2138422372","loc":[-85.6183863,41.9522203]},"n2138422373":{"id":"n2138422373","loc":[-85.6180984,41.9525266]},"n2138422374":{"id":"n2138422374","loc":[-85.6179159,41.9524295]},"n2138422375":{"id":"n2138422375","loc":[-85.617854,41.9524979]},"n2138422376":{"id":"n2138422376","loc":[-85.6177686,41.9524531]},"n2138422377":{"id":"n2138422377","loc":[-85.6174716,41.9527765]},"n2138422378":{"id":"n2138422378","loc":[-85.6178545,41.9529756]},"n2138425424":{"id":"n2138425424","loc":[-85.6171736,41.9536385]},"n2138425425":{"id":"n2138425425","loc":[-85.6180159,41.9535782]},"n2138425426":{"id":"n2138425426","loc":[-85.6181068,41.9536282]},"n2138425427":{"id":"n2138425427","loc":[-85.6180673,41.9542678]},"n2138425428":{"id":"n2138425428","loc":[-85.6178636,41.9542634]},"n2138425429":{"id":"n2138425429","loc":[-85.6176204,41.9542046]},"n2138425430":{"id":"n2138425430","loc":[-85.6174366,41.9541031]},"n2138425431":{"id":"n2138425431","loc":[-85.6172942,41.9539781]},"n2138425432":{"id":"n2138425432","loc":[-85.6172171,41.9538399]},"n2138425433":{"id":"n2138425433","loc":[-85.6168138,41.9543266]},"n2138425434":{"id":"n2138425434","loc":[-85.6167779,41.9538098]},"n2138425435":{"id":"n2138425435","loc":[-85.6165849,41.9537073]},"n2138425441":{"id":"n2138425441","loc":[-85.616458,41.9543184]},"n2138425442":{"id":"n2138425442","loc":[-85.6166428,41.954345]},"n2138425445":{"id":"n2138425445","loc":[-85.6181332,41.9514117]},"n2138425446":{"id":"n2138425446","loc":[-85.6183263,41.9514111]},"n2138425447":{"id":"n2138425447","loc":[-85.6185033,41.9514102]},"n2138425449":{"id":"n2138425449","loc":[-85.6186809,41.9514093]},"n2138425451":{"id":"n2138425451","loc":[-85.6188681,41.9514082]},"n2138436008":{"id":"n2138436008","loc":[-85.6170474,41.9513604]},"n2138436009":{"id":"n2138436009","loc":[-85.6164937,41.9519586]},"n2138436010":{"id":"n2138436010","loc":[-85.616497,41.9520725]},"n2138436011":{"id":"n2138436011","loc":[-85.6165654,41.9521645]},"n2138436012":{"id":"n2138436012","loc":[-85.6166631,41.9522178]},"n2138436013":{"id":"n2138436013","loc":[-85.6167327,41.9522554]},"n2138436014":{"id":"n2138436014","loc":[-85.6172383,41.9525125]},"n2138439319":{"id":"n2138439319","loc":[-85.6170432,41.9524057]},"n2138439320":{"id":"n2138439320","loc":[-85.617691,41.9517107]},"n2138439321":{"id":"n2138439321","loc":[-85.6177727,41.9516794]},"n2138439322":{"id":"n2138439322","loc":[-85.619085,41.9516811]},"n2138439323":{"id":"n2138439323","loc":[-85.6179432,41.952895]},"n2138439324":{"id":"n2138439324","loc":[-85.6180389,41.9529384]},"n2138439325":{"id":"n2138439325","loc":[-85.6176303,41.9533604]},"n2138439326":{"id":"n2138439326","loc":[-85.6175538,41.9534396]},"n2138439327":{"id":"n2138439327","loc":[-85.6173806,41.9523658]},"n2138439328":{"id":"n2138439328","loc":[-85.6171841,41.9522542]},"n2138439329":{"id":"n2138439329","loc":[-85.6172077,41.9524958]},"n2138439330":{"id":"n2138439330","loc":[-85.6171235,41.9525809]},"n2138439331":{"id":"n2138439331","loc":[-85.6180938,41.9527349]},"n2138439332":{"id":"n2138439332","loc":[-85.6177023,41.9525253]},"n2138439333":{"id":"n2138439333","loc":[-85.6175543,41.9526865]},"n2138439334":{"id":"n2138439334","loc":[-85.6179589,41.9528783]},"n185948820":{"id":"n185948820","loc":[-85.6163249,41.952701]},"n185948822":{"id":"n185948822","loc":[-85.6163757,41.952855]},"n185955123":{"id":"n185955123","loc":[-85.6198103,41.9510408]},"n185958839":{"id":"n185958839","loc":[-85.611651,41.954761]},"n185965033":{"id":"n185965033","loc":[-85.614195,41.954754]},"n185976502":{"id":"n185976502","loc":[-85.617375,41.947559]},"n185976504":{"id":"n185976504","loc":[-85.6174164,41.9510804]},"n185978828":{"id":"n185978828","loc":[-85.613542,41.954756]},"n185978830":{"id":"n185978830","loc":[-85.610373,41.954774]},"n2138420713":{"id":"n2138420713","loc":[-85.6174641,41.9506942]},"n2138420717":{"id":"n2138420717","loc":[-85.6173027,41.9512895]},"n2138420768":{"id":"n2138420768","loc":[-85.61745,41.9501974]},"n2138420773":{"id":"n2138420773","loc":[-85.6174135,41.9489136]},"n2138425436":{"id":"n2138425436","loc":[-85.6159148,41.9538036]},"n2138425437":{"id":"n2138425437","loc":[-85.6159534,41.9539677]},"n2138425438":{"id":"n2138425438","loc":[-85.6160306,41.9540846]},"n2138425439":{"id":"n2138425439","loc":[-85.6161354,41.954181]},"n2138425440":{"id":"n2138425440","loc":[-85.6162733,41.954263]},"n2138425443":{"id":"n2138425443","loc":[-85.6183273,41.9510826]},"n2138425444":{"id":"n2138425444","loc":[-85.6181354,41.9510835]},"n2138425448":{"id":"n2138425448","loc":[-85.6185033,41.9510816]},"n2138425450":{"id":"n2138425450","loc":[-85.6186816,41.9510808]},"n2138425452":{"id":"n2138425452","loc":[-85.6188641,41.9510818]},"n2138435984":{"id":"n2138435984","loc":[-85.6167607,41.9501009]},"n2138436000":{"id":"n2138436000","loc":[-85.6173169,41.947558]},"n2138436001":{"id":"n2138436001","loc":[-85.6173362,41.948883]},"n2138436002":{"id":"n2138436002","loc":[-85.6167791,41.9492952]},"n2138436003":{"id":"n2138436003","loc":[-85.6167543,41.949349]},"n2138436004":{"id":"n2138436004","loc":[-85.6167648,41.9509125]},"n2138436005":{"id":"n2138436005","loc":[-85.6168832,41.9510412]},"n2138436006":{"id":"n2138436006","loc":[-85.6170045,41.9511417]},"n2138436007":{"id":"n2138436007","loc":[-85.6170624,41.9512483]},"n2138436017":{"id":"n2138436017","loc":[-85.6168094,41.9492729]},"n2138436021":{"id":"n2138436021","loc":[-85.6167553,41.9494886]},"n2138436023":{"id":"n2138436023","loc":[-85.6167585,41.9499707]},"n2138436025":{"id":"n2138436025","loc":[-85.6167567,41.9497018]},"w203838284":{"id":"w203838284","tags":{"area":"yes","leisure":"pitch","sport":"baseball"},"nodes":["n2138425424","n2138425425","n2138425426","n2138425427","n2138425428","n2138425429","n2138425430","n2138425431","n2138425432","n2138425424"]},"w203837928":{"id":"w203837928","tags":{"highway":"service"},"nodes":["n2138420717","n2138420718","n2138420719","n2138420720","n2138420721","n2138420722","n185948818","n2138420723","n2138420724","n2138420715"]},"w203839364":{"id":"w203839364","tags":{"highway":"footway"},"nodes":["n2138439331","n2138439332"]},"w203837932":{"id":"w203837932","tags":{"amenity":"parking","area":"yes"},"nodes":["n2138420744","n2138420745","n2138420746","n2138420747","n2138420748","n2138420749","n2138420750","n2138420751","n2138420744"]},"w203839362":{"id":"w203839362","tags":{"highway":"footway"},"nodes":["n2138439327","n2138439328"]},"w203839363":{"id":"w203839363","tags":{"highway":"footway"},"nodes":["n2138439329","n2138439330"]},"w203837933":{"id":"w203837933","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n185955128","n2138420760","n2138420753","n2138420764","n2138420759","n2138420758","n2138420754","n2138420755","n2138420766","n2138420756"]},"w203837936":{"id":"w203837936","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420765","n2138420766"]},"w17966364":{"id":"w17966364","tags":{"access":"private","highway":"service","name":"Collins Dr"},"nodes":["n185961362","n185976502","n2138420773","n2138420768","n2138420713","n185976504","n2138420717","n2138420714","n2138420715","n2138420727","n2138420728","n2138420716"]},"w203838040":{"id":"w203838040","tags":{"amenity":"school","area":"yes","building":"yes","name":"Three Rivers Middle School"},"nodes":["n2138422349","n2138422350","n2138422351","n2138422352","n2138422353","n2138422354","n2138422355","n2138422356","n2138422357","n2138439330","n2138422358","n2138422359","n2138422360","n2138436014","n2138439327","n2138422361","n2138422362","n2138422363","n2138422364","n2138422365","n2138422366","n2138422367","n2138422368","n2138422369","n2138422370","n2138422371","n2138422372","n2138422373","n2138422374","n2138422375","n2138422376","n2138439332","n2138439333","n2138422377","n2138422378","n2138422349"]},"w17964049":{"id":"w17964049","tags":{"highway":"service"},"nodes":["n185955120","n185955123","n2138420716","n185955128","n2138420762","n2138420752","n2138420761","n2138420759"]},"w41074899":{"id":"w41074899","tags":{"highway":"secondary","name":"E Hoffman St","ref":"M 60"},"nodes":["n185978817","n185978819","n185978821","n185965033","n185978828","n185958839","n185978830"]},"w203839365":{"id":"w203839365","tags":{"highway":"footway"},"nodes":["n2138439333","n2138439334"]},"w203837935":{"id":"w203837935","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420762","n2138420763","n2138420764"]},"w203838287":{"id":"w203838287","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425446","n2138425447","n2138425448","n2138425443","n2138425446"]},"w203837934":{"id":"w203837934","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420760","n2138420763","n2138420761"]},"w203838289":{"id":"w203838289","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425449","n2138425451","n2138425452","n2138425450","n2138425449"]},"w17963047":{"id":"w17963047","tags":{"highway":"service"},"nodes":["n185948818","n2138436013","n185948820","n185948822","n185948824","n2138439326","n2138420767","n2138420766"]},"w203839091":{"id":"w203839091","tags":{"highway":"footway"},"nodes":["n185976502","n2138436000","n2138436001","n2138436017","n2138436002","n2138436003","n2138436021","n2138436025","n2138436023","n2138435984","n2138436004","n2138436005","n2138436006","n2138436007","n2138436008","n2138436009","n2138436010","n2138436011","n2138436012","n2138436013","n2138439319","n2138439329","n2138436014"]},"w204830797":{"id":"w204830797","tags":{"highway":"service","service":"parking_aisle"},"nodes":["n2138420756","n2138420757","n2138420765","n2138420758"]},"w203838288":{"id":"w203838288","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425447","n2138425449","n2138425450","n2138425448","n2138425447"]},"w203838285":{"id":"w203838285","tags":{"area":"yes","leisure":"pitch","sport":"baseball"},"nodes":["n2138425433","n2138425434","n2138425435","n2138425436","n2138425437","n2138425438","n2138425439","n2138425440","n2138425441","n2138425442","n2138425433"]},"w203838286":{"id":"w203838286","tags":{"area":"yes","leisure":"pitch","sport":"tennis"},"nodes":["n2138425443","n2138425444","n2138425445","n2138425446","n2138425443"]},"w203837929":{"id":"w203837929","tags":{"amenity":"parking","area":"yes"},"nodes":["n2138420725","n2138420726","n2138420727","n2138420728","n2138420725"]},"w203839361":{"id":"w203839361","tags":{"highway":"footway"},"nodes":["n2138439319","n2138439328","n2138439320","n2138439321","n2138439322","n2138439331","n2138439334","n2138439323","n2138439324","n2138439325","n2138439326"]},"n394381698":{"id":"n394381698","loc":[-85.614471,41.954755]},"n394381699":{"id":"n394381699","loc":[-85.6152,41.954744]},"n394381700":{"id":"n394381700","loc":[-85.615201,41.954081]},"n394381701":{"id":"n394381701","loc":[-85.614426,41.954042]},"n394381702":{"id":"n394381702","loc":[-85.616319,41.954749]},"n394381704":{"id":"n394381704","loc":[-85.616152,41.954752]},"n394381706":{"id":"n394381706","loc":[-85.615201,41.95483]},"n394490775":{"id":"n394490775","loc":[-85.613971,41.954839]},"n394490782":{"id":"n394490782","loc":[-85.614372,41.954841]},"n185958835":{"id":"n185958835","loc":[-85.611615,41.953704]},"n185958837":{"id":"n185958837","loc":[-85.611636,41.953938]},"n185958842":{"id":"n185958842","loc":[-85.611187,41.951686]},"n185958844":{"id":"n185958844","loc":[-85.611087,41.951741]},"n185958845":{"id":"n185958845","loc":[-85.611034,41.951852]},"n185958847":{"id":"n185958847","loc":[-85.611016,41.95196]},"n185958849":{"id":"n185958849","loc":[-85.610989,41.95328]},"n185958851":{"id":"n185958851","loc":[-85.611021,41.953484]},"n185958852":{"id":"n185958852","loc":[-85.611091,41.953603]},"n185958853":{"id":"n185958853","loc":[-85.6112,41.953661]},"n185958855":{"id":"n185958855","loc":[-85.611364,41.953686]},"n185965031":{"id":"n185965031","loc":[-85.614204,41.953696]},"n185965032":{"id":"n185965032","loc":[-85.6142,41.953978]},"n185965062":{"id":"n185965062","loc":[-85.614617,41.951639]},"n185965064":{"id":"n185965064","loc":[-85.61463,41.951852]},"n185965066":{"id":"n185965066","loc":[-85.614642,41.953436]},"n185965068":{"id":"n185965068","loc":[-85.6146,41.953551]},"n185965071":{"id":"n185965071","loc":[-85.614487,41.95363]},"n185965073":{"id":"n185965073","loc":[-85.614354,41.953672]},"n185966288":{"id":"n185966288","loc":[-85.61179,41.953695]},"n185966290":{"id":"n185966290","loc":[-85.612232,41.953685]},"n185966293":{"id":"n185966293","loc":[-85.613438,41.953677]},"n185966349":{"id":"n185966349","loc":[-85.611323,41.951653]},"n185966351":{"id":"n185966351","loc":[-85.611892,41.951642]},"n185966352":{"id":"n185966352","loc":[-85.612216,41.951641]},"n185966353":{"id":"n185966353","loc":[-85.613111,41.951639]},"n185966354":{"id":"n185966354","loc":[-85.613396,41.95164]},"n185966355":{"id":"n185966355","loc":[-85.614221,41.95164]},"n185973839":{"id":"n185973839","loc":[-85.61341,41.951919]},"n185973840":{"id":"n185973840","loc":[-85.613438,41.953308]},"n185980222":{"id":"n185980222","loc":[-85.613781,41.955164]},"n185980223":{"id":"n185980223","loc":[-85.613815,41.955237]},"n185980225":{"id":"n185980225","loc":[-85.613837,41.955316]},"n185990345":{"id":"n185990345","loc":[-85.612211,41.951977]},"n185955743":{"id":"n185955743","loc":[-85.613873,41.95635]},"n185980227":{"id":"n185980227","loc":[-85.613851,41.955415]},"n185980229":{"id":"n185980229","loc":[-85.613918,41.957134]},"n394381703":{"id":"n394381703","loc":[-85.616287,41.955674]},"n394381705":{"id":"n394381705","loc":[-85.615164,41.955676]},"n394490777":{"id":"n394490777","loc":[-85.613973,41.955979]},"n394490780":{"id":"n394490780","loc":[-85.614364,41.955987]},"w17965307":{"id":"w17965307","tags":{"highway":"residential","name":"Bates Ave"},"nodes":["n185958842","n185966349","n185966351","n185966352","n185966353","n185966354","n185966355","n185965062"]},"w17967957":{"id":"w17967957","tags":{"highway":"residential","name":"Krum Ave"},"nodes":["n185966352","n185990345","n185966290"]},"w17964508":{"id":"w17964508","tags":{"highway":"residential","name":"Blossom Dr"},"nodes":["n185958842","n185958844","n185958845","n185958847","n185958849","n185958851","n185958852","n185958853","n185958855","n185958835"]},"w17964507":{"id":"w17964507","tags":{"highway":"residential","name":"Blossom Dr"},"nodes":["n185958835","n185958837","n185958839"]},"w34367080":{"id":"w34367080","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394381699","n394381706","n394381705","n394381703","n394381702","n394381704","n394381699"]},"w17965302":{"id":"w17965302","tags":{"highway":"residential","name":"Clausen Ave"},"nodes":["n185958835","n185966288","n185966290","n185966293","n185965031"]},"w17965156":{"id":"w17965156","tags":{"highway":"residential","name":"Orchard Dr"},"nodes":["n185965062","n185965064","n185965066","n185965068","n185965071","n185965073","n185965031"]},"w34369812":{"id":"w34369812","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394490775","n394490777","n394490780","n394490782","n394490775"]},"w17965151":{"id":"w17965151","tags":{"highway":"residential","name":"Orchard Dr"},"nodes":["n185965031","n185965032","n185965033"]},"w17966756":{"id":"w17966756","tags":{"access":"private","highway":"service","name":"Lockport Dr"},"nodes":["n185978828","n185980222","n185980223","n185980225","n185980227","n185955743","n185980229"]},"w17966056":{"id":"w17966056","tags":{"highway":"residential","name":"Angell Ave"},"nodes":["n185966354","n185973839","n185973840","n185966293"]},"w34367079":{"id":"w34367079","tags":{"admin_level":"8","boundary":"administrative"},"nodes":["n394381700","n394381701","n394381698","n394381699","n394381700"]},"n185955744":{"id":"n185955744","loc":[-85.611753,41.956208]},"n185988932":{"id":"n185988932","loc":[-85.6159,41.956336]},"n185988934":{"id":"n185988934","loc":[-85.6159158,41.9590646]},"n185988935":{"id":"n185988935","loc":[-85.6157358,41.959364],"tags":{"highway":"turning_circle"}},"n2138447007":{"id":"n2138447007","loc":[-85.6130784,41.9590689]},"n2138447008":{"id":"n2138447008","loc":[-85.6133328,41.9593805]},"n2138447003":{"id":"n2138447003","loc":[-85.610238,41.9547745]},"n2138447004":{"id":"n2138447004","loc":[-85.6102652,41.9566041]},"n2138447005":{"id":"n2138447005","loc":[-85.610325,41.9568823]},"n2138447006":{"id":"n2138447006","loc":[-85.6105644,41.9571383]},"n2138447009":{"id":"n2138447009","loc":[-85.6135946,41.959948]},"n2138447010":{"id":"n2138447010","loc":[-85.6136071,41.9629372]},"n2138447011":{"id":"n2138447011","loc":[-85.6134392,41.9633182]},"n2138447012":{"id":"n2138447012","loc":[-85.6130151,41.9636073]},"n2138447013":{"id":"n2138447013","loc":[-85.6122729,41.9637125]},"n2138447014":{"id":"n2138447014","loc":[-85.6056682,41.963752]},"w17964174":{"id":"w17964174","tags":{"access":"private","highway":"service"},"nodes":["n185955743","n185955744"]},"w17967743":{"id":"w17967743","tags":{"access":"private","highway":"service","name":"Manistee River Rd"},"nodes":["n185971574","n185988932","n185971407","n185981301","n185967987","n185988934","n185988935"]},"w203839666":{"id":"w203839666","tags":{"highway":"residential","name":"Hov Aire Drive"},"nodes":["n2138447003","n2138447004","n2138447005","n2138447006","n2138447007","n2138447008","n2138447009","n2138447010","n2138447011","n2138447012","n2138447013","n2138447014"]}}';iD.data = {"deprecated":[{"old":{"amenity":"firepit"},"replace":{"leisure":"firepit"}},{"old":{"barrier":"wire_fence"},"replace":{"barrier":"fence","fence_type":"chain"}},{"old":{"barrier":"wood_fence"},"replace":{"barrier":"fence","fence_type":"wood"}},{"old":{"highway":"ford"},"replace":{"ford":"yes"}},{"old":{"highway":"stile"},"replace":{"barrier":"stile"}},{"old":{"highway":"incline"},"replace":{"highway":"road","incline":"up"}},{"old":{"highway":"incline_steep"},"replace":{"highway":"road","incline":"up"}},{"old":{"highway":"unsurfaced"},"replace":{"highway":"road","incline":"unpaved"}},{"old":{"landuse":"wood"},"replace":{"landuse":"forest","natural":"wood"}},{"old":{"natural":"marsh"},"replace":{"natural":"wetland","wetland":"marsh"}},{"old":{"power_source":"*"},"replace":{"generator:source":"$1"}},{"old":{"power_rating":"*"},"replace":{"generator:output":"$1"}},{"old":{"shop":"organic"},"replace":{"shop":"supermarket","organic":"only"}}],"discarded":["created_by","odbl","odbl:note","tiger:upload_uuid","tiger:tlid","tiger:source","tiger:separated","geobase:datasetName","geobase:uuid","sub_sea:type","KSJ2:ADS","KSJ2:ARE","KSJ2:AdminArea","KSJ2:COP_label","KSJ2:DFD","KSJ2:INT","KSJ2:INT_label","KSJ2:LOC","KSJ2:LPN","KSJ2:OPC","KSJ2:PubFacAdmin","KSJ2:RAC","KSJ2:RAC_label","KSJ2:RIC","KSJ2:RIN","KSJ2:WSC","KSJ2:coordinate","KSJ2:curve_id","KSJ2:curve_type","KSJ2:filename","KSJ2:lake_id","KSJ2:lat","KSJ2:long","KSJ2:river_id","yh:LINE_NAME","yh:LINE_NUM","yh:STRUCTURE","yh:TOTYUMONO","yh:TYPE","yh:WIDTH","yh:WIDTH_RANK","SK53_bulk:load"],"wikipedia":[["Abkhazian","Аҧсшәа","ab"],["Achinese","Acèh","ace"],["Afrikaans","Afrikaans","af"],["Akan","Akan","ak"],["Alemannisch","Alemannisch","als"],["Amharic","አማርኛ","am"],["Aragonese","aragonés","an"],["Old English","Ænglisc","ang"],["Arabic","العربية","ar"],["Aramaic","ܐܪܡܝܐ","arc"],["Egyptian Arabic","مصرى","arz"],["Assamese","অসমীয়া","as"],["Asturian","asturianu","ast"],["Avaric","авар","av"],["Aymara","Aymar aru","ay"],["Azerbaijani","azərbaycanca","az"],["South Azerbaijani","تۆرکجه","azb"],["Bashkir","башҡортса","ba"],["Bavarian","Boarisch","bar"],["Samogitian","žemaitėška","bat-smg"],["Bikol Central","Bikol Central","bcl"],["Belarusian","беларуская","be"],["беларуская (тарашкевіца)‎","беларуская (тарашкевіца)‎","be-x-old"],["Bulgarian","български","bg"],["भोजपुरी","भोजपुरी","bh"],["Bislama","Bislama","bi"],["Banjar","Bahasa Banjar","bjn"],["Bambara","bamanankan","bm"],["Bengali","বাংলা","bn"],["Tibetan","བོད་ཡིག","bo"],["Bishnupriya","বিষ্ণুপ্রিয়া মণিপুরী","bpy"],["Breton","brezhoneg","br"],["Bosnian","bosanski","bs"],["Buginese","ᨅᨔ ᨕᨘᨁᨗ","bug"],["буряад","буряад","bxr"],["Catalan","català","ca"],["Chavacano de Zamboanga","Chavacano de Zamboanga","cbk-zam"],["Min Dong Chinese","Mìng-dĕ̤ng-ngṳ̄","cdo"],["Chechen","нохчийн","ce"],["Cebuano","Cebuano","ceb"],["Chamorro","Chamoru","ch"],["Cherokee","ᏣᎳᎩ","chr"],["Cheyenne","Tsetsêhestâhese","chy"],["Central Kurdish","کوردیی ناوەندی","ckb"],["Corsican","corsu","co"],["Cree","Nēhiyawēwin / ᓀᐦᐃᔭᐍᐏᐣ","cr"],["Crimean Turkish","qırımtatarca","crh"],["Czech","čeština","cs"],["Kashubian","kaszëbsczi","csb"],["Church Slavic","словѣньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ","cu"],["Chuvash","Чӑвашла","cv"],["Welsh","Cymraeg","cy"],["Danish","dansk","da"],["German","Deutsch","de"],["Zazaki","Zazaki","diq"],["Lower Sorbian","dolnoserbski","dsb"],["Divehi","ދިވެހިބަސް","dv"],["Dzongkha","ཇོང་ཁ","dz"],["Ewe","eʋegbe","ee"],["Greek","Ελληνικά","el"],["Emiliano-Romagnolo","emiliàn e rumagnòl","eml"],["English","English","en"],["Esperanto","Esperanto","eo"],["Spanish","español","es"],["Estonian","eesti","et"],["Basque","euskara","eu"],["Extremaduran","estremeñu","ext"],["Persian","فارسی","fa"],["Fulah","Fulfulde","ff"],["Finnish","suomi","fi"],["Võro","Võro","fiu-vro"],["Fijian","Na Vosa Vakaviti","fj"],["Faroese","føroyskt","fo"],["French","français","fr"],["Arpitan","arpetan","frp"],["Northern Frisian","Nordfriisk","frr"],["Friulian","furlan","fur"],["Western Frisian","Frysk","fy"],["Irish","Gaeilge","ga"],["Gagauz","Gagauz","gag"],["Gan Chinese","贛語","gan"],["Scottish Gaelic","Gàidhlig","gd"],["Galician","galego","gl"],["Gilaki","گیلکی","glk"],["Guarani","Avañe'ẽ","gn"],["Goan Konkani","गोवा कोंकणी / Gova Konknni","gom"],["Gothic","𐌲𐌿𐍄𐌹𐍃𐌺","got"],["Gujarati","ગુજરાતી","gu"],["Manx","Gaelg","gv"],["Hausa","Hausa","ha"],["Hakka Chinese","客家語/Hak-kâ-ngî","hak"],["Hawaiian","Hawai`i","haw"],["Hebrew","עברית","he"],["Hindi","हिन्दी","hi"],["Fiji Hindi","Fiji Hindi","hif"],["Croatian","hrvatski","hr"],["Upper Sorbian","hornjoserbsce","hsb"],["Haitian","Kreyòl ayisyen","ht"],["Hungarian","magyar","hu"],["Armenian","Հայերեն","hy"],["Interlingua","interlingua","ia"],["Indonesian","Bahasa Indonesia","id"],["Interlingue","Interlingue","ie"],["Igbo","Igbo","ig"],["Inupiaq","Iñupiak","ik"],["Iloko","Ilokano","ilo"],["Ido","Ido","io"],["Icelandic","íslenska","is"],["Italian","italiano","it"],["Inuktitut","ᐃᓄᒃᑎᑐᑦ/inuktitut","iu"],["Japanese","日本語","ja"],["Lojban","Lojban","jbo"],["Javanese","Basa Jawa","jv"],["Georgian","ქართული","ka"],["Kara-Kalpak","Qaraqalpaqsha","kaa"],["Kabyle","Taqbaylit","kab"],["Kabardian","Адыгэбзэ","kbd"],["Kongo","Kongo","kg"],["Kikuyu","Gĩkũyũ","ki"],["Kazakh","қазақша","kk"],["Kalaallisut","kalaallisut","kl"],["Khmer","ភាសាខ្មែរ","km"],["Kannada","ಕನ್ನಡ","kn"],["Korean","한국어","ko"],["Komi-Permyak","Перем Коми","koi"],["Karachay-Balkar","къарачай-малкъар","krc"],["Kashmiri","कॉशुर / کٲشُر","ks"],["Colognian","Ripoarisch","ksh"],["Kurdish","Kurdî","ku"],["Komi","коми","kv"],["Cornish","kernowek","kw"],["Kyrgyz","Кыргызча","ky"],["Latin","Latina","la"],["Ladino","Ladino","lad"],["Luxembourgish","Lëtzebuergesch","lb"],["лакку","лакку","lbe"],["Lezghian","лезги","lez"],["Ganda","Luganda","lg"],["Limburgish","Limburgs","li"],["Ligurian","Ligure","lij"],["Lombard","lumbaart","lmo"],["Lingala","lingála","ln"],["Lao","ລາວ","lo"],["Northern Luri","لۊری شومالی","lrc"],["Lithuanian","lietuvių","lt"],["Latgalian","latgaļu","ltg"],["Latvian","latviešu","lv"],["Maithili","मैथिली","mai"],["Basa Banyumasan","Basa Banyumasan","map-bms"],["Moksha","мокшень","mdf"],["Malagasy","Malagasy","mg"],["Eastern Mari","олык марий","mhr"],["Maori","Māori","mi"],["Minangkabau","Baso Minangkabau","min"],["Macedonian","македонски","mk"],["Malayalam","മലയാളം","ml"],["Mongolian","монгол","mn"],["Marathi","मराठी","mr"],["Western Mari","кырык мары","mrj"],["Malay","Bahasa Melayu","ms"],["Maltese","Malti","mt"],["Mirandese","Mirandés","mwl"],["Burmese","မြန်မာဘာသာ","my"],["Erzya","эрзянь","myv"],["Mazanderani","مازِرونی","mzn"],["Nauru","Dorerin Naoero","na"],["Nāhuatl","Nāhuatl","nah"],["Neapolitan","Napulitano","nap"],["Low German","Plattdüütsch","nds"],["Low Saxon (Netherlands)","Nedersaksies","nds-nl"],["Nepali","नेपाली","ne"],["Newari","नेपाल भाषा","new"],["Dutch","Nederlands","nl"],["Norwegian Nynorsk","norsk nynorsk","nn"],["Norwegian","norsk bokmål","no"],["Novial","Novial","nov"],["Nouormand","Nouormand","nrm"],["Northern Sotho","Sesotho sa Leboa","nso"],["Navajo","Diné bizaad","nv"],["Nyanja","Chi-Chewa","ny"],["Occitan","occitan","oc"],["Oromo","Oromoo","om"],["Oriya","ଓଡ଼ିଆ","or"],["Ossetic","Ирон","os"],["Punjabi","ਪੰਜਾਬੀ","pa"],["Pangasinan","Pangasinan","pag"],["Pampanga","Kapampangan","pam"],["Papiamento","Papiamentu","pap"],["Picard","Picard","pcd"],["Pennsylvania German","Deitsch","pdc"],["Palatine German","Pälzisch","pfl"],["Pali","पालि","pi"],["Norfuk / Pitkern","Norfuk / Pitkern","pih"],["Polish","polski","pl"],["Piedmontese","Piemontèis","pms"],["Western Punjabi","پنجابی","pnb"],["Pontic","Ποντιακά","pnt"],["Pashto","پښتو","ps"],["Portuguese","português","pt"],["Quechua","Runa Simi","qu"],["Romansh","rumantsch","rm"],["Romani","Romani","rmy"],["Rundi","Kirundi","rn"],["Romanian","română","ro"],["Aromanian","armãneashti","roa-rup"],["tarandíne","tarandíne","roa-tara"],["Russian","русский","ru"],["Rusyn","русиньскый","rue"],["Kinyarwanda","Kinyarwanda","rw"],["Sanskrit","संस्कृतम्","sa"],["Sakha","саха тыла","sah"],["Sardinian","sardu","sc"],["Sicilian","sicilianu","scn"],["Scots","Scots","sco"],["Sindhi","سنڌي","sd"],["Northern Sami","sámegiella","se"],["Sango","Sängö","sg"],["Serbo-Croatian","srpskohrvatski / српскохрватски","sh"],["Sinhala","සිංහල","si"],["Simple English","Simple English","simple"],["Slovak","slovenčina","sk"],["Slovenian","slovenščina","sl"],["Samoan","Gagana Samoa","sm"],["Shona","chiShona","sn"],["Somali","Soomaaliga","so"],["Albanian","shqip","sq"],["Serbian","српски / srpski","sr"],["Sranan Tongo","Sranantongo","srn"],["Swati","SiSwati","ss"],["Southern Sotho","Sesotho","st"],["Saterland Frisian","Seeltersk","stq"],["Sundanese","Basa Sunda","su"],["Swedish","svenska","sv"],["Swahili","Kiswahili","sw"],["Silesian","ślůnski","szl"],["Tamil","தமிழ்","ta"],["Telugu","తెలుగు","te"],["Tetum","tetun","tet"],["Tajik","тоҷикӣ","tg"],["Thai","ไทย","th"],["Tigrinya","ትግርኛ","ti"],["Turkmen","Türkmençe","tk"],["Tagalog","Tagalog","tl"],["Tswana","Setswana","tn"],["Tongan","lea faka-Tonga","to"],["Tok Pisin","Tok Pisin","tpi"],["Turkish","Türkçe","tr"],["Tsonga","Xitsonga","ts"],["Tatar","татарча/tatarça","tt"],["Tumbuka","chiTumbuka","tum"],["Twi","Twi","tw"],["Tahitian","reo tahiti","ty"],["Tuvinian","тыва дыл","tyv"],["Udmurt","удмурт","udm"],["Uyghur","ئۇيغۇرچە / Uyghurche","ug"],["Ukrainian","українська","uk"],["Urdu","اردو","ur"],["Uzbek","oʻzbekcha/ўзбекча","uz"],["Venda","Tshivenda","ve"],["Venetian","vèneto","vec"],["Veps","vepsän kel’","vep"],["Vietnamese","Tiếng Việt","vi"],["West Flemish","West-Vlams","vls"],["Volapük","Volapük","vo"],["Walloon","walon","wa"],["Waray","Winaray","war"],["Wolof","Wolof","wo"],["Wu Chinese","吴语","wuu"],["Kalmyk","хальмг","xal"],["Xhosa","isiXhosa","xh"],["Mingrelian","მარგალური","xmf"],["Yiddish","ייִדיש","yi"],["Yoruba","Yorùbá","yo"],["Zhuang","Vahcuengh","za"],["Zeelandic","Zeêuws","zea"],["Chinese","中文","zh"],["Classical Chinese","文言","zh-classical"],["Chinese (Min Nan)","Bân-lâm-gú","zh-min-nan"],["Cantonese","粵語","zh-yue"],["Zulu","isiZulu","zu"]],"imperial":{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"id":0},"geometry":{"type":"MultiPolygon","coordinates":[[[[-1.426496,50.639342],[-1.445953,50.648139],[-1.452789,50.654283],[-1.485951,50.669338],[-1.497426,50.672309],[-1.535146,50.669379],[-1.551503,50.665107],[-1.569488,50.658026],[-1.545318,50.686103],[-1.50593,50.707709],[-1.418691,50.733791],[-1.420888,50.730455],[-1.423451,50.7237],[-1.425364,50.72012],[-1.400868,50.721991],[-1.377553,50.734198],[-1.343495,50.761054],[-1.318512,50.772162],[-1.295766,50.773179],[-1.144276,50.733791],[-1.119537,50.734198],[-1.10912,50.732856],[-1.097035,50.726955],[-1.096425,50.724433],[-1.097646,50.71601],[-1.097035,50.713324],[-1.094228,50.712633],[-1.085561,50.714016],[-1.082753,50.713324],[-1.062327,50.692816],[-1.062327,50.685289],[-1.066965,50.685248],[-1.069651,50.683498],[-1.071889,50.680976],[-1.075307,50.678534],[-1.112701,50.671454],[-1.128651,50.666449],[-1.156361,50.650784],[-1.162221,50.645982],[-1.164703,50.640937],[-1.164666,50.639543],[-1.426496,50.639342]]],[[[-7.240314,55.050389],[-7.013736,55.1615],[-6.958913,55.20349],[-6.571562,55.268366],[-6.509633,55.31398],[-6.226158,55.344406],[-6.07105,55.25001],[-5.712696,55.017635],[-5.242021,54.415204],[-5.695554,54.14284],[-5.72473,54.07455],[-6.041633,54.006238],[-6.153953,54.054931],[-6.220539,54.098803],[-6.242502,54.099758],[-6.263661,54.104682],[-6.269887,54.097927],[-6.28465,54.105226],[-6.299585,54.104037],[-6.313796,54.099696],[-6.327128,54.097888],[-6.338962,54.102952],[-6.346662,54.109877],[-6.354827,54.110652],[-6.368108,54.097319],[-6.369348,54.091118],[-6.367643,54.083418],[-6.366919,54.075098],[-6.371157,54.066778],[-6.377513,54.063264],[-6.401026,54.060887],[-6.426761,54.05541],[-6.433892,54.055306],[-6.4403,54.057993],[-6.446243,54.062438],[-6.450222,54.066675],[-6.450894,54.068432],[-6.47854,54.067709],[-6.564013,54.04895],[-6.571868,54.049519],[-6.587164,54.053343],[-6.595071,54.052412],[-6.60029,54.04895],[-6.605217,54.044475],[-6.610987,54.039235],[-6.616465,54.037271],[-6.630624,54.041819],[-6.657289,54.061146],[-6.672534,54.068432],[-6.657082,54.091945],[-6.655791,54.103314],[-6.666436,54.114786],[-6.643957,54.131839],[-6.634552,54.150133],[-6.640339,54.168013],[-6.648448,54.173665],[-6.663025,54.183826],[-6.683954,54.194368],[-6.694651,54.197985],[-6.706537,54.198915],[-6.717234,54.195143],[-6.724779,54.188631],[-6.73284,54.183567],[-6.744777,54.184187],[-6.766481,54.192352],[-6.787824,54.202998],[-6.807358,54.21633],[-6.823946,54.23235],[-6.829733,54.242375],[-6.833196,54.25209],[-6.837743,54.260513],[-6.846683,54.266456],[-6.882185,54.277257],[-6.864667,54.282734],[-6.856657,54.292811],[-6.858414,54.307332],[-6.870015,54.326001],[-6.879705,54.341594],[-6.885957,54.345624],[-6.897895,54.346193],[-6.905956,54.349035],[-6.915051,54.365933],[-6.922028,54.372703],[-6.984091,54.403089],[-7.017836,54.413166],[-7.049255,54.411512],[-7.078504,54.394717],[-7.127028,54.349759],[-7.159894,54.335186],[-7.168059,54.335031],[-7.185629,54.336943],[-7.18947,54.335692],[-7.19245,54.334721],[-7.193949,54.329967],[-7.191468,54.323869],[-7.187644,54.318804],[-7.185009,54.317254],[-7.184647,54.316634],[-7.192399,54.307384],[-7.193691,54.307539],[-7.199168,54.303457],[-7.206661,54.304903],[-7.211467,54.30418],[-7.209038,54.293431],[-7.1755,54.283664],[-7.181495,54.269763],[-7.14589,54.25209],[-7.159739,54.24067],[-7.153331,54.224237],[-7.174725,54.216072],[-7.229502,54.207545],[-7.240871,54.202326],[-7.249088,54.197416],[-7.255496,54.190854],[-7.261128,54.18088],[-7.256322,54.176901],[-7.247021,54.17225],[-7.24578,54.166979],[-7.265366,54.16114],[-7.26087,54.151166],[-7.263505,54.140986],[-7.27074,54.132253],[-7.280042,54.126155],[-7.293788,54.122021],[-7.297353,54.125896],[-7.29632,54.134991],[-7.296423,54.146515],[-7.295028,54.155404],[-7.292134,54.162638],[-7.295545,54.165119],[-7.325982,54.154577],[-7.333165,54.149409],[-7.333165,54.142743],[-7.310324,54.114683],[-7.316489,54.11428],[-7.326964,54.113597],[-7.375488,54.123312],[-7.390216,54.121194],[-7.39466,54.121917],[-7.396624,54.126258],[-7.403962,54.135043],[-7.41223,54.136438],[-7.422255,54.135456],[-7.425769,54.136955],[-7.414659,54.145688],[-7.439619,54.146929],[-7.480753,54.127653],[-7.502302,54.125121],[-7.609014,54.139901],[-7.620796,54.144965],[-7.624052,54.153336],[-7.625706,54.162173],[-7.632682,54.168529],[-7.70477,54.200362],[-7.722599,54.202326],[-7.782078,54.2],[-7.836959,54.204341],[-7.856441,54.211421],[-7.86967,54.226872],[-7.873649,54.271055],[-7.880264,54.287023],[-7.894966,54.293586],[-7.93411,54.297049],[-7.942075,54.298873],[-7.950802,54.300873],[-7.96801,54.31219],[-7.981033,54.326556],[-8.002194,54.357923],[-8.03134,54.358027],[-8.05648,54.365882],[-8.079941,54.380196],[-8.122419,54.415233],[-8.146346,54.430736],[-8.156035,54.439055],[-8.158128,54.447117],[-8.161177,54.454817],[-8.173837,54.461741],[-8.168467,54.463477],[-8.15017,54.46939],[-8.097046,54.478588],[-8.072448,54.487063],[-8.060976,54.493316],[-8.05586,54.497553],[-8.043561,54.512229],[-8.023278,54.529696],[-8.002194,54.543442],[-7.926411,54.533055],[-7.887137,54.532125],[-7.848844,54.54091],[-7.749264,54.596152],[-7.707871,54.604162],[-7.707944,54.604708],[-7.707951,54.604763],[-7.710558,54.624264],[-7.721204,54.625866],[-7.736758,54.619251],[-7.753553,54.614497],[-7.769159,54.618011],[-7.801199,54.634806],[-7.814996,54.639457],[-7.822541,54.638113],[-7.838044,54.63124],[-7.846416,54.631447],[-7.85427,54.636408],[-7.864347,54.649069],[-7.872771,54.652221],[-7.890082,54.655063],[-7.906619,54.661316],[-7.914835,54.671651],[-7.907135,54.686689],[-7.913233,54.688653],[-7.929666,54.696714],[-7.880109,54.711029],[-7.845899,54.731027],[-7.832153,54.730614],[-7.803576,54.716145],[-7.770503,54.706016],[-7.736603,54.707463],[-7.70229,54.718883],[-7.667512,54.738779],[-7.649683,54.744877],[-7.61537,54.739347],[-7.585398,54.744722],[-7.566639,54.738675],[-7.556149,54.738365],[-7.543075,54.741673],[-7.543023,54.743791],[-7.548398,54.747202],[-7.551705,54.754695],[-7.549741,54.779603],[-7.543385,54.793091],[-7.470831,54.845284],[-7.45507,54.863009],[-7.444735,54.884455],[-7.444735,54.894893],[-7.448972,54.920318],[-7.445251,54.932152],[-7.436983,54.938301],[-7.417139,54.943056],[-7.415755,54.944372],[-7.408665,54.951117],[-7.407424,54.959437],[-7.413109,54.984965],[-7.409078,54.992045],[-7.403755,54.99313],[-7.40112,54.994836],[-7.405254,55.003569],[-7.376987,55.02889],[-7.366962,55.035557],[-7.355024,55.040931],[-7.291152,55.046615],[-7.282987,55.051835],[-7.275288,55.058863],[-7.266503,55.065167],[-7.247097,55.069328],[-7.2471,55.069322],[-7.256744,55.050686],[-7.240956,55.050279],[-7.240314,55.050389]]],[[[-13.688588,57.596259],[-13.690419,57.596259],[-13.691314,57.596503],[-13.691314,57.597154],[-13.690419,57.597805],[-13.688588,57.597805],[-13.687652,57.597154],[-13.687652,57.596869],[-13.688588,57.596259]]],[[[-4.839121,54.469789],[-4.979941,54.457977],[-5.343644,54.878637],[-5.308469,55.176452],[-6.272566,55.418443],[-8.690528,57.833706],[-6.344705,59.061083],[-4.204785,58.63305],[-2.31566,60.699068],[-1.695335,60.76432],[-1.58092,60.866001],[-0.17022,60.897204],[-0.800508,59.770037],[-1.292368,57.732574],[-1.850077,55.766368],[-1.73054,55.782219],[1.892395,52.815229],[1.742775,51.364209],[1.080173,50.847526],[0.000774,50.664982],[-0.162997,50.752401],[-0.725152,50.731879],[-0.768853,50.741516],[-0.770985,50.736884],[-0.789947,50.730048],[-0.812815,50.734768],[-0.877742,50.761156],[-0.942879,50.758338],[-0.992581,50.737379],[-1.18513,50.766989],[-1.282741,50.792353],[-1.375004,50.772063],[-1.523427,50.719605],[-1.630649,50.695128],[-1.663617,50.670508],[-1.498021,50.40831],[-4.097427,49.735486],[-6.825199,49.700905],[-5.541541,51.446591],[-6.03361,51.732369],[-4.791746,52.635365],[-4.969244,52.637413],[-5.049473,53.131209],[-4.787393,53.409491],[-4.734148,53.424866],[-4.917096,53.508212],[-4.839121,54.469789]]]]}},{"type":"Feature","properties":{"id":0},"geometry":{"type":"MultiPolygon","coordinates":[[[[-157.018938,19.300864],[-179.437336,27.295312],[-179.480084,28.991459],[-168.707465,26.30325],[-163.107414,24.60499],[-153.841679,20.079306],[-154.233846,19.433391],[-153.61725,18.900587],[-154.429471,18.171036],[-156.780638,18.718492],[-157.018938,19.300864]]],[[[-78.91269,43.037032],[-78.964351,42.976393],[-78.981718,42.979043],[-78.998055,42.991111],[-79.01189,43.004358],[-79.022046,43.010539],[-79.023076,43.017015],[-79.00983,43.050867],[-79.011449,43.065291],[-78.993051,43.066174],[-78.975536,43.069707],[-78.958905,43.070884],[-78.943304,43.065291],[-78.917399,43.058521],[-78.908569,43.049396],[-78.91269,43.037032]]],[[[-123.03529,48.992515],[-123.035308,48.992499],[-123.045277,48.984361],[-123.08849,48.972235],[-123.089345,48.987982],[-123.090484,48.992499],[-123.090488,48.992515],[-123.035306,48.992515],[-123.03529,48.992515]]],[[[-103.837038,29.279906],[-103.864121,29.281366],[-103.928122,29.293019],[-104.01915,29.32033],[-104.057313,29.339037],[-104.105424,29.385675],[-104.139789,29.400584],[-104.161648,29.416759],[-104.194514,29.448927],[-104.212291,29.484661],[-104.218698,29.489829],[-104.227148,29.493033],[-104.251022,29.508588],[-104.267171,29.526571],[-104.292751,29.532824],[-104.320604,29.532255],[-104.338484,29.524013],[-104.349026,29.537578],[-104.430443,29.582795],[-104.437832,29.58543],[-104.444008,29.589203],[-104.448555,29.597678],[-104.452069,29.607109],[-104.455222,29.613387],[-104.469381,29.625402],[-104.516639,29.654315],[-104.530824,29.667906],[-104.535036,29.677802],[-104.535191,29.687853],[-104.537103,29.702116],[-104.543666,29.71643],[-104.561391,29.745421],[-104.570279,29.787511],[-104.583586,29.802575],[-104.601207,29.81477],[-104.619682,29.833064],[-104.623764,29.841487],[-104.637588,29.887996],[-104.656346,29.908201],[-104.660635,29.918433],[-104.663478,29.923084],[-104.676526,29.93683],[-104.680479,29.942308],[-104.682469,29.952126],[-104.680117,29.967784],[-104.680479,29.976466],[-104.699108,30.03145],[-104.701589,30.055324],[-104.698592,30.075271],[-104.684639,30.111135],[-104.680479,30.134131],[-104.67867,30.170356],[-104.681564,30.192939],[-104.695853,30.208441],[-104.715231,30.243995],[-104.724585,30.252211],[-104.742155,30.25986],[-104.74939,30.264459],[-104.761689,30.284199],[-104.774143,30.311588],[-104.788767,30.335927],[-104.807732,30.346418],[-104.8129,30.350707],[-104.814967,30.360577],[-104.816001,30.371997],[-104.818274,30.380524],[-104.824269,30.38719],[-104.83755,30.394063],[-104.844939,30.40104],[-104.853259,30.41215],[-104.855016,30.417473],[-104.853621,30.423984],[-104.852432,30.438867],[-104.854655,30.448737],[-104.864473,30.462018],[-104.866695,30.473025],[-104.865248,30.479898],[-104.859615,30.491112],[-104.859254,30.497261],[-104.863026,30.502377],[-104.879718,30.510852],[-104.882146,30.520929],[-104.884007,30.541858],[-104.886591,30.551883],[-104.898166,30.569401],[-104.928242,30.599529],[-104.93434,30.610536],[-104.941057,30.61405],[-104.972735,30.618029],[-104.98276,30.620716],[-104.989117,30.629553],[-104.991649,30.640301],[-104.992941,30.651464],[-104.995783,30.661747],[-105.008495,30.676992],[-105.027977,30.690117],[-105.049475,30.699264],[-105.06813,30.702675],[-105.087043,30.709806],[-105.133604,30.757917],[-105.140425,30.750476],[-105.153241,30.763188],[-105.157788,30.76572],[-105.160889,30.764118],[-105.162698,30.774919],[-105.167297,30.781171],[-105.17479,30.783962],[-105.185125,30.784634],[-105.195306,30.787941],[-105.204917,30.80241],[-105.2121,30.805718],[-105.21825,30.806803],[-105.229257,30.810214],[-105.232874,30.809128],[-105.239851,30.801532],[-105.243985,30.799103],[-105.249049,30.798845],[-105.259488,30.802979],[-105.265844,30.808405],[-105.270753,30.814348],[-105.277006,30.819412],[-105.334315,30.843803],[-105.363771,30.850366],[-105.376173,30.859565],[-105.41555,30.902456],[-105.496682,30.95651],[-105.530789,30.991701],[-105.555955,31.002605],[-105.565722,31.016661],[-105.578641,31.052163],[-105.59094,31.071438],[-105.605875,31.081928],[-105.623496,31.090351],[-105.643805,31.103684],[-105.668042,31.127869],[-105.675225,31.131951],[-105.692278,31.137635],[-105.76819,31.18001],[-105.777854,31.192722],[-105.78483,31.211016],[-105.861983,31.288376],[-105.880147,31.300881],[-105.896994,31.305997],[-105.897149,31.309511],[-105.908802,31.317004],[-105.928052,31.326461],[-105.934563,31.335504],[-105.941772,31.352351],[-105.948515,31.361239],[-105.961202,31.371006],[-106.004739,31.396948],[-106.021147,31.402167],[-106.046261,31.404648],[-106.065304,31.410952],[-106.099385,31.428884],[-106.141113,31.439167],[-106.164316,31.447797],[-106.174471,31.460251],[-106.209249,31.477305],[-106.215424,31.483919],[-106.21744,31.488725],[-106.218731,31.494616],[-106.222891,31.50459],[-106.232658,31.519938],[-106.274749,31.562622],[-106.286298,31.580141],[-106.312292,31.648612],[-106.331309,31.68215],[-106.35849,31.717548],[-106.39177,31.745919],[-106.428951,31.758476],[-106.473135,31.755065],[-106.492797,31.759044],[-106.501425,31.766344],[-106.506052,31.770258],[-106.517189,31.773824],[-106.558969,31.773876],[-106.584859,31.773927],[-106.610697,31.773979],[-106.636587,31.774082],[-106.662477,31.774134],[-106.688315,31.774237],[-106.714205,31.774237],[-106.740095,31.774289],[-106.765933,31.774392],[-106.791823,31.774444],[-106.817713,31.774496],[-106.843603,31.774547],[-106.869441,31.774599],[-106.895331,31.774702],[-106.921221,31.774702],[-106.947111,31.774754],[-106.973001,31.774857],[-106.998891,31.774909],[-107.02478,31.774961],[-107.05067,31.775013],[-107.076509,31.775064],[-107.102398,31.775168],[-107.128288,31.775168],[-107.154127,31.775219],[-107.180016,31.775374],[-107.205906,31.775374],[-107.231796,31.775426],[-107.257634,31.775478],[-107.283524,31.775529],[-107.309414,31.775633],[-107.335252,31.775684],[-107.361142,31.775788],[-107.387032,31.775788],[-107.412896,31.775839],[-107.438786,31.775943],[-107.464676,31.775994],[-107.490566,31.776098],[-107.516404,31.776149],[-107.542294,31.776201],[-107.568184,31.776253],[-107.594074,31.776304],[-107.619964,31.776408],[-107.645854,31.776459],[-107.671744,31.776459],[-107.697633,31.776563],[-107.723472,31.776614],[-107.749362,31.776666],[-107.775251,31.776718],[-107.801141,31.77677],[-107.82698,31.776873],[-107.852869,31.776925],[-107.878759,31.776925],[-107.904598,31.777028],[-107.930487,31.77708],[-107.956377,31.777131],[-107.982216,31.777183],[-108.008105,31.777235],[-108.033995,31.777338],[-108.059885,31.77739],[-108.085723,31.77739],[-108.111613,31.777545],[-108.137503,31.777545],[-108.163341,31.777648],[-108.189283,31.7777],[-108.215121,31.777751],[-108.215121,31.770723],[-108.215121,31.763695],[-108.215121,31.756667],[-108.215121,31.749639],[-108.215121,31.74256],[-108.215121,31.735583],[-108.215121,31.728555],[-108.215121,31.721476],[-108.215121,31.714396],[-108.215121,31.70742],[-108.215121,31.700392],[-108.215121,31.693312],[-108.215121,31.686284],[-108.215121,31.679256],[-108.215121,31.672176],[-108.21507,31.665148],[-108.215018,31.658172],[-108.215018,31.651092],[-108.215018,31.644064],[-108.215018,31.637036],[-108.215018,31.630008],[-108.215018,31.62298],[-108.215018,31.615952],[-108.215018,31.608873],[-108.215018,31.601845],[-108.215018,31.594817],[-108.215018,31.587789],[-108.215018,31.580761],[-108.215018,31.573733],[-108.215018,31.566653],[-108.215018,31.559625],[-108.214966,31.552597],[-108.214966,31.545569],[-108.214966,31.538489],[-108.214966,31.531461],[-108.214966,31.524485],[-108.214966,31.517405],[-108.214966,31.510378],[-108.214966,31.503401],[-108.214966,31.496322],[-108.214966,31.489242],[-108.214966,31.482214],[-108.214966,31.475238],[-108.214966,31.468158],[-108.214966,31.46113],[-108.214966,31.454102],[-108.214966,31.447074],[-108.214915,31.440046],[-108.214863,31.432966],[-108.214863,31.425938],[-108.214863,31.41891],[-108.214863,31.411882],[-108.214863,31.404803],[-108.214863,31.397826],[-108.214863,31.390798],[-108.214863,31.383719],[-108.214863,31.376639],[-108.214863,31.369663],[-108.214863,31.362635],[-108.214863,31.355555],[-108.214863,31.348527],[-108.214863,31.341551],[-108.214863,31.334471],[-108.214811,31.327443],[-108.257573,31.327391],[-108.300336,31.327391],[-108.34302,31.327391],[-108.385731,31.327391],[-108.428442,31.327391],[-108.471152,31.327391],[-108.513837,31.327391],[-108.556547,31.327391],[-108.59931,31.327391],[-108.64202,31.327391],[-108.684757,31.327391],[-108.727467,31.327391],[-108.770178,31.327391],[-108.812914,31.327391],[-108.855625,31.327391],[-108.898335,31.327391],[-108.941046,31.327391],[-108.968282,31.327391],[-108.983731,31.327391],[-109.026493,31.327391],[-109.04743,31.327391],[-109.069203,31.327391],[-109.111914,31.327391],[-109.154599,31.327391],[-109.197361,31.327391],[-109.240072,31.32734],[-109.282782,31.32734],[-109.325519,31.32734],[-109.368229,31.32734],[-109.410914,31.32734],[-109.45365,31.32734],[-109.496387,31.32734],[-109.539071,31.32734],[-109.581808,31.32734],[-109.624493,31.32734],[-109.667177,31.32734],[-109.709965,31.32734],[-109.75265,31.32734],[-109.795335,31.32734],[-109.838123,31.32734],[-109.880808,31.32734],[-109.923596,31.327288],[-109.96628,31.327236],[-110.008965,31.327236],[-110.051702,31.327236],[-110.094386,31.327236],[-110.137071,31.327236],[-110.179807,31.327236],[-110.222544,31.327236],[-110.265229,31.327236],[-110.308017,31.327236],[-110.350753,31.327236],[-110.39349,31.327236],[-110.436174,31.327236],[-110.478859,31.327236],[-110.521595,31.327236],[-110.56428,31.327236],[-110.606965,31.327236],[-110.649727,31.327236],[-110.692438,31.327236],[-110.7352,31.327236],[-110.777885,31.327236],[-110.820595,31.327236],[-110.863358,31.327236],[-110.906068,31.327236],[-110.948753,31.327185],[-111.006269,31.327185],[-111.067118,31.333644],[-111.094455,31.342532],[-111.145924,31.359069],[-111.197446,31.375554],[-111.248864,31.392142],[-111.300333,31.40873],[-111.351803,31.425318],[-111.403299,31.441855],[-111.454768,31.458339],[-111.506238,31.474979],[-111.915464,31.601431],[-112.324715,31.727987],[-112.733967,31.854543],[-113.143218,31.981046],[-113.552444,32.107602],[-113.961696,32.234132],[-114.370921,32.360687],[-114.780147,32.487243],[-114.816785,32.498534],[-114.819373,32.499363],[-114.822108,32.50024],[-114.809447,32.511324],[-114.795546,32.552226],[-114.794203,32.574111],[-114.802678,32.594497],[-114.786813,32.621033],[-114.781542,32.628061],[-114.758804,32.64483],[-114.751156,32.65222],[-114.739477,32.669066],[-114.731209,32.686636],[-114.723871,32.711519],[-114.724284,32.712835],[-114.724285,32.712836],[-114.764541,32.709839],[-114.838076,32.704206],[-114.911612,32.698703],[-114.985199,32.693122],[-115.058734,32.687567],[-115.13227,32.681986],[-115.205806,32.676456],[-115.27929,32.670823],[-115.352851,32.665346],[-115.426386,32.659765],[-115.499922,32.654209],[-115.573535,32.648654],[-115.647019,32.643073],[-115.720529,32.637518],[-115.794064,32.631963],[-115.8676,32.626408],[-115.941213,32.620827],[-116.014748,32.615271],[-116.088232,32.609664],[-116.161742,32.604161],[-116.235329,32.598554],[-116.308891,32.593025],[-116.382426,32.587469],[-116.455962,32.581888],[-116.529472,32.576333],[-116.603007,32.570804],[-116.676543,32.565223],[-116.750104,32.559667],[-116.82364,32.554086],[-116.897201,32.548531],[-116.970737,32.542976],[-117.044221,32.537421],[-117.125121,32.531669],[-117.125969,32.538258],[-117.239623,32.531308],[-120.274098,32.884264],[-121.652736,34.467248],[-124.367265,37.662798],[-126.739806,41.37928],[-126.996297,45.773888],[-124.770704,48.44258],[-123.734053,48.241906],[-123.1663,48.27837],[-123.193018,48.501035],[-123.176987,48.65482],[-122.912481,48.753561],[-122.899122,48.897797],[-122.837671,48.97502],[-122.743986,48.980582],[-122.753,48.992499],[-122.753012,48.992515],[-122.653258,48.992515],[-122.433375,48.992515],[-122.213517,48.992515],[-121.993763,48.992515],[-121.773958,48.992515],[-121.554152,48.992515],[-121.33432,48.992515],[-121.114515,48.992515],[-95.396937,48.99267],[-95.177106,48.99267],[-95.168527,48.995047],[-95.161887,49.001145],[-95.159329,49.01179],[-95.159665,49.10951],[-95.160027,49.223353],[-95.160337,49.313012],[-95.160569,49.369494],[-95.102821,49.35394],[-94.982518,49.356162],[-94.926087,49.345568],[-94.856195,49.318283],[-94.839142,49.308878],[-94.827256,49.292858],[-94.819892,49.252034],[-94.810358,49.229606],[-94.806121,49.210899],[-94.811185,49.166561],[-94.803743,49.146407],[-94.792039,49.12646],[-94.753772,49.026156],[-94.711217,48.914586],[-94.711734,48.862755],[-94.712147,48.842446],[-94.713284,48.823843],[-94.710907,48.807513],[-94.701786,48.790098],[-94.688893,48.778832],[-94.592852,48.726433],[-94.519161,48.70447],[-94.4795,48.700698],[-94.311577,48.713927],[-94.292586,48.711912],[-94.284034,48.709069],[-94.274499,48.704108],[-94.265482,48.697752],[-94.258454,48.690828],[-94.255767,48.683541],[-94.252459,48.662405],[-94.251038,48.65729],[-94.23215,48.652019],[-94.03485,48.643311],[-93.874885,48.636206],[-93.835741,48.617137],[-93.809386,48.543576],[-93.778664,48.519468],[-93.756779,48.516549],[-93.616297,48.531302],[-93.599889,48.526341],[-93.566584,48.538279],[-93.491756,48.542309],[-93.459924,48.557399],[-93.45225,48.572721],[-93.453774,48.586958],[-93.451475,48.597422],[-93.417316,48.604114],[-93.385716,48.614863],[-93.25774,48.630314],[-93.131701,48.62463],[-92.97972,48.61768],[-92.955588,48.612228],[-92.884197,48.579878],[-92.72555,48.548692],[-92.648604,48.536263],[-92.630181,48.519468],[-92.627468,48.502777],[-92.646743,48.497428],[-92.691366,48.489858],[-92.710641,48.482882],[-92.718909,48.459782],[-92.704052,48.445158],[-92.677129,48.441747],[-92.657053,48.438233],[-92.570521,48.446656],[-92.526932,48.445623],[-92.490629,48.433117],[-92.474532,48.410483],[-92.467581,48.394282],[-92.467064,48.353225],[-92.462465,48.329299],[-92.451381,48.312685],[-92.41823,48.282041],[-92.38464,48.232406],[-92.371851,48.222587],[-92.353815,48.222897],[-92.327874,48.229435],[-92.303663,48.239279],[-92.291029,48.249562],[-92.292062,48.270336],[-92.301416,48.290645],[-92.303095,48.310928],[-92.281598,48.33178],[-92.259118,48.339635],[-92.154732,48.350125],[-92.070499,48.346714],[-92.043421,48.334596],[-92.030114,48.313176],[-92.021355,48.287441],[-92.007997,48.262482],[-91.992158,48.247909],[-91.975492,48.236566],[-91.957302,48.228323],[-91.852244,48.195974],[-91.764988,48.187344],[-91.744137,48.179593],[-91.727575,48.168327],[-91.695509,48.13758],[-91.716438,48.112051],[-91.692512,48.097866],[-91.618615,48.089572],[-91.597479,48.090399],[-91.589676,48.088332],[-91.581098,48.080942],[-91.579806,48.070969],[-91.585129,48.06084],[-91.586989,48.052572],[-91.574845,48.048205],[-91.487098,48.053476],[-91.464722,48.048955],[-91.446274,48.040738],[-91.427929,48.036449],[-91.3654,48.057843],[-91.276362,48.064768],[-91.23807,48.082648],[-91.203963,48.107659],[-91.071103,48.170859],[-91.02816,48.184838],[-91.008109,48.194372],[-90.923153,48.227109],[-90.873802,48.234344],[-90.840678,48.220107],[-90.837939,48.210547],[-90.848843,48.198713],[-90.849721,48.189566],[-90.843003,48.176983],[-90.83427,48.171789],[-90.823883,48.168327],[-90.812307,48.160989],[-90.803057,48.147166],[-90.796701,48.117064],[-90.786469,48.10045],[-90.750347,48.083991],[-90.701307,48.08456],[-90.611079,48.103499],[-90.586843,48.104817],[-90.573872,48.097892],[-90.562194,48.088849],[-90.542014,48.083733],[-90.531601,48.08456],[-90.501887,48.094275],[-90.490493,48.096239],[-90.483465,48.094482],[-90.477858,48.091536],[-90.470623,48.089882],[-90.178625,48.116444],[-90.120386,48.115359],[-90.073257,48.101199],[-90.061036,48.091019],[-90.008222,48.029731],[-89.995329,48.018595],[-89.980317,48.010094],[-89.92045,47.98746],[-89.902441,47.985909],[-89.803454,48.013763],[-89.780975,48.017199],[-89.763302,48.017303],[-89.745964,48.013763],[-89.724596,48.005908],[-89.712788,48.003376],[-89.678656,48.008699],[-89.65659,48.007975],[-89.593105,47.996503],[-89.581753,47.996333],[-89.586724,47.992938],[-89.310872,47.981097],[-89.072861,48.046842],[-88.49789,48.212841],[-88.286621,48.156675],[-85.939935,47.280501],[-84.784644,46.770068],[-84.516909,46.435083],[-84.489712,46.446652],[-84.491052,46.457658],[-84.478301,46.466467],[-84.465408,46.478172],[-84.448096,46.489722],[-84.42324,46.511581],[-84.389702,46.520262],[-84.352469,46.522743],[-84.30534,46.501607],[-84.242011,46.526464],[-84.197285,46.546359],[-84.147676,46.541346],[-84.110443,46.526464],[-84.158812,46.433343],[-84.147676,46.399882],[-84.129046,46.375026],[-84.10543,46.347741],[-84.105944,46.346374],[-84.117195,46.347157],[-84.117489,46.338326],[-84.122361,46.331922],[-84.112061,46.287102],[-84.092672,46.227469],[-84.111983,46.20337],[-84.015118,46.149712],[-83.957038,46.045736],[-83.676821,46.15388],[-83.429449,46.086221],[-83.523049,45.892052],[-83.574563,45.890259],[-82.551615,44.857931],[-82.655591,43.968545],[-82.440632,43.096285],[-82.460131,43.084392],[-82.458894,43.083247],[-82.431813,43.039387],[-82.424748,43.02408],[-82.417242,43.01731],[-82.416369,43.01742],[-82.416412,43.017143],[-82.414603,42.983243],[-82.430442,42.951307],[-82.453179,42.918983],[-82.464781,42.883637],[-82.468036,42.863974],[-82.482325,42.835113],[-82.485271,42.818524],[-82.473618,42.798164],[-82.470982,42.790568],[-82.471344,42.779845],[-82.476951,42.761474],[-82.48341,42.719254],[-82.511264,42.646675],[-82.526224,42.619906],[-82.549246,42.590941],[-82.575833,42.571795],[-82.608467,42.561098],[-82.644331,42.557817],[-82.644698,42.557533],[-82.644932,42.561634],[-82.637132,42.568405],[-82.60902,42.579296],[-82.616673,42.582828],[-82.636985,42.599607],[-82.625357,42.616092],[-82.629331,42.626394],[-82.638751,42.633459],[-82.644344,42.640524],[-82.644166,42.641056],[-82.716083,42.617461],[-82.777592,42.408506],[-82.888693,42.406093],[-82.889991,42.403266],[-82.905739,42.387665],[-82.923842,42.374419],[-82.937972,42.366176],[-82.947686,42.363527],[-82.979624,42.359406],[-83.042618,42.340861],[-83.061899,42.32732],[-83.081622,42.30907],[-83.11342,42.279619],[-83.145306,42.066968],[-83.177398,41.960666],[-83.21512,41.794493],[-82.219051,41.516445],[-80.345329,42.13344],[-80.316455,42.123137],[-79.270266,42.591872],[-79.221058,42.582892],[-78.871842,42.860012],[-78.875011,42.867184],[-78.896205,42.897209],[-78.901651,42.908101],[-78.90901,42.952255],[-78.913426,42.957848],[-78.932118,42.9708],[-78.936386,42.979631],[-78.927997,43.002003],[-78.893114,43.029379],[-78.887963,43.051456],[-78.914897,43.076477],[-79.026167,43.086485],[-79.065231,43.10573],[-79.065273,43.105897],[-79.065738,43.120237],[-79.061423,43.130288],[-79.055583,43.138427],[-79.051604,43.146851],[-79.04933,43.159847],[-79.048607,43.170622],[-79.053775,43.260358],[-79.058425,43.277799],[-79.058631,43.2782],[-78.990696,43.286947],[-78.862059,43.324332],[-78.767813,43.336418],[-78.516117,43.50645],[-76.363317,43.943219],[-76.396746,44.106667],[-76.364697,44.111631],[-76.366146,44.117349],[-76.357462,44.131478],[-76.183493,44.223025],[-76.162644,44.229888],[-76.176117,44.30795],[-76.046414,44.354817],[-75.928746,44.391137],[-75.852508,44.381639],[-75.849095,44.386103],[-75.847623,44.392579],[-75.84674,44.398172],[-75.845415,44.40141],[-75.780803,44.432318],[-75.770205,44.446153],[-75.772266,44.463815],[-75.779184,44.48236],[-75.791496,44.496513],[-75.791183,44.496768],[-75.754622,44.527567],[-75.69969,44.581673],[-75.578199,44.661513],[-75.455958,44.741766],[-75.341831,44.816749],[-75.270233,44.863774],[-75.129647,44.925166],[-75.075594,44.935501],[-75.058721,44.941031],[-75.0149,44.96599],[-74.998647,44.972398],[-74.940201,44.987746],[-74.903744,45.005213],[-74.88651,45.009398],[-74.868474,45.010122],[-74.741557,44.998857],[-74.712961,44.999254],[-74.695875,44.99803],[-74.596114,44.998495],[-74.496352,44.999012],[-74.197146,45.000458],[-71.703551,45.012757],[-71.603816,45.013274],[-71.505848,45.013731],[-71.50408,45.013739],[-71.506613,45.037045],[-71.504752,45.052962],[-71.497259,45.066553],[-71.45659,45.110994],[-71.451215,45.121691],[-71.445996,45.140295],[-71.441604,45.150682],[-71.413026,45.186184],[-71.406567,45.204942],[-71.42269,45.217189],[-71.449045,45.226905],[-71.438813,45.233468],[-71.394888,45.241529],[-71.381245,45.250779],[-71.3521,45.278323],[-71.334323,45.28871],[-71.311534,45.294136],[-71.293396,45.292327],[-71.20937,45.254758],[-71.185133,45.248557],[-71.160329,45.245767],[-71.141725,45.252329],[-71.111029,45.287108],[-71.095242,45.300905],[-71.085553,45.304213],[-71.084952,45.304293],[-71.064211,45.307055],[-71.054418,45.310362],[-71.036667,45.323385],[-71.027598,45.33465],[-71.016539,45.343125],[-70.993155,45.347827],[-70.968118,45.34452],[-70.951608,45.332014],[-70.906908,45.246232],[-70.892412,45.234604],[-70.874351,45.245663],[-70.870605,45.255275],[-70.872491,45.274189],[-70.870243,45.283129],[-70.862621,45.290363],[-70.842389,45.301215],[-70.835258,45.309794],[-70.83208,45.328552],[-70.835465,45.373097],[-70.833837,45.393096],[-70.825982,45.410459],[-70.812986,45.42343],[-70.794873,45.430406],[-70.771877,45.430045],[-70.75255,45.422345],[-70.718004,45.397282],[-70.696739,45.388652],[-70.675785,45.388704],[-70.65359,45.395473],[-70.641316,45.408496],[-70.650257,45.427461],[-70.668162,45.439036],[-70.707385,45.4564],[-70.722836,45.470921],[-70.732009,45.491591],[-70.730329,45.507973],[-70.686792,45.572723],[-70.589614,45.651788],[-70.572406,45.662279],[-70.514735,45.681709],[-70.484763,45.699641],[-70.4728,45.703568],[-70.450424,45.703723],[-70.439132,45.705893],[-70.419315,45.716901],[-70.407351,45.731525],[-70.402442,45.749663],[-70.403941,45.771161],[-70.408282,45.781651],[-70.413682,45.787697],[-70.41717,45.793795],[-70.415232,45.804389],[-70.409935,45.810745],[-70.389807,45.825059],[-70.312654,45.867641],[-70.283173,45.890482],[-70.262528,45.923038],[-70.255939,45.948876],[-70.263148,45.956834],[-70.280434,45.959315],[-70.303947,45.968616],[-70.316298,45.982982],[-70.316892,45.999002],[-70.306143,46.035331],[-70.303637,46.038483],[-70.294309,46.044943],[-70.29201,46.048663],[-70.293017,46.054038],[-70.296092,46.057862],[-70.300795,46.061737],[-70.304774,46.065975],[-70.311362,46.071866],[-70.312629,46.079566],[-70.30033,46.089281],[-70.26444,46.106593],[-70.24948,46.120597],[-70.244002,46.141009],[-70.249247,46.162765],[-70.263329,46.183229],[-70.284801,46.191859],[-70.280899,46.211857],[-70.253407,46.251493],[-70.236173,46.288339],[-70.223693,46.300793],[-70.201886,46.305495],[-70.199509,46.315262],[-70.197028,46.336863],[-70.188398,46.358412],[-70.167418,46.368179],[-70.153052,46.372829],[-70.074323,46.419545],[-70.061817,46.445409],[-70.050086,46.511271],[-70.032723,46.609766],[-70.023628,46.661287],[-70.007763,46.704075],[-69.989961,46.721697],[-69.899708,46.811562],[-69.809403,46.901299],[-69.719099,46.991086],[-69.628794,47.080797],[-69.538464,47.17061],[-69.448159,47.260346],[-69.357906,47.350134],[-69.267628,47.439844],[-69.25091,47.452919],[-69.237268,47.45881],[-69.221972,47.459688],[-69.069655,47.431886],[-69.054023,47.418399],[-69.054333,47.389253],[-69.066193,47.32967],[-69.065134,47.296339],[-69.06356,47.290809],[-69.057486,47.269467],[-69.0402,47.249055],[-68.906229,47.190221],[-68.889718,47.190609],[-68.761819,47.23704],[-68.71779,47.245231],[-68.668801,47.243422],[-68.644203,47.245283],[-68.6256,47.255205],[-68.607926,47.269829],[-68.58524,47.28249],[-68.539662,47.299853],[-68.518009,47.304762],[-68.492016,47.307553],[-68.466746,47.305692],[-68.435327,47.291275],[-68.422563,47.293109],[-68.410212,47.297424],[-68.385614,47.301713],[-68.383392,47.307139],[-68.384839,47.315873],[-68.382049,47.32781],[-68.347839,47.358506],[-68.299728,47.367833],[-68.24645,47.360573],[-68.197047,47.341401],[-68.184335,47.333133],[-68.156068,47.306674],[-68.145061,47.301455],[-68.115398,47.292282],[-68.101446,47.286185],[-68.039382,47.245231],[-67.993184,47.223217],[-67.962436,47.197689],[-67.953703,47.18663],[-67.949982,47.172936],[-67.943419,47.164538],[-67.899132,47.138778],[-67.870607,47.107358],[-67.854742,47.09785],[-67.813556,47.081908],[-67.808699,47.075138],[-67.805185,47.035631],[-67.802549,46.901247],[-67.800017,46.766785],[-67.797433,46.632297],[-67.794849,46.497861],[-67.792317,46.363476],[-67.789733,46.229014],[-67.78715,46.094552],[-67.784566,45.960142],[-67.782757,45.95053],[-67.776556,45.942933],[-67.767461,45.935957],[-67.759658,45.928567],[-67.757849,45.919472],[-67.769425,45.903969],[-67.787356,45.890017],[-67.799242,45.875651],[-67.792627,45.858907],[-67.776091,45.840821],[-67.772835,45.828057],[-67.779863,45.815706],[-67.794126,45.799169],[-67.80627,45.781754],[-67.811127,45.76651],[-67.810816,45.762414],[-67.817811,45.754896],[-67.821785,45.740767],[-67.827673,45.739001],[-67.868884,45.744593],[-67.856815,45.723694],[-67.835768,45.703971],[-67.793821,45.676301],[-67.733034,45.651869],[-67.723173,45.645393],[-67.711546,45.642155],[-67.697564,45.64922],[-67.66695,45.620077],[-67.649435,45.611247],[-67.603073,45.605948],[-67.561862,45.596234],[-67.54052,45.593879],[-67.442056,45.603593],[-67.440939,45.604586],[-67.431306,45.597941],[-67.422107,45.568796],[-67.42619,45.533449],[-67.443036,45.522184],[-67.467531,45.508283],[-67.493214,45.493142],[-67.48231,45.455521],[-67.428825,45.38705],[-67.434561,45.350308],[-67.459056,45.318424],[-67.468668,45.301835],[-67.475024,45.282353],[-67.471303,45.266282],[-67.427585,45.236568],[-67.390533,45.193108],[-67.356272,45.165926],[-67.31922,45.153886],[-67.284648,45.169699],[-67.279584,45.179052],[-67.279222,45.187372],[-67.277207,45.195072],[-67.267336,45.202513],[-67.254986,45.205045],[-67.242428,45.202565],[-67.219071,45.192126],[-67.206166,45.189401],[-67.176015,45.178656],[-67.191274,45.180365],[-67.204376,45.178209],[-67.204724,45.177791],[-67.152423,45.148932],[-67.048033,45.043407],[-66.962727,45.047088],[-66.857192,44.968696],[-66.897268,44.817275],[-67.2159,44.593511],[-67.122366,44.423624],[-67.68447,44.192544],[-67.459678,40.781645],[-76.607854,32.495823],[-76.798479,32.713735],[-78.561892,29.037718],[-78.892446,29.039659],[-79.762295,26.719312],[-80.026352,24.932961],[-82.368794,23.994833],[-83.806281,29.068506],[-87.460772,29.089961],[-87.922646,28.666131],[-90.461001,28.246758],[-91.787336,29.11536],[-93.311871,29.12431],[-96.423449,26.057857],[-97.129057,25.991017],[-97.129509,25.966833],[-97.139358,25.965876],[-97.202171,25.960893],[-97.202176,25.960857],[-97.204941,25.960639],[-97.253051,25.963481],[-97.266358,25.960639],[-97.2692,25.944361],[-97.287649,25.928651],[-97.310981,25.922088],[-97.328447,25.933302],[-97.351107,25.918419],[-97.355112,25.912786],[-97.35227,25.894493],[-97.345165,25.871704],[-97.345733,25.852222],[-97.36599,25.843902],[-97.376015,25.846744],[-97.380124,25.853203],[-97.383121,25.860541],[-97.389891,25.865657],[-97.397823,25.865812],[-97.399476,25.861162],[-97.39989,25.855115],[-97.404179,25.851395],[-97.425418,25.854857],[-97.435727,25.869275],[-97.441309,25.884933],[-97.448259,25.892322],[-97.469421,25.892943],[-97.486319,25.895733],[-97.502209,25.901883],[-97.52027,25.912786],[-97.565177,25.954748],[-97.594322,25.966375],[-97.604787,25.979966],[-97.613055,25.995985],[-97.622641,26.00906],[-97.641451,26.022495],[-97.659874,26.03066],[-97.679614,26.034639],[-97.766948,26.039652],[-97.780306,26.043218],[-97.782321,26.058617],[-97.80201,26.063733],[-97.878181,26.063733],[-97.941666,26.056809],[-97.999233,26.064302],[-98.013057,26.063682],[-98.044166,26.048799],[-98.065457,26.042184],[-98.075146,26.046628],[-98.083311,26.070916],[-98.103103,26.074947],[-98.150232,26.063682],[-98.185062,26.065232],[-98.222656,26.075412],[-98.300429,26.111431],[-98.309809,26.121094],[-98.333037,26.15303],[-98.339264,26.159851],[-98.365774,26.160161],[-98.377272,26.163572],[-98.377272,26.173649],[-98.36934,26.19401],[-98.397193,26.201141],[-98.428845,26.217729],[-98.456544,26.225946],[-98.472383,26.207652],[-98.49295,26.230596],[-98.521527,26.240932],[-98.552791,26.248321],[-98.581627,26.262274],[-98.640564,26.24181],[-98.653663,26.244291],[-98.664696,26.250647],[-98.685289,26.268475],[-98.693325,26.270542],[-98.702239,26.271628],[-98.704255,26.27664],[-98.691465,26.290231],[-98.701413,26.299119],[-98.713169,26.303357],[-98.726217,26.30439],[-98.739911,26.303253],[-98.735932,26.320048],[-98.746397,26.332141],[-98.780839,26.351674],[-98.795851,26.368314],[-98.801329,26.372138],[-98.810295,26.372448],[-98.817323,26.368521],[-98.825023,26.366454],[-98.836081,26.372138],[-98.842334,26.365834],[-98.850835,26.364077],[-98.860524,26.366299],[-98.870214,26.372138],[-98.893029,26.367849],[-98.9299,26.39224],[-98.945377,26.378288],[-98.954136,26.393946],[-98.962844,26.399527],[-98.986951,26.400095],[-99.004056,26.393842],[-99.010515,26.392602],[-99.016432,26.394462],[-99.022995,26.403351],[-99.027878,26.406245],[-99.047645,26.406968],[-99.066351,26.404746],[-99.085498,26.40764],[-99.106427,26.423039],[-99.108907,26.434253],[-99.102525,26.446966],[-99.09374,26.459781],[-99.089373,26.47115],[-99.091492,26.484018],[-99.10299,26.512078],[-99.115108,26.525617],[-99.140946,26.531405],[-99.164873,26.540448],[-99.17128,26.563961],[-99.171548,26.56583],[-99.213953,26.568537],[-99.242801,26.579723],[-99.254575,26.6018],[-99.258844,26.614752],[-99.277683,26.638007],[-99.281951,26.649781],[-99.277389,26.657729],[-99.26635,26.653314],[-99.252662,26.644483],[-99.240299,26.639184],[-99.244861,26.652431],[-99.240299,26.697763],[-99.242507,26.713658],[-99.252368,26.743683],[-99.254575,26.75899],[-99.252368,26.799024],[-99.254575,26.810504],[-99.257666,26.813153],[-99.262229,26.814036],[-99.266497,26.817863],[-99.268263,26.827872],[-99.271649,26.832876],[-99.289458,26.84465],[-99.308444,26.830521],[-99.316539,26.822279],[-99.323457,26.810504],[-99.328166,26.797258],[-99.329197,26.789016],[-99.331699,26.78254],[-99.340383,26.77312],[-99.366728,26.761345],[-99.380269,26.777241],[-99.391896,26.796963],[-99.412207,26.796963],[-99.410883,26.808149],[-99.405437,26.818452],[-99.396606,26.824928],[-99.384979,26.824928],[-99.377178,26.816686],[-99.374823,26.804028],[-99.374234,26.791076],[-99.371291,26.783128],[-99.360694,26.780479],[-99.359369,26.790487],[-99.36452,26.810504],[-99.357897,26.822279],[-99.351274,26.83111],[-99.346123,26.840824],[-99.344062,26.855247],[-99.348772,26.899696],[-99.355101,26.920302],[-99.36452,26.934726],[-99.403377,26.952093],[-99.413974,26.964162],[-99.401758,26.985651],[-99.399991,26.999192],[-99.418831,27.007728],[-99.441938,27.013615],[-99.453271,27.019797],[-99.455332,27.025979],[-99.464751,27.039225],[-99.466959,27.047467],[-99.462544,27.057181],[-99.461635,27.056839],[-99.461728,27.056954],[-99.442039,27.089614],[-99.439404,27.098347],[-99.441419,27.107494],[-99.445734,27.114728],[-99.450178,27.120465],[-99.452452,27.125012],[-99.450333,27.145166],[-99.435786,27.188419],[-99.431988,27.207591],[-99.434029,27.22697],[-99.440902,27.244798],[-99.451832,27.26118],[-99.46612,27.276527],[-99.468963,27.278233],[-99.480409,27.283297],[-99.482941,27.286708],[-99.484879,27.294821],[-99.486584,27.297611],[-99.493199,27.30128],[-99.521362,27.311254],[-99.5148,27.321796],[-99.497591,27.338798],[-99.494026,27.348203],[-99.492889,27.358848],[-99.487721,27.37187],[-99.484621,27.391766],[-99.475706,27.414762],[-99.472916,27.426647],[-99.473639,27.463803],[-99.472916,27.468299],[-99.47643,27.48251],[-99.480409,27.490778],[-99.48829,27.494654],[-99.503689,27.495584],[-99.509503,27.500028],[-99.510071,27.510518],[-99.507074,27.533437],[-99.507203,27.57377],[-99.515006,27.588601],[-99.535031,27.604828],[-99.55503,27.613509],[-99.572264,27.61847],[-99.578232,27.622811],[-99.590247,27.642061],[-99.600169,27.646427],[-99.612442,27.643637],[-99.633526,27.633069],[-99.644869,27.632733],[-99.648642,27.636919],[-99.658693,27.654024],[-99.664739,27.659398],[-99.70037,27.659191],[-99.705692,27.66317],[-99.710674,27.670116],[-99.723056,27.687381],[-99.730652,27.691825],[-99.734037,27.702031],[-99.736311,27.713607],[-99.740445,27.722159],[-99.747344,27.726009],[-99.765198,27.731177],[-99.774577,27.735828],[-99.78685,27.748488],[-99.795428,27.761924],[-99.806963,27.771423],[-99.808167,27.772414],[-99.83292,27.776755],[-99.832971,27.782181],[-99.844779,27.793576],[-99.858241,27.803524],[-99.863357,27.804661],[-99.864727,27.814324],[-99.861858,27.83608],[-99.863357,27.845666],[-99.870928,27.854477],[-99.880204,27.859231],[-99.888007,27.864812],[-99.891288,27.876026],[-99.882684,27.89158],[-99.878808,27.901838],[-99.88134,27.906463],[-99.896766,27.912923],[-99.914336,27.928245],[-99.929916,27.946331],[-99.939683,27.961085],[-99.928289,27.975761],[-99.940717,27.983254],[-99.961852,27.987492],[-99.976606,27.992453],[-99.991127,28.007801],[-100.000584,28.02041],[-100.007457,28.033561],[-100.014123,28.050459],[-100.013503,28.056971],[-100.010506,28.063611],[-100.010196,28.068882],[-100.017585,28.070949],[-100.031538,28.081801],[-100.045077,28.095289],[-100.048023,28.102523],[-100.048901,28.115959],[-100.056498,28.137922],[-100.074895,28.154407],[-100.172873,28.198538],[-100.189203,28.201329],[-100.197626,28.207168],[-100.201192,28.220346],[-100.202949,28.234428],[-100.205946,28.242877],[-100.212819,28.245073],[-100.240724,28.249698],[-100.257932,28.260524],[-100.275089,28.277242],[-100.284339,28.296517],[-100.277931,28.314888],[-100.278551,28.331088],[-100.293899,28.353413],[-100.322631,28.386899],[-100.331675,28.422013],[-100.336326,28.458574],[-100.340201,28.464259],[-100.348315,28.470253],[-100.355549,28.478185],[-100.35679,28.489322],[-100.351622,28.496711],[-100.322631,28.510406],[-100.364024,28.524797],[-100.38423,28.537174],[-100.397769,28.557586],[-100.398751,28.568645],[-100.397097,28.592726],[-100.401438,28.60226],[-100.411463,28.609314],[-100.434821,28.619133],[-100.44619,28.626497],[-100.444898,28.643782],[-100.481381,28.686054],[-100.493939,28.708378],[-100.519054,28.804961],[-100.524996,28.814831],[-100.529285,28.819947],[-100.534453,28.830231],[-100.538639,28.835631],[-100.54515,28.83899],[-100.559671,28.839378],[-100.566234,28.842504],[-100.569696,28.84961],[-100.56334,28.86209],[-100.566234,28.869789],[-100.571763,28.8732],[-100.586543,28.879789],[-100.58954,28.883458],[-100.594966,28.899322],[-100.606955,28.910123],[-100.618841,28.917926],[-100.624318,28.924721],[-100.624783,28.93777],[-100.626696,28.948338],[-100.630778,28.956683],[-100.637909,28.962884],[-100.628918,28.98433],[-100.632793,29.005156],[-100.652224,29.044817],[-100.660854,29.102669],[-100.668967,29.116208],[-100.678165,29.119412],[-100.690826,29.121014],[-100.70204,29.12365],[-100.706846,29.130187],[-100.70974,29.135561],[-100.762501,29.173776],[-100.770098,29.187289],[-100.762088,29.208658],[-100.783172,29.243074],[-100.796143,29.257673],[-100.81609,29.270773],[-100.86389,29.290616],[-100.871797,29.296456],[-100.891227,29.318547],[-100.91474,29.337048],[-100.987397,29.366322],[-100.998301,29.372472],[-101.008068,29.380585],[-101.016232,29.390068],[-101.022175,29.40048],[-101.025948,29.414356],[-101.029617,29.442984],[-101.037782,29.460063],[-101.039026,29.460452],[-101.040188,29.457132],[-101.045487,29.451245],[-101.060205,29.449184],[-101.067711,29.45095],[-101.076101,29.453894],[-101.085962,29.454483],[-101.098031,29.449184],[-101.113043,29.466552],[-101.142774,29.475383],[-101.174124,29.475971],[-101.193699,29.469495],[-101.198703,29.473911],[-101.198851,29.476854],[-101.184132,29.497754],[-101.184868,29.512767],[-101.195171,29.521892],[-101.214157,29.518065],[-101.245213,29.493044],[-101.265818,29.487157],[-101.290545,29.49746],[-101.297315,29.503936],[-101.300995,29.512767],[-101.294372,29.520715],[-101.273177,29.524247],[-101.259195,29.533372],[-101.243888,29.554861],[-101.231966,29.580176],[-101.227845,29.599899],[-101.239178,29.616677],[-101.26052,29.613439],[-101.281272,29.597249],[-101.290545,29.575761],[-101.295255,29.570168],[-101.306146,29.574583],[-101.317626,29.584003],[-101.323955,29.592539],[-101.323661,29.603137],[-101.318804,29.616383],[-101.311445,29.628158],[-101.303497,29.634045],[-101.303669,29.631411],[-101.302727,29.633851],[-101.301073,29.649509],[-101.30978,29.654548],[-101.336239,29.654315],[-101.349029,29.660103],[-101.357684,29.667441],[-101.364351,29.676665],[-101.376624,29.700643],[-101.383368,29.718497],[-101.39962,29.740718],[-101.406545,29.752888],[-101.409309,29.765781],[-101.405098,29.778442],[-101.414012,29.774411],[-101.424218,29.771414],[-101.435096,29.770122],[-101.446103,29.771052],[-101.455689,29.77591],[-101.462433,29.788932],[-101.470908,29.791516],[-101.490286,29.785547],[-101.505763,29.773894],[-101.521809,29.765936],[-101.542893,29.771052],[-101.539689,29.779191],[-101.530516,29.796477],[-101.528604,29.801438],[-101.531912,29.811101],[-101.539172,29.817974],[-101.546458,29.820145],[-101.549766,29.815701],[-101.553977,29.796684],[-101.564907,29.786478],[-101.580281,29.781568],[-101.632216,29.775651],[-101.794531,29.795857],[-101.80298,29.801438],[-101.805978,29.811928],[-101.812695,29.812032],[-101.82409,29.805184],[-101.857602,29.805184],[-101.877524,29.810843],[-101.88742,29.81229],[-101.895455,29.808621],[-101.90238,29.803247],[-101.910881,29.799888],[-101.920157,29.798182],[-101.929613,29.797717],[-101.942662,29.803608],[-101.957054,29.814047],[-101.972246,29.818181],[-101.98793,29.805184],[-102.014595,29.810998],[-102.109344,29.80211],[-102.145647,29.815701],[-102.157248,29.824537],[-102.203679,29.846138],[-102.239775,29.849135],[-102.253444,29.855285],[-102.258276,29.873475],[-102.276181,29.869547],[-102.289023,29.878126],[-102.302175,29.889391],[-102.321011,29.893939],[-102.330235,29.888926],[-102.339769,29.870633],[-102.351061,29.866602],[-102.36323,29.864276],[-102.370723,29.857765],[-102.374547,29.848102],[-102.376589,29.821488],[-102.380051,29.811386],[-102.404132,29.780793],[-102.406096,29.777279],[-102.515288,29.784721],[-102.523066,29.782318],[-102.531127,29.769915],[-102.54154,29.762474],[-102.543349,29.760123],[-102.546578,29.757875],[-102.553141,29.756738],[-102.558309,29.759089],[-102.562882,29.769347],[-102.566758,29.771052],[-102.58531,29.764696],[-102.621225,29.747281],[-102.638743,29.743715],[-102.676054,29.74449],[-102.683469,29.743715],[-102.69104,29.736817],[-102.693624,29.729401],[-102.694709,29.720616],[-102.697758,29.709557],[-102.726748,29.664495],[-102.73127,29.650594],[-102.735507,29.649509],[-102.751656,29.622457],[-102.75176,29.620157],[-102.761346,29.603414],[-102.767598,29.59729],[-102.779665,29.592303],[-102.774084,29.579617],[-102.776461,29.575948],[-102.785892,29.571814],[-102.78075,29.558249],[-102.786512,29.550497],[-102.795478,29.54427],[-102.827311,29.470502],[-102.833951,29.461355],[-102.839067,29.45195],[-102.841134,29.438308],[-102.838705,29.426939],[-102.834984,29.415699],[-102.835191,29.403839],[-102.844545,29.390533],[-102.845578,29.384719],[-102.838033,29.370534],[-102.837672,29.366322],[-102.84656,29.361749],[-102.853872,29.361],[-102.859867,29.361155],[-102.864957,29.359527],[-102.876972,29.350871],[-102.883069,29.343766],[-102.885188,29.333379],[-102.885498,29.314801],[-102.899399,29.276095],[-102.899709,29.2639],[-102.892139,29.254391],[-102.867954,29.240387],[-102.858781,29.229147],[-102.869866,29.224781],[-102.896893,29.220285],[-102.942265,29.190209],[-102.947536,29.182018],[-102.969757,29.192845],[-102.988386,29.177135],[-103.015826,29.126776],[-103.024275,29.116157],[-103.032621,29.110214],[-103.072541,29.091404],[-103.080758,29.085203],[-103.085589,29.07572],[-103.091532,29.057866],[-103.095356,29.060294],[-103.104684,29.057866],[-103.109205,29.023372],[-103.122771,28.996474],[-103.147989,28.985105],[-103.187108,28.990221],[-103.241756,29.003502],[-103.301545,29.002365],[-103.316247,29.010065],[-103.311514,29.026043],[-103.309994,29.031175],[-103.3248,29.026808],[-103.330484,29.023733],[-103.342602,29.041226],[-103.351671,29.039417],[-103.360534,29.029831],[-103.372083,29.023733],[-103.38663,29.028798],[-103.414639,29.052414],[-103.423605,29.057866],[-103.435697,29.061121],[-103.478537,29.08205],[-103.529748,29.126776],[-103.535588,29.135122],[-103.538223,29.142408],[-103.541711,29.148816],[-103.550238,29.154656],[-103.558015,29.156206],[-103.58499,29.154656],[-103.673125,29.173569],[-103.702477,29.187858],[-103.749476,29.222972],[-103.759062,29.226848],[-103.770767,29.229845],[-103.777718,29.235297],[-103.769424,29.257543],[-103.774229,29.267517],[-103.78366,29.274803],[-103.794177,29.277594],[-103.837038,29.279906]]],[[[178.301106,52.056551],[179.595462,52.142083],[179.825447,51.992849],[179.661729,51.485763],[179.723231,51.459963],[179.408066,51.209841],[178.411463,51.523605],[177.698335,51.877899],[177.16784,51.581866],[176.487008,52.175325],[174.484678,52.08716],[172.866263,52.207379],[172.825506,52.716846],[172.747012,52.654022],[172.08261,52.952695],[172.942925,53.183013],[173.029416,52.993628],[173.127208,52.99494],[173.143321,52.990383],[173.175059,52.971747],[173.182932,52.968373],[176.45233,52.628178],[176.468135,52.488358],[177.900385,52.488358],[178.007601,52.179677],[178.301106,52.056551]]],[[[-168.899607,65.747626],[-168.909861,65.739569],[-168.926218,65.739895],[-168.942128,65.74372],[-168.951731,65.75316],[-168.942983,65.764716],[-168.920115,65.768866],[-168.907908,65.768297],[-168.902781,65.761542],[-168.899607,65.747626]]],[[[-131.160718,54.787192],[-132.853508,54.482536],[-134.77719,54.717786],[-142.6966,55.845503],[-142.861997,49.948308],[-155.675916,51.109976],[-164.492732,50.603976],[-164.691217,50.997975],[-171.246993,49.948308],[-171.215436,50.576636],[-173.341669,50.968826],[-173.362022,51.082198],[-177.799603,51.272899],[-179.155463,50.982285],[-179.476076,52.072632],[-177.11459,52.248701],[-177.146284,52.789384],[-174.777218,52.443779],[-174.773743,52.685853],[-173.653194,52.704099],[-173.790528,53.469081],[-171.063371,53.604473],[-170.777733,59.291898],[-174.324884,60.332184],[-171.736408,62.68026],[-172.315705,62.725352],[-171.995091,63.999658],[-168.501424,65.565173],[-168.714145,65.546708],[-168.853077,68.370871],[-161.115601,72.416214],[-146.132257,70.607941],[-140.692512,69.955349],[-141.145395,69.671641],[-141.015207,69.654202],[-141.006459,69.651272],[-141.005564,69.650946],[-141.005549,69.650941],[-141.005471,69.505164],[-141.001208,60.466879],[-141.001156,60.321074],[-140.994929,60.304382],[-140.979555,60.295804],[-140.909146,60.28366],[-140.768457,60.259269],[-140.660505,60.24051],[-140.533743,60.218548],[-140.518705,60.22387],[-140.506664,60.236324],[-140.475323,60.276477],[-140.462791,60.289138],[-140.447805,60.29446],[-140.424111,60.293168],[-140.32497,60.267537],[-140.169243,60.227229],[-140.01579,60.187387],[-139.967757,60.188369],[-139.916933,60.207851],[-139.826318,60.256478],[-139.728417,60.309033],[-139.679816,60.32681],[-139.628346,60.334096],[-139.517965,60.336732],[-139.413992,60.339212],[-139.262193,60.342778],[-139.101608,60.346602],[-139.079465,60.341021],[-139.06869,60.322056],[-139.073186,60.299835],[-139.113468,60.226816],[-139.149615,60.161187],[-139.183231,60.100157],[-139.182146,60.073389],[-139.112305,60.031376],[-139.060207,60.000059],[-139.051611,59.994892],[-139.003759,59.977219],[-138.842425,59.937686],[-138.742586,59.913192],[-138.704888,59.898464],[-138.697188,59.89371],[-138.692098,59.886888],[-138.654349,59.805498],[-138.63745,59.784052],[-138.59921,59.753822],[-138.488881,59.696357],[-138.363617,59.631142],[-138.219543,59.556004],[-138.067614,59.476991],[-137.91057,59.395187],[-137.758305,59.315915],[-137.611363,59.239331],[-137.594181,59.225275],[-137.582088,59.206568],[-137.5493,59.134531],[-137.521007,59.072364],[-137.484394,58.991904],[-137.507752,58.939969],[-137.50876,58.914906],[-137.486875,58.900075],[-137.453466,58.899145],[-137.423106,58.907723],[-137.338098,58.955472],[-137.2819,58.98715],[-137.172346,59.027148],[-137.062367,59.067572],[-137.047109,59.07331],[-136.942282,59.11107],[-136.840816,59.148174],[-136.785496,59.157217],[-136.671911,59.150809],[-136.613491,59.15422],[-136.569489,59.172152],[-136.484791,59.2538],[-136.483551,59.257469],[-136.466549,59.287803],[-136.467092,59.38449],[-136.467557,59.461643],[-136.415958,59.452238],[-136.36684,59.449551],[-136.319995,59.459059],[-136.275036,59.486448],[-136.244728,59.528202],[-136.258474,59.556107],[-136.29935,59.575745],[-136.350329,59.592384],[-136.2585,59.621582],[-136.145406,59.636826],[-136.02686,59.652846],[-135.923818,59.666747],[-135.830955,59.693257],[-135.641251,59.747362],[-135.482759,59.792475],[-135.465137,59.789685],[-135.404392,59.753305],[-135.345791,59.731032],[-135.259879,59.698218],[-135.221897,59.675273],[-135.192028,59.64711],[-135.157792,59.623287],[-135.106684,59.613158],[-135.087874,59.606544],[-135.032942,59.573109],[-135.018524,59.559363],[-135.016198,59.543447],[-135.01948,59.493166],[-135.023252,59.477146],[-135.037489,59.461591],[-135.078598,59.438337],[-135.095754,59.418855],[-134.993254,59.381906],[-135.00483,59.367127],[-135.014441,59.35152],[-135.016198,59.336173],[-134.979973,59.297415],[-134.95783,59.280982],[-134.932431,59.270647],[-134.839465,59.258141],[-134.74345,59.245119],[-134.70552,59.240106],[-134.692084,59.235249],[-134.68286,59.223001],[-134.671439,59.193752],[-134.66038,59.181298],[-134.610771,59.144556],[-134.582788,59.128847],[-134.556717,59.123059],[-134.509072,59.122801],[-134.477575,59.114946],[-134.451013,59.097893],[-134.398019,59.051952],[-134.387167,59.036863],[-134.385591,59.018828],[-134.399389,58.974954],[-134.343423,58.968857],[-134.329651,58.963017],[-134.320039,58.952682],[-134.32314,58.949168],[-134.330323,58.945344],[-134.333036,58.93413],[-134.327403,58.916457],[-134.316939,58.903796],[-134.22219,58.842714],[-134.108838,58.808246],[-133.983109,58.769902],[-133.87123,58.735899],[-133.831129,58.718019],[-133.796402,58.693421],[-133.700077,58.59937],[-133.626283,58.546402],[-133.547063,58.505577],[-133.463089,58.462221],[-133.392241,58.403878],[-133.43012,58.372097],[-133.41503,58.330549],[-133.374567,58.290965],[-133.257262,58.210298],[-133.165588,58.147305],[-133.142127,58.120588],[-133.094843,58.0331],[-133.075154,58.007882],[-132.99335,57.941917],[-132.917153,57.880499],[-132.83212,57.791564],[-132.70944,57.663303],[-132.629057,57.579277],[-132.552447,57.499075],[-132.455735,57.420992],[-132.362304,57.3457],[-132.304684,57.280355],[-132.230994,57.19682],[-132.276366,57.14889],[-132.34122,57.080393],[-132.16229,57.050317],[-132.031859,57.028406],[-132.107384,56.858753],[-131.871558,56.79346],[-131.865874,56.785708],[-131.872411,56.77297],[-131.882617,56.759146],[-131.887966,56.747958],[-131.886028,56.737055],[-131.880705,56.728838],[-131.864789,56.71349],[-131.838976,56.682278],[-131.830424,56.664759],[-131.826574,56.644606],[-131.832103,56.603368],[-131.825592,56.593343],[-131.799108,56.587658],[-131.692293,56.585074],[-131.585891,56.595048],[-131.560363,56.594066],[-131.536437,56.585229],[-131.491659,56.560166],[-131.345699,56.503271],[-131.215604,56.45255],[-131.100546,56.407669],[-131.016934,56.38705],[-130.839089,56.372452],[-130.760334,56.345192],[-130.645768,56.261942],[-130.602256,56.247059],[-130.495518,56.232434],[-130.47229,56.22489],[-130.458053,56.210653],[-130.427926,56.143964],[-130.418159,56.129702],[-130.403974,56.121898],[-130.290311,56.10097],[-130.243156,56.092391],[-130.211246,56.089962],[-130.116756,56.105646],[-130.094328,56.101486],[-130.071539,56.084123],[-130.039319,56.045521],[-130.026632,56.024101],[-130.01901,56.002216],[-130.014695,55.963252],[-130.016788,55.918913],[-130.019612,55.907978],[-130.019618,55.907952],[-130.022817,55.901353],[-130.049387,55.871405],[-130.104726,55.825263],[-130.136627,55.806464],[-130.148834,55.795356],[-130.163482,55.771145],[-130.167307,55.766262],[-130.170806,55.759833],[-130.173655,55.749498],[-130.170806,55.740953],[-130.163808,55.734565],[-130.160064,55.727118],[-130.167388,55.715399],[-130.155914,55.700141],[-130.142893,55.689521],[-130.131825,55.676581],[-130.126454,55.653998],[-130.12857,55.63642],[-130.135121,55.619127],[-130.153147,55.58511],[-130.148671,55.578192],[-130.146881,55.569322],[-130.146962,55.547187],[-130.112172,55.509345],[-130.101674,55.481147],[-130.095082,55.472113],[-130.065419,55.446112],[-130.057525,55.434882],[-130.052561,55.414008],[-130.054311,55.366645],[-130.05012,55.345445],[-130.039296,55.330756],[-129.989247,55.284003],[-130.031239,55.26435],[-130.050038,55.252875],[-130.067494,55.239],[-130.078236,55.233791],[-130.100494,55.230292],[-130.104726,55.225653],[-130.105702,55.211127],[-130.10912,55.200751],[-130.115793,55.191596],[-130.126454,55.180976],[-130.151967,55.163275],[-130.159983,55.153713],[-130.167592,55.129584],[-130.173695,55.117743],[-130.200266,55.104153],[-130.211781,55.084133],[-130.228871,55.04385],[-130.238678,55.03441],[-130.261342,55.022895],[-130.269846,55.016547],[-130.275706,55.006985],[-130.286366,54.983222],[-130.294342,54.971869],[-130.326568,54.952094],[-130.335561,54.938707],[-130.365387,54.907294],[-130.385243,54.896552],[-130.430816,54.881252],[-130.488759,54.844184],[-130.580312,54.806383],[-130.597485,54.803391],[-130.71074,54.733215],[-131.160718,54.787192]]]]}}]},"featureIcons":{"circle-stroked-24":{"x":0,"y":0,"width":24,"height":24},"circle-stroked-18":{"x":24,"y":0,"width":18,"height":18},"circle-stroked-12":{"x":42,"y":0,"width":12,"height":12},"circle-24":{"x":54,"y":0,"width":24,"height":24},"circle-18":{"x":78,"y":0,"width":18,"height":18},"circle-12":{"x":96,"y":0,"width":12,"height":12},"square-stroked-24":{"x":108,"y":0,"width":24,"height":24},"square-stroked-18":{"x":132,"y":0,"width":18,"height":18},"square-stroked-12":{"x":150,"y":0,"width":12,"height":12},"square-24":{"x":162,"y":0,"width":24,"height":24},"square-18":{"x":186,"y":0,"width":18,"height":18},"square-12":{"x":204,"y":0,"width":12,"height":12},"triangle-stroked-24":{"x":216,"y":0,"width":24,"height":24},"triangle-stroked-18":{"x":240,"y":0,"width":18,"height":18},"triangle-stroked-12":{"x":258,"y":0,"width":12,"height":12},"triangle-24":{"x":0,"y":24,"width":24,"height":24},"triangle-18":{"x":24,"y":24,"width":18,"height":18},"triangle-12":{"x":42,"y":24,"width":12,"height":12},"star-stroked-24":{"x":54,"y":24,"width":24,"height":24},"star-stroked-18":{"x":78,"y":24,"width":18,"height":18},"star-stroked-12":{"x":96,"y":24,"width":12,"height":12},"star-24":{"x":108,"y":24,"width":24,"height":24},"star-18":{"x":132,"y":24,"width":18,"height":18},"star-12":{"x":150,"y":24,"width":12,"height":12},"cross-24":{"x":162,"y":24,"width":24,"height":24},"cross-18":{"x":186,"y":24,"width":18,"height":18},"cross-12":{"x":204,"y":24,"width":12,"height":12},"marker-stroked-24":{"x":216,"y":24,"width":24,"height":24},"marker-stroked-18":{"x":240,"y":24,"width":18,"height":18},"marker-stroked-12":{"x":258,"y":24,"width":12,"height":12},"marker-24":{"x":0,"y":48,"width":24,"height":24},"marker-18":{"x":24,"y":48,"width":18,"height":18},"marker-12":{"x":42,"y":48,"width":12,"height":12},"religious-jewish-24":{"x":54,"y":48,"width":24,"height":24},"religious-jewish-18":{"x":78,"y":48,"width":18,"height":18},"religious-jewish-12":{"x":96,"y":48,"width":12,"height":12},"religious-christian-24":{"x":108,"y":48,"width":24,"height":24},"religious-christian-18":{"x":132,"y":48,"width":18,"height":18},"religious-christian-12":{"x":150,"y":48,"width":12,"height":12},"religious-muslim-24":{"x":162,"y":48,"width":24,"height":24},"religious-muslim-18":{"x":186,"y":48,"width":18,"height":18},"religious-muslim-12":{"x":204,"y":48,"width":12,"height":12},"cemetery-24":{"x":216,"y":48,"width":24,"height":24},"cemetery-18":{"x":240,"y":48,"width":18,"height":18},"cemetery-12":{"x":258,"y":48,"width":12,"height":12},"rocket-24":{"x":0,"y":72,"width":24,"height":24},"rocket-18":{"x":24,"y":72,"width":18,"height":18},"rocket-12":{"x":42,"y":72,"width":12,"height":12},"airport-24":{"x":54,"y":72,"width":24,"height":24},"airport-18":{"x":78,"y":72,"width":18,"height":18},"airport-12":{"x":96,"y":72,"width":12,"height":12},"heliport-24":{"x":108,"y":72,"width":24,"height":24},"heliport-18":{"x":132,"y":72,"width":18,"height":18},"heliport-12":{"x":150,"y":72,"width":12,"height":12},"rail-24":{"x":162,"y":72,"width":24,"height":24},"rail-18":{"x":186,"y":72,"width":18,"height":18},"rail-12":{"x":204,"y":72,"width":12,"height":12},"rail-metro-24":{"x":216,"y":72,"width":24,"height":24},"rail-metro-18":{"x":240,"y":72,"width":18,"height":18},"rail-metro-12":{"x":258,"y":72,"width":12,"height":12},"rail-light-24":{"x":0,"y":96,"width":24,"height":24},"rail-light-18":{"x":24,"y":96,"width":18,"height":18},"rail-light-12":{"x":42,"y":96,"width":12,"height":12},"bus-24":{"x":54,"y":96,"width":24,"height":24},"bus-18":{"x":78,"y":96,"width":18,"height":18},"bus-12":{"x":96,"y":96,"width":12,"height":12},"fuel-24":{"x":108,"y":96,"width":24,"height":24},"fuel-18":{"x":132,"y":96,"width":18,"height":18},"fuel-12":{"x":150,"y":96,"width":12,"height":12},"parking-24":{"x":162,"y":96,"width":24,"height":24},"parking-18":{"x":186,"y":96,"width":18,"height":18},"parking-12":{"x":204,"y":96,"width":12,"height":12},"parking-garage-24":{"x":216,"y":96,"width":24,"height":24},"parking-garage-18":{"x":240,"y":96,"width":18,"height":18},"parking-garage-12":{"x":258,"y":96,"width":12,"height":12},"airfield-24":{"x":0,"y":120,"width":24,"height":24},"airfield-18":{"x":24,"y":120,"width":18,"height":18},"airfield-12":{"x":42,"y":120,"width":12,"height":12},"roadblock-24":{"x":54,"y":120,"width":24,"height":24},"roadblock-18":{"x":78,"y":120,"width":18,"height":18},"roadblock-12":{"x":96,"y":120,"width":12,"height":12},"ferry-24":{"x":108,"y":120,"width":24,"height":24},"ferry-18":{"x":132,"y":120,"width":18,"height":18},"ferry-12":{"x":150,"y":120,"width":12,"height":12},"harbor-24":{"x":162,"y":120,"width":24,"height":24},"harbor-18":{"x":186,"y":120,"width":18,"height":18},"harbor-12":{"x":204,"y":120,"width":12,"height":12},"bicycle-24":{"x":216,"y":120,"width":24,"height":24},"bicycle-18":{"x":240,"y":120,"width":18,"height":18},"bicycle-12":{"x":258,"y":120,"width":12,"height":12},"park-24":{"x":0,"y":144,"width":24,"height":24},"park-18":{"x":24,"y":144,"width":18,"height":18},"park-12":{"x":42,"y":144,"width":12,"height":12},"park2-24":{"x":54,"y":144,"width":24,"height":24},"park2-18":{"x":78,"y":144,"width":18,"height":18},"park2-12":{"x":96,"y":144,"width":12,"height":12},"museum-24":{"x":108,"y":144,"width":24,"height":24},"museum-18":{"x":132,"y":144,"width":18,"height":18},"museum-12":{"x":150,"y":144,"width":12,"height":12},"lodging-24":{"x":162,"y":144,"width":24,"height":24},"lodging-18":{"x":186,"y":144,"width":18,"height":18},"lodging-12":{"x":204,"y":144,"width":12,"height":12},"monument-24":{"x":216,"y":144,"width":24,"height":24},"monument-18":{"x":240,"y":144,"width":18,"height":18},"monument-12":{"x":258,"y":144,"width":12,"height":12},"zoo-24":{"x":0,"y":168,"width":24,"height":24},"zoo-18":{"x":24,"y":168,"width":18,"height":18},"zoo-12":{"x":42,"y":168,"width":12,"height":12},"garden-24":{"x":54,"y":168,"width":24,"height":24},"garden-18":{"x":78,"y":168,"width":18,"height":18},"garden-12":{"x":96,"y":168,"width":12,"height":12},"campsite-24":{"x":108,"y":168,"width":24,"height":24},"campsite-18":{"x":132,"y":168,"width":18,"height":18},"campsite-12":{"x":150,"y":168,"width":12,"height":12},"theatre-24":{"x":162,"y":168,"width":24,"height":24},"theatre-18":{"x":186,"y":168,"width":18,"height":18},"theatre-12":{"x":204,"y":168,"width":12,"height":12},"art-gallery-24":{"x":216,"y":168,"width":24,"height":24},"art-gallery-18":{"x":240,"y":168,"width":18,"height":18},"art-gallery-12":{"x":258,"y":168,"width":12,"height":12},"pitch-24":{"x":0,"y":192,"width":24,"height":24},"pitch-18":{"x":24,"y":192,"width":18,"height":18},"pitch-12":{"x":42,"y":192,"width":12,"height":12},"soccer-24":{"x":54,"y":192,"width":24,"height":24},"soccer-18":{"x":78,"y":192,"width":18,"height":18},"soccer-12":{"x":96,"y":192,"width":12,"height":12},"america-football-24":{"x":108,"y":192,"width":24,"height":24},"america-football-18":{"x":132,"y":192,"width":18,"height":18},"america-football-12":{"x":150,"y":192,"width":12,"height":12},"tennis-24":{"x":162,"y":192,"width":24,"height":24},"tennis-18":{"x":186,"y":192,"width":18,"height":18},"tennis-12":{"x":204,"y":192,"width":12,"height":12},"basketball-24":{"x":216,"y":192,"width":24,"height":24},"basketball-18":{"x":240,"y":192,"width":18,"height":18},"basketball-12":{"x":258,"y":192,"width":12,"height":12},"baseball-24":{"x":0,"y":216,"width":24,"height":24},"baseball-18":{"x":24,"y":216,"width":18,"height":18},"baseball-12":{"x":42,"y":216,"width":12,"height":12},"golf-24":{"x":54,"y":216,"width":24,"height":24},"golf-18":{"x":78,"y":216,"width":18,"height":18},"golf-12":{"x":96,"y":216,"width":12,"height":12},"swimming-24":{"x":108,"y":216,"width":24,"height":24},"swimming-18":{"x":132,"y":216,"width":18,"height":18},"swimming-12":{"x":150,"y":216,"width":12,"height":12},"cricket-24":{"x":162,"y":216,"width":24,"height":24},"cricket-18":{"x":186,"y":216,"width":18,"height":18},"cricket-12":{"x":204,"y":216,"width":12,"height":12},"skiing-24":{"x":216,"y":216,"width":24,"height":24},"skiing-18":{"x":240,"y":216,"width":18,"height":18},"skiing-12":{"x":258,"y":216,"width":12,"height":12},"school-24":{"x":0,"y":240,"width":24,"height":24},"school-18":{"x":24,"y":240,"width":18,"height":18},"school-12":{"x":42,"y":240,"width":12,"height":12},"college-24":{"x":54,"y":240,"width":24,"height":24},"college-18":{"x":78,"y":240,"width":18,"height":18},"college-12":{"x":96,"y":240,"width":12,"height":12},"library-24":{"x":108,"y":240,"width":24,"height":24},"library-18":{"x":132,"y":240,"width":18,"height":18},"library-12":{"x":150,"y":240,"width":12,"height":12},"post-24":{"x":162,"y":240,"width":24,"height":24},"post-18":{"x":186,"y":240,"width":18,"height":18},"post-12":{"x":204,"y":240,"width":12,"height":12},"fire-station-24":{"x":216,"y":240,"width":24,"height":24},"fire-station-18":{"x":240,"y":240,"width":18,"height":18},"fire-station-12":{"x":258,"y":240,"width":12,"height":12},"town-hall-24":{"x":0,"y":264,"width":24,"height":24},"town-hall-18":{"x":24,"y":264,"width":18,"height":18},"town-hall-12":{"x":42,"y":264,"width":12,"height":12},"police-24":{"x":54,"y":264,"width":24,"height":24},"police-18":{"x":78,"y":264,"width":18,"height":18},"police-12":{"x":96,"y":264,"width":12,"height":12},"prison-24":{"x":108,"y":264,"width":24,"height":24},"prison-18":{"x":132,"y":264,"width":18,"height":18},"prison-12":{"x":150,"y":264,"width":12,"height":12},"embassy-24":{"x":162,"y":264,"width":24,"height":24},"embassy-18":{"x":186,"y":264,"width":18,"height":18},"embassy-12":{"x":204,"y":264,"width":12,"height":12},"beer-24":{"x":216,"y":264,"width":24,"height":24},"beer-18":{"x":240,"y":264,"width":18,"height":18},"beer-12":{"x":258,"y":264,"width":12,"height":12},"restaurant-24":{"x":0,"y":288,"width":24,"height":24},"restaurant-18":{"x":24,"y":288,"width":18,"height":18},"restaurant-12":{"x":42,"y":288,"width":12,"height":12},"cafe-24":{"x":54,"y":288,"width":24,"height":24},"cafe-18":{"x":78,"y":288,"width":18,"height":18},"cafe-12":{"x":96,"y":288,"width":12,"height":12},"shop-24":{"x":108,"y":288,"width":24,"height":24},"shop-18":{"x":132,"y":288,"width":18,"height":18},"shop-12":{"x":150,"y":288,"width":12,"height":12},"fast-food-24":{"x":162,"y":288,"width":24,"height":24},"fast-food-18":{"x":186,"y":288,"width":18,"height":18},"fast-food-12":{"x":204,"y":288,"width":12,"height":12},"bar-24":{"x":216,"y":288,"width":24,"height":24},"bar-18":{"x":240,"y":288,"width":18,"height":18},"bar-12":{"x":258,"y":288,"width":12,"height":12},"bank-24":{"x":0,"y":312,"width":24,"height":24},"bank-18":{"x":24,"y":312,"width":18,"height":18},"bank-12":{"x":42,"y":312,"width":12,"height":12},"grocery-24":{"x":54,"y":312,"width":24,"height":24},"grocery-18":{"x":78,"y":312,"width":18,"height":18},"grocery-12":{"x":96,"y":312,"width":12,"height":12},"cinema-24":{"x":108,"y":312,"width":24,"height":24},"cinema-18":{"x":132,"y":312,"width":18,"height":18},"cinema-12":{"x":150,"y":312,"width":12,"height":12},"pharmacy-24":{"x":162,"y":312,"width":24,"height":24},"pharmacy-18":{"x":186,"y":312,"width":18,"height":18},"pharmacy-12":{"x":204,"y":312,"width":12,"height":12},"hospital-24":{"x":216,"y":312,"width":24,"height":24},"hospital-18":{"x":240,"y":312,"width":18,"height":18},"hospital-12":{"x":258,"y":312,"width":12,"height":12},"danger-24":{"x":0,"y":336,"width":24,"height":24},"danger-18":{"x":24,"y":336,"width":18,"height":18},"danger-12":{"x":42,"y":336,"width":12,"height":12},"industrial-24":{"x":54,"y":336,"width":24,"height":24},"industrial-18":{"x":78,"y":336,"width":18,"height":18},"industrial-12":{"x":96,"y":336,"width":12,"height":12},"warehouse-24":{"x":108,"y":336,"width":24,"height":24},"warehouse-18":{"x":132,"y":336,"width":18,"height":18},"warehouse-12":{"x":150,"y":336,"width":12,"height":12},"commercial-24":{"x":162,"y":336,"width":24,"height":24},"commercial-18":{"x":186,"y":336,"width":18,"height":18},"commercial-12":{"x":204,"y":336,"width":12,"height":12},"building-24":{"x":216,"y":336,"width":24,"height":24},"building-18":{"x":240,"y":336,"width":18,"height":18},"building-12":{"x":258,"y":336,"width":12,"height":12},"place-of-worship-24":{"x":0,"y":360,"width":24,"height":24},"place-of-worship-18":{"x":24,"y":360,"width":18,"height":18},"place-of-worship-12":{"x":42,"y":360,"width":12,"height":12},"alcohol-shop-24":{"x":54,"y":360,"width":24,"height":24},"alcohol-shop-18":{"x":78,"y":360,"width":18,"height":18},"alcohol-shop-12":{"x":96,"y":360,"width":12,"height":12},"logging-24":{"x":108,"y":360,"width":24,"height":24},"logging-18":{"x":132,"y":360,"width":18,"height":18},"logging-12":{"x":150,"y":360,"width":12,"height":12},"oil-well-24":{"x":162,"y":360,"width":24,"height":24},"oil-well-18":{"x":186,"y":360,"width":18,"height":18},"oil-well-12":{"x":204,"y":360,"width":12,"height":12},"slaughterhouse-24":{"x":216,"y":360,"width":24,"height":24},"slaughterhouse-18":{"x":240,"y":360,"width":18,"height":18},"slaughterhouse-12":{"x":258,"y":360,"width":12,"height":12},"dam-24":{"x":0,"y":384,"width":24,"height":24},"dam-18":{"x":24,"y":384,"width":18,"height":18},"dam-12":{"x":42,"y":384,"width":12,"height":12},"water-24":{"x":54,"y":384,"width":24,"height":24},"water-18":{"x":78,"y":384,"width":18,"height":18},"water-12":{"x":96,"y":384,"width":12,"height":12},"wetland-24":{"x":108,"y":384,"width":24,"height":24},"wetland-18":{"x":132,"y":384,"width":18,"height":18},"wetland-12":{"x":150,"y":384,"width":12,"height":12},"disability-24":{"x":162,"y":384,"width":24,"height":24},"disability-18":{"x":186,"y":384,"width":18,"height":18},"disability-12":{"x":204,"y":384,"width":12,"height":12},"telephone-24":{"x":216,"y":384,"width":24,"height":24},"telephone-18":{"x":240,"y":384,"width":18,"height":18},"telephone-12":{"x":258,"y":384,"width":12,"height":12},"emergency-telephone-24":{"x":0,"y":408,"width":24,"height":24},"emergency-telephone-18":{"x":24,"y":408,"width":18,"height":18},"emergency-telephone-12":{"x":42,"y":408,"width":12,"height":12},"toilets-24":{"x":54,"y":408,"width":24,"height":24},"toilets-18":{"x":78,"y":408,"width":18,"height":18},"toilets-12":{"x":96,"y":408,"width":12,"height":12},"waste-basket-24":{"x":108,"y":408,"width":24,"height":24},"waste-basket-18":{"x":132,"y":408,"width":18,"height":18},"waste-basket-12":{"x":150,"y":408,"width":12,"height":12},"music-24":{"x":162,"y":408,"width":24,"height":24},"music-18":{"x":186,"y":408,"width":18,"height":18},"music-12":{"x":204,"y":408,"width":12,"height":12},"land-use-24":{"x":216,"y":408,"width":24,"height":24},"land-use-18":{"x":240,"y":408,"width":18,"height":18},"land-use-12":{"x":258,"y":408,"width":12,"height":12},"city-24":{"x":0,"y":432,"width":24,"height":24},"city-18":{"x":24,"y":432,"width":18,"height":18},"city-12":{"x":42,"y":432,"width":12,"height":12},"town-24":{"x":54,"y":432,"width":24,"height":24},"town-18":{"x":78,"y":432,"width":18,"height":18},"town-12":{"x":96,"y":432,"width":12,"height":12},"village-24":{"x":108,"y":432,"width":24,"height":24},"village-18":{"x":132,"y":432,"width":18,"height":18},"village-12":{"x":150,"y":432,"width":12,"height":12},"farm-24":{"x":162,"y":432,"width":24,"height":24},"farm-18":{"x":186,"y":432,"width":18,"height":18},"farm-12":{"x":204,"y":432,"width":12,"height":12},"bakery-24":{"x":216,"y":432,"width":24,"height":24},"bakery-18":{"x":240,"y":432,"width":18,"height":18},"bakery-12":{"x":258,"y":432,"width":12,"height":12},"dog-park-24":{"x":0,"y":456,"width":24,"height":24},"dog-park-18":{"x":24,"y":456,"width":18,"height":18},"dog-park-12":{"x":42,"y":456,"width":12,"height":12},"lighthouse-24":{"x":54,"y":456,"width":24,"height":24},"lighthouse-18":{"x":78,"y":456,"width":18,"height":18},"lighthouse-12":{"x":96,"y":456,"width":12,"height":12},"clothing-store-24":{"x":108,"y":456,"width":24,"height":24},"clothing-store-18":{"x":132,"y":456,"width":18,"height":18},"clothing-store-12":{"x":150,"y":456,"width":12,"height":12},"polling-place-24":{"x":162,"y":456,"width":24,"height":24},"polling-place-18":{"x":186,"y":456,"width":18,"height":18},"polling-place-12":{"x":204,"y":456,"width":12,"height":12},"playground-24":{"x":216,"y":456,"width":24,"height":24},"playground-18":{"x":240,"y":456,"width":18,"height":18},"playground-12":{"x":258,"y":456,"width":12,"height":12},"entrance-24":{"x":0,"y":480,"width":24,"height":24},"entrance-18":{"x":24,"y":480,"width":18,"height":18},"entrance-12":{"x":42,"y":480,"width":12,"height":12},"heart-24":{"x":54,"y":480,"width":24,"height":24},"heart-18":{"x":78,"y":480,"width":18,"height":18},"heart-12":{"x":96,"y":480,"width":12,"height":12},"london-underground-24":{"x":108,"y":480,"width":24,"height":24},"london-underground-18":{"x":132,"y":480,"width":18,"height":18},"london-underground-12":{"x":150,"y":480,"width":12,"height":12},"minefield-24":{"x":162,"y":480,"width":24,"height":24},"minefield-18":{"x":186,"y":480,"width":18,"height":18},"minefield-12":{"x":204,"y":480,"width":12,"height":12},"rail-underground-24":{"x":216,"y":480,"width":24,"height":24},"rail-underground-18":{"x":240,"y":480,"width":18,"height":18},"rail-underground-12":{"x":258,"y":480,"width":12,"height":12},"rail-above-24":{"x":0,"y":504,"width":24,"height":24},"rail-above-18":{"x":24,"y":504,"width":18,"height":18},"rail-above-12":{"x":42,"y":504,"width":12,"height":12},"camera-24":{"x":54,"y":504,"width":24,"height":24},"camera-18":{"x":78,"y":504,"width":18,"height":18},"camera-12":{"x":96,"y":504,"width":12,"height":12},"laundry-24":{"x":108,"y":504,"width":24,"height":24},"laundry-18":{"x":132,"y":504,"width":18,"height":18},"laundry-12":{"x":150,"y":504,"width":12,"height":12},"car-24":{"x":162,"y":504,"width":24,"height":24},"car-18":{"x":186,"y":504,"width":18,"height":18},"car-12":{"x":204,"y":504,"width":12,"height":12},"suitcase-24":{"x":216,"y":504,"width":24,"height":24},"suitcase-18":{"x":240,"y":504,"width":18,"height":18},"suitcase-12":{"x":258,"y":504,"width":12,"height":12},"hairdresser-24":{"x":0,"y":528,"width":24,"height":24},"hairdresser-18":{"x":24,"y":528,"width":18,"height":18},"hairdresser-12":{"x":42,"y":528,"width":12,"height":12},"chemist-24":{"x":54,"y":528,"width":24,"height":24},"chemist-18":{"x":78,"y":528,"width":18,"height":18},"chemist-12":{"x":96,"y":528,"width":12,"height":12},"mobilephone-24":{"x":108,"y":528,"width":24,"height":24},"mobilephone-18":{"x":132,"y":528,"width":18,"height":18},"mobilephone-12":{"x":150,"y":528,"width":12,"height":12},"scooter-24":{"x":162,"y":528,"width":24,"height":24},"scooter-18":{"x":186,"y":528,"width":18,"height":18},"scooter-12":{"x":204,"y":528,"width":12,"height":12},"gift-24":{"x":216,"y":528,"width":24,"height":24},"gift-18":{"x":240,"y":528,"width":18,"height":18},"gift-12":{"x":258,"y":528,"width":12,"height":12},"ice-cream-24":{"x":0,"y":552,"width":24,"height":24},"ice-cream-18":{"x":24,"y":552,"width":18,"height":18},"ice-cream-12":{"x":42,"y":552,"width":12,"height":12}},"locales":["af","sq","ar","ar-AA","hy","ast","bn","bs","bg-BG","ca","zh","zh-CN","zh-HK","zh-TW","yue","hr","cs","da","nl","en-GB","eo","et","fi","fr","gl","de","el","hi","hu","is","id","it","ja","kn","ko","lv","lij","lt","no","fa","pl","pt","pt-BR","ro","ru","sc","sr","si","sk","sl","es","sv","tl","ta","te","th","tr","uk","vi"],"en":{"modes":{"add_area":{"title":"Area","description":"Add parks, buildings, lakes or other areas to the map.","tail":"Click on the map to start drawing an area, like a park, lake, or building."},"add_line":{"title":"Line","description":"Add highways, streets, pedestrian paths, canals or other lines to the map.","tail":"Click on the map to start drawing a road, path, or route."},"add_point":{"title":"Point","description":"Add restaurants, monuments, postal boxes or other points to the map.","tail":"Click on the map to add a point."},"browse":{"title":"Browse","description":"Pan and zoom the map."},"draw_area":{"tail":"Click to add nodes to your area. Click the first node to finish the area."},"draw_line":{"tail":"Click to add more nodes to the line. Click on other lines to connect to them, and double-click to end the line."}},"operations":{"add":{"annotation":{"point":"Added a point.","vertex":"Added a node to a way.","relation":"Added a relation."}},"start":{"annotation":{"line":"Started a line.","area":"Started an area."}},"continue":{"key":"A","title":"Continue","description":"Continue this line.","not_eligible":"No line can be continued here.","multiple":"Several lines can be continued here. To choose a line, press the Shift key and click on it to select it.","annotation":{"line":"Continued a line.","area":"Continued an area."}},"cancel_draw":{"annotation":"Canceled drawing."},"change_role":{"annotation":"Changed the role of a relation member."},"change_tags":{"annotation":"Changed tags."},"circularize":{"title":"Circularize","description":{"line":"Make this line circular.","area":"Make this area circular."},"key":"O","annotation":{"line":"Made a line circular.","area":"Made an area circular."},"not_closed":"This can't be made circular because it's not a loop.","too_large":"This can't be made circular because not enough of it is currently visible.","connected_to_hidden":"This can't be made circular because it is connected to a hidden feature."},"orthogonalize":{"title":"Square","description":{"line":"Square the corners of this line.","area":"Square the corners of this area."},"key":"S","annotation":{"line":"Squared the corners of a line.","area":"Squared the corners of an area."},"not_squarish":"This can't be made square because it is not squarish.","too_large":"This can't be made square because not enough of it is currently visible.","connected_to_hidden":"This can't be made square because it is connected to a hidden feature."},"straighten":{"title":"Straighten","description":"Straighten this line.","key":"S","annotation":"Straightened a line.","too_bendy":"This can't be straightened because it bends too much.","connected_to_hidden":"This line can't be straightened because it is connected to a hidden feature."},"delete":{"title":"Delete","description":"Delete object permanently.","annotation":{"point":"Deleted a point.","vertex":"Deleted a node from a way.","line":"Deleted a line.","area":"Deleted an area.","relation":"Deleted a relation.","multiple":"Deleted {n} objects."},"incomplete_relation":"This feature can't be deleted because it hasn't been fully downloaded.","part_of_relation":"This feature can't be deleted because it's part of a larger relation. You must remove it from the relation first.","connected_to_hidden":"This can't be deleted because it is connected to a hidden feature."},"add_member":{"annotation":"Added a member to a relation."},"delete_member":{"annotation":"Removed a member from a relation."},"connect":{"annotation":{"point":"Connected a way to a point.","vertex":"Connected a way to another.","line":"Connected a way to a line.","area":"Connected a way to an area."}},"disconnect":{"title":"Disconnect","description":"Disconnect these lines/areas from each other.","key":"D","annotation":"Disconnected lines/areas.","not_connected":"There aren't enough lines/areas here to disconnect.","connected_to_hidden":"This can't be disconnected because it is connected to a hidden feature.","relation":"This can't be disconnected because it connects members of a relation."},"merge":{"title":"Merge","description":"Merge these features.","key":"C","annotation":"Merged {n} features.","not_eligible":"These features can't be merged.","not_adjacent":"These features can't be merged because they aren't connected.","restriction":"These features can't be merged because at least one is a member of a \"{relation}\" relation.","incomplete_relation":"These features can't be merged because at least one hasn't been fully downloaded.","conflicting_tags":"These features can't be merged because some of their tags have conflicting values."},"move":{"title":"Move","description":"Move this to a different location.","key":"M","annotation":{"point":"Moved a point.","vertex":"Moved a node in a way.","line":"Moved a line.","area":"Moved an area.","multiple":"Moved multiple objects."},"incomplete_relation":"This feature can't be moved because it hasn't been fully downloaded.","too_large":"This can't be moved because not enough of it is currently visible.","connected_to_hidden":"This can't be moved because it is connected to a hidden feature."},"rotate":{"title":"Rotate","description":"Rotate this object around its center point.","key":"R","annotation":{"line":"Rotated a line.","area":"Rotated an area."},"too_large":"This can't be rotated because not enough of it is currently visible.","connected_to_hidden":"This can't be rotated because it is connected to a hidden feature."},"reverse":{"title":"Reverse","description":"Make this line go in the opposite direction.","key":"V","annotation":"Reversed a line."},"split":{"title":"Split","description":{"line":"Split this line into two at this node.","area":"Split the boundary of this area into two.","multiple":"Split the lines/area boundaries at this node into two."},"key":"X","annotation":{"line":"Split a line.","area":"Split an area boundary.","multiple":"Split {n} lines/area boundaries."},"not_eligible":"Lines can't be split at their beginning or end.","multiple_ways":"There are too many lines here to split.","connected_to_hidden":"This can't be split because it is connected to a hidden feature."},"restriction":{"help":{"select":"Click to select a road segment.","toggle":"Click to toggle turn restrictions.","toggle_on":"Click to add a \"{restriction}\" restriction.","toggle_off":"Click to remove the \"{restriction}\" restriction."},"annotation":{"create":"Added a turn restriction","delete":"Deleted a turn restriction"}}},"undo":{"tooltip":"Undo: {action}","nothing":"Nothing to undo."},"redo":{"tooltip":"Redo: {action}","nothing":"Nothing to redo."},"tooltip_keyhint":"Shortcut:","browser_notice":"This editor is supported in Firefox, Chrome, Safari, Opera, and Internet Explorer 11 and above. Please upgrade your browser or use Potlatch 2 to edit the map.","translate":{"translate":"Translate","localized_translation_label":"Multilingual name","localized_translation_language":"Choose language","localized_translation_name":"Name"},"zoom_in_edit":"Zoom in to Edit","logout":"logout","loading_auth":"Connecting to OpenStreetMap...","report_a_bug":"Report a bug","help_translate":"Help translate","feature_info":{"hidden_warning":"{count} hidden features","hidden_details":"These features are currently hidden: {details}"},"status":{"error":"Unable to connect to API.","offline":"The API is offline. Please try editing later.","readonly":"The API is read-only. You will need to wait to save your changes."},"commit":{"title":"Save Changes","description_placeholder":"Brief description of your contributions","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"},"none":"None","node":"Node","way":"Way","relation":"Relation","location":"Location","add_fields":"Add field:"},"background":{"title":"Background","description":"Background settings","percent_brightness":"{opacity}% brightness","none":"None","best_imagery":"Best known imagery source for this location","custom":"Custom","custom_button":"Edit custom background","custom_prompt":"Enter a tile URL template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme.","fix_misalignment":"Adjust imagery offset","imagery_source_faq":"Where does this imagery come from?","reset":"reset","offset":"Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters.","minimap":{"description":"Minimap","tooltip":"Show a zoomed out map to help locate the area currently displayed."}},"map_data":{"title":"Map Data","description":"Map Data","data_layers":"Data Layers","fill_area":"Fill Areas","map_features":"Map Features","autohidden":"These features have been automatically hidden because too many would be shown on the screen.  You can zoom in to edit them."},"feature":{"points":{"description":"Points","tooltip":"Points of Interest"},"traffic_roads":{"description":"Traffic Roads","tooltip":"Highways, Streets, etc."},"service_roads":{"description":"Service Roads","tooltip":"Service Roads, Parking Aisles, Tracks, etc."},"paths":{"description":"Paths","tooltip":"Sidewalks, Foot Paths, Cycle Paths, etc."},"buildings":{"description":"Buildings","tooltip":"Buildings, Shelters, Garages, etc."},"landuse":{"description":"Landuse Features","tooltip":"Forests, Farmland, Parks, Residential, Commercial, etc."},"boundaries":{"description":"Boundaries","tooltip":"Administrative Boundaries"},"water":{"description":"Water Features","tooltip":"Rivers, Lakes, Ponds, Basins, etc."},"rail":{"description":"Rail Features","tooltip":"Railways"},"power":{"description":"Power Features","tooltip":"Power Lines, Power Plants, Substations, etc."},"past_future":{"description":"Past/Future","tooltip":"Proposed, Construction, Abandoned, Demolished, etc."},"others":{"description":"Others","tooltip":"Everything Else"}},"area_fill":{"wireframe":{"description":"No Fill (Wireframe)","tooltip":"Enabling wireframe mode makes it easy to see the background imagery."},"partial":{"description":"Partial Fill","tooltip":"Areas are drawn with fill only around their inner edges. (Recommended for beginner mappers)"},"full":{"description":"Full Fill","tooltip":"Areas are drawn fully filled."}},"restore":{"heading":"You have unsaved changes","description":"Do you wish to restore unsaved changes from a previous editing session?","restore":"Restore","reset":"Reset"},"save":{"title":"Save","help":"Save changes to OpenStreetMap, making them visible to other users.","no_changes":"No changes to save.","error":"Errors occurred while trying to save","status_code":"Server returned status code {code}","unknown_error_details":"Please ensure you are connected to the internet.","uploading":"Uploading changes to OpenStreetMap.","unsaved_changes":"You have unsaved changes","conflict":{"header":"Resolve conflicting edits","count":"Conflict {num} of {total}","previous":"< Previous","next":"Next >","keep_local":"Keep mine","keep_remote":"Use theirs","restore":"Restore","delete":"Leave Deleted","download_changes":"Or download your changes.","done":"All conflicts resolved!","help":"Another user changed some of the same map features you changed.\nClick on each item below for more details about the conflict, and choose whether to keep\nyour changes or the other user's changes.\n"}},"merge_remote_changes":{"conflict":{"deleted":"This object has been deleted by {user}.","location":"This object was moved by both you and {user}.","nodelist":"Nodes were changed by both you and {user}.","memberlist":"Relation members were changed by both you and {user}.","tags":"You changed the <b>{tag}</b> tag to \"{local}\" and {user} changed it to \"{remote}\"."}},"success":{"edited_osm":"Edited OSM!","just_edited":"You just edited OpenStreetMap!","view_on_osm":"View on OSM","facebook":"Share on Facebook","twitter":"Share on Twitter","google":"Share on Google+","help_html":"Your changes should appear in the \"Standard\" layer in a few minutes. Other layers, and certain features, may take longer.","help_link_text":"Details","help_link_url":"https://wiki.openstreetmap.org/wiki/FAQ#I_have_just_made_some_changes_to_the_map._How_do_I_get_to_see_my_changes.3F"},"confirm":{"okay":"Okay","cancel":"Cancel"},"splash":{"welcome":"Welcome to the iD OpenStreetMap editor","text":"iD is a friendly but powerful tool for contributing to the world's best free world map. This is version {version}. For more information see {website} and report bugs at {github}.","walkthrough":"Start the Walkthrough","start":"Edit Now"},"source_switch":{"live":"live","lose_changes":"You have unsaved changes. Switching the map server will discard them. Are you sure you want to switch servers?","dev":"dev"},"tag_reference":{"description":"Description","on_wiki":"{tag} on wiki.osm.org","used_with":"used with {type}"},"validations":{"untagged_point":"Untagged point","untagged_line":"Untagged line","untagged_area":"Untagged area","many_deletions":"You're deleting {n} objects. Are you sure you want to do this? This will delete them from the map that everyone else sees on openstreetmap.org.","tag_suggests_area":"The tag {tag} suggests line should be area, but it is not an area","untagged_point_tooltip":"Select a feature type that describes what this point is.","untagged_line_tooltip":"Select a feature type that describes what this line is.","untagged_area_tooltip":"Select a feature type that describes what this area is.","deprecated_tags":"Deprecated tags: {tags}"},"zoom":{"in":"Zoom In","out":"Zoom Out"},"cannot_zoom":"Cannot zoom out further in current mode.","full_screen":"Toggle Full Screen","gpx":{"local_layer":"Local GPX file","drag_drop":"Drag and drop a .gpx file on the page, or click the button to the right to browse","zoom":"Zoom to GPX track","browse":"Browse for a .gpx file"},"mapillary_images":{"tooltip":"Street-level photos from Mapillary","title":"Photo Overlay (Mapillary)"},"mapillary_signs":{"tooltip":"Traffic signs from Mapillary","title":"Traffic Sign Overlay (Mapillary)"},"mapillary":{"view_on_mapillary":"View this image on Mapillary"},"help":{"title":"Help","help":"# Help\n\nThis is an editor for [OpenStreetMap](http://www.openstreetmap.org/), the\nfree and editable map of the world. You can use it to add and update\ndata in your area, making an open-source and open-data map of the world\nbetter for everyone.\n\nEdits that you make on this map will be visible to everyone who uses\nOpenStreetMap. In order to make an edit, you'll need to\n[log in](https://www.openstreetmap.org/login).\n\nThe [iD editor](http://ideditor.com/) is a collaborative project with [source\ncode available on GitHub](https://github.com/openstreetmap/iD).\n","editing_saving":"# Editing & Saving\n\nThis editor is designed to work primarily online, and you're accessing\nit through a website right now.\n\n### Selecting Features\n\nTo select a map feature, like a road or point of interest, click\non it on the map. This will highlight the selected feature, open a panel with\ndetails about it, and show a menu of things you can do with the feature.\n\nTo select multiple features, hold down the 'Shift' key. Then either click\non the features you want to select, or drag on the map to draw a rectangle.\nThis will draw a box and select all the points within it.\n\n### Saving Edits\n\nWhen you make changes like editing roads, buildings, and places, these are\nstored locally until you save them to the server. Don't worry if you make\na mistake - you can undo changes by clicking the undo button, and redo\nchanges by clicking the redo button.\n\nClick 'Save' to finish a group of edits - for instance, if you've completed\nan area of town and would like to start on a new area. You'll have a chance\nto review what you've done, and the editor supplies helpful suggestions\nand warnings if something doesn't seem right about the changes.\n\nIf everything looks good, you can enter a short comment explaining the change\nyou made, and click 'Save' again to post the changes\nto [OpenStreetMap.org](http://www.openstreetmap.org/), where they are visible\nto all other users and available for others to build and improve upon.\n\nIf you can't finish your edits in one sitting, you can leave the editor\nwindow and come back (on the same browser and computer), and the\neditor application will offer to restore your work.\n\n### Using the editor\n\nA list of available keyboard shortcuts can be found [here](http://wiki.openstreetmap.org/wiki/ID/Shortcuts).\n","roads":"# Roads\n\nYou can create, fix, and delete roads with this editor. Roads can be all\nkinds: paths, highways, trails, cycleways, and more - any often-crossed\nsegment should be mappable.\n\n### Selecting\n\nClick on a road to select it. An outline should become visible, along\nwith a small tools menu on the map and a sidebar showing more information\nabout the road.\n\n### Modifying\n\nOften you'll see roads that aren't aligned to the imagery behind them\nor to a GPS track. You can adjust these roads so they are in the correct\nplace.\n\nFirst click on the road you want to change. This will highlight it and show\ncontrol points along it that you can drag to better locations. If\nyou want to add new control points for more detail, double-click a part\nof the road without a node, and one will be added.\n\nIf the road connects to another road, but doesn't properly connect on\nthe map, you can drag one of its control points onto the other road in\norder to join them. Having roads connect is important for the map\nand essential for providing driving directions.\n\nYou can also click the 'Move' tool or press the `M` shortcut key to move the entire road at\none time, and then click again to save that movement.\n\n### Deleting\n\nIf a road is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the road could simply be newly built.\n\nYou can delete a road by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n\n### Creating\n\nFound somewhere there should be a road but there isn't? Click the 'Line'\nicon in the top-left of the editor or press the shortcut key `2` to start drawing\na line.\n\nClick on the start of the road on the map to start drawing. If the road\nbranches off from an existing road, start by clicking on the place where they connect.\n\nThen click on points along the road so that it follows the right path, according\nto satellite imagery or GPS. If the road you are drawing crosses another road, connect\nit by clicking on the intersection point. When you're done drawing, double-click\nor press 'Return' or 'Enter' on your keyboard.\n","gps":"# GPS\n\nCollected GPS traces are one valuable source of data for OpenStreetMap. This editor\nsupports local traces - `.gpx` files on your local computer. You can collect\nthis kind of GPS trace with a number of smartphone applications as well as\npersonal GPS hardware.\n\nFor information on how to perform a GPS survey, read\n[Mapping with a smartphone, GPS, or paper](http://learnosm.org/en/mobile-mapping/).\n\nTo use a GPX track for mapping, drag and drop the GPX file onto the map\neditor. If it's recognized, it will be added to the map as a bright purple\nline. Click on the 'Map Data' menu on the right side to enable,\ndisable, or zoom to this new GPX-powered layer.\n\nThe GPX track isn't directly uploaded to OpenStreetMap - the best way to\nuse it is to draw on the map, using it as a guide for the new features that\nyou add, and also to [upload it to OpenStreetMap](http://www.openstreetmap.org/trace/create)\nfor other users to use.\n","imagery":"# Imagery\n\nAerial imagery is an important resource for mapping. A combination of\nairplane flyovers, satellite views, and freely-compiled sources are available\nin the editor under the 'Background Settings' menu on the right.\n\nBy default a [Bing Maps](http://www.bing.com/maps/) satellite layer is\npresented in the editor, but as you pan and zoom the map to new geographical\nareas, new sources will become available. Some countries, like the United\nStates, France, and Denmark have very high-quality imagery available for some areas.\n\nImagery is sometimes offset from the map data because of a mistake on the\nimagery provider's side. If you see a lot of roads shifted from the background,\ndon't immediately move them all to match the background. Instead you can adjust\nthe imagery so that it matches the existing data by clicking 'Fix alignment' at\nthe bottom of the Background Settings UI.\n","addresses":"# Addresses\n\nAddresses are some of the most useful information for the map.\n\nAlthough addresses are often represented as parts of streets, in OpenStreetMap\nthey're recorded as attributes of buildings and places along streets.\n\nYou can add address information to places mapped as building outlines\nas well as those mapped as single points. The optimal source of address\ndata is from an on-the-ground survey or personal knowledge - as with any\nother feature, copying from commercial sources like Google Maps is strictly\nforbidden.\n","inspector":"# Using the Inspector\n\nThe inspector is the section on the left side of the page that allows you to\nedit the details of the selected feature.\n\n### Selecting a Feature Type\n\nAfter you add a point, line, or area, you can choose what type of feature it\nis, like whether it's a highway or residential road, supermarket or cafe.\nThe inspector will display buttons for common feature types, and you can\nfind others by typing what you're looking for in the search box.\n\nClick the 'i' in the bottom-right-hand corner of a feature type button to\nlearn more about it. Click a button to choose that type.\n\n### Using Forms and Editing Tags\n\nAfter you choose a feature type, or when you select a feature that already\nhas a type assigned, the inspector will display fields with details about\nthe feature like its name and address.\n\nBelow the fields you see, you can click the 'Add field' dropdown to add\nother details, like a Wikipedia link, wheelchair access, and more.\n\nAt the bottom of the inspector, click 'Additional tags' to add arbitrary\nother tags to the element. [Taginfo](http://taginfo.openstreetmap.org/) is a\ngreat resource for learn more about popular tag combinations.\n\nChanges you make in the inspector are automatically applied to the map.\nYou can undo them at any time by clicking the 'Undo' button.\n","buildings":"# Buildings\n\nOpenStreetMap is the world's largest database of buildings. You can create\nand improve this database.\n\n### Selecting\n\nYou can select a building by clicking on its border. This will highlight the\nbuilding and open a small tools menu and a sidebar showing more information\nabout the building.\n\n### Modifying\n\nSometimes buildings are incorrectly placed or have incorrect tags.\n\nTo move an entire building, select it, then click the 'Move' tool. Move your\nmouse to shift the building, and click when it's correctly placed.\n\nTo fix the specific shape of a building, click and drag the nodes that form\nits border into better places.\n\n### Creating\n\nOne of the main questions around adding buildings to the map is that\nOpenStreetMap records buildings both as shapes and points. The rule of thumb\nis to _map a building as a shape whenever possible_, and map companies, homes,\namenities, and other things that operate out of buildings as points placed\nwithin the building shape.\n\nStart drawing a building as a shape by clicking the 'Area' button in the top\nleft of the interface, and end it either by pressing 'Return' on your keyboard\nor clicking on the first node drawn to close the shape.\n\n### Deleting\n\nIf a building is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the building could simply be newly built.\n\nYou can delete a building by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n","relations":"# Relations\n\nA relation is a special type of feature in OpenStreetMap that groups together\nother features. For example, two common types of relations are *route relations*,\nwhich group together sections of road that belong to a specific freeway or\nhighway, and *multipolygons*, which group together several lines that define\na complex area (one with several pieces or holes in it like a donut).\n\nThe group of features in a relation are called *members*. In the sidebar, you can\nsee which relations a feature is a member of, and click on a relation there\nto select the it. When the relation is selected, you can see all of its\nmembers listed in the sidebar and highlighted on the map.\n\nFor the most part, iD will take care of maintaining relations automatically\nwhile you edit. The main thing you should be aware of is that if you delete a\nsection of road to redraw it more accurately, you should make sure that the\nnew section is a member of the same relations as the original.\n\n## Editing Relations\n\nIf you want to edit relations, here are the basics.\n\nTo add a feature to a relation, select the feature, click the \"+\" button in the\n\"All relations\" section of the sidebar, and select or type the name of the relation.\n\nTo create a new relation, select the first feature that should be a member,\nclick the \"+\" button in the \"All relations\" section, and select \"New relation...\".\n\nTo remove a feature from a relation, select the feature and click the trash\nbutton next to the relation you want to remove it from.\n\nYou can create multipolygons with holes using the \"Merge\" tool. Draw two areas (inner\nand outer), hold the Shift key and click on each of them to select them both, and then\nclick the \"Merge\" (+) button.\n"},"intro":{"graph":{"city_hall":"Three Rivers City Hall","fire_department":"Three Rivers Fire Department","memory_isle_park":"Memory Isle Park","riverwalk_trail":"Riverwalk Trail","w_michigan_ave":"West Michigan Avenue","e_michigan_ave":"East Michigan Avenue","spring_st":"Spring Street","scidmore_park":"Scidmore Park","petting_zoo":"Scidmore Park Petting Zoo","n_andrews_st":"North Andrews Street","s_andrews_st":"South Andrews Street","n_constantine_st":"North Constantine Street","s_constantine_st":"South Constantine Street","rocky_river":"Rocky River","railroad_dr":"Railroad Drive","conrail_rr":"Conrail Railroad","st_joseph_river":"Saint Joseph River","n_main_st":"North Main Street","s_main_st":"South Main Street","water_st":"Water Street","foster_st":"Foster Street","portage_river":"Portage River","flower_st":"Flower Street","elm_st":"Elm Street","walnut_st":"Walnut Street","morris_ave":"Morris Avenue","east_st":"East Street","portage_ave":"Portage Avenue"},"navigation":{"title":"Navigation","drag":"The main map area shows OpenStreetMap data on top of a background. You can navigate by dragging and scrolling, just like any web map. **Drag the map!**","select":"Map features are represented three ways: using points, lines or areas. All features can be selected by clicking on them. **Click on the point to select it.**","pane":"When a feature is selected, the feature editor is displayed. The header shows us the feature type and the main pane shows the feature's attributes, such as its name and address. **Close the feature editor by pressing the {button} button in the top right.**","search":"You can also search for features in the current view, or worldwide. **Search for '{name}'**","choose":"**Choose {name} from the list to select it.**","chosen":"Great! {name} is now selected. **Close the feature editor by pressing the {button} button.**"},"points":{"title":"Points","add":"Points can be used to represent features such as shops, restaurants, and monuments. They mark a specific location, and describe what's there. **Click the {button} Point button to add a new point.**","place":"The point can be placed by clicking on the map. **Click the map to place the new point on top of the building.**","search":"There are many different features that can be represented by points. The point you just added is a Cafe. **Search for '{name}'**","choose":"**Choose Cafe from the list.**","describe":"The point is now marked as a cafe. Using the feature editor, we can add more information about the feature. **Add a name**","close":"The feature editor will remember all of your changes automatically.  When you change a feature, the close button will change to a checkmark. **Click the {button} button to close the feature editor**","reselect":"Often points will already exist, but have mistakes or be incomplete. We can edit existing points. **Click to select the point you just created.**","fixname":"**Change the name, then click the {button} button to close the feature editor.**","reselect_delete":"All features on the map can be deleted. **Click to select the point you created.**","delete":"The menu around the point contains operations that can be performed on it, including delete. **Click on the {button} button to delete the point.**"},"areas":{"title":"Areas","add":"Areas are used to show the boundaries of features like lakes, buildings, and residential areas. They can be also be used for more detailed mapping of many features you might normally map as points. **Click the {button} Area button to add a new area.**","corner":"Areas are drawn by placing nodes that mark the boundary of the area. **Click to place a starting node on one of the corners of the playground.**","place":"Draw the area by placing more nodes. Finish the area by clicking on the starting node. **Draw an area for the playground.**","search":"**Search for '{name}'.**","choose":"**Choose Playground from the list.**","describe":"**Add a name, then click the {button} button to close the feature editor**"},"lines":{"title":"Lines","add":"Lines are used to represent features such as roads, railroads, and rivers. **Click the {button} Line button to add a new line.**","start":"**Start the line by clicking on the end of the road.**","intersect":"Click to add more nodes to the line. You can drag the map while drawing if necessary. Roads, and many other types of lines, are part of a larger network. It is important for these lines to be connected properly in order for routing applications to work. **Click on {name} to create an intersection connecting the two lines.**","finish":"Lines can be finished by clicking on the last node again. **Finish drawing the road.**","road":"**Select Road from the list**","residential":"There are different types of roads, the most common of which is Residential. **Choose the Residential road type**","describe":"**Name the road, then click the {button} button to close the feature editor.**","restart":"The road needs to intersect {name}.","wrong_preset":"You didn't select the Residential road type. **Click here to choose again**"},"startediting":{"title":"Start Editing","help":"You can replay this walkthrough or view more documentation by clicking the {button} Help button.","save":"Don't forget to regularly save your changes!","start":"Start mapping!"}},"presets":{"categories":{"category-building":{"name":"Building Features"},"category-golf":{"name":"Golf Features"},"category-landuse":{"name":"Land Use Features"},"category-path":{"name":"Path Features"},"category-rail":{"name":"Rail Features"},"category-restriction":{"name":"Restriction Features"},"category-road":{"name":"Road Features"},"category-route":{"name":"Route Features"},"category-water-area":{"name":"Water Features"},"category-water-line":{"name":"Water Features"}},"fields":{"access":{"label":"Allowed Access","placeholder":"Not Specified","types":{"access":"All","foot":"Foot","motor_vehicle":"Motor Vehicles","bicycle":"Bicycles","horse":"Horses"},"options":{"yes":{"title":"Allowed","description":"Access permitted by law; a right of way"},"no":{"title":"Prohibited","description":"Access not permitted to the general public"},"permissive":{"title":"Permissive","description":"Access permitted until such time as the owner revokes the permission"},"private":{"title":"Private","description":"Access permitted only with permission of the owner on an individual basis"},"designated":{"title":"Designated","description":"Access permitted according to signs or specific local laws"},"destination":{"title":"Destination","description":"Access permitted only to reach a destination"},"dismount":{"title":"Dismount","description":"Access permitted but rider must dismount"}}},"access_simple":{"label":"Allowed Access","placeholder":"yes"},"access_toilets":{"label":"Access"},"address":{"label":"Address","placeholders":{"housename":"Housename","housenumber":"123","conscriptionnumber":"123","street":"Street","city":"City","postcode":"Postcode","place":"Place","hamlet":"Hamlet","suburb":"Suburb","subdistrict":"Subdistrict","district":"District","province":"Province","state":"State","country":"Country"}},"admin_level":{"label":"Admin Level"},"aerialway":{"label":"Type"},"aerialway/access":{"label":"Access","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aerialway/bubble":{"label":"Bubble"},"aerialway/capacity":{"label":"Capacity (per hour)","placeholder":"500, 2500, 5000..."},"aerialway/duration":{"label":"Duration (minutes)","placeholder":"1, 2, 3..."},"aerialway/heating":{"label":"Heated"},"aerialway/occupancy":{"label":"Occupancy","placeholder":"2, 4, 8..."},"aerialway/summer/access":{"label":"Access (summer)","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aeroway":{"label":"Type"},"amenity":{"label":"Type"},"area/highway":{"label":"Type"},"artist":{"label":"Artist"},"artwork_type":{"label":"Type"},"atm":{"label":"ATM"},"backrest":{"label":"Backrest"},"barrier":{"label":"Type"},"bench":{"label":"Bench"},"bicycle_parking":{"label":"Type"},"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"},"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/biodiesel":{"label":"Sells Biodiesel"},"fuel/diesel":{"label":"Sells Diesel"},"fuel/e10":{"label":"Sells E10"},"fuel/e85":{"label":"Sells E85"},"fuel/lpg":{"label":"Sells Propane"},"fuel/octane_100":{"label":"Sells Racing Gasoline"},"fuel/octane_91":{"label":"Sells Regular Gasoline"},"fuel/octane_95":{"label":"Sells Midgrade Gasoline"},"fuel/octane_98":{"label":"Sells Premium Gasoline"},"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"}},"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/cans":{"label":"Accepts Cans"},"recycling/clothes":{"label":"Accepts Clothes"},"recycling/glass":{"label":"Accepts Glass"},"recycling/glass_bottles":{"label":"Accepts Glass Bottles"},"recycling/paper":{"label":"Accepts Paper"},"recycling/plastic":{"label":"Accepts Plastic"},"recycling/type":{"label":"Recycling Type","options":{"container":"Container","centre":"Recycling Center"}},"ref":{"label":"Reference"},"relation":{"label":"Type"},"religion":{"label":"Religion"},"restriction":{"label":"Type"},"restrictions":{"label":"Turn Restrictions"},"route":{"label":"Type"},"route_master":{"label":"Type"},"sac_scale":{"label":"Hiking Difficulty","placeholder":"Mountain Hiking, Alpine Hiking...","options":{"hiking":"T1: Hiking","mountain_hiking":"T2: Mountain Hiking","demanding_mountain_hiking":"T3: Demanding Mountain Hiking","alpine_hiking":"T4: Alpine Hiking","demanding_alpine_hiking":"T5: Demanding Alpine Hiking","difficult_alpine_hiking":"T6: Difficult Alpine Hiking"}},"sanitary_dump_station":{"label":"Toilet Disposal"},"seasonal":{"label":"Seasonal"},"service":{"label":"Type"},"service/bicycle/chain_tool":{"label":"Chain Tool","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service/bicycle/pump":{"label":"Air Pump","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service_rail":{"label":"Service Type","options":{"spur":"Spur","yard":"Yard","siding":"Siding","crossover":"Crossover"}},"shelter":{"label":"Shelter"},"shelter_type":{"label":"Type"},"shop":{"label":"Type"},"site":{"label":"Type"},"sloped_curb":{"label":"Sloped Curb"},"smoking":{"label":"Smoking","placeholder":"No, Separated, Yes...","options":{"no":"No smoking anywhere","separated":"In smoking areas, not physically isolated","isolated":"In smoking areas, physically isolated","outside":"Allowed outside","yes":"Allowed everywhere","dedicated":"Dedicated to smokers (e.g. smokers' club)"}},"smoothness":{"label":"Smoothness","placeholder":"Thin Rollers, Wheels, Off-Road...","options":{"excellent":"Thin Rollers: rollerblade, skateboard","good":"Thin Wheels: racing bike","intermediate":"Wheels: city bike, wheelchair, scooter","bad":"Robust Wheels: trekking bike, car, rickshaw","very_bad":"High Clearance: light duty off-road vehicle","horrible":"Off-Road: heavy duty off-road vehicle","very_horrible":"Specialized off-road: tractor, ATV","impassable":"Impassable / No wheeled vehicle"}},"social_facility_for":{"label":"People served","placeholder":"Homeless, Disabled, Child, etc"},"source":{"label":"Source"},"sport":{"label":"Sport"},"sport_ice":{"label":"Sport"},"sport_racing":{"label":"Sport"},"structure":{"label":"Structure","placeholder":"Unknown","options":{"bridge":"Bridge","tunnel":"Tunnel","embankment":"Embankment","cutting":"Cutting","ford":"Ford"}},"studio":{"label":"Type"},"substation":{"label":"Type"},"supervised":{"label":"Supervised"},"surface":{"label":"Surface"},"tactile_paving":{"label":"Tactile Paving"},"takeaway":{"label":"Takeaway","placeholder":"Yes, No, Takeaway Only...","options":{"yes":"Yes","no":"No","only":"Takeaway Only"}},"toilets/disposal":{"label":"Disposal","options":{"flush":"Flush","pitlatrine":"Pit/Latrine","chemical":"Chemical","bucket":"Bucket"}},"tourism":{"label":"Type"},"towertype":{"label":"Tower type"},"tracktype":{"label":"Track Type","placeholder":"Solid, Mostly Solid, Soft...","options":{"grade1":"Solid: paved or heavily compacted hardcore surface","grade2":"Mostly Solid: gravel/rock with some soft material mixed in","grade3":"Even mixture of hard and soft materials","grade4":"Mostly Soft: soil/sand/grass with some hard material mixed in","grade5":"Soft: soil/sand/grass"}},"traffic_signals":{"label":"Type"},"trail_visibility":{"label":"Trail Visibility","placeholder":"Excellent, Good, Bad...","options":{"excellent":"Excellent: unambiguous path or markers everywhere","good":"Good: markers visible, sometimes require searching","intermediate":"Intermediate: few markers, path mostly visible","bad":"Bad: no markers, path sometimes invisible/pathless","horrible":"Horrible: often pathless, some orientation skills required","no":"No: pathless, excellent orientation skills required"}},"trees":{"label":"Trees"},"tunnel":{"label":"Tunnel"},"vending":{"label":"Type of Goods"},"water":{"label":"Type"},"water_point":{"label":"Water Point"},"waterway":{"label":"Type"},"website":{"label":"Website","placeholder":"http://example.com/"},"wetland":{"label":"Type"},"wheelchair":{"label":"Wheelchair Access"},"width":{"label":"Width (Meters)"},"wikipedia":{"label":"Wikipedia"}},"presets":{"address":{"name":"Address","terms":""},"aerialway":{"name":"Aerialway","terms":"ski lift,funifor,funitel"},"aerialway/cable_car":{"name":"Cable Car","terms":"tramway,ropeway"},"aerialway/chair_lift":{"name":"Chair Lift","terms":""},"aerialway/gondola":{"name":"Gondola","terms":""},"aerialway/magic_carpet":{"name":"Magic Carpet Lift","terms":""},"aerialway/platter":{"name":"Platter Lift","terms":"button lift,poma lift"},"aerialway/pylon":{"name":"Aerialway Pylon","terms":""},"aerialway/rope_tow":{"name":"Rope Tow Lift","terms":"handle tow,bugel lift"},"aerialway/station":{"name":"Aerialway Station","terms":""},"aerialway/t-bar":{"name":"T-bar Lift","terms":""},"aeroway":{"name":"Aeroway","terms":""},"aeroway/aerodrome":{"name":"Airport","terms":"airplane,airport,aerodrome"},"aeroway/apron":{"name":"Apron","terms":"ramp"},"aeroway/gate":{"name":"Airport gate","terms":""},"aeroway/hangar":{"name":"Hangar","terms":""},"aeroway/helipad":{"name":"Helipad","terms":"helicopter,helipad,heliport"},"aeroway/runway":{"name":"Runway","terms":"landing strip"},"aeroway/taxiway":{"name":"Taxiway","terms":""},"aeroway/terminal":{"name":"Airport terminal","terms":"airport,aerodrome"},"amenity":{"name":"Amenity","terms":""},"amenity/arts_centre":{"name":"Arts Center","terms":""},"amenity/atm":{"name":"ATM","terms":"money,cash,machine"},"amenity/bank":{"name":"Bank","terms":"credit union,check,deposit,fund,investment,repository,reserve,safe,savings,stock,treasury,trust,vault"},"amenity/bar":{"name":"Bar","terms":"dive,beer,bier,booze"},"amenity/bbq":{"name":"Barbecue/Grill","terms":"bbq"},"amenity/bench":{"name":"Bench","terms":"seat"},"amenity/bicycle_parking":{"name":"Bicycle Parking","terms":"bike"},"amenity/bicycle_rental":{"name":"Bicycle Rental","terms":"bike"},"amenity/bicycle_repair_station":{"name":"Bicycle Repair Tool Stand","terms":"bike,repair,chain,pump"},"amenity/biergarten":{"name":"Beer Garden","terms":"beer,bier,booze"},"amenity/boat_rental":{"name":"Boat Rental","terms":""},"amenity/bureau_de_change":{"name":"Currency Exchange","terms":"bureau de change,money changer"},"amenity/bus_station":{"name":"Bus Station","terms":""},"amenity/cafe":{"name":"Cafe","terms":"bistro,coffee,tea"},"amenity/car_rental":{"name":"Car Rental","terms":""},"amenity/car_sharing":{"name":"Car Sharing","terms":""},"amenity/car_wash":{"name":"Car Wash","terms":""},"amenity/casino":{"name":"Casino","terms":"gambling,roulette,craps,poker,blackjack"},"amenity/charging_station":{"name":"Charging Station","terms":"EV,Electric Vehicle,Supercharger"},"amenity/childcare":{"name":"Nursery/Childcare","terms":"daycare,orphanage,playgroup"},"amenity/cinema":{"name":"Cinema","terms":"drive-in,film,flick,movie,theater,picture,show,screen"},"amenity/clinic":{"name":"Clinic","terms":"medical,urgentcare"},"amenity/clock":{"name":"Clock","terms":""},"amenity/college":{"name":"College Grounds","terms":"university"},"amenity/community_centre":{"name":"Community Center","terms":"event,hall"},"amenity/compressed_air":{"name":"Compressed Air","terms":""},"amenity/courthouse":{"name":"Courthouse","terms":""},"amenity/dentist":{"name":"Dentist","terms":"tooth,teeth"},"amenity/doctors":{"name":"Doctor","terms":"medic*"},"amenity/dojo":{"name":"Dojo / Martial Arts Academy","terms":"martial arts,dojang"},"amenity/drinking_water":{"name":"Drinking Water","terms":"fountain,potable"},"amenity/embassy":{"name":"Embassy","terms":""},"amenity/fast_food":{"name":"Fast Food","terms":"restaurant"},"amenity/ferry_terminal":{"name":"Ferry Terminal","terms":""},"amenity/fire_station":{"name":"Fire Station","terms":""},"amenity/fountain":{"name":"Fountain","terms":""},"amenity/fuel":{"name":"Gas Station","terms":"petrol,fuel,gasoline,propane,diesel,lng,cng,biodiesel"},"amenity/grave_yard":{"name":"Graveyard","terms":""},"amenity/grit_bin":{"name":"Grit Bin","terms":"salt,sand"},"amenity/hospital":{"name":"Hospital Grounds","terms":"clinic,doctor,emergency room,health service,hospice,infirmary,institution,nursing home,sanatorium,sanitarium,sick,surgery,ward"},"amenity/hunting_stand":{"name":"Hunting Stand","terms":""},"amenity/kindergarten":{"name":"Preschool/Kindergarten Grounds","terms":"kindergarden,pre-school"},"amenity/library":{"name":"Library","terms":"book"},"amenity/marketplace":{"name":"Marketplace","terms":""},"amenity/motorcycle_parking":{"name":"Motorcycle Parking","terms":""},"amenity/nightclub":{"name":"Nightclub","terms":"disco*,night club,dancing,dance club"},"amenity/parking":{"name":"Car Parking","terms":""},"amenity/parking_entrance":{"name":"Parking Garage Entrance/Exit","terms":""},"amenity/parking_space":{"name":"Parking Space","terms":""},"amenity/pharmacy":{"name":"Pharmacy","terms":"drug,medicine"},"amenity/place_of_worship":{"name":"Place of Worship","terms":"abbey,basilica,bethel,cathedral,chancel,chantry,chapel,church,fold,house of God,house of prayer,house of worship,minster,mission,mosque,oratory,parish,sacellum,sanctuary,shrine,synagogue,tabernacle,temple"},"amenity/place_of_worship/buddhist":{"name":"Buddhist Temple","terms":"stupa,vihara,monastery,temple,pagoda,zendo,dojo"},"amenity/place_of_worship/christian":{"name":"Church","terms":"christian,abbey,basilica,bethel,cathedral,chancel,chantry,chapel,fold,house of God,house of prayer,house of worship,minster,mission,oratory,parish,sacellum,sanctuary,shrine,tabernacle,temple"},"amenity/place_of_worship/jewish":{"name":"Synagogue","terms":"jewish"},"amenity/place_of_worship/muslim":{"name":"Mosque","terms":"muslim"},"amenity/police":{"name":"Police","terms":"badge,constable,constabulary,cop,detective,fed,law,enforcement,officer,patrol"},"amenity/post_box":{"name":"Mailbox","terms":"letter,post"},"amenity/post_office":{"name":"Post Office","terms":"letter,mail"},"amenity/prison":{"name":"Prison Grounds","terms":"cell,jail"},"amenity/pub":{"name":"Pub","terms":"dive,beer,bier,booze"},"amenity/public_bookcase":{"name":"Public Bookcase","terms":"library,bookcrossing"},"amenity/ranger_station":{"name":"Ranger Station","terms":"visitor center,visitor centre,permit center,permit centre,backcountry office,warden office,warden center"},"amenity/recycling":{"name":"Recycling","terms":"can,bottle,garbage,scrap,trash"},"amenity/register_office":{"name":"Register Office","terms":""},"amenity/restaurant":{"name":"Restaurant","terms":"bar,breakfast,cafe,café,canteen,coffee,dine,dining,dinner,drive-in,eat,grill,lunch,table"},"amenity/sanitary_dump_station":{"name":"RV Toilet Disposal","terms":"Motor Home,Camper,Sanitary,Dump Station,Elsan,CDP,CTDP,Chemical Toilet"},"amenity/school":{"name":"School Grounds","terms":"academy,elementary school,middle school,high school"},"amenity/shelter":{"name":"Shelter","terms":"lean-to,gazebo,picnic"},"amenity/social_facility":{"name":"Social Facility","terms":""},"amenity/social_facility/food_bank":{"name":"Food Bank","terms":""},"amenity/social_facility/group_home":{"name":"Elderly Group Home","terms":"old,senior,living"},"amenity/social_facility/homeless_shelter":{"name":"Homeless Shelter","terms":"houseless,unhoused,displaced"},"amenity/studio":{"name":"Studio","terms":"recording,radio,television"},"amenity/swimming_pool":{"name":"Swimming Pool","terms":""},"amenity/taxi":{"name":"Taxi Stand","terms":"cab"},"amenity/telephone":{"name":"Telephone","terms":"phone"},"amenity/theatre":{"name":"Theater","terms":"theatre,performance,play,musical"},"amenity/toilets":{"name":"Toilets","terms":"bathroom,restroom,outhouse,privy,head,lavatory,latrine,water closet,WC,W.C."},"amenity/townhall":{"name":"Town Hall","terms":"village,city,government,courthouse,municipal"},"amenity/university":{"name":"University Grounds","terms":"college"},"amenity/vending_machine/cigarettes":{"name":"Cigarette Vending Machine","terms":"cigarette"},"amenity/vending_machine/condoms":{"name":"Condom Vending Machine","terms":"condom"},"amenity/vending_machine/drinks":{"name":"Drink Vending Machine","terms":"drink,soda,beverage,juice,pop"},"amenity/vending_machine/excrement_bags":{"name":"Excrement Bag Vending Machine","terms":"excrement bags,poop,dog,animal"},"amenity/vending_machine/news_papers":{"name":"Newspaper Vending Machine","terms":"newspaper"},"amenity/vending_machine/parcel_pickup_dropoff":{"name":"Parcel Pickup/Dropoff Vending Machine","terms":"parcel,mail,pickup"},"amenity/vending_machine/parking_tickets":{"name":"Parking Ticket Vending Machine","terms":"parking,ticket"},"amenity/vending_machine/public_transport_tickets":{"name":"Transit Ticket Vending Machine","terms":"bus,train,ferry,rail,ticket,transportation"},"amenity/vending_machine/sweets":{"name":"Snack Vending Machine","terms":"candy,gum,chip,pretzel,cookie,cracker"},"amenity/vending_machine/vending_machine":{"name":"Vending Machine","terms":""},"amenity/veterinary":{"name":"Veterinary","terms":"pet clinic,veterinarian,animal hospital,pet doctor"},"amenity/waste_basket":{"name":"Waste Basket","terms":"bin,rubbish,litter,trash,garbage"},"amenity/waste_disposal":{"name":"Garbage Dumpster","terms":"rubbish,litter,trash"},"amenity/water_point":{"name":"RV Drinking Water","terms":""},"area":{"name":"Area","terms":""},"area/highway":{"name":"Road Surface","terms":""},"barrier":{"name":"Barrier","terms":""},"barrier/block":{"name":"Block","terms":""},"barrier/bollard":{"name":"Bollard","terms":""},"barrier/cattle_grid":{"name":"Cattle Grid","terms":""},"barrier/city_wall":{"name":"City Wall","terms":""},"barrier/cycle_barrier":{"name":"Cycle Barrier","terms":""},"barrier/ditch":{"name":"Ditch","terms":""},"barrier/entrance":{"name":"Entrance","terms":""},"barrier/fence":{"name":"Fence","terms":""},"barrier/gate":{"name":"Gate","terms":""},"barrier/hedge":{"name":"Hedge","terms":""},"barrier/kissing_gate":{"name":"Kissing Gate","terms":""},"barrier/lift_gate":{"name":"Lift Gate","terms":""},"barrier/retaining_wall":{"name":"Retaining Wall","terms":""},"barrier/stile":{"name":"Stile","terms":""},"barrier/toll_booth":{"name":"Toll Booth","terms":""},"barrier/wall":{"name":"Wall","terms":""},"boundary/administrative":{"name":"Administrative Boundary","terms":""},"building":{"name":"Building","terms":""},"building/apartments":{"name":"Apartments","terms":""},"building/barn":{"name":"Barn","terms":""},"building/bunker":{"name":"Bunker","terms":""},"building/cabin":{"name":"Cabin","terms":""},"building/cathedral":{"name":"Cathedral Building","terms":""},"building/chapel":{"name":"Chapel Building","terms":""},"building/church":{"name":"Church Building","terms":""},"building/college":{"name":"College Building","terms":"university"},"building/commercial":{"name":"Commercial Building","terms":""},"building/construction":{"name":"Building Under Construction","terms":""},"building/detached":{"name":"Detached House","terms":"home,single,family,residence,dwelling"},"building/dormitory":{"name":"Dormitory","terms":""},"building/entrance":{"name":"Entrance/Exit","terms":""},"building/garage":{"name":"Garage","terms":""},"building/garages":{"name":"Garages","terms":""},"building/greenhouse":{"name":"Greenhouse","terms":""},"building/hospital":{"name":"Hospital Building","terms":""},"building/hotel":{"name":"Hotel Building","terms":""},"building/house":{"name":"House","terms":"home,family,residence,dwelling"},"building/hut":{"name":"Hut","terms":""},"building/industrial":{"name":"Industrial Building","terms":""},"building/kindergarten":{"name":"Preschool/Kindergarten Building","terms":"kindergarden,pre-school"},"building/public":{"name":"Public Building","terms":""},"building/residential":{"name":"Residential Building","terms":""},"building/retail":{"name":"Retail Building","terms":""},"building/roof":{"name":"Roof","terms":""},"building/school":{"name":"School Building","terms":"academy,elementary school,middle school,high school"},"building/semidetached_house":{"name":"Semi-Detached House","terms":"home,double,duplex,twin,family,residence,dwelling"},"building/shed":{"name":"Shed","terms":""},"building/stable":{"name":"Stable","terms":""},"building/static_caravan":{"name":"Static Mobile Home","terms":""},"building/terrace":{"name":"Row Houses","terms":"home,terrace,brownstone,family,residence,dwelling"},"building/train_station":{"name":"Train Station","terms":""},"building/university":{"name":"University Building","terms":"college"},"building/warehouse":{"name":"Warehouse","terms":""},"craft":{"name":"Craft","terms":""},"craft/basket_maker":{"name":"Basket Maker","terms":""},"craft/beekeeper":{"name":"Beekeeper","terms":""},"craft/blacksmith":{"name":"Blacksmith","terms":""},"craft/boatbuilder":{"name":"Boat Builder","terms":""},"craft/bookbinder":{"name":"Bookbinder","terms":"book repair"},"craft/brewery":{"name":"Brewery","terms":"beer,bier"},"craft/carpenter":{"name":"Carpenter","terms":"woodworker"},"craft/carpet_layer":{"name":"Carpet Layer","terms":""},"craft/caterer":{"name":"Caterer","terms":""},"craft/clockmaker":{"name":"Clockmaker","terms":""},"craft/confectionery":{"name":"Confectionery","terms":"sweets,candy"},"craft/dressmaker":{"name":"Dressmaker","terms":"seamstress"},"craft/electrician":{"name":"Electrician","terms":"power,wire"},"craft/gardener":{"name":"Gardener","terms":"landscaper,grounds keeper"},"craft/glaziery":{"name":"Glaziery","terms":"glass,stained-glass,window"},"craft/handicraft":{"name":"Handicraft","terms":""},"craft/hvac":{"name":"HVAC","terms":"heat*,vent*,air conditioning"},"craft/insulator":{"name":"Insulator","terms":""},"craft/jeweler":{"name":"Jeweler","terms":""},"craft/key_cutter":{"name":"Key Cutter","terms":""},"craft/locksmith":{"name":"Locksmith","terms":""},"craft/metal_construction":{"name":"Metal Construction","terms":""},"craft/optician":{"name":"Optician","terms":""},"craft/painter":{"name":"Painter","terms":""},"craft/photographer":{"name":"Photographer","terms":""},"craft/photographic_laboratory":{"name":"Photographic Laboratory","terms":"film"},"craft/plasterer":{"name":"Plasterer","terms":""},"craft/plumber":{"name":"Plumber","terms":"pipe"},"craft/pottery":{"name":"Pottery","terms":"ceramic"},"craft/rigger":{"name":"Rigger","terms":""},"craft/roofer":{"name":"Roofer","terms":""},"craft/saddler":{"name":"Saddler","terms":""},"craft/sailmaker":{"name":"Sailmaker","terms":""},"craft/sawmill":{"name":"Sawmill","terms":"lumber"},"craft/scaffolder":{"name":"Scaffolder","terms":""},"craft/sculpter":{"name":"Sculpter","terms":""},"craft/shoemaker":{"name":"Shoemaker","terms":"cobbler"},"craft/stonemason":{"name":"Stonemason","terms":"masonry"},"craft/sweep":{"name":"Chimney Sweep","terms":""},"craft/tailor":{"name":"Tailor","terms":"clothes,suit"},"craft/tiler":{"name":"Tiler","terms":""},"craft/tinsmith":{"name":"Tinsmith","terms":""},"craft/upholsterer":{"name":"Upholsterer","terms":""},"craft/watchmaker":{"name":"Watchmaker","terms":""},"craft/window_construction":{"name":"Window Construction","terms":"glass"},"craft/winery":{"name":"Winery","terms":""},"embankment":{"name":"Embankment","terms":""},"emergency/ambulance_station":{"name":"Ambulance Station","terms":"EMS,EMT,rescue"},"emergency/fire_hydrant":{"name":"Fire Hydrant","terms":""},"emergency/phone":{"name":"Emergency Phone","terms":""},"entrance":{"name":"Entrance/Exit","terms":""},"footway/crossing":{"name":"Street Crossing","terms":""},"footway/crosswalk":{"name":"Pedestrian Crosswalk","terms":"zebra crossing"},"footway/sidewalk":{"name":"Sidewalk","terms":""},"ford":{"name":"Ford","terms":""},"golf/bunker":{"name":"Sand Trap","terms":"hazard,bunker"},"golf/fairway":{"name":"Fairway","terms":""},"golf/green":{"name":"Putting Green","terms":""},"golf/hole":{"name":"Golf Hole","terms":""},"golf/lateral_water_hazard":{"name":"Lateral Water Hazard","terms":""},"golf/rough":{"name":"Rough","terms":""},"golf/tee":{"name":"Tee Box","terms":"teeing ground"},"golf/water_hazard":{"name":"Water Hazard","terms":""},"highway":{"name":"Highway","terms":""},"highway/bridleway":{"name":"Bridle Path","terms":"bridleway,equestrian,horse"},"highway/bus_stop":{"name":"Bus Stop","terms":""},"highway/corridor":{"name":"Indoor Corridor","terms":"gallery,hall,hallway,indoor,passage,passageway"},"highway/crossing":{"name":"Street Crossing","terms":""},"highway/crosswalk":{"name":"Pedestrian Crosswalk","terms":"zebra crossing"},"highway/cycleway":{"name":"Cycle Path","terms":"bike"},"highway/footway":{"name":"Foot Path","terms":"hike,hiking,trackway,trail,walk"},"highway/living_street":{"name":"Living Street","terms":""},"highway/mini_roundabout":{"name":"Mini-Roundabout","terms":""},"highway/motorway":{"name":"Motorway","terms":""},"highway/motorway_junction":{"name":"Motorway Junction / Exit","terms":""},"highway/motorway_link":{"name":"Motorway Link","terms":"ramp,on ramp,off ramp"},"highway/path":{"name":"Path","terms":"hike,hiking,trackway,trail,walk"},"highway/pedestrian":{"name":"Pedestrian Street","terms":""},"highway/primary":{"name":"Primary Road","terms":""},"highway/primary_link":{"name":"Primary Link","terms":"ramp,on ramp,off ramp"},"highway/raceway":{"name":"Motor Raceway","terms":"auto*,race*,nascar"},"highway/residential":{"name":"Residential Road","terms":""},"highway/rest_area":{"name":"Rest Area","terms":"rest stop"},"highway/road":{"name":"Unknown Road","terms":""},"highway/secondary":{"name":"Secondary Road","terms":""},"highway/secondary_link":{"name":"Secondary Link","terms":"ramp,on ramp,off ramp"},"highway/service":{"name":"Service Road","terms":""},"highway/service/alley":{"name":"Alley","terms":""},"highway/service/drive-through":{"name":"Drive-Through","terms":""},"highway/service/driveway":{"name":"Driveway","terms":""},"highway/service/emergency_access":{"name":"Emergency Access","terms":""},"highway/service/parking_aisle":{"name":"Parking Aisle","terms":""},"highway/services":{"name":"Service Area","terms":"services,travel plaza,service station"},"highway/steps":{"name":"Steps","terms":"stairs,staircase"},"highway/stop":{"name":"Stop Sign","terms":"stop sign"},"highway/street_lamp":{"name":"Street Lamp","terms":"streetlight,street light,lamp,light,gaslight"},"highway/tertiary":{"name":"Tertiary Road","terms":""},"highway/tertiary_link":{"name":"Tertiary Link","terms":"ramp,on ramp,off ramp"},"highway/track":{"name":"Unmaintained Track Road","terms":"woods road,forest road,logging road,fire road,farm road,agricultural road,ranch road,carriage road,primitive,unmaintained,rut,offroad,4wd,4x4,four wheel drive,atv,quad,jeep,double track,two track"},"highway/traffic_signals":{"name":"Traffic Signals","terms":"light,stoplight,traffic light"},"highway/trunk":{"name":"Trunk Road","terms":""},"highway/trunk_link":{"name":"Trunk Link","terms":"ramp,on ramp,off ramp"},"highway/turning_circle":{"name":"Turning Circle","terms":"cul-de-sac"},"highway/unclassified":{"name":"Minor/Unclassified Road","terms":""},"historic":{"name":"Historic Site","terms":""},"historic/archaeological_site":{"name":"Archaeological Site","terms":""},"historic/boundary_stone":{"name":"Boundary Stone","terms":""},"historic/castle":{"name":"Castle","terms":""},"historic/memorial":{"name":"Memorial","terms":""},"historic/monument":{"name":"Monument","terms":""},"historic/ruins":{"name":"Ruins","terms":""},"historic/wayside_cross":{"name":"Wayside Cross","terms":""},"historic/wayside_shrine":{"name":"Wayside Shrine","terms":""},"junction":{"name":"Junction","terms":""},"landuse":{"name":"Land Use","terms":""},"landuse/allotments":{"name":"Community Garden","terms":"allotment,garden"},"landuse/basin":{"name":"Basin","terms":""},"landuse/cemetery":{"name":"Cemetery","terms":""},"landuse/churchyard":{"name":"Churchyard","terms":""},"landuse/commercial":{"name":"Commercial Area","terms":""},"landuse/construction":{"name":"Construction","terms":""},"landuse/farm":{"name":"Farmland","terms":""},"landuse/farmland":{"name":"Farmland","terms":""},"landuse/farmyard":{"name":"Farmyard","terms":""},"landuse/forest":{"name":"Forest","terms":"tree"},"landuse/garages":{"name":"Garages","terms":""},"landuse/grass":{"name":"Grass","terms":""},"landuse/industrial":{"name":"Industrial Area","terms":""},"landuse/landfill":{"name":"Landfill","terms":"dump"},"landuse/meadow":{"name":"Meadow","terms":""},"landuse/military":{"name":"Military Area","terms":""},"landuse/orchard":{"name":"Orchard","terms":""},"landuse/plant_nursery":{"name":"Plant Nursery","terms":"vivero"},"landuse/quarry":{"name":"Quarry","terms":""},"landuse/residential":{"name":"Residential Area","terms":""},"landuse/retail":{"name":"Retail Area","terms":""},"landuse/vineyard":{"name":"Vineyard","terms":""},"leisure":{"name":"Leisure","terms":""},"leisure/adult_gaming_centre":{"name":"Adult Gaming Center","terms":"gambling,slot machine"},"leisure/bird_hide":{"name":"Bird Hide","terms":"machan,ornithology"},"leisure/bowling_alley":{"name":"Bowling Alley","terms":""},"leisure/common":{"name":"Common","terms":"open space"},"leisure/dog_park":{"name":"Dog Park","terms":""},"leisure/firepit":{"name":"Firepit","terms":"fireplace,campfire"},"leisure/garden":{"name":"Garden","terms":""},"leisure/golf_course":{"name":"Golf Course","terms":"links"},"leisure/ice_rink":{"name":"Ice Rink","terms":"hockey,skating,curling"},"leisure/marina":{"name":"Marina","terms":"boat"},"leisure/nature_reserve":{"name":"Nature Reserve","terms":"protected,wildlife"},"leisure/park":{"name":"Park","terms":"esplanade,estate,forest,garden,grass,green,grounds,lawn,lot,meadow,parkland,place,playground,plaza,pleasure garden,recreation area,square,tract,village green,woodland"},"leisure/picnic_table":{"name":"Picnic Table","terms":"bench"},"leisure/pitch":{"name":"Sport Pitch","terms":"field"},"leisure/pitch/american_football":{"name":"American Football Field","terms":""},"leisure/pitch/baseball":{"name":"Baseball Diamond","terms":""},"leisure/pitch/basketball":{"name":"Basketball Court","terms":""},"leisure/pitch/rugby_league":{"name":"Rugby League Field","terms":""},"leisure/pitch/rugby_union":{"name":"Rugby Union Field","terms":""},"leisure/pitch/skateboard":{"name":"Skate Park","terms":""},"leisure/pitch/soccer":{"name":"Soccer Field","terms":""},"leisure/pitch/tennis":{"name":"Tennis Court","terms":""},"leisure/pitch/volleyball":{"name":"Volleyball Court","terms":""},"leisure/playground":{"name":"Playground","terms":"jungle gym,play area"},"leisure/running_track":{"name":"Running Track","terms":""},"leisure/slipway":{"name":"Slipway","terms":"boat launch,boat ramp"},"leisure/sports_centre":{"name":"Sports Center / Gym","terms":"gym"},"leisure/sports_centre/swimming":{"name":"Swimming Pool Facility","terms":"dive,water"},"leisure/stadium":{"name":"Stadium","terms":""},"leisure/swimming_pool":{"name":"Swimming Pool","terms":"dive,water"},"leisure/track":{"name":"Racetrack (non-Motorsport)","terms":""},"leisure/water_park":{"name":"Water Park","terms":"swim,pool,dive"},"line":{"name":"Line","terms":""},"man_made":{"name":"Man Made","terms":""},"man_made/adit":{"name":"Adit","terms":"entrance,underground,mine,cave"},"man_made/breakwater":{"name":"Breakwater","terms":""},"man_made/chimney":{"name":"Chimney","terms":""},"man_made/cutline":{"name":"Cut line","terms":""},"man_made/embankment":{"name":"Embankment","terms":""},"man_made/flagpole":{"name":"Flagpole","terms":""},"man_made/gasometer":{"name":"Gasometer","terms":"gas holder"},"man_made/groyne":{"name":"Groyne","terms":""},"man_made/lighthouse":{"name":"Lighthouse","terms":""},"man_made/mast":{"name":"Radio Mast","terms":"broadcast tower,cell phone tower,cell tower,guyed tower,mobile phone tower,radio tower,television tower,transmission mast,transmission tower,tv tower"},"man_made/observation":{"name":"Observation Tower","terms":"lookout tower,fire tower"},"man_made/petroleum_well":{"name":"Oil Well","terms":"drilling rig,oil derrick,oil drill,oil horse,oil rig,oil pump,petroleum well,pumpjack"},"man_made/pier":{"name":"Pier","terms":"dock"},"man_made/pipeline":{"name":"Pipeline","terms":""},"man_made/silo":{"name":"Silo","terms":"grain,corn,wheat"},"man_made/storage_tank":{"name":"Storage Tank","terms":"water,oil,gas,petrol"},"man_made/surveillance":{"name":"Surveillance","terms":""},"man_made/survey_point":{"name":"Survey Point","terms":""},"man_made/tower":{"name":"Tower","terms":""},"man_made/wastewater_plant":{"name":"Wastewater Plant","terms":"sewage*,water treatment plant,reclamation plant"},"man_made/water_tower":{"name":"Water Tower","terms":""},"man_made/water_well":{"name":"Water Well","terms":""},"man_made/water_works":{"name":"Water Works","terms":""},"man_made/works":{"name":"Works","terms":"car assembly plant,aluminium processing plant,brewery,furniture manufacture factory,oil refinery,plastic recycling"},"military/airfield":{"name":"Airfield","terms":""},"military/barracks":{"name":"Barracks","terms":""},"military/bunker":{"name":"Bunker","terms":""},"military/checkpoint":{"name":"Checkpoint","terms":""},"military/danger_area":{"name":"Danger Area","terms":""},"military/naval_base":{"name":"Naval Base","terms":""},"military/obstacle_course":{"name":"Obstacle Course","terms":""},"military/range":{"name":"Military Range","terms":""},"military/training_area":{"name":"Training area","terms":""},"natural":{"name":"Natural","terms":""},"natural/bay":{"name":"Bay","terms":""},"natural/beach":{"name":"Beach","terms":""},"natural/cave_entrance":{"name":"Cave Entrance","terms":"cavern,hollow,grotto,shelter,cavity"},"natural/cliff":{"name":"Cliff","terms":""},"natural/coastline":{"name":"Coastline","terms":"shore"},"natural/fell":{"name":"Fell","terms":""},"natural/glacier":{"name":"Glacier","terms":""},"natural/grassland":{"name":"Grassland","terms":""},"natural/heath":{"name":"Heath","terms":""},"natural/peak":{"name":"Peak","terms":"acme,aiguille,alp,climax,crest,crown,hill,mount,mountain,pinnacle,summit,tip,top"},"natural/saddle":{"name":"Saddle","terms":"pass,mountain pass,top"},"natural/scree":{"name":"Scree","terms":"loose rocks"},"natural/scrub":{"name":"Scrub","terms":"bush,shrubs"},"natural/spring":{"name":"Spring","terms":""},"natural/tree":{"name":"Tree","terms":""},"natural/tree_row":{"name":"Tree row","terms":""},"natural/volcano":{"name":"Volcano","terms":"mountain,crater"},"natural/water":{"name":"Water","terms":""},"natural/water/lake":{"name":"Lake","terms":"lakelet,loch,mere"},"natural/water/pond":{"name":"Pond","terms":"lakelet,millpond,tarn,pool,mere"},"natural/water/reservoir":{"name":"Reservoir","terms":""},"natural/wetland":{"name":"Wetland","terms":""},"natural/wood":{"name":"Wood","terms":"tree"},"office":{"name":"Office","terms":""},"office/accountant":{"name":"Accountant","terms":""},"office/administrative":{"name":"Administrative Office","terms":""},"office/architect":{"name":"Architect","terms":""},"office/company":{"name":"Company Office","terms":""},"office/educational_institution":{"name":"Educational Institution","terms":""},"office/employment_agency":{"name":"Employment Agency","terms":"job"},"office/estate_agent":{"name":"Real Estate Office","terms":""},"office/financial":{"name":"Financial Office","terms":""},"office/government":{"name":"Government Office","terms":""},"office/insurance":{"name":"Insurance Office","terms":""},"office/it":{"name":"IT Office","terms":""},"office/lawyer":{"name":"Law Office","terms":""},"office/newspaper":{"name":"Newspaper","terms":""},"office/ngo":{"name":"NGO Office","terms":""},"office/physician":{"name":"Physician","terms":""},"office/political_party":{"name":"Political Party","terms":""},"office/research":{"name":"Research Office","terms":""},"office/telecommunication":{"name":"Telecom Office","terms":""},"office/therapist":{"name":"Therapist","terms":""},"office/travel_agent":{"name":"Travel Agency","terms":""},"piste":{"name":"Piste/Ski Trail","terms":"ski,sled,sleigh,snowboard,nordic,downhill,snowmobile"},"place":{"name":"Place","terms":""},"place/city":{"name":"City","terms":""},"place/farm":{"name":"Farm","terms":""},"place/hamlet":{"name":"Hamlet","terms":""},"place/island":{"name":"Island","terms":"archipelago,atoll,bar,cay,isle,islet,key,reef"},"place/isolated_dwelling":{"name":"Isolated Dwelling","terms":""},"place/locality":{"name":"Locality","terms":""},"place/neighbourhood":{"name":"Neighborhood","terms":"neighbourhood"},"place/suburb":{"name":"Borough","terms":"Boro,Quarter"},"place/town":{"name":"Town","terms":""},"place/village":{"name":"Village","terms":""},"point":{"name":"Point","terms":""},"power":{"name":"Power","terms":""},"power/generator":{"name":"Power Generator","terms":""},"power/line":{"name":"Power Line","terms":""},"power/minor_line":{"name":"Minor Power Line","terms":""},"power/pole":{"name":"Power Pole","terms":""},"power/sub_station":{"name":"Substation","terms":""},"power/substation":{"name":"Substation","terms":""},"power/tower":{"name":"High-Voltage Tower","terms":""},"power/transformer":{"name":"Transformer","terms":""},"public_transport/platform":{"name":"Platform","terms":""},"public_transport/stop_position":{"name":"Stop Position","terms":""},"railway":{"name":"Railway","terms":""},"railway/abandoned":{"name":"Abandoned Railway","terms":""},"railway/disused":{"name":"Disused Railway","terms":""},"railway/funicular":{"name":"Funicular","terms":"venicular,cliff railway,cable car,cable railway,funicular railway"},"railway/halt":{"name":"Railway Halt","terms":"break,interrupt,rest,wait,interruption"},"railway/level_crossing":{"name":"Railway Crossing","terms":"crossing,railroad crossing,level crossing,grade crossing,road through railroad,train crossing"},"railway/monorail":{"name":"Monorail","terms":""},"railway/narrow_gauge":{"name":"Narrow Gauge Rail","terms":"narrow gauge railway,narrow gauge railroad"},"railway/platform":{"name":"Railway Platform","terms":""},"railway/rail":{"name":"Rail","terms":""},"railway/station":{"name":"Railway Station","terms":"train station,station"},"railway/subway":{"name":"Subway","terms":""},"railway/subway_entrance":{"name":"Subway Entrance","terms":""},"railway/tram":{"name":"Tram","terms":"streetcar"},"relation":{"name":"Relation","terms":""},"roundabout":{"name":"Roundabout","terms":""},"route/ferry":{"name":"Ferry Route","terms":""},"shop":{"name":"Shop","terms":""},"shop/alcohol":{"name":"Liquor Store","terms":"alcohol,beer,booze,wine"},"shop/anime":{"name":"Anime Shop","terms":""},"shop/antiques":{"name":"Antiques Shop","terms":""},"shop/art":{"name":"Art Store","terms":"art*,exhibit*,gallery"},"shop/baby_goods":{"name":"Baby Goods Store","terms":""},"shop/bag":{"name":"Bag/Luggage Store","terms":"handbag,purse"},"shop/bakery":{"name":"Bakery","terms":""},"shop/bathroom_furnishing":{"name":"Bathroom Furnishing Store","terms":""},"shop/beauty":{"name":"Beauty Shop","terms":"nail spa,spa,salon,tanning"},"shop/bed":{"name":"Bedding/Mattress Store","terms":""},"shop/beverages":{"name":"Beverage Store","terms":""},"shop/bicycle":{"name":"Bicycle Shop","terms":"bike,repair"},"shop/bookmaker":{"name":"Bookmaker","terms":""},"shop/books":{"name":"Book Store","terms":""},"shop/boutique":{"name":"Boutique","terms":""},"shop/butcher":{"name":"Butcher","terms":"meat"},"shop/candles":{"name":"Candle Shop","terms":""},"shop/car":{"name":"Car Dealership","terms":"auto"},"shop/car_parts":{"name":"Car Parts Store","terms":"auto"},"shop/car_repair":{"name":"Car Repair Shop","terms":"auto"},"shop/carpet":{"name":"Carpet Store","terms":"rug"},"shop/cheese":{"name":"Cheese Store","terms":""},"shop/chemist":{"name":"Chemist","terms":""},"shop/chocolate":{"name":"Chocolate Store","terms":""},"shop/clothes":{"name":"Clothing Store","terms":""},"shop/coffee":{"name":"Coffee Store","terms":""},"shop/computer":{"name":"Computer Store","terms":""},"shop/confectionery":{"name":"Candy Store","terms":""},"shop/convenience":{"name":"Convenience Store","terms":""},"shop/copyshop":{"name":"Copy Store","terms":""},"shop/cosmetics":{"name":"Cosmetics Store","terms":""},"shop/craft":{"name":"Arts and Crafts Store","terms":"art*,paint*,frame"},"shop/curtain":{"name":"Curtain Store","terms":"drape*,window"},"shop/dairy":{"name":"Dairy Store","terms":"milk,egg,cheese"},"shop/deli":{"name":"Deli","terms":"lunch,meat,sandwich"},"shop/department_store":{"name":"Department Store","terms":""},"shop/doityourself":{"name":"DIY Store","terms":""},"shop/dry_cleaning":{"name":"Dry Cleaner","terms":""},"shop/electronics":{"name":"Electronics Store","terms":"appliance,audio,computer,tv"},"shop/erotic":{"name":"Erotic Store","terms":"sex,porn"},"shop/fabric":{"name":"Fabric Store","terms":"sew"},"shop/farm":{"name":"Produce Stand","terms":"farm shop,farm stand"},"shop/fashion":{"name":"Fashion Store","terms":""},"shop/fishmonger":{"name":"Fishmonger","terms":""},"shop/florist":{"name":"Florist","terms":"flower"},"shop/frame":{"name":"Framing Shop","terms":"art*,paint*,photo*,frame"},"shop/funeral_directors":{"name":"Funeral Home","terms":"undertaker,memorial home"},"shop/furnace":{"name":"Furnace Store","terms":"oven,stove"},"shop/furniture":{"name":"Furniture Store","terms":"chair,sofa,table"},"shop/garden_centre":{"name":"Garden Center","terms":"landscape,mulch,shrub,tree"},"shop/gift":{"name":"Gift Shop","terms":""},"shop/greengrocer":{"name":"Greengrocer","terms":"fruit,vegetable"},"shop/hairdresser":{"name":"Hairdresser","terms":""},"shop/hardware":{"name":"Hardware Store","terms":""},"shop/hearing_aids":{"name":"Hearing Aids Store","terms":""},"shop/herbalist":{"name":"Herbalist","terms":""},"shop/hifi":{"name":"Hifi Store","terms":"stereo,video"},"shop/houseware":{"name":"Houseware Store","terms":"home,household"},"shop/interior_decoration":{"name":"Interior Decoration Store","terms":""},"shop/jewelry":{"name":"Jeweler","terms":"diamond,gem,ring"},"shop/kiosk":{"name":"News Kiosk","terms":""},"shop/kitchen":{"name":"Kitchen Design Store","terms":""},"shop/laundry":{"name":"Laundry","terms":""},"shop/leather":{"name":"Leather Store","terms":""},"shop/locksmith":{"name":"Locksmith","terms":"key,lockpick"},"shop/lottery":{"name":"Lottery Shop","terms":""},"shop/mall":{"name":"Mall","terms":""},"shop/massage":{"name":"Massage Shop","terms":""},"shop/medical_supply":{"name":"Medical Supply Store","terms":""},"shop/mobile_phone":{"name":"Mobile Phone Store","terms":""},"shop/money_lender":{"name":"Money Lender","terms":""},"shop/motorcycle":{"name":"Motorcycle Dealership","terms":""},"shop/music":{"name":"Music Store","terms":"CD,vinyl"},"shop/musical_instrument":{"name":"Musical Instrument Store","terms":""},"shop/newsagent":{"name":"Newspaper/Magazine Shop","terms":""},"shop/nutrition_supplements":{"name":"Nutrition Supplements Store","terms":""},"shop/optician":{"name":"Optician","terms":"eye,glasses"},"shop/organic":{"name":"Organic Goods Store","terms":""},"shop/outdoor":{"name":"Outdoors Store","terms":"camping,climbing,hiking"},"shop/paint":{"name":"Paint Store","terms":""},"shop/pawnbroker":{"name":"Pawn Shop","terms":""},"shop/pet":{"name":"Pet Store","terms":"cat,dog,fish"},"shop/photo":{"name":"Photography Store","terms":"camera,film"},"shop/pyrotechnics":{"name":"Fireworks Store","terms":""},"shop/radiotechnics":{"name":"Radio/Electronic Component Store","terms":""},"shop/religion":{"name":"Religious Store","terms":""},"shop/scuba_diving":{"name":"Scuba Diving Shop","terms":""},"shop/seafood":{"name":"Seafood Shop","terms":"fishmonger"},"shop/second_hand":{"name":"Consignment/Thrift Store","terms":"secondhand,second hand,resale,thrift,used"},"shop/shoes":{"name":"Shoe Store","terms":""},"shop/sports":{"name":"Sporting Goods Store","terms":""},"shop/stationery":{"name":"Stationery Store","terms":"card,paper"},"shop/storage_rental":{"name":"Storage Rental","terms":""},"shop/supermarket":{"name":"Supermarket","terms":"grocery,store,shop"},"shop/tailor":{"name":"Tailor","terms":"clothes,suit"},"shop/tattoo":{"name":"Tattoo Parlor","terms":""},"shop/tea":{"name":"Tea Store","terms":""},"shop/ticket":{"name":"Ticket Seller","terms":""},"shop/tobacco":{"name":"Tobacco Shop","terms":""},"shop/toys":{"name":"Toy Store","terms":""},"shop/travel_agency":{"name":"Travel Agency","terms":""},"shop/tyres":{"name":"Tire Store","terms":""},"shop/vacant":{"name":"Vacant Shop","terms":""},"shop/vacuum_cleaner":{"name":"Vacuum Cleaner Store","terms":""},"shop/variety_store":{"name":"Variety Store","terms":""},"shop/video":{"name":"Video Store","terms":"DVD"},"shop/video_games":{"name":"Video Game Store","terms":""},"shop/water_sports":{"name":"Watersport/Swim Shop","terms":""},"shop/weapons":{"name":"Weapon Shop","terms":"ammo,gun,knife,knives"},"shop/window_blind":{"name":"Window Blind Store","terms":""},"shop/wine":{"name":"Wine Shop","terms":""},"tourism":{"name":"Tourism","terms":""},"tourism/alpine_hut":{"name":"Alpine Hut","terms":""},"tourism/artwork":{"name":"Artwork","terms":"mural,sculpture,statue"},"tourism/attraction":{"name":"Tourist Attraction","terms":""},"tourism/camp_site":{"name":"Camp Site","terms":"Tent"},"tourism/caravan_site":{"name":"RV Park","terms":"Motor Home,Camper"},"tourism/chalet":{"name":"Chalet","terms":""},"tourism/gallery":{"name":"Art Gallery","terms":"art*,exhibit*,paint*,photo*,sculpt*"},"tourism/guest_house":{"name":"Guest House","terms":"B&B,Bed and Breakfast"},"tourism/hostel":{"name":"Hostel","terms":""},"tourism/hotel":{"name":"Hotel","terms":""},"tourism/information":{"name":"Information","terms":""},"tourism/motel":{"name":"Motel","terms":""},"tourism/museum":{"name":"Museum","terms":"art*,exhibit*,gallery,foundation,hall,institution,paint*,photo*,sculpt*"},"tourism/picnic_site":{"name":"Picnic Site","terms":"camp"},"tourism/theme_park":{"name":"Theme Park","terms":""},"tourism/viewpoint":{"name":"Viewpoint","terms":""},"tourism/zoo":{"name":"Zoo","terms":""},"traffic_calming/bump":{"name":"Speed Bump","terms":"speed hump"},"traffic_calming/hump":{"name":"Speed Hump","terms":"speed bump"},"traffic_calming/rumble_strip":{"name":"Rumble Strip","terms":"sleeper lines,audible lines,growlers"},"traffic_calming/table":{"name":"Raised Pedestrian Crossing","terms":"speed table,flat top hump"},"type/boundary":{"name":"Boundary","terms":""},"type/boundary/administrative":{"name":"Administrative Boundary","terms":""},"type/multipolygon":{"name":"Multipolygon","terms":""},"type/restriction":{"name":"Restriction","terms":""},"type/restriction/no_left_turn":{"name":"No Left Turn","terms":""},"type/restriction/no_right_turn":{"name":"No Right Turn","terms":""},"type/restriction/no_straight_on":{"name":"No Straight On","terms":""},"type/restriction/no_u_turn":{"name":"No U-turn","terms":""},"type/restriction/only_left_turn":{"name":"Left Turn Only","terms":""},"type/restriction/only_right_turn":{"name":"Right Turn Only","terms":""},"type/restriction/only_straight_on":{"name":"No Turns","terms":""},"type/route":{"name":"Route","terms":""},"type/route/bicycle":{"name":"Cycle Route","terms":""},"type/route/bus":{"name":"Bus Route","terms":""},"type/route/detour":{"name":"Detour Route","terms":""},"type/route/ferry":{"name":"Ferry Route","terms":""},"type/route/foot":{"name":"Foot Route","terms":""},"type/route/hiking":{"name":"Hiking Route","terms":""},"type/route/pipeline":{"name":"Pipeline Route","terms":""},"type/route/power":{"name":"Power Route","terms":""},"type/route/road":{"name":"Road Route","terms":""},"type/route/train":{"name":"Train Route","terms":""},"type/route/tram":{"name":"Tram Route","terms":""},"type/route_master":{"name":"Route Master","terms":""},"type/site":{"name":"Site","terms":""},"vertex":{"name":"Other","terms":""},"waterway":{"name":"Waterway","terms":""},"waterway/boatyard":{"name":"Boatyard","terms":""},"waterway/canal":{"name":"Canal","terms":""},"waterway/dam":{"name":"Dam","terms":""},"waterway/ditch":{"name":"Ditch","terms":""},"waterway/dock":{"name":"Wet Dock / Dry Dock","terms":"boat,ship,vessel,marine"},"waterway/drain":{"name":"Drain","terms":""},"waterway/fuel":{"name":"Marine Fuel Station","terms":"petrol,gas,diesel,boat"},"waterway/river":{"name":"River","terms":"beck,branch,brook,course,creek,estuary,rill,rivulet,run,runnel,stream,tributary,watercourse"},"waterway/riverbank":{"name":"Riverbank","terms":""},"waterway/sanitary_dump_station":{"name":"Marine Toilet Disposal","terms":"Boat,Watercraft,Sanitary,Dump Station,Pumpout,Pump out,Elsan,CDP,CTDP,Chemical Toilet"},"waterway/stream":{"name":"Stream","terms":"beck,branch,brook,burn,course,creek,current,drift,flood,flow,freshet,race,rill,rindle,rivulet,run,runnel,rush,spate,spritz,surge,tide,torrent,tributary,watercourse"},"waterway/water_point":{"name":"Marine Drinking Water","terms":""},"waterway/weir":{"name":"Weir","terms":""}}}},"suggestions":{"amenity":{"fuel":{"76":{"count":314},"Neste":{"count":189},"BP":{"count":2511},"Shell":{"count":8380},"Agip":{"count":2651},"Migrol":{"count":65},"Avia":{"count":897},"Texaco":{"count":680},"Total":{"count":2607},"Statoil":{"count":596},"Esso":{"count":3652},"Jet":{"count":441},"Avanti":{"count":90},"Sainsbury's":{"count":58},"OMV":{"count":701},"Aral":{"count":1339},"Tesco":{"count":197},"JET":{"count":180},"Morrisons":{"count":111},"United":{"count":91},"Canadian Tire":{"count":66},"Mobil":{"count":613},"Caltex":{"count":1001},"Sunoco":{"count":355},"Q8":{"count":1161},"ABC":{"count":79},"ARAL":{"count":375},"CEPSA":{"count":1018},"BFT":{"count":89},"Petron":{"count":878},"Intermarché":{"count":434},"Total Access":{"count":51},"Super U":{"count":124},"Auchan":{"count":53},"Elf":{"count":129},"Carrefour":{"count":205},"Station Service E. Leclerc":{"count":530},"Shell Express":{"count":131},"Hess":{"count":127},"Flying V":{"count":129},"bft":{"count":168},"Gulf":{"count":199},"PTT":{"count":191},"St1":{"count":100},"Teboil":{"count":115},"HEM":{"count":212},"GALP":{"count":626},"OK":{"count":163},"ÖMV":{"count":101},"Tinq":{"count":215},"OKQ8":{"count":186},"Repsol":{"count":424},"Westfalen":{"count":96},"Esso Express":{"count":98},"Raiffeisenbank":{"count":117},"Tamoil":{"count":866},"Engen":{"count":241},"Sasol":{"count":59},"Topaz":{"count":78},"LPG":{"count":174},"Coop":{"count":62},"Orlen":{"count":598},"Oilibya":{"count":68},"Tango":{"count":122},"Star":{"count":319},"Петрол":{"count":84},"Cepsa":{"count":96},"OIL!":{"count":63},"Ultramar":{"count":125},"Irving":{"count":87},"Lukoil":{"count":701},"Petro-Canada":{"count":489},"7-Eleven":{"count":488},"Agrola":{"count":69},"Husky":{"count":126},"Slovnaft":{"count":219},"Sheetz":{"count":134},"Mol":{"count":61},"Petronas":{"count":159},"Газпромнефть":{"count":748},"Лукойл":{"count":1477},"Elan":{"count":112},"Роснефть":{"count":638},"Turmöl":{"count":57},"Neste A24":{"count":55},"Marathon":{"count":189},"Valero":{"count":366},"Eni":{"count":236},"Chevron":{"count":954},"ТНК":{"count":520},"REPSOL":{"count":1603},"MOL":{"count":228},"Bliska":{"count":150},"Api":{"count":302},"Arco":{"count":179},"Pemex":{"count":423},"Exxon":{"count":506},"Coles Express":{"count":115},"Petrom":{"count":259},"PETRONOR":{"count":207},"Rompetrol":{"count":174},"Lotos":{"count":178},"ОМВ":{"count":60},"BR":{"count":129},"Copec":{"count":505},"Petrobras":{"count":270},"Liberty":{"count":55},"IP":{"count":871},"Erg":{"count":596},"Eneos":{"count":97},"Citgo":{"count":279},"Metano":{"count":208},"Сургутнефтегаз":{"count":61},"EKO":{"count":59},"Eko":{"count":58},"Indipend.":{"count":172},"IES":{"count":63},"TotalErg":{"count":89},"Cenex":{"count":115},"ПТК":{"count":82},"HP":{"count":79},"Phillips 66":{"count":216},"CARREFOUR":{"count":74},"ERG":{"count":76},"Speedway":{"count":148},"Benzina":{"count":96},"Татнефть":{"count":264},"Terpel":{"count":259},"WOG":{"count":189},"Seaoil":{"count":54},"АЗС":{"count":1077},"Kwik Trip":{"count":108},"Wawa":{"count":89},"Pertamina":{"count":186},"COSMO":{"count":64},"Z":{"count":76},"Indian Oil":{"count":183},"АГЗС":{"count":494},"INA":{"count":121},"JOMO":{"count":62},"Holiday":{"count":97},"YPF":{"count":70},"IDEMITSU":{"count":87},"ENEOS":{"count":736},"Bharat Petroleum":{"count":64},"CAMPSA":{"count":615},"Casey's General Store":{"count":190},"Башнефть":{"count":60},"Kangaroo":{"count":60},"コスモ石油 (COSMO)":{"count":136},"MEROIL":{"count":77},"1-2-3":{"count":71},"出光":{"count":228,"tags":{"name:en":"IDEMITSU"}},"НК Альянс":{"count":88},"Sinclair":{"count":100},"Conoco":{"count":189},"SPBU":{"count":54},"Макпетрол":{"count":109},"Circle K":{"count":166},"Posto Ipiranga":{"count":70},"Posto Shell":{"count":54},"Phoenix":{"count":144},"Ipiranga":{"count":119},"OKKO":{"count":85},"ОККО":{"count":119},"บางจาก":{"count":60},"QuikTrip":{"count":105},"Stewart's":{"count":63},"Posto BR":{"count":68},"ป ต ท":{"count":152},"ปตท":{"count":88},"ANP":{"count":80},"Kum & Go":{"count":80},"Petrolimex":{"count":55},"Sokimex":{"count":66},"Tela":{"count":154},"Posto":{"count":71},"H-E-B":{"count":182},"Укрнафта":{"count":58},"Татнефтепродукт":{"count":54},"Afriquia":{"count":88},"Murphy USA":{"count":67},"昭和シェル (Showa-shell)":{"count":94},"エネオス":{"count":53},"CNG":{"count":94}},"pub":{"Kings Arms":{"count":67},"The Ship":{"count":89},"The White Horse":{"count":204},"The White Hart":{"count":226},"Royal Oak":{"count":150},"The Red Lion":{"count":233},"The Kings Arms":{"count":58},"The Star":{"count":73},"The Anchor":{"count":64},"The Cross Keys":{"count":55},"The Wheatsheaf":{"count":117},"The Crown Inn":{"count":67},"The Kings Head":{"count":53},"The Castle":{"count":62},"The Railway":{"count":102},"The White Lion":{"count":118},"The Bell":{"count":121},"The Bull":{"count":68},"The Plough":{"count":179},"The George":{"count":110},"The Royal Oak":{"count":209},"The Fox":{"count":74},"Prince of Wales":{"count":77},"The Rising Sun":{"count":71},"The Prince of Wales":{"count":51},"The Crown":{"count":244},"The Chequers":{"count":66},"The Swan":{"count":152},"Rose and Crown":{"count":79},"The Victoria":{"count":67},"New Inn":{"count":90},"Royal Hotel":{"count":57},"Red Lion":{"count":207},"Cross Keys":{"count":61},"The Greyhound":{"count":96},"The Black Horse":{"count":94},"The New Inn":{"count":105},"Kings Head":{"count":59},"The Albion":{"count":51},"The Angel":{"count":52},"The Queens Head":{"count":52},"The Ship Inn":{"count":83},"Rose & Crown":{"count":51},"Queens Head":{"count":52},"Irish Pub":{"count":76}},"fast_food":{"Quick":{"count":484},"McDonald's":{"count":12376,"tags":{"cuisine":"burger"}},"Subway":{"count":5576,"tags":{"cuisine":"sandwich"}},"Burger King":{"count":3734,"tags":{"cuisine":"burger"}},"Ali Baba":{"count":61},"Hungry Jacks":{"count":173,"tags":{"cuisine":"burger"}},"Red Rooster":{"count":148},"KFC":{"count":3198,"tags":{"cuisine":"chicken"}},"Domino's Pizza":{"count":985,"tags":{"cuisine":"pizza"}},"Chowking":{"count":142},"Jollibee":{"count":396},"Hesburger":{"count":102},"肯德基":{"count":86},"Wendy's":{"count":1621,"tags":{"cuisine":"burger"}},"Tim Hortons":{"count":323},"Steers":{"count":151},"Hardee's":{"count":268,"tags":{"cuisine":"burger"}},"Arby's":{"count":782},"A&W":{"count":283},"Dairy Queen":{"count":791},"Hallo Pizza":{"count":76},"Fish & Chips":{"count":93},"Harvey's":{"count":90},"麥當勞":{"count":65},"Pizza Pizza":{"count":215},"Kotipizza":{"count":74},"Jack in the Box":{"count":546,"tags":{"cuisine":"burger"}},"Istanbul":{"count":56},"Kochlöffel":{"count":68},"Döner":{"count":228},"Telepizza":{"count":201},"Sibylla":{"count":61},"Carl's Jr.":{"count":298,"tags":{"cuisine":"burger"}},"Quiznos":{"count":266,"tags":{"cuisine":"sandwich"}},"Wimpy":{"count":141},"Sonic":{"count":566,"tags":{"cuisine":"burger"}},"Taco Bell":{"count":1423,"tags":{"cuisine":"mexican"}},"Pizza Nova":{"count":63},"Papa John's":{"count":304,"tags":{"cuisine":"pizza"}},"Nordsee":{"count":159},"Mr. Sub":{"count":103},"Макдоналдс":{"count":324,"tags":{"name:en":"McDonald's"}},"Asia Imbiss":{"count":111},"Chipotle":{"count":290,"tags":{"cuisine":"mexican"}},"マクドナルド":{"count":692,"tags":{"name:en":"McDonald's","cuisine":"burger"}},"In-N-Out Burger":{"count":65},"Jimmy John's":{"count":141},"Jamba Juice":{"count":68},"Робин Сдобин":{"count":82},"Baskin Robbins":{"count":74},"ケンタッキーフライドチキン":{"count":164,"tags":{"name:en":"KFC","cuisine":"chicken"}},"吉野家":{"count":191},"Taco Time":{"count":88},"松屋":{"count":281,"tags":{"name:en":"Matsuya"}},"Little Caesars":{"count":81},"El Pollo Loco":{"count":63},"Del Taco":{"count":141},"White Castle":{"count":80},"Boston Market":{"count":66},"Chick-fil-A":{"count":257,"tags":{"cuisine":"chicken"}},"Panda Express":{"count":238,"tags":{"cuisine":"chinese"}},"Whataburger":{"count":364},"Taco John's":{"count":78},"Теремок":{"count":68},"Culver's":{"count":425},"Five Guys":{"count":141},"Church's Chicken":{"count":95},"Popeye's":{"count":167,"tags":{"cuisine":"chicken"}},"Long John Silver's":{"count":93},"Pollo Campero":{"count":62},"Zaxby's":{"count":51},"すき家":{"count":276,"tags":{"name:en":"SUKIYA"}},"モスバーガー":{"count":257,"tags":{"name:en":"MOS BURGER"}},"Русский Аппетит":{"count":69},"なか卯":{"count":63}},"restaurant":{"Pizza Hut":{"count":1180,"tags":{"cuisine":"pizza"}},"Little Chef":{"count":64},"Adler":{"count":158},"Zur Krone":{"count":90},"Deutsches Haus":{"count":90},"Krone":{"count":171},"Akropolis":{"count":152},"Schützenhaus":{"count":124},"Kreuz":{"count":74},"Waldschänke":{"count":55},"La Piazza":{"count":69},"Lamm":{"count":66},"Zur Sonne":{"count":73},"Zur Linde":{"count":204},"Poseidon":{"count":110},"Shanghai":{"count":82},"Red Lobster":{"count":235},"Zum Löwen":{"count":84},"Swiss Chalet":{"count":107},"Olympia":{"count":74},"Wagamama":{"count":64},"Frankie & Benny's":{"count":66},"Hooters":{"count":103},"Sternen":{"count":78},"Hirschen":{"count":79},"Denny's":{"count":450},"Athen":{"count":68},"Sonne":{"count":126},"Hirsch":{"count":79},"Ratskeller":{"count":150},"La Cantina":{"count":56},"Gasthaus Krone":{"count":56},"El Greco":{"count":86},"Gasthof zur Post":{"count":79},"Nando's":{"count":246},"Löwen":{"count":112},"La Pataterie":{"count":51},"Bella Napoli":{"count":53},"Pizza Express":{"count":262},"Mandarin":{"count":65},"Hong Kong":{"count":83},"Zizzi":{"count":68},"Cracker Barrel":{"count":183},"Rhodos":{"count":81},"Lindenhof":{"count":79},"Milano":{"count":54},"Dolce Vita":{"count":77},"Kirchenwirt":{"count":81},"Kantine":{"count":52},"Ochsen":{"count":95},"Spur":{"count":62},"Mykonos":{"count":59},"Lotus":{"count":66},"Applebee's":{"count":531},"Flunch":{"count":72},"Zur Post":{"count":116},"China Town":{"count":76},"La Dolce Vita":{"count":73},"Waffle House":{"count":207},"Delphi":{"count":88},"Linde":{"count":103},"Outback Steakhouse":{"count":218},"Dionysos":{"count":69},"Kelsey's":{"count":57},"Boston Pizza":{"count":165},"Bella Italia":{"count":132},"Sizzler":{"count":53},"Grüner Baum":{"count":116},"Taj Mahal":{"count":104},"Rössli":{"count":68},"Wimpy":{"count":51},"Traube":{"count":65},"Adria":{"count":52},"Red Robin":{"count":185},"Roma":{"count":61},"San Marco":{"count":67},"Hellas":{"count":55},"La Perla":{"count":67},"Vips":{"count":53},"Panera Bread":{"count":218},"Da Vinci":{"count":54},"Hippopotamus":{"count":96},"Prezzo":{"count":75},"Courtepaille":{"count":106},"Hard Rock Cafe":{"count":70},"Panorama":{"count":61},"デニーズ":{"count":82},"Sportheim":{"count":65},"餃子の王将":{"count":57},"Bären":{"count":60},"Alte Post":{"count":60},"Pizzeria Roma":{"count":51},"China Garden":{"count":66},"Vapiano":{"count":82},"Mamma Mia":{"count":64},"Schwarzer Adler":{"count":57},"IHOP":{"count":317},"Chili's":{"count":328},"Asia":{"count":51},"Olive Garden":{"count":279},"TGI Friday's":{"count":159},"Friendly's":{"count":78},"Buffalo Grill":{"count":202},"Texas Roadhouse":{"count":110},"ガスト":{"count":230,"tags":{"name:en":"Gusto"}},"Sakura":{"count":75},"Mensa":{"count":99},"The Keg":{"count":53},"サイゼリヤ":{"count":93},"La Strada":{"count":52},"Village Inn":{"count":92},"Buffalo Wild Wings":{"count":176},"Peking":{"count":59},"Boston Market":{"count":61},"Round Table Pizza":{"count":53},"Jimmy John's":{"count":69},"California Pizza Kitchen":{"count":61},"Якитория":{"count":77},"Golden Corral":{"count":101},"Perkins":{"count":105},"Ruby Tuesday":{"count":162},"Shari's":{"count":65},"Bob Evans":{"count":129},"바다횟집 (Bada Fish Restaurant)":{"count":55},"Mang Inasal":{"count":84},"Евразия":{"count":102},"ジョナサン":{"count":59},"Longhorn Steakhouse":{"count":66}},"bank":{"Chase":{"count":721},"Commonwealth Bank":{"count":232},"Citibank":{"count":277},"HSBC":{"count":1102},"Barclays":{"count":965},"Westpac":{"count":208},"NAB":{"count":131},"ANZ":{"count":218},"Lloyds Bank":{"count":547},"Landbank":{"count":81},"Sparkasse":{"count":4555},"UCPB":{"count":92},"PNB":{"count":244},"Metrobank":{"count":269},"BDO":{"count":290},"Volksbank":{"count":2591},"BPI":{"count":415},"Postbank":{"count":443},"NatWest":{"count":628},"Raiffeisenbank":{"count":2119},"Yorkshire Bank":{"count":63},"ABSA":{"count":95},"Standard Bank":{"count":109},"FNB":{"count":97},"Deutsche Bank":{"count":855},"SEB":{"count":133},"Commerzbank":{"count":806},"Targobank":{"count":166},"ABN AMRO":{"count":130},"Handelsbanken":{"count":184},"Swedbank":{"count":223},"Kreissparkasse":{"count":600},"UniCredit Bank":{"count":408},"Monte dei Paschi di Siena":{"count":132},"Caja Rural":{"count":99},"Dresdner Bank":{"count":66},"Sparda-Bank":{"count":320},"VÚB":{"count":107},"Slovenská sporiteľňa":{"count":134},"Bank of Montreal":{"count":118},"KBC":{"count":203},"Royal Bank of Scotland":{"count":111},"TSB":{"count":80},"US Bank":{"count":256},"HypoVereinsbank":{"count":561},"Bank Austria":{"count":176},"ING":{"count":496},"Erste Bank":{"count":180},"CIBC":{"count":326},"Scotiabank":{"count":413},"Caisse d'Épargne":{"count":882},"Santander":{"count":1323},"Bank of Scotland":{"count":89},"TD Canada Trust":{"count":450},"BMO":{"count":169},"Danske Bank":{"count":131},"OTP":{"count":192},"Crédit Agricole":{"count":1239},"LCL":{"count":553},"VR-Bank":{"count":430},"ČSOB":{"count":160},"Česká spořitelna":{"count":212},"BNP":{"count":112},"Royal Bank":{"count":65},"Nationwide":{"count":209},"Halifax":{"count":225},"BAWAG PSK":{"count":102},"National Bank":{"count":84},"Nedbank":{"count":80},"First National Bank":{"count":85},"Nordea":{"count":319},"Rabobank":{"count":609},"Sparkasse KölnBonn":{"count":69},"Tatra banka":{"count":67},"Berliner Sparkasse":{"count":62},"Berliner Volksbank":{"count":77},"Wells Fargo":{"count":874},"Credit Suisse":{"count":71},"Société Générale":{"count":634},"Osuuspankki":{"count":75},"Sparkasse Aachen":{"count":56},"Hamburger Sparkasse":{"count":156},"Cassa di Risparmio del Veneto":{"count":68},"BNP Paribas":{"count":617},"Banque Populaire":{"count":433},"BNP Paribas Fortis":{"count":209},"Banco Popular":{"count":291},"Bancaja":{"count":55},"Banesto":{"count":208},"La Caixa":{"count":583},"Santander Consumer Bank":{"count":88},"BRD":{"count":191},"BCR":{"count":143},"Banca Transilvania":{"count":141},"BW-Bank":{"count":97},"Komerční banka":{"count":132},"Banco Pastor":{"count":64},"Stadtsparkasse":{"count":86},"Ulster Bank":{"count":86},"Sberbank":{"count":58},"CIC":{"count":427},"Bancpost":{"count":56},"Caja Madrid":{"count":115},"Maybank":{"count":94},"中国银行":{"count":85},"Unicredit Banca":{"count":243},"Crédit Mutuel":{"count":690},"BBVA":{"count":647},"Intesa San Paolo":{"count":69},"TD Bank":{"count":206},"Belfius":{"count":231},"Bank of America":{"count":924},"RBC":{"count":230},"Alpha Bank":{"count":123},"Сбербанк":{"count":4794},"Россельхозбанк":{"count":201},"Crédit du Nord":{"count":96},"BancoEstado":{"count":80},"Millennium Bank":{"count":414},"State Bank of India":{"count":151},"Беларусбанк":{"count":242},"ING Bank Śląski":{"count":67},"Caixa Geral de Depósitos":{"count":129},"Kreissparkasse Köln":{"count":65},"Banco BCI":{"count":51},"Banco de Chile":{"count":98},"ВТБ24":{"count":326},"UBS":{"count":134},"PKO BP":{"count":265},"Chinabank":{"count":55},"PSBank":{"count":59},"Union Bank":{"count":124},"China Bank":{"count":66},"RCBC":{"count":122},"Unicaja":{"count":83},"BBK":{"count":79},"Ibercaja":{"count":69},"RBS":{"count":143},"Commercial Bank of Ceylon PLC":{"count":79},"Bank of Ireland":{"count":109},"BNL":{"count":87},"Banco Santander":{"count":138},"Banco Itaú":{"count":111},"AIB":{"count":72},"BZ WBK":{"count":77},"Banco do Brasil":{"count":557},"Caixa Econômica Federal":{"count":184},"Fifth Third Bank":{"count":84},"Banca Popolare di Vicenza":{"count":81},"Wachovia":{"count":58},"OLB":{"count":53},"みずほ銀行":{"count":78},"BES":{"count":72},"ICICI Bank":{"count":91},"HDFC Bank":{"count":91},"La Banque Postale":{"count":67},"Pekao SA":{"count":56},"Oberbank":{"count":90},"Bradesco":{"count":295},"Oldenburgische Landesbank":{"count":56},"Bendigo Bank":{"count":93},"Argenta":{"count":86},"AXA":{"count":68},"Axis Bank":{"count":61},"Banco Nación":{"count":67},"GE Money Bank":{"count":72},"Альфа-Банк":{"count":185},"Белагропромбанк":{"count":70},"Caja Círculo":{"count":65},"Banco Galicia":{"count":51},"Eurobank":{"count":97},"Banca Intesa":{"count":62},"Canara Bank":{"count":92},"Cajamar":{"count":77},"Banamex":{"count":149},"Crédit Mutuel de Bretagne":{"count":335},"Davivienda":{"count":83},"Bank Spółdzielczy":{"count":159},"Credit Agricole":{"count":157},"Bankinter":{"count":59},"Banque Nationale":{"count":63},"Bank of the West":{"count":96},"Key Bank":{"count":155},"Western Union":{"count":88},"Citizens Bank":{"count":115},"ПриватБанк":{"count":513},"Security Bank":{"count":78},"Millenium":{"count":60},"Bankia":{"count":149},"三菱東京UFJ銀行":{"count":159},"Caixa":{"count":117},"Banco de Costa Rica":{"count":63},"SunTrust Bank":{"count":73},"Itaú":{"count":338},"PBZ":{"count":52},"中国工商银行":{"count":51},"Bancolombia":{"count":89},"Райффайзен Банк Аваль":{"count":64},"Bancomer":{"count":115},"Banorte":{"count":80},"Alior Bank":{"count":81},"BOC":{"count":51},"Банк Москвы":{"count":118},"ВТБ":{"count":59},"Getin Bank":{"count":55},"Caja Duero":{"count":57},"Regions Bank":{"count":62},"Росбанк":{"count":177},"Banco Estado":{"count":72},"BCI":{"count":68},"SunTrust":{"count":68},"PNC Bank":{"count":254},"신한은행":{"count":217,"tags":{"name:en":"Sinhan Bank"}},"우리은행":{"count":291,"tags":{"name:en":"Uri Bank"}},"국민은행":{"count":165,"tags":{"name:en":"Gungmin Bank"}},"중소기업은행":{"count":52,"tags":{"name:en":"Industrial Bank of Korea"}},"광주은행":{"count":51,"tags":{"name:en":"Gwangju Bank"}},"Газпромбанк":{"count":100},"M&T Bank":{"count":92},"Caja de Burgos":{"count":51},"Santander Totta":{"count":69},"УкрСиббанк":{"count":192},"Ощадбанк":{"count":364},"Уралсиб":{"count":85},"りそな銀行":{"count":225,"tags":{"name:en":"Mizuho Bank"}},"Ecobank":{"count":66},"Cajero Automatico Bancared":{"count":145},"Промсвязьбанк":{"count":93},"三井住友銀行":{"count":129},"Banco Provincia":{"count":67},"BB&T":{"count":147},"Возрождение":{"count":59},"Capital One":{"count":59},"横浜銀行":{"count":51},"Bank Mandiri":{"count":62},"Banco de la Nación":{"count":92},"Banco G&T Continental":{"count":62},"Peoples Bank":{"count":60},"工商银行":{"count":51},"Совкомбанк":{"count":55},"Provincial":{"count":56},"Banco de Desarrollo Banrural":{"count":73},"Banco Bradesco":{"count":65},"Bicentenario":{"count":182},"ლიბერთი ბანკი":{"count":54,"tags":{"name:en":"Liberty Bank"}},"Banesco":{"count":108},"Mercantil":{"count":75},"Bank BRI":{"count":53},"Del Tesoro":{"count":91},"하나은행":{"count":77},"CityCommerce Bank":{"count":71},"De Venezuela":{"count":117}},"car_rental":{"Europcar":{"count":291},"Budget":{"count":92},"Sixt":{"count":161},"Avis":{"count":282},"Hertz":{"count":293},"Enterprise":{"count":199},"stadtmobil CarSharing-Station":{"count":148}},"pharmacy":{"Rowlands Pharmacy":{"count":71},"Boots":{"count":840},"Marien-Apotheke":{"count":314},"Mercury Drug":{"count":426},"Löwen-Apotheke":{"count":356},"Superdrug":{"count":117},"Sonnen-Apotheke":{"count":311},"Rathaus-Apotheke":{"count":132},"Engel-Apotheke":{"count":123},"Hirsch-Apotheke":{"count":83},"Stern-Apotheke":{"count":67},"Lloyds Pharmacy":{"count":295},"Rosen-Apotheke":{"count":208},"Stadt-Apotheke":{"count":302},"Markt-Apotheke":{"count":164},"Аптека":{"count":1989},"Pharmasave":{"count":64},"Brunnen-Apotheke":{"count":53},"Shoppers Drug Mart":{"count":430},"Apotheke am Markt":{"count":60},"Alte Apotheke":{"count":88},"Neue Apotheke":{"count":109},"Gintarinė vaistinė":{"count":101},"Rats-Apotheke":{"count":84},"Adler Apotheke":{"count":313},"Pharmacie Centrale":{"count":64},"Walgreens":{"count":1619},"Rite Aid":{"count":745},"Apotheke":{"count":165},"Linden-Apotheke":{"count":211},"Bahnhof-Apotheke":{"count":66},"Burg-Apotheke":{"count":55},"Jean Coutu":{"count":62},"Pharmaprix":{"count":60},"Farmacias Ahumada":{"count":104},"Farmacia Comunale":{"count":113},"Farmacias Cruz Verde":{"count":86},"Cruz Verde":{"count":99},"Hubertus Apotheke":{"count":52},"CVS":{"count":1560},"Farmacias SalcoBrand":{"count":133},"Фармация":{"count":120},"Bären-Apotheke":{"count":74},"Clicks":{"count":113},"セイジョー":{"count":53},"マツモトキヨシ":{"count":115},"Dr. Max":{"count":51},"Вита":{"count":106},"Радуга":{"count":70},"サンドラッグ":{"count":61},"Apteka":{"count":366},"Первая помощь":{"count":74},"Ригла":{"count":113},"Имплозия":{"count":63},"Kinney Drugs":{"count":68},"Классика":{"count":67},"Ljekarna":{"count":53},"SalcoBrand":{"count":88},"Аптека 36,6":{"count":224},"Фармакор":{"count":75},"スギ薬局":{"count":84},"Аптечный пункт":{"count":148},"Невис":{"count":60},"トモズ (Tomod's)":{"count":83},"Eurovaistinė":{"count":65},"Farmacity":{"count":68},"аптека":{"count":96},"The Generics Pharmacy":{"count":95},"Farmatodo":{"count":123},"Duane Reade":{"count":61},"H-E-B":{"count":262},"Фармленд":{"count":82},"ドラッグてらしま (Drug Terashima)":{"count":96},"Арніка":{"count":125},"ავერსი (Aversi)":{"count":62},"Farmahorro":{"count":58}},"cafe":{"Starbucks":{"count":4238,"tags":{"cuisine":"coffee_shop"}},"Cafeteria":{"count":115},"Costa":{"count":618},"Caffè Nero":{"count":169},"Кафе":{"count":226},"Café Central":{"count":61},"Second Cup":{"count":193},"Dunkin Donuts":{"count":428,"tags":{"cuisine":"donut"}},"Espresso House":{"count":53},"Segafredo":{"count":69},"Coffee Time":{"count":94},"Cafe Coffee Day":{"count":120},"Eiscafe Venezia":{"count":180},"スターバックス":{"count":251,"tags":{"name:en":"Starbucks"}},"Шоколадница":{"count":145},"Pret A Manger":{"count":119},"Столовая":{"count":391},"Jamba Juice":{"count":53},"ドトール":{"count":164,"tags":{"name:en":"DOUTOR"}},"Tchibo":{"count":100},"Кофе Хауз":{"count":104},"Caribou Coffee":{"count":100},"Уют":{"count":51},"Шашлычная":{"count":58},"คาเฟ่ อเมซอน":{"count":62},"Traveler's Coffee":{"count":60},"カフェ・ド・クリエ":{"count":67,"tags":{"name:en":"Cafe de CRIE"}},"Cafe Amazon":{"count":65}}},"shop":{"supermarket":{"Budgens":{"count":88},"Morrisons":{"count":411},"Interspar":{"count":142},"Merkur":{"count":107},"Sainsbury's":{"count":547},"Lidl":{"count":7130},"Edeka":{"count":2293},"Coles":{"count":400},"Iceland":{"count":315},"Coop":{"count":2100},"Tesco":{"count":1297},"Woolworths":{"count":541},"Zielpunkt":{"count":239},"Nahkauf":{"count":170},"Billa":{"count":1432},"Kaufland":{"count":1004},"Plus":{"count":120},"ALDI":{"count":5172},"Checkers":{"count":128},"Tesco Metro":{"count":137},"NP":{"count":153},"Penny":{"count":1759},"Norma":{"count":1068},"Asda":{"count":225},"Netto":{"count":4379},"Rewe":{"count":2645},"Aldi Süd":{"count":594},"Real":{"count":246},"Tesco Express":{"count":406},"King Soopers":{"count":72},"Kiwi":{"count":167},"Pick n Pay":{"count":241},"ICA":{"count":192},"Tengelmann":{"count":188},"Carrefour":{"count":1640},"Waitrose":{"count":258},"Spar":{"count":2386},"Hofer":{"count":442},"M-Preis":{"count":76},"tegut":{"count":210},"Sainsbury's Local":{"count":118},"E-Center":{"count":66},"Aldi Nord":{"count":210},"nahkauf":{"count":84},"Meijer":{"count":76},"Safeway":{"count":410},"Costco":{"count":152},"Albert":{"count":185},"Jumbo":{"count":194},"Shoprite":{"count":244},"MPreis":{"count":54},"Penny Market":{"count":429},"Tesco Extra":{"count":123},"Albert Heijn":{"count":476},"IGA":{"count":363},"Super U":{"count":488},"Metro":{"count":260},"Neukauf":{"count":77},"Migros":{"count":459},"Marktkauf":{"count":121},"Delikatesy Centrum":{"count":59},"C1000":{"count":307},"Hoogvliet":{"count":53},"Food Basics":{"count":75},"Casino":{"count":264},"Penny Markt":{"count":466},"Giant":{"count":191},"COOP Jednota":{"count":73},"Rema 1000":{"count":368},"Kaufpark":{"count":96},"ALDI SÜD":{"count":113},"Simply Market":{"count":330},"Konzum":{"count":230},"Carrefour Express":{"count":353},"Eurospar":{"count":270},"Mercator":{"count":125},"Famila":{"count":130},"Hemköp":{"count":82},"real,-":{"count":81},"Markant":{"count":88},"Volg":{"count":135},"Leader Price":{"count":267},"Treff 3000":{"count":94},"SuperBrugsen":{"count":67},"Kaiser's":{"count":256},"K+K":{"count":106},"Unimarkt":{"count":86},"Carrefour City":{"count":126},"Sobeys":{"count":122},"S-Market":{"count":109},"Combi":{"count":55},"Denner":{"count":276},"Konsum":{"count":133},"Franprix":{"count":312},"Monoprix":{"count":198},"Diska":{"count":69},"PENNY":{"count":79},"Dia":{"count":835},"Giant Eagle":{"count":85},"NORMA":{"count":115},"AD Delhaize":{"count":63},"Auchan":{"count":152},"Mercadona":{"count":769},"Consum":{"count":130},"Carrefour Market":{"count":80},"Whole Foods":{"count":210,"tags":{"shop":"supermarket"}},"Pam":{"count":56},"sky":{"count":105},"Despar":{"count":146},"Eroski":{"count":208},"Costcutter":{"count":63},"Maxi":{"count":108},"Colruyt":{"count":180},"The Co-operative":{"count":64},"Intermarché":{"count":1210},"Delhaize":{"count":207},"CBA":{"count":176},"Shopi":{"count":53},"Walmart":{"count":644},"Kroger":{"count":317},"Albertsons":{"count":242},"Trader Joe's":{"count":235},"Feneberg":{"count":58},"denn's Biomarkt":{"count":52},"dm":{"count":114},"Kvickly":{"count":55},"Makro":{"count":140},"Dico":{"count":53},"Nah & Frisch":{"count":73},"Champion":{"count":59},"ICA Supermarket":{"count":51},"Fakta":{"count":235},"Магнит":{"count":1760},"Caprabo":{"count":103},"Famiglia Cooperativa":{"count":64},"Народная 7Я семьЯ":{"count":154},"Esselunga":{"count":85},"Maxima":{"count":102},"Petit Casino":{"count":111},"Wasgau":{"count":60},"Pingo Doce":{"count":253},"Match":{"count":140},"Profi":{"count":60},"Lider":{"count":65},"Unimarc":{"count":177},"The Co-operative Food":{"count":190},"Santa Isabel":{"count":128},"Седьмой континент":{"count":79},"HIT":{"count":59},"Rimi":{"count":106},"Conad":{"count":304},"Фуршет":{"count":76},"Willys":{"count":56},"Farmfoods":{"count":64},"U Express":{"count":51},"Фора":{"count":52},"Dunnes Stores":{"count":73},"Сільпо":{"count":125},"マルエツ":{"count":59},"Piggly Wiggly":{"count":57},"Crai":{"count":54},"El Árbol":{"count":73},"Centre Commercial E. Leclerc":{"count":549},"Foodland":{"count":100},"Super Brugsen":{"count":67},"Дикси":{"count":683},"Пятёрочка":{"count":1344},"Publix":{"count":339},"Føtex":{"count":66},"coop":{"count":73},"Fressnapf":{"count":69},"Coop Konsum":{"count":79},"Carrefour Contact":{"count":83},"No Frills":{"count":105},"Plodine":{"count":52},"ADEG":{"count":68},"Minipreço":{"count":111},"Biedronka":{"count":1335},"Eurospin":{"count":155},"Семья":{"count":62},"Gadis":{"count":53},"Евроопт":{"count":68},"Centra":{"count":51},"Квартал":{"count":82},"New World":{"count":69},"Countdown":{"count":95},"Reliance Fresh":{"count":61},"Stokrotka":{"count":98},"Coop Jednota":{"count":74},"Fred Meyer":{"count":64},"Irma":{"count":58},"Continente":{"count":75},"Price Chopper":{"count":99},"Game":{"count":52},"Soriana":{"count":93},"Alimerka":{"count":64},"Piotr i Paweł":{"count":53},"Перекресток":{"count":312},"Maxima X":{"count":117},"Карусель":{"count":55},"ALDI Nord":{"count":51},"Condis":{"count":67},"Sam's Club":{"count":138},"Копейка":{"count":87},"Géant Casino":{"count":54},"ASDA":{"count":180},"Intermarche":{"count":115},"Stop & Shop":{"count":66},"Food Lion":{"count":216},"Harris Teeter":{"count":92},"Foodworks":{"count":62},"Polo Market":{"count":86},"Лента":{"count":51},"西友 (SEIYU)":{"count":58},"H-E-B":{"count":293},"Атак":{"count":53},"Полушка":{"count":139},"Extra":{"count":82},"Lewiatan":{"count":94},"Sigma":{"count":51},"АТБ":{"count":322},"Społem":{"count":55},"Bodega Aurrera":{"count":82},"Tesco Lotus":{"count":77},"Мария-Ра":{"count":108},"Магнолия":{"count":72},"Магазин":{"count":120},"Монетка":{"count":174},"Hy-Vee":{"count":75},"Walmart Supercenter":{"count":133},"Hannaford":{"count":57},"Wegmans":{"count":83},"業務スーパー":{"count":61},"Norfa XL":{"count":55},"ヨークマート (YorkMart)":{"count":64},"Leclerc Drive":{"count":76}},"electronics":{"Media Markt":{"count":285},"Maplin":{"count":65},"Best Buy":{"count":345},"Future Shop":{"count":73},"Saturn":{"count":134},"Currys":{"count":80},"Radio Shack":{"count":269},"Euronics":{"count":115},"Expert":{"count":123},"Эльдорадо":{"count":184},"Darty":{"count":74},"М.Видео":{"count":89},"ヤマダ電機":{"count":51}},"convenience":{"Shell":{"count":255},"Spar":{"count":1119},"McColl's":{"count":100},"Tesco Express":{"count":426},"Sainsbury's Local":{"count":104},"Aral":{"count":56},"One Stop":{"count":146},"The Co-operative Food":{"count":115},"Londis":{"count":352},"7-Eleven":{"count":4440},"CBA":{"count":135},"Coop":{"count":678},"Sale":{"count":80},"Statoil":{"count":69},"Sheetz":{"count":54},"Konzum":{"count":173},"Siwa":{"count":216},"Mercator":{"count":57},"Esso":{"count":67},"COOP Jednota":{"count":181},"Mac's":{"count":152},"Alepa":{"count":62},"Hasty Market":{"count":54},"K-Market":{"count":54},"Costcutter":{"count":292},"Valintatalo":{"count":62},"Casino":{"count":90},"Franprix":{"count":61},"Circle K":{"count":289},"セブンイレブン":{"count":3011,"tags":{"name:en":"7-Eleven"}},"ローソン":{"count":1596,"tags":{"name:en":"LAWSON"}},"BP":{"count":163},"Tesco":{"count":55},"Petit Casino":{"count":233},"Volg":{"count":116},"Mace":{"count":115},"Mini Market":{"count":272},"Nisa Local":{"count":77},"Dorfladen":{"count":75},"Продукты":{"count":4285},"Mini Stop":{"count":228},"LAWSON":{"count":419},"デイリーヤマザキ":{"count":141},"Biedronka":{"count":83},"Надежда":{"count":56},"Mobil":{"count":66},"Nisa":{"count":51},"Premier":{"count":129},"ABC":{"count":152},"ミニストップ":{"count":316,"tags":{"name:en":"MINISTOP"}},"サンクス":{"count":560,"tags":{"name:en":"sunkus"}},"スリーエフ":{"count":88},"8 à Huit":{"count":61},"Tchibo":{"count":56},"Żabka":{"count":546},"Almacen":{"count":229},"Vival":{"count":194},"FamilyMart":{"count":529},"ファミリーマート":{"count":1608,"tags":{"name:en":"FamilyMart"}},"Carrefour City":{"count":57},"Sunkus":{"count":62},"Casey's General Store":{"count":95},"セブンイレブン(Seven-Eleven)":{"count":65},"Jednota":{"count":58},"Магазин":{"count":915},"Гастроном":{"count":152},"Centra":{"count":111},"Магнит":{"count":701},"サークルK":{"count":538,"tags":{"name:en":"Circle K"}},"Wawa":{"count":135},"Proxi":{"count":123},"Универсам":{"count":78},"Перекресток":{"count":51},"Groszek":{"count":65},"Select":{"count":62},"Večerka":{"count":51},"Potraviny":{"count":249},"Смак":{"count":78},"Эконом":{"count":55},"Березка":{"count":77},"Społem":{"count":93},"Carrefour Express":{"count":84},"Cumberland Farms":{"count":63},"Chevron":{"count":59},"Coop Jednota":{"count":66},"Tesco Lotus Express":{"count":67},"Kiosk":{"count":55},"Sklep spożywczy":{"count":130},"24 часа":{"count":58},"Минимаркет":{"count":102},"Oxxo":{"count":669},"Пятёрочка":{"count":398},"abc":{"count":74},"7/11":{"count":51},"Stewart's":{"count":255},"Продукти":{"count":171},"ローソンストア100 (LAWSON STORE 100)":{"count":85},"Дикси":{"count":119},"Радуга":{"count":86},"ローソンストア100":{"count":76},"เซเว่นอีเลฟเว่น":{"count":185},"Delikatesy Centrum":{"count":53},"Citgo":{"count":62},"Фортуна":{"count":51},"Kum & Go":{"count":59},"Мария-Ра":{"count":76},"Picard":{"count":57},"Four Square":{"count":52},"Визит":{"count":57},"Авоська":{"count":55},"Dollar General":{"count":127},"Studenac":{"count":76},"Central Convenience Store":{"count":55},"Монетка":{"count":62},"продукты":{"count":114},"Теремок":{"count":56},"Kwik Trip":{"count":69},"Кулинария":{"count":55},"全家":{"count":90},"Мечта":{"count":54},"Epicerie":{"count":102},"Кировский":{"count":67},"Food Mart":{"count":117},"Delikatesy":{"count":81},"ポプラ":{"count":54},"Lewiatan":{"count":135},"Продуктовый магазин":{"count":149},"Продуктовый":{"count":84},"セイコーマート (Seicomart)":{"count":72},"Виктория":{"count":70},"Весна":{"count":57},"Mini Market Non-Stop":{"count":60},"QuikTrip":{"count":75},"Копеечка":{"count":51},"Royal Farms":{"count":51},"Alfamart":{"count":103},"Indomaret":{"count":141},"магазин":{"count":171},"全家便利商店":{"count":156},"Boutique":{"count":59},"მარკეტი (Market)":{"count":144},"Stores":{"count":61}},"chemist":{"dm":{"count":939},"Müller":{"count":212},"Schlecker":{"count":187},"Etos":{"count":467},"Bipa":{"count":289},"Rossmann":{"count":1669},"DM Drogeriemarkt":{"count":55},"Ihr Platz":{"count":73},"Douglas":{"count":62},"Kruidvat":{"count":123}},"car_repair":{"Peugeot":{"count":83},"Kwik Fit":{"count":128},"ATU":{"count":261},"Midas":{"count":202},"Feu Vert":{"count":113},"Norauto":{"count":152},"Speedy":{"count":115},"Автозапчасти":{"count":212},"Renault":{"count":171},"Pit Stop":{"count":58},"Jiffy Lube":{"count":198},"Шиномонтаж":{"count":1157},"СТО":{"count":395},"O'Reilly Auto Parts":{"count":81},"Carglass":{"count":112},"шиномонтаж":{"count":62},"Citroen":{"count":51},"Euromaster":{"count":87},"Firestone":{"count":88},"Автосервис":{"count":361},"Advance Auto Parts":{"count":52},"Roady":{"count":56}},"furniture":{"IKEA":{"count":169},"Jysk":{"count":109},"Roller":{"count":78},"Dänisches Bettenlager":{"count":309},"Conforama":{"count":99},"Matratzen Concord":{"count":52},"Мебель":{"count":210},"But":{"count":63}},"doityourself":{"Hornbach":{"count":123},"B&Q":{"count":225},"Hubo":{"count":77},"Mr Bricolage":{"count":88},"Gamma":{"count":111},"OBI":{"count":422},"Lowes":{"count":1152},"Wickes":{"count":123},"Hagebau":{"count":59},"Max Bahr":{"count":79},"Castorama":{"count":153},"Rona":{"count":61},"Home Depot":{"count":865},"Toom Baumarkt":{"count":71},"Homebase":{"count":225},"Baumax":{"count":95},"Lagerhaus":{"count":79},"Bauhaus":{"count":186},"Canadian Tire":{"count":97},"Leroy Merlin":{"count":209},"Hellweg":{"count":58},"Brico":{"count":98},"Bricomarché":{"count":235},"Toom":{"count":67},"Hagebaumarkt":{"count":107},"Praktiker":{"count":122},"Menards":{"count":70},"Weldom":{"count":73},"Bunnings Warehouse":{"count":91},"Ace Hardware":{"count":147},"Home Hardware":{"count":72},"Хозтовары":{"count":86},"Стройматериалы":{"count":197},"Bricorama":{"count":60},"Point P":{"count":59}},"stationery":{"Staples":{"count":299},"McPaper":{"count":83},"Office Depot":{"count":98},"Канцтовары":{"count":63}},"car":{"Skoda":{"count":97},"BMW":{"count":149},"Citroen":{"count":277},"Renault":{"count":382},"Mercedes-Benz":{"count":235},"Volvo":{"count":96},"Ford":{"count":239},"Volkswagen":{"count":217},"Mazda":{"count":105},"Mitsubishi":{"count":73},"Fiat":{"count":93},"Автозапчасти":{"count":277},"Opel":{"count":165},"Audi":{"count":121},"Toyota":{"count":271},"Nissan":{"count":189},"Suzuki":{"count":75},"Honda":{"count":157},"Peugeot":{"count":308},"Шиномонтаж":{"count":259},"Hyundai":{"count":166},"Subaru":{"count":58},"Chevrolet":{"count":86},"Автомагазин":{"count":72}},"clothes":{"Matalan":{"count":90},"KiK":{"count":1219},"H&M":{"count":658},"Urban Outfitters":{"count":63},"Vögele":{"count":132},"Zeeman":{"count":121},"Takko":{"count":515},"Adler":{"count":55},"C&A":{"count":506},"Zara":{"count":217},"Vero Moda":{"count":95},"NKD":{"count":486},"Ernsting's family":{"count":312},"Winners":{"count":65},"River Island":{"count":59},"Next":{"count":176},"Gap":{"count":81},"Adidas":{"count":92},"Woolworths":{"count":117},"Mr Price":{"count":88},"Jet":{"count":61},"Pep":{"count":134},"Edgars":{"count":110},"Ackermans":{"count":91},"Truworths":{"count":65},"Ross":{"count":93},"Burton":{"count":51},"Dorothy Perkins":{"count":53},"Deichmann":{"count":61},"Lindex":{"count":73},"s.Oliver":{"count":56},"Cecil":{"count":51},"Dress Barn":{"count":52},"Old Navy":{"count":174},"Jack & Jones":{"count":52},"Pimkie":{"count":73},"Esprit":{"count":231},"Primark":{"count":92},"Bonita":{"count":155},"Mexx":{"count":67},"Gerry Weber":{"count":71},"Tally Weijl":{"count":70},"Mango":{"count":133},"TK Maxx":{"count":84},"Benetton":{"count":101},"Ulla Popken":{"count":61},"AWG":{"count":66},"Tommy Hilfiger":{"count":75},"New Yorker":{"count":180},"Orsay":{"count":73},"Jeans Fritz":{"count":51},"Charles Vögele":{"count":69},"New Look":{"count":126},"Lacoste":{"count":78},"Etam":{"count":53},"Kiabi":{"count":148},"Jack Wolfskin":{"count":60},"American Apparel":{"count":57},"Men's Wearhouse":{"count":54},"Intimissimi":{"count":52},"United Colors of Benetton":{"count":96},"Jules":{"count":63},"Second Hand":{"count":53},"AOKI":{"count":57},"Calzedonia":{"count":68},"洋服の青山":{"count":100},"Levi's":{"count":63},"Celio":{"count":74},"TJ Maxx":{"count":57},"Promod":{"count":82},"Street One":{"count":72},"ユニクロ":{"count":59},"Banana Republic":{"count":57},"Одежда":{"count":75},"Marshalls":{"count":56},"La Halle":{"count":62},"Peacocks":{"count":89},"しまむら":{"count":60}},"books":{"Bruna":{"count":58},"Waterstones":{"count":90},"Libro":{"count":57},"Barnes & Noble":{"count":267},"Weltbild":{"count":74},"Thalia":{"count":121},"Книги":{"count":112}},"department_store":{"Debenhams":{"count":67},"Canadian Tire":{"count":75},"Karstadt":{"count":64},"Walmart":{"count":517},"Kmart":{"count":143},"Target":{"count":574},"Galeria Kaufhof":{"count":61},"Marks & Spencer":{"count":66},"Big W":{"count":57},"Woolworth":{"count":78},"Универмаг":{"count":72},"Sears":{"count":235},"Walmart Supercenter":{"count":101},"Kohl's":{"count":153},"Macy's":{"count":147},"Sam's Club":{"count":54},"JCPenney":{"count":66}},"alcohol":{"Alko":{"count":145},"The Beer Store":{"count":150},"Systembolaget":{"count":210},"LCBO":{"count":239},"Ароматный мир":{"count":62},"Bargain Booze":{"count":62},"Nicolas":{"count":119},"BWS":{"count":70},"Botilleria":{"count":77},"SAQ":{"count":72},"Gall & Gall":{"count":512},"Живое пиво":{"count":70}},"bakery":{"Kamps":{"count":252},"Banette":{"count":52},"Bäckerei Schmidt":{"count":57},"Anker":{"count":73},"Hofpfisterei":{"count":111},"Greggs":{"count":276},"Oebel":{"count":57},"Boulangerie":{"count":266},"Stadtbäckerei":{"count":57},"Steinecke":{"count":145},"Ihle":{"count":76},"Goldilocks":{"count":59},"Dat Backhus":{"count":67},"K&U":{"count":61},"Der Beck":{"count":96},"Thürmann":{"count":54},"Backwerk":{"count":95},"Schäfer's":{"count":51},"Panaderia":{"count":168},"Goeken backen":{"count":51},"Stadtbäckerei Junge":{"count":51},"Boulangerie Patisserie":{"count":119},"Paul":{"count":81},"Хлеб":{"count":89},"Пекарня":{"count":52},"Кулиничи":{"count":51}},"sports":{"Sports Direct":{"count":57},"Decathlon":{"count":309},"Intersport":{"count":283},"Sports Authority":{"count":75},"Спортмастер":{"count":87},"Sport 2000":{"count":90},"Dick's Sporting Goods":{"count":77}},"variety_store":{"Tedi":{"count":157},"Dollarama":{"count":103},"Family Dollar":{"count":61},"Dollar Tree":{"count":110},"Dollar General":{"count":80}},"pet":{"Fressnapf":{"count":318},"PetSmart":{"count":177},"Das Futterhaus":{"count":69},"Pets at Home":{"count":62},"Petco":{"count":101},"Зоомагазин":{"count":100}},"shoes":{"Deichmann":{"count":622},"Reno":{"count":183},"Ecco":{"count":55},"Clarks":{"count":109},"La Halle aux Chaussures":{"count":69},"Brantano":{"count":71},"Geox":{"count":51},"Salamander":{"count":51},"Обувь":{"count":100},"Payless Shoe Source":{"count":67},"Famous Footwear":{"count":59},"Quick Schuh":{"count":72},"Shoe Zone":{"count":55},"Foot Locker":{"count":82},"Bata":{"count":101},"ЦентрОбувь":{"count":51}},"toys":{"La Grande Récré":{"count":56},"Toys R Us":{"count":151,"tags":{"shop":"toys"}},"Intertoys":{"count":57},"Детский мир":{"count":86},"Игрушки":{"count":58}},"travel_agency":{"Flight Centre":{"count":92},"Thomas Cook":{"count":119}},"jewelry":{"Bijou Brigitte":{"count":57},"Christ":{"count":57},"Swarovski":{"count":74}},"optician":{"Fielmann":{"count":232},"Apollo Optik":{"count":150},"Vision Express":{"count":58},"Оптика":{"count":182},"Optic 2000":{"count":98},"Alain Afflelou":{"count":73},"Specsavers":{"count":124},"Krys":{"count":77},"Atol":{"count":55}},"video":{"Blockbuster":{"count":184},"World of Video":{"count":64}},"mobile_phone":{"Билайн":{"count":128},"ソフトバンクショップ (SoftBank shop)":{"count":255},"Vodafone":{"count":355},"O2":{"count":208},"Carphone Warehouse":{"count":127},"Orange":{"count":246},"Verizon Wireless":{"count":125},"Sprint":{"count":109},"T-Mobile":{"count":175},"МТС":{"count":352},"Евросеть":{"count":506},"Bell":{"count":190},"The Phone House":{"count":83},"SFR":{"count":71},"Связной":{"count":439},"Мегафон":{"count":251},"AT&T":{"count":124},"ドコモショップ (docomo shop)":{"count":114},"au":{"count":65},"Movistar":{"count":77},"Bitė":{"count":72}},"hifi":{},"computer":{"PC World":{"count":55},"DNS":{"count":128}},"hairdresser":{"Klier":{"count":119},"Supercuts":{"count":106},"Hairkiller":{"count":51},"Great Clips":{"count":182},"Парикмахерская":{"count":510},"Стиль":{"count":51},"Franck Provost":{"count":70},"Салон красоты":{"count":70}},"hardware":{"1000 мелочей":{"count":61},"Хозтовары":{"count":151},"Стройматериалы":{"count":54}},"motorcycle":{"Yamaha":{"count":67},"Honda":{"count":69}}}},"addressFormats":[{"format":[["housenumber","street"],["city","postcode"]]},{"countryCodes":["gb"],"format":[["housename"],["housenumber","street"],["city","postcode"]]},{"countryCodes":["ie"],"format":[["housename"],["housenumber","street"],["city"],["postcode"]]},{"countryCodes":["ad","at","ba","be","ch","cz","de","dk","es","fi","gr","hr","is","it","li","nl","no","pl","pt","se","si","sk","sm","va"],"format":[["street","housenumber"],["postcode","city"]]},{"countryCodes":["fr","lu","mo"],"format":[["housenumber","street"],["postcode","city"]]},{"countryCodes":["br"],"format":[["street"],["housenumber","suburb"],["city","postcode"]]},{"countryCodes":["vn"],"format":[["housenumber","street"],["subdistrict"],["district"],["city"],["province","postcode"]]},{"countryCodes":["us"],"format":[["housenumber","street"],["city","state","postcode"]]},{"countryCodes":["ca"],"format":[["housenumber","street"],["city","province","postcode"]]}]};