]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
Update to iD v1.9.1
[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
6227     var fetcher = function(val, cb) {
6228         cb(data.filter(function(d) {
6229             return d.value
6230                 .toString()
6231                 .toLowerCase()
6232                 .indexOf(val.toLowerCase()) !== -1;
6233         }));
6234     };
6235
6236     var combobox = function(input) {
6237         var idx = -1,
6238             container = d3.select(document.body)
6239                 .selectAll('div.combobox')
6240                 .filter(function(d) { return d === input.node(); }),
6241             shown = !container.empty();
6242
6243         input
6244             .classed('combobox-input', true)
6245             .on('focus.typeahead', focus)
6246             .on('blur.typeahead', blur)
6247             .on('keydown.typeahead', keydown)
6248             .on('keyup.typeahead', keyup)
6249             .on('input.typeahead', change)
6250             .each(function() {
6251                 var parent = this.parentNode,
6252                     sibling = this.nextSibling;
6253
6254                 var caret = d3.select(parent).selectAll('.combobox-caret')
6255                     .filter(function(d) { return d === input.node(); })
6256                     .data([input.node()]);
6257
6258                 caret.enter().insert('div', function() { return sibling; })
6259                     .attr('class', 'combobox-caret');
6260
6261                 caret
6262                     .on('mousedown', function () {
6263                         // prevent the form element from blurring. it blurs
6264                         // on mousedown
6265                         d3.event.stopPropagation();
6266                         d3.event.preventDefault();
6267                         if (!shown) {
6268                             input.node().focus();
6269                             fetch('', render);
6270                         } else {
6271                             hide();
6272                         }
6273                     });
6274             });
6275
6276         function focus() {
6277             fetch(value(), render);
6278         }
6279
6280         function blur() {
6281             window.setTimeout(hide, 150);
6282         }
6283
6284         function show() {
6285             if (!shown) {
6286                 container = d3.select(document.body)
6287                     .insert('div', ':first-child')
6288                     .datum(input.node())
6289                     .attr('class', 'combobox')
6290                     .style({
6291                         position: 'absolute',
6292                         display: 'block',
6293                         left: '0px'
6294                     })
6295                     .on('mousedown', function () {
6296                         // prevent moving focus out of the text field
6297                         d3.event.preventDefault();
6298                     });
6299
6300                 d3.select(document.body)
6301                     .on('scroll.combobox', render, true);
6302
6303                 shown = true;
6304             }
6305         }
6306
6307         function hide() {
6308             if (shown) {
6309                 idx = -1;
6310                 container.remove();
6311
6312                 d3.select(document.body)
6313                     .on('scroll.combobox', null);
6314
6315                 shown = false;
6316             }
6317         }
6318
6319         function keydown() {
6320            switch (d3.event.keyCode) {
6321                // backspace, delete
6322                case 8:
6323                case 46:
6324                    input.on('input.typeahead', function() {
6325                        idx = -1;
6326                        render();
6327                        var start = input.property('selectionStart');
6328                        input.node().setSelectionRange(start, start);
6329                        input.on('input.typeahead', change);
6330                    });
6331                    break;
6332                // tab
6333                case 9:
6334                    container.selectAll('a.selected').each(event.accept);
6335                    break;
6336                // return
6337                case 13:
6338                    d3.event.preventDefault();
6339                    break;
6340                // up arrow
6341                case 38:
6342                    nav(-1);
6343                    d3.event.preventDefault();
6344                    break;
6345                // down arrow
6346                case 40:
6347                    nav(+1);
6348                    d3.event.preventDefault();
6349                    break;
6350            }
6351            d3.event.stopPropagation();
6352         }
6353
6354         function keyup() {
6355             switch (d3.event.keyCode) {
6356                 // escape
6357                 case 27:
6358                     hide();
6359                     break;
6360                 // return
6361                 case 13:
6362                     container.selectAll('a.selected').each(event.accept);
6363                     hide();
6364                     break;
6365             }
6366         }
6367
6368         function change() {
6369             fetch(value(), function() {
6370                 autocomplete();
6371                 render();
6372             });
6373         }
6374
6375         function nav(dir) {
6376             idx = Math.max(Math.min(idx + dir, suggestions.length - 1), 0);
6377             input.property('value', suggestions[idx].value);
6378             render();
6379             ensureVisible();
6380         }
6381
6382         function value() {
6383             var value = input.property('value'),
6384                 start = input.property('selectionStart'),
6385                 end = input.property('selectionEnd');
6386
6387             if (start && end) {
6388                 value = value.substring(0, start);
6389             }
6390
6391             return value;
6392         }
6393
6394         function fetch(v, cb) {
6395             fetcher.call(input, v, function(_) {
6396                 suggestions = _;
6397                 cb();
6398             });
6399         }
6400
6401         function autocomplete() {
6402             var v = value();
6403
6404             idx = -1;
6405
6406             if (!v) return;
6407
6408             for (var i = 0; i < suggestions.length; i++) {
6409                 if (suggestions[i].value.toLowerCase().indexOf(v.toLowerCase()) === 0) {
6410                     var completion = suggestions[i].value;
6411                     idx = i;
6412                     input.property('value', completion);
6413                     input.node().setSelectionRange(v.length, completion.length);
6414                     return;
6415                 }
6416             }
6417         }
6418
6419         function render() {
6420             if (suggestions.length >= minItems && document.activeElement === input.node()) {
6421                 show();
6422             } else {
6423                 hide();
6424                 return;
6425             }
6426
6427             var options = container
6428                 .selectAll('a.combobox-option')
6429                 .data(suggestions, function(d) { return d.value; });
6430
6431             options.enter().append('a')
6432                 .attr('class', 'combobox-option')
6433                 .text(function(d) { return d.value; });
6434
6435             options
6436                 .attr('title', function(d) { return d.title; })
6437                 .classed('selected', function(d, i) { return i == idx; })
6438                 .on('mouseover', select)
6439                 .on('click', accept)
6440                 .order();
6441
6442             options.exit()
6443                 .remove();
6444
6445             var rect = input.node().getBoundingClientRect();
6446
6447             container.style({
6448                 'left': rect.left + 'px',
6449                 'width': rect.width + 'px',
6450                 'top': rect.height + rect.top + 'px'
6451             });
6452         }
6453
6454         function select(d, i) {
6455             idx = i;
6456             render();
6457         }
6458
6459         function ensureVisible() {
6460             var node = container.selectAll('a.selected').node();
6461             if (node) node.scrollIntoView();
6462         }
6463
6464         function accept(d) {
6465             if (!shown) return;
6466             input
6467                 .property('value', d.value)
6468                 .trigger('change');
6469             event.accept(d);
6470             hide();
6471         }
6472     };
6473
6474     combobox.fetcher = function(_) {
6475         if (!arguments.length) return fetcher;
6476         fetcher = _;
6477         return combobox;
6478     };
6479
6480     combobox.data = function(_) {
6481         if (!arguments.length) return data;
6482         data = _;
6483         return combobox;
6484     };
6485
6486     combobox.minItems = function(_) {
6487         if (!arguments.length) return minItems;
6488         minItems = _;
6489         return combobox;
6490     };
6491
6492     return d3.rebind(combobox, event, 'on');
6493 };
6494
6495 d3.combobox.off = function(input) {
6496     data = null;
6497     fetcher = null;
6498
6499     input
6500         .on('focus.typeahead', null)
6501         .on('blur.typeahead', null)
6502         .on('keydown.typeahead', null)
6503         .on('keyup.typeahead', null)
6504         .on('input.typeahead', null)
6505         .each(function() {
6506             d3.select(this.parentNode).selectAll('.combobox-caret')
6507                 .filter(function(d) { return d === input.node(); })
6508                 .on('mousedown', null);
6509         });
6510
6511     d3.select(document.body)
6512         .on('scroll.combobox', null);
6513 };
6514 d3.geo.tile = function() {
6515   var size = [960, 500],
6516       scale = 256,
6517       scaleExtent = [0, 20],
6518       translate = [size[0] / 2, size[1] / 2],
6519       zoomDelta = 0;
6520
6521   function bound(_) {
6522       return Math.min(scaleExtent[1], Math.max(scaleExtent[0], _));
6523   }
6524
6525   function tile() {
6526     var z = Math.max(Math.log(scale) / Math.LN2 - 8, 0),
6527         z0 = bound(Math.round(z + zoomDelta)),
6528         k = Math.pow(2, z - z0 + 8),
6529         origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k],
6530         tiles = [],
6531         cols = d3.range(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))),
6532         rows = d3.range(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1])));
6533
6534     rows.forEach(function(y) {
6535       cols.forEach(function(x) {
6536         tiles.push([x, y, z0]);
6537       });
6538     });
6539
6540     tiles.translate = origin;
6541     tiles.scale = k;
6542
6543     return tiles;
6544   }
6545
6546   tile.scaleExtent = function(_) {
6547     if (!arguments.length) return scaleExtent;
6548     scaleExtent = _;
6549     return tile;
6550   };
6551
6552   tile.size = function(_) {
6553     if (!arguments.length) return size;
6554     size = _;
6555     return tile;
6556   };
6557
6558   tile.scale = function(_) {
6559     if (!arguments.length) return scale;
6560     scale = _;
6561     return tile;
6562   };
6563
6564   tile.translate = function(_) {
6565     if (!arguments.length) return translate;
6566     translate = _;
6567     return tile;
6568   };
6569
6570   tile.zoomDelta = function(_) {
6571     if (!arguments.length) return zoomDelta;
6572     zoomDelta = +_;
6573     return tile;
6574   };
6575
6576   return tile;
6577 };
6578 d3.jsonp = function (url, callback) {
6579   function rand() {
6580     var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
6581       c = '', i = -1;
6582     while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
6583     return c;
6584   }
6585
6586   function create(url) {
6587     var e = url.match(/callback=d3.jsonp.(\w+)/),
6588       c = e ? e[1] : rand();
6589     d3.jsonp[c] = function(data) {
6590       callback(data);
6591       delete d3.jsonp[c];
6592       script.remove();
6593     };
6594     return 'd3.jsonp.' + c;
6595   }
6596
6597   var cb = create(url),
6598     script = d3.select('head')
6599     .append('script')
6600     .attr('type', 'text/javascript')
6601     .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
6602 };
6603 /*
6604  * This code is licensed under the MIT license.
6605  *
6606  * Copyright © 2013, iD authors.
6607  *
6608  * Portions copyright © 2011, Keith Cirkel
6609  * See https://github.com/keithamus/jwerty
6610  *
6611  */
6612 d3.keybinding = function(namespace) {
6613     var bindings = [];
6614
6615     function matches(binding, event) {
6616         for (var p in binding.event) {
6617             if (event[p] != binding.event[p])
6618                 return false;
6619         }
6620
6621         return (!binding.capture) === (event.eventPhase !== Event.CAPTURING_PHASE);
6622     }
6623
6624     function capture() {
6625         for (var i = 0; i < bindings.length; i++) {
6626             var binding = bindings[i];
6627             if (matches(binding, d3.event)) {
6628                 binding.callback();
6629             }
6630         }
6631     }
6632
6633     function bubble() {
6634         var tagName = d3.select(d3.event.target).node().tagName;
6635         if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
6636             return;
6637         }
6638         capture();
6639     }
6640
6641     function keybinding(selection) {
6642         selection = selection || d3.select(document);
6643         selection.on('keydown.capture' + namespace, capture, true);
6644         selection.on('keydown.bubble' + namespace, bubble, false);
6645         return keybinding;
6646     }
6647
6648     keybinding.off = function(selection) {
6649         selection = selection || d3.select(document);
6650         selection.on('keydown.capture' + namespace, null);
6651         selection.on('keydown.bubble' + namespace, null);
6652         return keybinding;
6653     };
6654
6655     keybinding.on = function(code, callback, capture) {
6656         var binding = {
6657             event: {
6658                 keyCode: 0,
6659                 shiftKey: false,
6660                 ctrlKey: false,
6661                 altKey: false,
6662                 metaKey: false
6663             },
6664             capture: capture,
6665             callback: callback
6666         };
6667
6668         code = code.toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
6669
6670         for (var i = 0; i < code.length; i++) {
6671             // Normalise matching errors
6672             if (code[i] === '++') code[i] = '+';
6673
6674             if (code[i] in d3.keybinding.modifierCodes) {
6675                 binding.event[d3.keybinding.modifierProperties[d3.keybinding.modifierCodes[code[i]]]] = true;
6676             } else if (code[i] in d3.keybinding.keyCodes) {
6677                 binding.event.keyCode = d3.keybinding.keyCodes[code[i]];
6678             }
6679         }
6680
6681         bindings.push(binding);
6682
6683         return keybinding;
6684     };
6685
6686     return keybinding;
6687 };
6688
6689 (function () {
6690     d3.keybinding.modifierCodes = {
6691         // Shift key, ⇧
6692         '⇧': 16, shift: 16,
6693         // CTRL key, on Mac: ⌃
6694         '⌃': 17, ctrl: 17,
6695         // ALT key, on Mac: ⌥ (Alt)
6696         '⌥': 18, alt: 18, option: 18,
6697         // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
6698         '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
6699     };
6700
6701     d3.keybinding.modifierProperties = {
6702         16: 'shiftKey',
6703         17: 'ctrlKey',
6704         18: 'altKey',
6705         91: 'metaKey'
6706     };
6707
6708     d3.keybinding.keyCodes = {
6709         // Backspace key, on Mac: ⌫ (Backspace)
6710         '⌫': 8, backspace: 8,
6711         // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
6712         '⇥': 9, '⇆': 9, tab: 9,
6713         // Return key, ↩
6714         '↩': 13, 'return': 13, enter: 13, '⌅': 13,
6715         // Pause/Break key
6716         'pause': 19, 'pause-break': 19,
6717         // Caps Lock key, ⇪
6718         '⇪': 20, caps: 20, 'caps-lock': 20,
6719         // Escape key, on Mac: ⎋, on Windows: Esc
6720         '⎋': 27, escape: 27, esc: 27,
6721         // Space key
6722         space: 32,
6723         // Page-Up key, or pgup, on Mac: ↖
6724         '↖': 33, pgup: 33, 'page-up': 33,
6725         // Page-Down key, or pgdown, on Mac: ↘
6726         '↘': 34, pgdown: 34, 'page-down': 34,
6727         // END key, on Mac: ⇟
6728         '⇟': 35, end: 35,
6729         // HOME key, on Mac: ⇞
6730         '⇞': 36, home: 36,
6731         // Insert key, or ins
6732         ins: 45, insert: 45,
6733         // Delete key, on Mac: ⌦ (Delete)
6734         '⌦': 46, del: 46, 'delete': 46,
6735         // Left Arrow Key, or ←
6736         '←': 37, left: 37, 'arrow-left': 37,
6737         // Up Arrow Key, or ↑
6738         '↑': 38, up: 38, 'arrow-up': 38,
6739         // Right Arrow Key, or →
6740         '→': 39, right: 39, 'arrow-right': 39,
6741         // Up Arrow Key, or ↓
6742         '↓': 40, down: 40, 'arrow-down': 40,
6743         // odities, printing characters that come out wrong:
6744         // Firefox Equals
6745         'ffequals': 61,
6746         // Num-Multiply, or *
6747         '*': 106, star: 106, asterisk: 106, multiply: 106,
6748         // Num-Plus or +
6749         '+': 107, 'plus': 107,
6750         // Num-Subtract, or -
6751         '-': 109, subtract: 109,
6752         // Firefox Minus
6753         'ffplus': 171,
6754         // Firefox Minus
6755         'ffminus': 173,
6756         // Semicolon
6757         ';': 186, semicolon: 186,
6758         // = or equals
6759         '=': 187, 'equals': 187,
6760         // Comma, or ,
6761         ',': 188, comma: 188,
6762         'dash': 189, //???
6763         // Period, or ., or full-stop
6764         '.': 190, period: 190, 'full-stop': 190,
6765         // Slash, or /, or forward-slash
6766         '/': 191, slash: 191, 'forward-slash': 191,
6767         // Tick, or `, or back-quote
6768         '`': 192, tick: 192, 'back-quote': 192,
6769         // Open bracket, or [
6770         '[': 219, 'open-bracket': 219,
6771         // Back slash, or \
6772         '\\': 220, 'back-slash': 220,
6773         // Close backet, or ]
6774         ']': 221, 'close-bracket': 221,
6775         // Apostrophe, or Quote, or '
6776         '\'': 222, quote: 222, apostrophe: 222
6777     };
6778
6779     // NUMPAD 0-9
6780     var i = 95, n = 0;
6781     while (++i < 106) {
6782         d3.keybinding.keyCodes['num-' + n] = i;
6783         ++n;
6784     }
6785
6786     // 0-9
6787     i = 47; n = 0;
6788     while (++i < 58) {
6789         d3.keybinding.keyCodes[n] = i;
6790         ++n;
6791     }
6792
6793     // F1-F25
6794     i = 111; n = 1;
6795     while (++i < 136) {
6796         d3.keybinding.keyCodes['f' + n] = i;
6797         ++n;
6798     }
6799
6800     // a-z
6801     i = 64;
6802     while (++i < 91) {
6803         d3.keybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
6804     }
6805 })();
6806 d3.selection.prototype.one = function (type, listener, capture) {
6807     var target = this, typeOnce = type + ".once";
6808     function one() {
6809         target.on(typeOnce, null);
6810         listener.apply(this, arguments);
6811     }
6812     target.on(typeOnce, one, capture);
6813     return this;
6814 };
6815 d3.selection.prototype.dimensions = function (dimensions) {
6816     var refresh = (function(node) {
6817         var cr = node.getBoundingClientRect();
6818         prop = [cr.width, cr.height];
6819         this.property('__dimensions__', prop);
6820         return prop;
6821     }).bind(this);
6822
6823     var node = this.node();
6824
6825     if (!arguments.length) {
6826         if (!node) return [0,0];
6827         return this.property('__dimensions__') || refresh(node);
6828     }
6829     if (dimensions === null) {
6830         if (!node) return [0,0];
6831         return refresh(node);
6832     }
6833
6834     return this
6835         .property('__dimensions__', [dimensions[0], dimensions[1]])
6836         .attr({width: dimensions[0], height: dimensions[1]});
6837 };
6838 d3.selection.prototype.trigger = function (type) {
6839     this.each(function() {
6840         var evt = document.createEvent('HTMLEvents');
6841         evt.initEvent(type, true, true);
6842         this.dispatchEvent(evt);
6843     });
6844 };
6845 // Tooltips and svg mask used to highlight certain features
6846 d3.curtain = function() {
6847
6848     var event = d3.dispatch(),
6849         surface,
6850         tooltip,
6851         darkness;
6852
6853     function curtain(selection) {
6854
6855         surface = selection.append('svg')
6856             .attr('id', 'curtain')
6857             .style({
6858                 'z-index': 1000,
6859                 'pointer-events': 'none',
6860                 'position': 'absolute',
6861                 'top': 0,
6862                 'left': 0
6863             });
6864
6865         darkness = surface.append('path')
6866             .attr({
6867                 x: 0,
6868                 y: 0,
6869                 'class': 'curtain-darkness'
6870             });
6871
6872         d3.select(window).on('resize.curtain', resize);
6873
6874         tooltip = selection.append('div')
6875             .attr('class', 'tooltip')
6876             .style('z-index', 1002);
6877
6878         tooltip.append('div').attr('class', 'tooltip-arrow');
6879         tooltip.append('div').attr('class', 'tooltip-inner');
6880
6881         resize();
6882
6883         function resize() {
6884             surface.attr({
6885                 width: window.innerWidth,
6886                 height: window.innerHeight
6887             });
6888             curtain.cut(darkness.datum());
6889         }
6890     }
6891
6892     curtain.reveal = function(box, text, tooltipclass, duration) {
6893         if (typeof box === 'string') box = d3.select(box).node();
6894         if (box.getBoundingClientRect) box = box.getBoundingClientRect();
6895
6896         curtain.cut(box, duration);
6897
6898         if (text) {
6899             // pseudo markdown bold text hack
6900             var parts = text.split('**');
6901             var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
6902             if (parts[1]) html += '<span class="bold">' + parts[1] + '</span>';
6903
6904             var dimensions = tooltip.classed('in', true)
6905                 .select('.tooltip-inner')
6906                     .html(html)
6907                     .dimensions();
6908
6909             var pos;
6910
6911             var w = window.innerWidth,
6912                 h = window.innerHeight;
6913
6914             if (box.top + box.height < Math.min(100, box.width + box.left)) {
6915                 side = 'bottom';
6916                 pos = [box.left + box.width / 2 - dimensions[0]/ 2, box.top + box.height];
6917
6918             } else if (box.left + box.width + 300 < window.innerWidth) {
6919                 side = 'right';
6920                 pos = [box.left + box.width, box.top + box.height / 2 - dimensions[1] / 2];
6921
6922             } else if (box.left > 300) {
6923                 side = 'left';
6924                 pos = [box.left - 200, box.top + box.height / 2 - dimensions[1] / 2];
6925             } else {
6926                 side = 'bottom';
6927                 pos = [box.left, box.top + box.height];
6928             }
6929
6930             pos = [
6931                 Math.min(Math.max(10, pos[0]), w - dimensions[0] - 10),
6932                 Math.min(Math.max(10, pos[1]), h - dimensions[1] - 10)
6933             ];
6934
6935
6936             if (duration !== 0 || !tooltip.classed(side)) tooltip.call(iD.ui.Toggle(true));
6937
6938             tooltip
6939                 .style('top', pos[1] + 'px')
6940                 .style('left', pos[0] + 'px')
6941                 .attr('class', 'curtain-tooltip tooltip in ' + side + ' ' + tooltipclass)
6942                 .select('.tooltip-inner')
6943                     .html(html);
6944
6945         } else {
6946             tooltip.call(iD.ui.Toggle(false));
6947         }
6948     };
6949
6950     curtain.cut = function(datum, duration) {
6951         darkness.datum(datum);
6952
6953         (duration === 0 ? darkness : darkness.transition().duration(duration || 600))
6954             .attr('d', function(d) {
6955                 var string = "M 0,0 L 0," + window.innerHeight + " L " +
6956                     window.innerWidth + "," + window.innerHeight + "L" +
6957                     window.innerWidth + ",0 Z";
6958
6959                 if (!d) return string;
6960                 return string + 'M' +
6961                     d.left + ',' + d.top + 'L' +
6962                     d.left + ',' + (d.top + d.height) + 'L' +
6963                     (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
6964                     (d.left + d.width) + ',' + (d.top) + 'Z';
6965
6966             });
6967     };
6968
6969     curtain.remove = function() {
6970         surface.remove();
6971         tooltip.remove();
6972     };
6973
6974     return d3.rebind(curtain, event, 'on');
6975 };
6976 // Like selection.property('value', ...), but avoids no-op value sets,
6977 // which can result in layout/repaint thrashing in some situations.
6978 d3.selection.prototype.value = function(value) {
6979     function d3_selection_value(value) {
6980       function valueNull() {
6981         delete this.value;
6982       }
6983
6984       function valueConstant() {
6985         if (this.value !== value) this.value = value;
6986       }
6987
6988       function valueFunction() {
6989         var x = value.apply(this, arguments);
6990         if (x == null) delete this.value;
6991         else if (this.value !== x) this.value = x;
6992       }
6993
6994       return value == null
6995           ? valueNull : (typeof value === "function"
6996           ? valueFunction : valueConstant);
6997     }
6998
6999     if (!arguments.length) return this.property('value');
7000     return this.each(d3_selection_value(value));
7001 };
7002 // Copyright (c) 2006, 2008 Tony Garnock-Jones <tonyg@lshift.net>
7003 // Copyright (c) 2006, 2008 LShift Ltd. <query@lshift.net>
7004 //
7005 // Permission is hereby granted, free of charge, to any person
7006 // obtaining a copy of this software and associated documentation files
7007 // (the "Software"), to deal in the Software without restriction,
7008 // including without limitation the rights to use, copy, modify, merge,
7009 // publish, distribute, sublicense, and/or sell copies of the Software,
7010 // and to permit persons to whom the Software is furnished to do so,
7011 // subject to the following conditions:
7012 //
7013 // The above copyright notice and this permission notice shall be
7014 // included in all copies or substantial portions of the Software.
7015 //
7016 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
7017 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
7018 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
7019 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
7020 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
7021 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7022 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7023 // SOFTWARE.
7024
7025 // source:  https://bitbucket.org/lshift/synchrotron/src
7026
7027 Diff3 = (function() {
7028     'use strict';
7029
7030     var diff3 = {
7031         longest_common_subsequence: function(file1, file2) {
7032             /* Text diff algorithm following Hunt and McIlroy 1976.
7033              * J. W. Hunt and M. D. McIlroy, An algorithm for differential file
7034              * comparison, Bell Telephone Laboratories CSTR #41 (1976)
7035              * http://www.cs.dartmouth.edu/~doug/
7036              *
7037              * Expects two arrays of strings.
7038              */
7039             var equivalenceClasses;
7040             var file2indices;
7041             var newCandidate;
7042             var candidates;
7043             var line;
7044             var c, i, j, jX, r, s;
7045
7046             equivalenceClasses = {};
7047             for (j = 0; j < file2.length; j++) {
7048                 line = file2[j];
7049                 if (equivalenceClasses[line]) {
7050                     equivalenceClasses[line].push(j);
7051                 } else {
7052                     equivalenceClasses[line] = [j];
7053                 }
7054             }
7055
7056             candidates = [{file1index: -1,
7057                            file2index: -1,
7058                            chain: null}];
7059
7060             for (i = 0; i < file1.length; i++) {
7061                 line = file1[i];
7062                 file2indices = equivalenceClasses[line] || [];
7063
7064                 r = 0;
7065                 c = candidates[0];
7066
7067                 for (jX = 0; jX < file2indices.length; jX++) {
7068                     j = file2indices[jX];
7069
7070                     for (s = 0; s < candidates.length; s++) {
7071                         if ((candidates[s].file2index < j) &&
7072                             ((s == candidates.length - 1) ||
7073                              (candidates[s + 1].file2index > j)))
7074                             break;
7075                     }
7076
7077                     if (s < candidates.length) {
7078                         newCandidate = {file1index: i,
7079                                         file2index: j,
7080                                         chain: candidates[s]};
7081                         if (r == candidates.length) {
7082                             candidates.push(c);
7083                         } else {
7084                             candidates[r] = c;
7085                         }
7086                         r = s + 1;
7087                         c = newCandidate;
7088                         if (r == candidates.length) {
7089                             break; // no point in examining further (j)s
7090                         }
7091                     }
7092                 }
7093
7094                 candidates[r] = c;
7095             }
7096
7097             // At this point, we know the LCS: it's in the reverse of the
7098             // linked-list through .chain of
7099             // candidates[candidates.length - 1].
7100
7101             return candidates[candidates.length - 1];
7102         },
7103
7104         diff_comm: function(file1, file2) {
7105             // We apply the LCS to build a "comm"-style picture of the
7106             // differences between file1 and file2.
7107
7108             var result = [];
7109             var tail1 = file1.length;
7110             var tail2 = file2.length;
7111             var common = {common: []};
7112
7113             function processCommon() {
7114                 if (common.common.length) {
7115                     common.common.reverse();
7116                     result.push(common);
7117                     common = {common: []};
7118                 }
7119             }
7120
7121             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7122                  candidate !== null;
7123                  candidate = candidate.chain)
7124             {
7125                 var different = {file1: [], file2: []};
7126
7127                 while (--tail1 > candidate.file1index) {
7128                     different.file1.push(file1[tail1]);
7129                 }
7130
7131                 while (--tail2 > candidate.file2index) {
7132                     different.file2.push(file2[tail2]);
7133                 }
7134
7135                 if (different.file1.length || different.file2.length) {
7136                     processCommon();
7137                     different.file1.reverse();
7138                     different.file2.reverse();
7139                     result.push(different);
7140                 }
7141
7142                 if (tail1 >= 0) {
7143                     common.common.push(file1[tail1]);
7144                 }
7145             }
7146
7147             processCommon();
7148
7149             result.reverse();
7150             return result;
7151         },
7152
7153         diff_patch: function(file1, file2) {
7154             // We apply the LCD to build a JSON representation of a
7155             // diff(1)-style patch.
7156
7157             var result = [];
7158             var tail1 = file1.length;
7159             var tail2 = file2.length;
7160
7161             function chunkDescription(file, offset, length) {
7162                 var chunk = [];
7163                 for (var i = 0; i < length; i++) {
7164                     chunk.push(file[offset + i]);
7165                 }
7166                 return {offset: offset,
7167                         length: length,
7168                         chunk: chunk};
7169             }
7170
7171             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7172                  candidate !== null;
7173                  candidate = candidate.chain)
7174             {
7175                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7176                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7177                 tail1 = candidate.file1index;
7178                 tail2 = candidate.file2index;
7179
7180                 if (mismatchLength1 || mismatchLength2) {
7181                     result.push({file1: chunkDescription(file1,
7182                                                          candidate.file1index + 1,
7183                                                          mismatchLength1),
7184                                  file2: chunkDescription(file2,
7185                                                          candidate.file2index + 1,
7186                                                          mismatchLength2)});
7187                 }
7188             }
7189
7190             result.reverse();
7191             return result;
7192         },
7193
7194         strip_patch: function(patch) {
7195         // Takes the output of Diff3.diff_patch(), and removes
7196         // information from it. It can still be used by patch(),
7197         // below, but can no longer be inverted.
7198         var newpatch = [];
7199         for (var i = 0; i < patch.length; i++) {
7200             var chunk = patch[i];
7201             newpatch.push({file1: {offset: chunk.file1.offset,
7202                        length: chunk.file1.length},
7203                    file2: {chunk: chunk.file2.chunk}});
7204         }
7205         return newpatch;
7206         },
7207
7208         invert_patch: function(patch) {
7209             // Takes the output of Diff3.diff_patch(), and inverts the
7210             // sense of it, so that it can be applied to file2 to give
7211             // file1 rather than the other way around.
7212
7213             for (var i = 0; i < patch.length; i++) {
7214                 var chunk = patch[i];
7215                 var tmp = chunk.file1;
7216                 chunk.file1 = chunk.file2;
7217                 chunk.file2 = tmp;
7218             }
7219         },
7220
7221         patch: function (file, patch) {
7222             // Applies a patch to a file.
7223             //
7224             // Given file1 and file2, Diff3.patch(file1,
7225             // Diff3.diff_patch(file1, file2)) should give file2.
7226
7227             var result = [];
7228             var commonOffset = 0;
7229
7230             function copyCommon(targetOffset) {
7231                 while (commonOffset < targetOffset) {
7232                     result.push(file[commonOffset]);
7233                     commonOffset++;
7234                 }
7235             }
7236
7237             for (var chunkIndex = 0; chunkIndex < patch.length; chunkIndex++) {
7238                 var chunk = patch[chunkIndex];
7239                 copyCommon(chunk.file1.offset);
7240                 for (var lineIndex = 0; lineIndex < chunk.file2.chunk.length; lineIndex++) {
7241                     result.push(chunk.file2.chunk[lineIndex]);
7242                 }
7243                 commonOffset += chunk.file1.length;
7244             }
7245
7246             copyCommon(file.length);
7247             return result;
7248         },
7249
7250         diff_indices: function(file1, file2) {
7251             // We apply the LCS to give a simple representation of the
7252             // offsets and lengths of mismatched chunks in the input
7253             // files. This is used by diff3_merge_indices below.
7254
7255             var result = [];
7256             var tail1 = file1.length;
7257             var tail2 = file2.length;
7258
7259             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7260                  candidate !== null;
7261                  candidate = candidate.chain)
7262             {
7263                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7264                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7265                 tail1 = candidate.file1index;
7266                 tail2 = candidate.file2index;
7267
7268                 if (mismatchLength1 || mismatchLength2) {
7269                     result.push({file1: [tail1 + 1, mismatchLength1],
7270                                  file2: [tail2 + 1, mismatchLength2]});
7271                 }
7272             }
7273
7274             result.reverse();
7275             return result;
7276         },
7277
7278         diff3_merge_indices: function (a, o, b) {
7279             // Given three files, A, O, and B, where both A and B are
7280             // independently derived from O, returns a fairly complicated
7281             // internal representation of merge decisions it's taken. The
7282             // interested reader may wish to consult
7283             //
7284             // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce. "A
7285             // Formal Investigation of Diff3." In Arvind and Prasad,
7286             // editors, Foundations of Software Technology and Theoretical
7287             // Computer Science (FSTTCS), December 2007.
7288             //
7289             // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
7290             var i;
7291
7292             var m1 = Diff3.diff_indices(o, a);
7293             var m2 = Diff3.diff_indices(o, b);
7294
7295             var hunks = [];
7296             function addHunk(h, side) {
7297                 hunks.push([h.file1[0], side, h.file1[1], h.file2[0], h.file2[1]]);
7298             }
7299             for (i = 0; i < m1.length; i++) { addHunk(m1[i], 0); }
7300             for (i = 0; i < m2.length; i++) { addHunk(m2[i], 2); }
7301             hunks.sort();
7302
7303             var result = [];
7304             var commonOffset = 0;
7305             function copyCommon(targetOffset) {
7306                 if (targetOffset > commonOffset) {
7307                     result.push([1, commonOffset, targetOffset - commonOffset]);
7308                     commonOffset = targetOffset;
7309                 }
7310             }
7311
7312             for (var hunkIndex = 0; hunkIndex < hunks.length; hunkIndex++) {
7313                 var firstHunkIndex = hunkIndex;
7314                 var hunk = hunks[hunkIndex];
7315                 var regionLhs = hunk[0];
7316                 var regionRhs = regionLhs + hunk[2];
7317                 while (hunkIndex < hunks.length - 1) {
7318                     var maybeOverlapping = hunks[hunkIndex + 1];
7319                     var maybeLhs = maybeOverlapping[0];
7320                     if (maybeLhs > regionRhs) break;
7321                     regionRhs = maybeLhs + maybeOverlapping[2];
7322                     hunkIndex++;
7323                 }
7324
7325                 copyCommon(regionLhs);
7326                 if (firstHunkIndex == hunkIndex) {
7327             // The "overlap" was only one hunk long, meaning that
7328             // there's no conflict here. Either a and o were the
7329             // same, or b and o were the same.
7330                     if (hunk[4] > 0) {
7331                         result.push([hunk[1], hunk[3], hunk[4]]);
7332                     }
7333                 } else {
7334             // A proper conflict. Determine the extents of the
7335             // regions involved from a, o and b. Effectively merge
7336             // all the hunks on the left into one giant hunk, and
7337             // do the same for the right; then, correct for skew
7338             // in the regions of o that each side changed, and
7339             // report appropriate spans for the three sides.
7340             var regions = {
7341                 0: [a.length, -1, o.length, -1],
7342                 2: [b.length, -1, o.length, -1]
7343             };
7344                     for (i = firstHunkIndex; i <= hunkIndex; i++) {
7345                 hunk = hunks[i];
7346                         var side = hunk[1];
7347                 var r = regions[side];
7348                 var oLhs = hunk[0];
7349                 var oRhs = oLhs + hunk[2];
7350                         var abLhs = hunk[3];
7351                         var abRhs = abLhs + hunk[4];
7352                 r[0] = Math.min(abLhs, r[0]);
7353                 r[1] = Math.max(abRhs, r[1]);
7354                 r[2] = Math.min(oLhs, r[2]);
7355                 r[3] = Math.max(oRhs, r[3]);
7356                     }
7357             var aLhs = regions[0][0] + (regionLhs - regions[0][2]);
7358             var aRhs = regions[0][1] + (regionRhs - regions[0][3]);
7359             var bLhs = regions[2][0] + (regionLhs - regions[2][2]);
7360             var bRhs = regions[2][1] + (regionRhs - regions[2][3]);
7361                     result.push([-1,
7362                      aLhs,      aRhs      - aLhs,
7363                      regionLhs, regionRhs - regionLhs,
7364                      bLhs,      bRhs      - bLhs]);
7365                 }
7366                 commonOffset = regionRhs;
7367             }
7368
7369             copyCommon(o.length);
7370             return result;
7371         },
7372
7373         diff3_merge: function (a, o, b, excludeFalseConflicts) {
7374             // Applies the output of Diff3.diff3_merge_indices to actually
7375             // construct the merged file; the returned result alternates
7376             // between "ok" and "conflict" blocks.
7377
7378             var result = [];
7379             var files = [a, o, b];
7380             var indices = Diff3.diff3_merge_indices(a, o, b);
7381
7382             var okLines = [];
7383             function flushOk() {
7384                 if (okLines.length) {
7385                     result.push({ok: okLines});
7386                 }
7387                 okLines = [];
7388             }
7389             function pushOk(xs) {
7390                 for (var j = 0; j < xs.length; j++) {
7391                     okLines.push(xs[j]);
7392                 }
7393             }
7394
7395             function isTrueConflict(rec) {
7396                 if (rec[2] != rec[6]) return true;
7397                 var aoff = rec[1];
7398                 var boff = rec[5];
7399                 for (var j = 0; j < rec[2]; j++) {
7400                     if (a[j + aoff] != b[j + boff]) return true;
7401                 }
7402                 return false;
7403             }
7404
7405             for (var i = 0; i < indices.length; i++) {
7406                 var x = indices[i];
7407                 var side = x[0];
7408                 if (side == -1) {
7409                     if (excludeFalseConflicts && !isTrueConflict(x)) {
7410                         pushOk(files[0].slice(x[1], x[1] + x[2]));
7411                     } else {
7412                         flushOk();
7413                         result.push({conflict: {a: a.slice(x[1], x[1] + x[2]),
7414                                                 aIndex: x[1],
7415                                                 o: o.slice(x[3], x[3] + x[4]),
7416                                                 oIndex: x[3],
7417                                                 b: b.slice(x[5], x[5] + x[6]),
7418                                                 bIndex: x[5]}});
7419                     }
7420                 } else {
7421                     pushOk(files[side].slice(x[1], x[1] + x[2]));
7422                 }
7423             }
7424
7425             flushOk();
7426             return result;
7427         }
7428     };
7429     return diff3;
7430 })();
7431
7432 if (typeof module !== 'undefined') module.exports = Diff3;
7433 var JXON = new (function () {
7434   var
7435     sValueProp = "keyValue", sAttributesProp = "keyAttributes", sAttrPref = "@", /* you can customize these values */
7436     aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
7437
7438   function parseText (sValue) {
7439     if (rIsNull.test(sValue)) { return null; }
7440     if (rIsBool.test(sValue)) { return sValue.toLowerCase() === "true"; }
7441     if (isFinite(sValue)) { return parseFloat(sValue); }
7442     if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
7443     return sValue;
7444   }
7445
7446   function EmptyTree () { }
7447   EmptyTree.prototype.toString = function () { return "null"; };
7448   EmptyTree.prototype.valueOf = function () { return null; };
7449
7450   function objectify (vValue) {
7451     return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
7452   }
7453
7454   function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
7455     var
7456       nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
7457       bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
7458
7459     var
7460       sProp, vContent, nLength = 0, sCollectedTxt = "",
7461       vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
7462
7463     if (bChildren) {
7464       for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
7465         oNode = oParentNode.childNodes.item(nItem);
7466         if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is "CDATASection" (4) */
7467         else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is "Text" (3) */
7468         else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is "Element" (1) */
7469       }
7470     }
7471
7472     var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
7473
7474     if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
7475
7476     for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
7477       sProp = aCache[nElId].nodeName.toLowerCase();
7478       vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
7479       if (vResult.hasOwnProperty(sProp)) {
7480         if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
7481         vResult[sProp].push(vContent);
7482       } else {
7483         vResult[sProp] = vContent;
7484         nLength++;
7485       }
7486     }
7487
7488     if (bAttributes) {
7489       var
7490         nAttrLen = oParentNode.attributes.length,
7491         sAPrefix = bNesteAttr ? "" : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
7492
7493       for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
7494         oAttrib = oParentNode.attributes.item(nAttrib);
7495         oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
7496       }
7497
7498       if (bNesteAttr) {
7499         if (bFreeze) { Object.freeze(oAttrParent); }
7500         vResult[sAttributesProp] = oAttrParent;
7501         nLength -= nAttrLen - 1;
7502       }
7503     }
7504
7505     if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
7506       vResult[sValueProp] = vBuiltVal;
7507     } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
7508       vResult = vBuiltVal;
7509     }
7510
7511     if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
7512
7513     aCache.length = nLevelStart;
7514
7515     return vResult;
7516   }
7517
7518   function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
7519     var vValue, oChild;
7520
7521     if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
7522       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
7523     } else if (oParentObj.constructor === Date) {
7524       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));    
7525     }
7526
7527     for (var sName in oParentObj) {
7528       vValue = oParentObj[sName];
7529       if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
7530       if (sName === sValueProp) {
7531         if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
7532       } else if (sName === sAttributesProp) { /* verbosity level is 3 */
7533         for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
7534       } else if (sName.charAt(0) === sAttrPref) {
7535         oParentEl.setAttribute(sName.slice(1), vValue);
7536       } else if (vValue.constructor === Array) {
7537         for (var nItem = 0; nItem < vValue.length; nItem++) {
7538           oChild = oXMLDoc.createElement(sName);
7539           loadObjTree(oXMLDoc, oChild, vValue[nItem]);
7540           oParentEl.appendChild(oChild);
7541         }
7542       } else {
7543         oChild = oXMLDoc.createElement(sName);
7544         if (vValue instanceof Object) {
7545           loadObjTree(oXMLDoc, oChild, vValue);
7546         } else if (vValue !== null && vValue !== true) {
7547           oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
7548         }
7549         oParentEl.appendChild(oChild);
7550      }
7551    }
7552   }
7553
7554   this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
7555     var _nVerb = arguments.length > 1 && typeof nVerbosity === "number" ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
7556     return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);    
7557   };
7558
7559   this.unbuild = function (oObjTree) {    
7560     var oNewDoc = document.implementation.createDocument("", "", null);
7561     loadObjTree(oNewDoc, oNewDoc, oObjTree);
7562     return oNewDoc;
7563   };
7564
7565   this.stringify = function (oObjTree) {
7566     return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
7567   };
7568 })();
7569 // var myObject = JXON.build(doc);
7570 // we got our javascript object! try: alert(JSON.stringify(myObject));
7571
7572 // var newDoc = JXON.unbuild(myObject);
7573 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
7574 /**
7575  * @license
7576  * lodash 3.9.3 (Custom Build) <https://lodash.com/>
7577  * 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"`
7578  * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
7579  * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
7580  * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
7581  * Available under MIT license <https://lodash.com/license>
7582  */
7583 ;(function() {
7584
7585   /** Used as a safe reference for `undefined` in pre-ES5 environments. */
7586   var undefined;
7587
7588   /** Used as the semantic version number. */
7589   var VERSION = '3.9.3';
7590
7591   /** Used to compose bitmasks for wrapper metadata. */
7592   var BIND_FLAG = 1,
7593       BIND_KEY_FLAG = 2,
7594       CURRY_BOUND_FLAG = 4,
7595       CURRY_FLAG = 8,
7596       CURRY_RIGHT_FLAG = 16,
7597       PARTIAL_FLAG = 32,
7598       PARTIAL_RIGHT_FLAG = 64,
7599       ARY_FLAG = 128,
7600       REARG_FLAG = 256;
7601
7602   /** Used to detect when a function becomes hot. */
7603   var HOT_COUNT = 150,
7604       HOT_SPAN = 16;
7605
7606   /** Used to indicate the type of lazy iteratees. */
7607   var LAZY_DROP_WHILE_FLAG = 0,
7608       LAZY_FILTER_FLAG = 1,
7609       LAZY_MAP_FLAG = 2;
7610
7611   /** Used as the `TypeError` message for "Functions" methods. */
7612   var FUNC_ERROR_TEXT = 'Expected a function';
7613
7614   /** Used as the internal argument placeholder. */
7615   var PLACEHOLDER = '__lodash_placeholder__';
7616
7617   /** `Object#toString` result references. */
7618   var argsTag = '[object Arguments]',
7619       arrayTag = '[object Array]',
7620       boolTag = '[object Boolean]',
7621       dateTag = '[object Date]',
7622       errorTag = '[object Error]',
7623       funcTag = '[object Function]',
7624       mapTag = '[object Map]',
7625       numberTag = '[object Number]',
7626       objectTag = '[object Object]',
7627       regexpTag = '[object RegExp]',
7628       setTag = '[object Set]',
7629       stringTag = '[object String]',
7630       weakMapTag = '[object WeakMap]';
7631
7632   var arrayBufferTag = '[object ArrayBuffer]',
7633       float32Tag = '[object Float32Array]',
7634       float64Tag = '[object Float64Array]',
7635       int8Tag = '[object Int8Array]',
7636       int16Tag = '[object Int16Array]',
7637       int32Tag = '[object Int32Array]',
7638       uint8Tag = '[object Uint8Array]',
7639       uint8ClampedTag = '[object Uint8ClampedArray]',
7640       uint16Tag = '[object Uint16Array]',
7641       uint32Tag = '[object Uint32Array]';
7642
7643   /** Used to match property names within property paths. */
7644   var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,
7645       reIsPlainProp = /^\w*$/,
7646       rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g;
7647
7648   /**
7649    * Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special).
7650    * In addition to special characters the forward slash is escaped to allow for
7651    * easier `eval` use and `Function` compilation.
7652    */
7653   var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g,
7654       reHasRegExpChars = RegExp(reRegExpChars.source);
7655
7656   /** Used to match backslashes in property paths. */
7657   var reEscapeChar = /\\(\\)?/g;
7658
7659   /** Used to match `RegExp` flags from their coerced string values. */
7660   var reFlags = /\w*$/;
7661
7662   /** Used to detect host constructors (Safari > 5). */
7663   var reIsHostCtor = /^\[object .+?Constructor\]$/;
7664
7665   /** Used to detect unsigned integer values. */
7666   var reIsUint = /^\d+$/;
7667
7668   /** Used to fix the JScript `[[DontEnum]]` bug. */
7669   var shadowProps = [
7670     'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
7671     'toLocaleString', 'toString', 'valueOf'
7672   ];
7673
7674   /** Used to identify `toStringTag` values of typed arrays. */
7675   var typedArrayTags = {};
7676   typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
7677   typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
7678   typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
7679   typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
7680   typedArrayTags[uint32Tag] = true;
7681   typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
7682   typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
7683   typedArrayTags[dateTag] = typedArrayTags[errorTag] =
7684   typedArrayTags[funcTag] = typedArrayTags[mapTag] =
7685   typedArrayTags[numberTag] = typedArrayTags[objectTag] =
7686   typedArrayTags[regexpTag] = typedArrayTags[setTag] =
7687   typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
7688
7689   /** Used to identify `toStringTag` values supported by `_.clone`. */
7690   var cloneableTags = {};
7691   cloneableTags[argsTag] = cloneableTags[arrayTag] =
7692   cloneableTags[arrayBufferTag] = cloneableTags[boolTag] =
7693   cloneableTags[dateTag] = cloneableTags[float32Tag] =
7694   cloneableTags[float64Tag] = cloneableTags[int8Tag] =
7695   cloneableTags[int16Tag] = cloneableTags[int32Tag] =
7696   cloneableTags[numberTag] = cloneableTags[objectTag] =
7697   cloneableTags[regexpTag] = cloneableTags[stringTag] =
7698   cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
7699   cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
7700   cloneableTags[errorTag] = cloneableTags[funcTag] =
7701   cloneableTags[mapTag] = cloneableTags[setTag] =
7702   cloneableTags[weakMapTag] = false;
7703
7704   /** Used as an internal `_.debounce` options object by `_.throttle`. */
7705   var debounceOptions = {
7706     'leading': false,
7707     'maxWait': 0,
7708     'trailing': false
7709   };
7710
7711   /** Used to determine if values are of the language type `Object`. */
7712   var objectTypes = {
7713     'function': true,
7714     'object': true
7715   };
7716
7717   /** Detect free variable `exports`. */
7718   var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
7719
7720   /** Detect free variable `module`. */
7721   var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
7722
7723   /** Detect free variable `global` from Node.js. */
7724   var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global;
7725
7726   /** Detect free variable `self`. */
7727   var freeSelf = objectTypes[typeof self] && self && self.Object && self;
7728
7729   /** Detect free variable `window`. */
7730   var freeWindow = objectTypes[typeof window] && window && window.Object && window;
7731
7732   /** Detect the popular CommonJS extension `module.exports`. */
7733   var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
7734
7735   /**
7736    * Used as a reference to the global object.
7737    *
7738    * The `this` value is used if it's the global object to avoid Greasemonkey's
7739    * restricted `window` object, otherwise the `window` object is used.
7740    */
7741   var root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this;
7742
7743   /*--------------------------------------------------------------------------*/
7744
7745   /**
7746    * The base implementation of `_.findIndex` and `_.findLastIndex` without
7747    * support for callback shorthands and `this` binding.
7748    *
7749    * @private
7750    * @param {Array} array The array to search.
7751    * @param {Function} predicate The function invoked per iteration.
7752    * @param {boolean} [fromRight] Specify iterating from right to left.
7753    * @returns {number} Returns the index of the matched value, else `-1`.
7754    */
7755   function baseFindIndex(array, predicate, fromRight) {
7756     var length = array.length,
7757         index = fromRight ? length : -1;
7758
7759     while ((fromRight ? index-- : ++index < length)) {
7760       if (predicate(array[index], index, array)) {
7761         return index;
7762       }
7763     }
7764     return -1;
7765   }
7766
7767   /**
7768    * The base implementation of `_.indexOf` without support for binary searches.
7769    *
7770    * @private
7771    * @param {Array} array The array to search.
7772    * @param {*} value The value to search for.
7773    * @param {number} fromIndex The index to search from.
7774    * @returns {number} Returns the index of the matched value, else `-1`.
7775    */
7776   function baseIndexOf(array, value, fromIndex) {
7777     if (value !== value) {
7778       return indexOfNaN(array, fromIndex);
7779     }
7780     var index = fromIndex - 1,
7781         length = array.length;
7782
7783     while (++index < length) {
7784       if (array[index] === value) {
7785         return index;
7786       }
7787     }
7788     return -1;
7789   }
7790
7791   /**
7792    * The base implementation of `_.isFunction` without support for environments
7793    * with incorrect `typeof` results.
7794    *
7795    * @private
7796    * @param {*} value The value to check.
7797    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
7798    */
7799   function baseIsFunction(value) {
7800     // Avoid a Chakra JIT bug in compatibility modes of IE 11.
7801     // See https://github.com/jashkenas/underscore/issues/1621 for more details.
7802     return typeof value == 'function' || false;
7803   }
7804
7805   /**
7806    * Converts `value` to a string if it's not one. An empty string is returned
7807    * for `null` or `undefined` values.
7808    *
7809    * @private
7810    * @param {*} value The value to process.
7811    * @returns {string} Returns the string.
7812    */
7813   function baseToString(value) {
7814     if (typeof value == 'string') {
7815       return value;
7816     }
7817     return value == null ? '' : (value + '');
7818   }
7819
7820   /**
7821    * Gets the index at which the first occurrence of `NaN` is found in `array`.
7822    *
7823    * @private
7824    * @param {Array} array The array to search.
7825    * @param {number} fromIndex The index to search from.
7826    * @param {boolean} [fromRight] Specify iterating from right to left.
7827    * @returns {number} Returns the index of the matched `NaN`, else `-1`.
7828    */
7829   function indexOfNaN(array, fromIndex, fromRight) {
7830     var length = array.length,
7831         index = fromIndex + (fromRight ? 0 : -1);
7832
7833     while ((fromRight ? index-- : ++index < length)) {
7834       var other = array[index];
7835       if (other !== other) {
7836         return index;
7837       }
7838     }
7839     return -1;
7840   }
7841
7842   /**
7843    * Checks if `value` is a host object in IE < 9.
7844    *
7845    * @private
7846    * @param {*} value The value to check.
7847    * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
7848    */
7849   var isHostObject = (function() {
7850     try {
7851       Object({ 'toString': 0 } + '');
7852     } catch(e) {
7853       return function() { return false; };
7854     }
7855     return function(value) {
7856       // IE < 9 presents many host objects as `Object` objects that can coerce
7857       // to strings despite having improperly defined `toString` methods.
7858       return typeof value.toString != 'function' && typeof (value + '') == 'string';
7859     };
7860   }());
7861
7862   /**
7863    * Checks if `value` is object-like.
7864    *
7865    * @private
7866    * @param {*} value The value to check.
7867    * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
7868    */
7869   function isObjectLike(value) {
7870     return !!value && typeof value == 'object';
7871   }
7872
7873   /**
7874    * Replaces all `placeholder` elements in `array` with an internal placeholder
7875    * and returns an array of their indexes.
7876    *
7877    * @private
7878    * @param {Array} array The array to modify.
7879    * @param {*} placeholder The placeholder to replace.
7880    * @returns {Array} Returns the new array of placeholder indexes.
7881    */
7882   function replaceHolders(array, placeholder) {
7883     var index = -1,
7884         length = array.length,
7885         resIndex = -1,
7886         result = [];
7887
7888     while (++index < length) {
7889       if (array[index] === placeholder) {
7890         array[index] = PLACEHOLDER;
7891         result[++resIndex] = index;
7892       }
7893     }
7894     return result;
7895   }
7896
7897   /**
7898    * An implementation of `_.uniq` optimized for sorted arrays without support
7899    * for callback shorthands and `this` binding.
7900    *
7901    * @private
7902    * @param {Array} array The array to inspect.
7903    * @param {Function} [iteratee] The function invoked per iteration.
7904    * @returns {Array} Returns the new duplicate-value-free array.
7905    */
7906   function sortedUniq(array, iteratee) {
7907     var seen,
7908         index = -1,
7909         length = array.length,
7910         resIndex = -1,
7911         result = [];
7912
7913     while (++index < length) {
7914       var value = array[index],
7915           computed = iteratee ? iteratee(value, index, array) : value;
7916
7917       if (!index || seen !== computed) {
7918         seen = computed;
7919         result[++resIndex] = value;
7920       }
7921     }
7922     return result;
7923   }
7924
7925   /*--------------------------------------------------------------------------*/
7926
7927   /** Used for native method references. */
7928   var arrayProto = Array.prototype,
7929       errorProto = Error.prototype,
7930       objectProto = Object.prototype,
7931       stringProto = String.prototype;
7932
7933   /** Used to resolve the decompiled source of functions. */
7934   var fnToString = Function.prototype.toString;
7935
7936   /** Used to check objects for own properties. */
7937   var hasOwnProperty = objectProto.hasOwnProperty;
7938
7939   /**
7940    * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
7941    * of values.
7942    */
7943   var objToString = objectProto.toString;
7944
7945   /** Used to detect if a method is native. */
7946   var reIsNative = RegExp('^' +
7947     escapeRegExp(fnToString.call(hasOwnProperty))
7948     .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
7949   );
7950
7951   /** Native method references. */
7952   var ArrayBuffer = getNative(root, 'ArrayBuffer'),
7953       bufferSlice = getNative(ArrayBuffer && new ArrayBuffer(0), 'slice'),
7954       ceil = Math.ceil,
7955       floor = Math.floor,
7956       getPrototypeOf = getNative(Object, 'getPrototypeOf'),
7957       push = arrayProto.push,
7958       propertyIsEnumerable = objectProto.propertyIsEnumerable,
7959       Set = getNative(root, 'Set'),
7960       splice = arrayProto.splice,
7961       Uint8Array = getNative(root, 'Uint8Array'),
7962       WeakMap = getNative(root, 'WeakMap');
7963
7964   /** Used to clone array buffers. */
7965   var Float64Array = (function() {
7966     // Safari 5 errors when using an array buffer to initialize a typed array
7967     // where the array buffer's `byteLength` is not a multiple of the typed
7968     // array's `BYTES_PER_ELEMENT`.
7969     try {
7970       var func = getNative(root, 'Float64Array'),
7971           result = new func(new ArrayBuffer(10), 0, 1) && func;
7972     } catch(e) {}
7973     return result || null;
7974   }());
7975
7976   /* Native method references for those with the same name as other `lodash` methods. */
7977   var nativeCreate = getNative(Object, 'create'),
7978       nativeIsArray = getNative(Array, 'isArray'),
7979       nativeKeys = getNative(Object, 'keys'),
7980       nativeMax = Math.max,
7981       nativeMin = Math.min,
7982       nativeNow = getNative(Date, 'now');
7983
7984   /** Used as references for `-Infinity` and `Infinity`. */
7985   var POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
7986
7987   /** Used as references for the maximum length and index of an array. */
7988   var MAX_ARRAY_LENGTH = 4294967295,
7989       MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
7990       HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
7991
7992   /** Used as the size, in bytes, of each `Float64Array` element. */
7993   var FLOAT64_BYTES_PER_ELEMENT = Float64Array ? Float64Array.BYTES_PER_ELEMENT : 0;
7994
7995   /**
7996    * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer)
7997    * of an array-like value.
7998    */
7999   var MAX_SAFE_INTEGER = 9007199254740991;
8000
8001   /** Used to store function metadata. */
8002   var metaMap = WeakMap && new WeakMap;
8003
8004   /** Used to lookup unminified function names. */
8005   var realNames = {};
8006
8007   /** Used to lookup a type array constructors by `toStringTag`. */
8008   var ctorByTag = {};
8009   ctorByTag[float32Tag] = root.Float32Array;
8010   ctorByTag[float64Tag] = root.Float64Array;
8011   ctorByTag[int8Tag] = root.Int8Array;
8012   ctorByTag[int16Tag] = root.Int16Array;
8013   ctorByTag[int32Tag] = root.Int32Array;
8014   ctorByTag[uint8Tag] = root.Uint8Array;
8015   ctorByTag[uint8ClampedTag] = root.Uint8ClampedArray;
8016   ctorByTag[uint16Tag] = root.Uint16Array;
8017   ctorByTag[uint32Tag] = root.Uint32Array;
8018
8019   /** Used to avoid iterating over non-enumerable properties in IE < 9. */
8020   var nonEnumProps = {};
8021   nonEnumProps[arrayTag] = nonEnumProps[dateTag] = nonEnumProps[numberTag] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
8022   nonEnumProps[boolTag] = nonEnumProps[stringTag] = { 'constructor': true, 'toString': true, 'valueOf': true };
8023   nonEnumProps[errorTag] = nonEnumProps[funcTag] = nonEnumProps[regexpTag] = { 'constructor': true, 'toString': true };
8024   nonEnumProps[objectTag] = { 'constructor': true };
8025
8026   arrayEach(shadowProps, function(key) {
8027     for (var tag in nonEnumProps) {
8028       if (hasOwnProperty.call(nonEnumProps, tag)) {
8029         var props = nonEnumProps[tag];
8030         props[key] = hasOwnProperty.call(props, key);
8031       }
8032     }
8033   });
8034
8035   /*------------------------------------------------------------------------*/
8036
8037   /**
8038    * Creates a `lodash` object which wraps `value` to enable implicit chaining.
8039    * Methods that operate on and return arrays, collections, and functions can
8040    * be chained together. Methods that return a boolean or single value will
8041    * automatically end the chain returning the unwrapped value. Explicit chaining
8042    * may be enabled using `_.chain`. The execution of chained methods is lazy,
8043    * that is, execution is deferred until `_#value` is implicitly or explicitly
8044    * called.
8045    *
8046    * Lazy evaluation allows several methods to support shortcut fusion. Shortcut
8047    * fusion is an optimization that merges iteratees to avoid creating intermediate
8048    * arrays and reduce the number of iteratee executions.
8049    *
8050    * Chaining is supported in custom builds as long as the `_#value` method is
8051    * directly or indirectly included in the build.
8052    *
8053    * In addition to lodash methods, wrappers have `Array` and `String` methods.
8054    *
8055    * The wrapper `Array` methods are:
8056    * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`,
8057    * `splice`, and `unshift`
8058    *
8059    * The wrapper `String` methods are:
8060    * `replace` and `split`
8061    *
8062    * The wrapper methods that support shortcut fusion are:
8063    * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`,
8064    * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`,
8065    * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`,
8066    * and `where`
8067    *
8068    * The chainable wrapper methods are:
8069    * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`,
8070    * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`,
8071    * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defer`, `delay`,
8072    * `difference`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `fill`,
8073    * `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, `forEach`,
8074    * `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `functions`,
8075    * `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, `invoke`, `keys`,
8076    * `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
8077    * `memoize`, `merge`, `method`, `methodOf`, `mixin`, `negate`, `omit`, `once`,
8078    * `pairs`, `partial`, `partialRight`, `partition`, `pick`, `plant`, `pluck`,
8079    * `property`, `propertyOf`, `pull`, `pullAt`, `push`, `range`, `rearg`,
8080    * `reject`, `remove`, `rest`, `restParam`, `reverse`, `set`, `shuffle`,
8081    * `slice`, `sort`, `sortBy`, `sortByAll`, `sortByOrder`, `splice`, `spread`,
8082    * `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `tap`, `throttle`,
8083    * `thru`, `times`, `toArray`, `toPlainObject`, `transform`, `union`, `uniq`,
8084    * `unshift`, `unzip`, `unzipWith`, `values`, `valuesIn`, `where`, `without`,
8085    * `wrap`, `xor`, `zip`, `zipObject`, `zipWith`
8086    *
8087    * The wrapper methods that are **not** chainable by default are:
8088    * `add`, `attempt`, `camelCase`, `capitalize`, `clone`, `cloneDeep`, `deburr`,
8089    * `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`,
8090    * `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, `get`,
8091    * `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`, `inRange`, `isArguments`,
8092    * `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`,
8093    * `isFinite` `isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`,
8094    * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`,
8095    * `isTypedArray`, `join`, `kebabCase`, `last`, `lastIndexOf`, `lt`, `lte`,
8096    * `max`, `min`, `noConflict`, `noop`, `now`, `pad`, `padLeft`, `padRight`,
8097    * `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`,
8098    * `runInContext`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`,
8099    * `sortedLastIndex`, `startCase`, `startsWith`, `sum`, `template`, `trim`,
8100    * `trimLeft`, `trimRight`, `trunc`, `unescape`, `uniqueId`, `value`, and `words`
8101    *
8102    * The wrapper method `sample` will return a wrapped value when `n` is provided,
8103    * otherwise an unwrapped value is returned.
8104    *
8105    * @name _
8106    * @constructor
8107    * @category Chain
8108    * @param {*} value The value to wrap in a `lodash` instance.
8109    * @returns {Object} Returns the new `lodash` wrapper instance.
8110    * @example
8111    *
8112    * var wrapped = _([1, 2, 3]);
8113    *
8114    * // returns an unwrapped value
8115    * wrapped.reduce(function(total, n) {
8116    *   return total + n;
8117    * });
8118    * // => 6
8119    *
8120    * // returns a wrapped value
8121    * var squares = wrapped.map(function(n) {
8122    *   return n * n;
8123    * });
8124    *
8125    * _.isArray(squares);
8126    * // => false
8127    *
8128    * _.isArray(squares.value());
8129    * // => true
8130    */
8131   function lodash(value) {
8132     if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
8133       if (value instanceof LodashWrapper) {
8134         return value;
8135       }
8136       if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) {
8137         return wrapperClone(value);
8138       }
8139     }
8140     return new LodashWrapper(value);
8141   }
8142
8143   /**
8144    * The function whose prototype all chaining wrappers inherit from.
8145    *
8146    * @private
8147    */
8148   function baseLodash() {
8149     // No operation performed.
8150   }
8151
8152   /**
8153    * The base constructor for creating `lodash` wrapper objects.
8154    *
8155    * @private
8156    * @param {*} value The value to wrap.
8157    * @param {boolean} [chainAll] Enable chaining for all wrapper methods.
8158    * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value.
8159    */
8160   function LodashWrapper(value, chainAll, actions) {
8161     this.__wrapped__ = value;
8162     this.__actions__ = actions || [];
8163     this.__chain__ = !!chainAll;
8164   }
8165
8166   /**
8167    * An object environment feature flags.
8168    *
8169    * @static
8170    * @memberOf _
8171    * @type Object
8172    */
8173   var support = lodash.support = {};
8174
8175   (function(x) {
8176     var Ctor = function() { this.x = x; },
8177         object = { '0': x, 'length': x },
8178         props = [];
8179
8180     Ctor.prototype = { 'valueOf': x, 'y': x };
8181     for (var key in new Ctor) { props.push(key); }
8182
8183     /**
8184      * Detect if the `toStringTag` of `arguments` objects is resolvable
8185      * (all but Firefox < 4, IE < 9).
8186      *
8187      * @memberOf _.support
8188      * @type boolean
8189      */
8190     support.argsTag = objToString.call(arguments) == argsTag;
8191
8192     /**
8193      * Detect if `name` or `message` properties of `Error.prototype` are
8194      * enumerable by default (IE < 9, Safari < 5.1).
8195      *
8196      * @memberOf _.support
8197      * @type boolean
8198      */
8199     support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') ||
8200       propertyIsEnumerable.call(errorProto, 'name');
8201
8202     /**
8203      * Detect if `prototype` properties are enumerable by default.
8204      *
8205      * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
8206      * (if the prototype or a property on the prototype has been set)
8207      * incorrectly set the `[[Enumerable]]` value of a function's `prototype`
8208      * property to `true`.
8209      *
8210      * @memberOf _.support
8211      * @type boolean
8212      */
8213     support.enumPrototypes = propertyIsEnumerable.call(Ctor, 'prototype');
8214
8215     /**
8216      * Detect if properties shadowing those on `Object.prototype` are non-enumerable.
8217      *
8218      * In IE < 9 an object's own properties, shadowing non-enumerable ones,
8219      * are made non-enumerable as well (a.k.a the JScript `[[DontEnum]]` bug).
8220      *
8221      * @memberOf _.support
8222      * @type boolean
8223      */
8224     support.nonEnumShadows = !/valueOf/.test(props);
8225
8226     /**
8227      * Detect if own properties are iterated after inherited properties (IE < 9).
8228      *
8229      * @memberOf _.support
8230      * @type boolean
8231      */
8232     support.ownLast = props[0] != 'x';
8233
8234     /**
8235      * Detect if `Array#shift` and `Array#splice` augment array-like objects
8236      * correctly.
8237      *
8238      * Firefox < 10, compatibility modes of IE 8, and IE < 9 have buggy Array
8239      * `shift()` and `splice()` functions that fail to remove the last element,
8240      * `value[0]`, of array-like objects even though the "length" property is
8241      * set to `0`. The `shift()` method is buggy in compatibility modes of IE 8,
8242      * while `splice()` is buggy regardless of mode in IE < 9.
8243      *
8244      * @memberOf _.support
8245      * @type boolean
8246      */
8247     support.spliceObjects = (splice.call(object, 0, 1), !object[0]);
8248
8249     /**
8250      * Detect lack of support for accessing string characters by index.
8251      *
8252      * IE < 8 can't access characters by index. IE 8 can only access characters
8253      * by index on string literals, not string objects.
8254      *
8255      * @memberOf _.support
8256      * @type boolean
8257      */
8258     support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
8259   }(1, 0));
8260
8261   /*------------------------------------------------------------------------*/
8262
8263   /**
8264    * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
8265    *
8266    * @private
8267    * @param {*} value The value to wrap.
8268    */
8269   function LazyWrapper(value) {
8270     this.__wrapped__ = value;
8271     this.__actions__ = null;
8272     this.__dir__ = 1;
8273     this.__dropCount__ = 0;
8274     this.__filtered__ = false;
8275     this.__iteratees__ = null;
8276     this.__takeCount__ = POSITIVE_INFINITY;
8277     this.__views__ = null;
8278   }
8279
8280   /**
8281    * Creates a clone of the lazy wrapper object.
8282    *
8283    * @private
8284    * @name clone
8285    * @memberOf LazyWrapper
8286    * @returns {Object} Returns the cloned `LazyWrapper` object.
8287    */
8288   function lazyClone() {
8289     var actions = this.__actions__,
8290         iteratees = this.__iteratees__,
8291         views = this.__views__,
8292         result = new LazyWrapper(this.__wrapped__);
8293
8294     result.__actions__ = actions ? arrayCopy(actions) : null;
8295     result.__dir__ = this.__dir__;
8296     result.__filtered__ = this.__filtered__;
8297     result.__iteratees__ = iteratees ? arrayCopy(iteratees) : null;
8298     result.__takeCount__ = this.__takeCount__;
8299     result.__views__ = views ? arrayCopy(views) : null;
8300     return result;
8301   }
8302
8303   /**
8304    * Reverses the direction of lazy iteration.
8305    *
8306    * @private
8307    * @name reverse
8308    * @memberOf LazyWrapper
8309    * @returns {Object} Returns the new reversed `LazyWrapper` object.
8310    */
8311   function lazyReverse() {
8312     if (this.__filtered__) {
8313       var result = new LazyWrapper(this);
8314       result.__dir__ = -1;
8315       result.__filtered__ = true;
8316     } else {
8317       result = this.clone();
8318       result.__dir__ *= -1;
8319     }
8320     return result;
8321   }
8322
8323   /**
8324    * Extracts the unwrapped value from its lazy wrapper.
8325    *
8326    * @private
8327    * @name value
8328    * @memberOf LazyWrapper
8329    * @returns {*} Returns the unwrapped value.
8330    */
8331   function lazyValue() {
8332     var array = this.__wrapped__.value();
8333     if (!isArray(array)) {
8334       return baseWrapperValue(array, this.__actions__);
8335     }
8336     var dir = this.__dir__,
8337         isRight = dir < 0,
8338         view = getView(0, array.length, this.__views__),
8339         start = view.start,
8340         end = view.end,
8341         length = end - start,
8342         index = isRight ? end : (start - 1),
8343         takeCount = nativeMin(length, this.__takeCount__),
8344         iteratees = this.__iteratees__,
8345         iterLength = iteratees ? iteratees.length : 0,
8346         resIndex = 0,
8347         result = [];
8348
8349     outer:
8350     while (length-- && resIndex < takeCount) {
8351       index += dir;
8352
8353       var iterIndex = -1,
8354           value = array[index];
8355
8356       while (++iterIndex < iterLength) {
8357         var data = iteratees[iterIndex],
8358             iteratee = data.iteratee,
8359             type = data.type;
8360
8361         if (type == LAZY_DROP_WHILE_FLAG) {
8362           if (data.done && (isRight ? (index > data.index) : (index < data.index))) {
8363             data.count = 0;
8364             data.done = false;
8365           }
8366           data.index = index;
8367           if (!data.done) {
8368             var limit = data.limit;
8369             if (!(data.done = limit > -1 ? (data.count++ >= limit) : !iteratee(value))) {
8370               continue outer;
8371             }
8372           }
8373         } else {
8374           var computed = iteratee(value);
8375           if (type == LAZY_MAP_FLAG) {
8376             value = computed;
8377           } else if (!computed) {
8378             if (type == LAZY_FILTER_FLAG) {
8379               continue outer;
8380             } else {
8381               break outer;
8382             }
8383           }
8384         }
8385       }
8386       result[resIndex++] = value;
8387     }
8388     return result;
8389   }
8390
8391   /*------------------------------------------------------------------------*/
8392
8393   /**
8394    *
8395    * Creates a cache object to store unique values.
8396    *
8397    * @private
8398    * @param {Array} [values] The values to cache.
8399    */
8400   function SetCache(values) {
8401     var length = values ? values.length : 0;
8402
8403     this.data = { 'hash': nativeCreate(null), 'set': new Set };
8404     while (length--) {
8405       this.push(values[length]);
8406     }
8407   }
8408
8409   /**
8410    * Checks if `value` is in `cache` mimicking the return signature of
8411    * `_.indexOf` by returning `0` if the value is found, else `-1`.
8412    *
8413    * @private
8414    * @param {Object} cache The cache to search.
8415    * @param {*} value The value to search for.
8416    * @returns {number} Returns `0` if `value` is found, else `-1`.
8417    */
8418   function cacheIndexOf(cache, value) {
8419     var data = cache.data,
8420         result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value];
8421
8422     return result ? 0 : -1;
8423   }
8424
8425   /**
8426    * Adds `value` to the cache.
8427    *
8428    * @private
8429    * @name push
8430    * @memberOf SetCache
8431    * @param {*} value The value to cache.
8432    */
8433   function cachePush(value) {
8434     var data = this.data;
8435     if (typeof value == 'string' || isObject(value)) {
8436       data.set.add(value);
8437     } else {
8438       data.hash[value] = true;
8439     }
8440   }
8441
8442   /*------------------------------------------------------------------------*/
8443
8444   /**
8445    * Copies the values of `source` to `array`.
8446    *
8447    * @private
8448    * @param {Array} source The array to copy values from.
8449    * @param {Array} [array=[]] The array to copy values to.
8450    * @returns {Array} Returns `array`.
8451    */
8452   function arrayCopy(source, array) {
8453     var index = -1,
8454         length = source.length;
8455
8456     array || (array = Array(length));
8457     while (++index < length) {
8458       array[index] = source[index];
8459     }
8460     return array;
8461   }
8462
8463   /**
8464    * A specialized version of `_.forEach` for arrays without support for callback
8465    * shorthands and `this` binding.
8466    *
8467    * @private
8468    * @param {Array} array The array to iterate over.
8469    * @param {Function} iteratee The function invoked per iteration.
8470    * @returns {Array} Returns `array`.
8471    */
8472   function arrayEach(array, iteratee) {
8473     var index = -1,
8474         length = array.length;
8475
8476     while (++index < length) {
8477       if (iteratee(array[index], index, array) === false) {
8478         break;
8479       }
8480     }
8481     return array;
8482   }
8483
8484   /**
8485    * A specialized version of `_.every` for arrays without support for callback
8486    * shorthands and `this` binding.
8487    *
8488    * @private
8489    * @param {Array} array The array to iterate over.
8490    * @param {Function} predicate The function invoked per iteration.
8491    * @returns {boolean} Returns `true` if all elements pass the predicate check,
8492    *  else `false`.
8493    */
8494   function arrayEvery(array, predicate) {
8495     var index = -1,
8496         length = array.length;
8497
8498     while (++index < length) {
8499       if (!predicate(array[index], index, array)) {
8500         return false;
8501       }
8502     }
8503     return true;
8504   }
8505
8506   /**
8507    * A specialized version of `_.filter` for arrays without support for callback
8508    * shorthands and `this` binding.
8509    *
8510    * @private
8511    * @param {Array} array The array to iterate over.
8512    * @param {Function} predicate The function invoked per iteration.
8513    * @returns {Array} Returns the new filtered array.
8514    */
8515   function arrayFilter(array, predicate) {
8516     var index = -1,
8517         length = array.length,
8518         resIndex = -1,
8519         result = [];
8520
8521     while (++index < length) {
8522       var value = array[index];
8523       if (predicate(value, index, array)) {
8524         result[++resIndex] = value;
8525       }
8526     }
8527     return result;
8528   }
8529
8530   /**
8531    * A specialized version of `_.map` for arrays without support for callback
8532    * shorthands and `this` binding.
8533    *
8534    * @private
8535    * @param {Array} array The array to iterate over.
8536    * @param {Function} iteratee The function invoked per iteration.
8537    * @returns {Array} Returns the new mapped array.
8538    */
8539   function arrayMap(array, iteratee) {
8540     var index = -1,
8541         length = array.length,
8542         result = Array(length);
8543
8544     while (++index < length) {
8545       result[index] = iteratee(array[index], index, array);
8546     }
8547     return result;
8548   }
8549
8550   /**
8551    * A specialized version of `_.reduce` for arrays without support for callback
8552    * shorthands and `this` binding.
8553    *
8554    * @private
8555    * @param {Array} array The array to iterate over.
8556    * @param {Function} iteratee The function invoked per iteration.
8557    * @param {*} [accumulator] The initial value.
8558    * @param {boolean} [initFromArray] Specify using the first element of `array`
8559    *  as the initial value.
8560    * @returns {*} Returns the accumulated value.
8561    */
8562   function arrayReduce(array, iteratee, accumulator, initFromArray) {
8563     var index = -1,
8564         length = array.length;
8565
8566     if (initFromArray && length) {
8567       accumulator = array[++index];
8568     }
8569     while (++index < length) {
8570       accumulator = iteratee(accumulator, array[index], index, array);
8571     }
8572     return accumulator;
8573   }
8574
8575   /**
8576    * A specialized version of `_.some` for arrays without support for callback
8577    * shorthands and `this` binding.
8578    *
8579    * @private
8580    * @param {Array} array The array to iterate over.
8581    * @param {Function} predicate The function invoked per iteration.
8582    * @returns {boolean} Returns `true` if any element passes the predicate check,
8583    *  else `false`.
8584    */
8585   function arraySome(array, predicate) {
8586     var index = -1,
8587         length = array.length;
8588
8589     while (++index < length) {
8590       if (predicate(array[index], index, array)) {
8591         return true;
8592       }
8593     }
8594     return false;
8595   }
8596
8597   /**
8598    * A specialized version of `_.assign` for customizing assigned values without
8599    * support for argument juggling, multiple sources, and `this` binding `customizer`
8600    * functions.
8601    *
8602    * @private
8603    * @param {Object} object The destination object.
8604    * @param {Object} source The source object.
8605    * @param {Function} customizer The function to customize assigned values.
8606    * @returns {Object} Returns `object`.
8607    */
8608   function assignWith(object, source, customizer) {
8609     var index = -1,
8610         props = keys(source),
8611         length = props.length;
8612
8613     while (++index < length) {
8614       var key = props[index],
8615           value = object[key],
8616           result = customizer(value, source[key], key, object, source);
8617
8618       if ((result === result ? (result !== value) : (value === value)) ||
8619           (value === undefined && !(key in object))) {
8620         object[key] = result;
8621       }
8622     }
8623     return object;
8624   }
8625
8626   /**
8627    * The base implementation of `_.assign` without support for argument juggling,
8628    * multiple sources, and `customizer` functions.
8629    *
8630    * @private
8631    * @param {Object} object The destination object.
8632    * @param {Object} source The source object.
8633    * @returns {Object} Returns `object`.
8634    */
8635   function baseAssign(object, source) {
8636     return source == null
8637       ? object
8638       : baseCopy(source, keys(source), object);
8639   }
8640
8641   /**
8642    * Copies properties of `source` to `object`.
8643    *
8644    * @private
8645    * @param {Object} source The object to copy properties from.
8646    * @param {Array} props The property names to copy.
8647    * @param {Object} [object={}] The object to copy properties to.
8648    * @returns {Object} Returns `object`.
8649    */
8650   function baseCopy(source, props, object) {
8651     object || (object = {});
8652
8653     var index = -1,
8654         length = props.length;
8655
8656     while (++index < length) {
8657       var key = props[index];
8658       object[key] = source[key];
8659     }
8660     return object;
8661   }
8662
8663   /**
8664    * The base implementation of `_.callback` which supports specifying the
8665    * number of arguments to provide to `func`.
8666    *
8667    * @private
8668    * @param {*} [func=_.identity] The value to convert to a callback.
8669    * @param {*} [thisArg] The `this` binding of `func`.
8670    * @param {number} [argCount] The number of arguments to provide to `func`.
8671    * @returns {Function} Returns the callback.
8672    */
8673   function baseCallback(func, thisArg, argCount) {
8674     var type = typeof func;
8675     if (type == 'function') {
8676       return thisArg === undefined
8677         ? func
8678         : bindCallback(func, thisArg, argCount);
8679     }
8680     if (func == null) {
8681       return identity;
8682     }
8683     if (type == 'object') {
8684       return baseMatches(func);
8685     }
8686     return thisArg === undefined
8687       ? property(func)
8688       : baseMatchesProperty(func, thisArg);
8689   }
8690
8691   /**
8692    * The base implementation of `_.clone` without support for argument juggling
8693    * and `this` binding `customizer` functions.
8694    *
8695    * @private
8696    * @param {*} value The value to clone.
8697    * @param {boolean} [isDeep] Specify a deep clone.
8698    * @param {Function} [customizer] The function to customize cloning values.
8699    * @param {string} [key] The key of `value`.
8700    * @param {Object} [object] The object `value` belongs to.
8701    * @param {Array} [stackA=[]] Tracks traversed source objects.
8702    * @param {Array} [stackB=[]] Associates clones with source counterparts.
8703    * @returns {*} Returns the cloned value.
8704    */
8705   function baseClone(value, isDeep, customizer, key, object, stackA, stackB) {
8706     var result;
8707     if (customizer) {
8708       result = object ? customizer(value, key, object) : customizer(value);
8709     }
8710     if (result !== undefined) {
8711       return result;
8712     }
8713     if (!isObject(value)) {
8714       return value;
8715     }
8716     var isArr = isArray(value);
8717     if (isArr) {
8718       result = initCloneArray(value);
8719       if (!isDeep) {
8720         return arrayCopy(value, result);
8721       }
8722     } else {
8723       var tag = objToString.call(value),
8724           isFunc = tag == funcTag;
8725
8726       if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
8727         if (isHostObject(value)) {
8728           return object ? value : {};
8729         }
8730         result = initCloneObject(isFunc ? {} : value);
8731         if (!isDeep) {
8732           return baseAssign(result, value);
8733         }
8734       } else {
8735         return cloneableTags[tag]
8736           ? initCloneByTag(value, tag, isDeep)
8737           : (object ? value : {});
8738       }
8739     }
8740     // Check for circular references and return corresponding clone.
8741     stackA || (stackA = []);
8742     stackB || (stackB = []);
8743
8744     var length = stackA.length;
8745     while (length--) {
8746       if (stackA[length] == value) {
8747         return stackB[length];
8748       }
8749     }
8750     // Add the source value to the stack of traversed objects and associate it with its clone.
8751     stackA.push(value);
8752     stackB.push(result);
8753
8754     // Recursively populate clone (susceptible to call stack limits).
8755     (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) {
8756       result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB);
8757     });
8758     return result;
8759   }
8760
8761   /**
8762    * The base implementation of `_.create` without support for assigning
8763    * properties to the created object.
8764    *
8765    * @private
8766    * @param {Object} prototype The object to inherit from.
8767    * @returns {Object} Returns the new object.
8768    */
8769   var baseCreate = (function() {
8770     function object() {}
8771     return function(prototype) {
8772       if (isObject(prototype)) {
8773         object.prototype = prototype;
8774         var result = new object;
8775         object.prototype = null;
8776       }
8777       return result || {};
8778     };
8779   }());
8780
8781   /**
8782    * The base implementation of `_.difference` which accepts a single array
8783    * of values to exclude.
8784    *
8785    * @private
8786    * @param {Array} array The array to inspect.
8787    * @param {Array} values The values to exclude.
8788    * @returns {Array} Returns the new array of filtered values.
8789    */
8790   function baseDifference(array, values) {
8791     var length = array ? array.length : 0,
8792         result = [];
8793
8794     if (!length) {
8795       return result;
8796     }
8797     var index = -1,
8798         indexOf = getIndexOf(),
8799         isCommon = indexOf == baseIndexOf,
8800         cache = (isCommon && values.length >= 200) ? createCache(values) : null,
8801         valuesLength = values.length;
8802
8803     if (cache) {
8804       indexOf = cacheIndexOf;
8805       isCommon = false;
8806       values = cache;
8807     }
8808     outer:
8809     while (++index < length) {
8810       var value = array[index];
8811
8812       if (isCommon && value === value) {
8813         var valuesIndex = valuesLength;
8814         while (valuesIndex--) {
8815           if (values[valuesIndex] === value) {
8816             continue outer;
8817           }
8818         }
8819         result.push(value);
8820       }
8821       else if (indexOf(values, value, 0) < 0) {
8822         result.push(value);
8823       }
8824     }
8825     return result;
8826   }
8827
8828   /**
8829    * The base implementation of `_.forEach` without support for callback
8830    * shorthands and `this` binding.
8831    *
8832    * @private
8833    * @param {Array|Object|string} collection The collection to iterate over.
8834    * @param {Function} iteratee The function invoked per iteration.
8835    * @returns {Array|Object|string} Returns `collection`.
8836    */
8837   var baseEach = createBaseEach(baseForOwn);
8838
8839   /**
8840    * The base implementation of `_.every` without support for callback
8841    * shorthands and `this` binding.
8842    *
8843    * @private
8844    * @param {Array|Object|string} collection The collection to iterate over.
8845    * @param {Function} predicate The function invoked per iteration.
8846    * @returns {boolean} Returns `true` if all elements pass the predicate check,
8847    *  else `false`
8848    */
8849   function baseEvery(collection, predicate) {
8850     var result = true;
8851     baseEach(collection, function(value, index, collection) {
8852       result = !!predicate(value, index, collection);
8853       return result;
8854     });
8855     return result;
8856   }
8857
8858   /**
8859    * The base implementation of `_.filter` without support for callback
8860    * shorthands and `this` binding.
8861    *
8862    * @private
8863    * @param {Array|Object|string} collection The collection to iterate over.
8864    * @param {Function} predicate The function invoked per iteration.
8865    * @returns {Array} Returns the new filtered array.
8866    */
8867   function baseFilter(collection, predicate) {
8868     var result = [];
8869     baseEach(collection, function(value, index, collection) {
8870       if (predicate(value, index, collection)) {
8871         result.push(value);
8872       }
8873     });
8874     return result;
8875   }
8876
8877   /**
8878    * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`,
8879    * without support for callback shorthands and `this` binding, which iterates
8880    * over `collection` using the provided `eachFunc`.
8881    *
8882    * @private
8883    * @param {Array|Object|string} collection The collection to search.
8884    * @param {Function} predicate The function invoked per iteration.
8885    * @param {Function} eachFunc The function to iterate over `collection`.
8886    * @param {boolean} [retKey] Specify returning the key of the found element
8887    *  instead of the element itself.
8888    * @returns {*} Returns the found element or its key, else `undefined`.
8889    */
8890   function baseFind(collection, predicate, eachFunc, retKey) {
8891     var result;
8892     eachFunc(collection, function(value, key, collection) {
8893       if (predicate(value, key, collection)) {
8894         result = retKey ? key : value;
8895         return false;
8896       }
8897     });
8898     return result;
8899   }
8900
8901   /**
8902    * The base implementation of `_.flatten` with added support for restricting
8903    * flattening and specifying the start index.
8904    *
8905    * @private
8906    * @param {Array} array The array to flatten.
8907    * @param {boolean} [isDeep] Specify a deep flatten.
8908    * @param {boolean} [isStrict] Restrict flattening to arrays-like objects.
8909    * @returns {Array} Returns the new flattened array.
8910    */
8911   function baseFlatten(array, isDeep, isStrict) {
8912     var index = -1,
8913         length = array.length,
8914         resIndex = -1,
8915         result = [];
8916
8917     while (++index < length) {
8918       var value = array[index];
8919       if (isObjectLike(value) && isArrayLike(value) &&
8920           (isStrict || isArray(value) || isArguments(value))) {
8921         if (isDeep) {
8922           // Recursively flatten arrays (susceptible to call stack limits).
8923           value = baseFlatten(value, isDeep, isStrict);
8924         }
8925         var valIndex = -1,
8926             valLength = value.length;
8927
8928         while (++valIndex < valLength) {
8929           result[++resIndex] = value[valIndex];
8930         }
8931       } else if (!isStrict) {
8932         result[++resIndex] = value;
8933       }
8934     }
8935     return result;
8936   }
8937
8938   /**
8939    * The base implementation of `baseForIn` and `baseForOwn` which iterates
8940    * over `object` properties returned by `keysFunc` invoking `iteratee` for
8941    * each property. Iteratee functions may exit iteration early by explicitly
8942    * returning `false`.
8943    *
8944    * @private
8945    * @param {Object} object The object to iterate over.
8946    * @param {Function} iteratee The function invoked per iteration.
8947    * @param {Function} keysFunc The function to get the keys of `object`.
8948    * @returns {Object} Returns `object`.
8949    */
8950   var baseFor = createBaseFor();
8951
8952   /**
8953    * The base implementation of `_.forIn` without support for callback
8954    * shorthands and `this` binding.
8955    *
8956    * @private
8957    * @param {Object} object The object to iterate over.
8958    * @param {Function} iteratee The function invoked per iteration.
8959    * @returns {Object} Returns `object`.
8960    */
8961   function baseForIn(object, iteratee) {
8962     return baseFor(object, iteratee, keysIn);
8963   }
8964
8965   /**
8966    * The base implementation of `_.forOwn` without support for callback
8967    * shorthands and `this` binding.
8968    *
8969    * @private
8970    * @param {Object} object The object to iterate over.
8971    * @param {Function} iteratee The function invoked per iteration.
8972    * @returns {Object} Returns `object`.
8973    */
8974   function baseForOwn(object, iteratee) {
8975     return baseFor(object, iteratee, keys);
8976   }
8977
8978   /**
8979    * The base implementation of `_.functions` which creates an array of
8980    * `object` function property names filtered from those provided.
8981    *
8982    * @private
8983    * @param {Object} object The object to inspect.
8984    * @param {Array} props The property names to filter.
8985    * @returns {Array} Returns the new array of filtered property names.
8986    */
8987   function baseFunctions(object, props) {
8988     var index = -1,
8989         length = props.length,
8990         resIndex = -1,
8991         result = [];
8992
8993     while (++index < length) {
8994       var key = props[index];
8995       if (isFunction(object[key])) {
8996         result[++resIndex] = key;
8997       }
8998     }
8999     return result;
9000   }
9001
9002   /**
9003    * The base implementation of `get` without support for string paths
9004    * and default values.
9005    *
9006    * @private
9007    * @param {Object} object The object to query.
9008    * @param {Array} path The path of the property to get.
9009    * @param {string} [pathKey] The key representation of path.
9010    * @returns {*} Returns the resolved value.
9011    */
9012   function baseGet(object, path, pathKey) {
9013     if (object == null) {
9014       return;
9015     }
9016     object = toObject(object);
9017     if (pathKey !== undefined && pathKey in object) {
9018       path = [pathKey];
9019     }
9020     var index = 0,
9021         length = path.length;
9022
9023     while (object != null && index < length) {
9024       object = toObject(object)[path[index++]];
9025     }
9026     return (index && index == length) ? object : undefined;
9027   }
9028
9029   /**
9030    * The base implementation of `_.isEqual` without support for `this` binding
9031    * `customizer` functions.
9032    *
9033    * @private
9034    * @param {*} value The value to compare.
9035    * @param {*} other The other value to compare.
9036    * @param {Function} [customizer] The function to customize comparing values.
9037    * @param {boolean} [isLoose] Specify performing partial comparisons.
9038    * @param {Array} [stackA] Tracks traversed `value` objects.
9039    * @param {Array} [stackB] Tracks traversed `other` objects.
9040    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
9041    */
9042   function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) {
9043     if (value === other) {
9044       return true;
9045     }
9046     if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
9047       return value !== value && other !== other;
9048     }
9049     return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB);
9050   }
9051
9052   /**
9053    * A specialized version of `baseIsEqual` for arrays and objects which performs
9054    * deep comparisons and tracks traversed objects enabling objects with circular
9055    * references to be compared.
9056    *
9057    * @private
9058    * @param {Object} object The object to compare.
9059    * @param {Object} other The other object to compare.
9060    * @param {Function} equalFunc The function to determine equivalents of values.
9061    * @param {Function} [customizer] The function to customize comparing objects.
9062    * @param {boolean} [isLoose] Specify performing partial comparisons.
9063    * @param {Array} [stackA=[]] Tracks traversed `value` objects.
9064    * @param {Array} [stackB=[]] Tracks traversed `other` objects.
9065    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9066    */
9067   function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
9068     var objIsArr = isArray(object),
9069         othIsArr = isArray(other),
9070         objTag = arrayTag,
9071         othTag = arrayTag;
9072
9073     if (!objIsArr) {
9074       objTag = objToString.call(object);
9075       if (objTag == argsTag) {
9076         objTag = objectTag;
9077       } else if (objTag != objectTag) {
9078         objIsArr = isTypedArray(object);
9079       }
9080     }
9081     if (!othIsArr) {
9082       othTag = objToString.call(other);
9083       if (othTag == argsTag) {
9084         othTag = objectTag;
9085       } else if (othTag != objectTag) {
9086         othIsArr = isTypedArray(other);
9087       }
9088     }
9089     var objIsObj = objTag == objectTag && !isHostObject(object),
9090         othIsObj = othTag == objectTag && !isHostObject(other),
9091         isSameTag = objTag == othTag;
9092
9093     if (isSameTag && !(objIsArr || objIsObj)) {
9094       return equalByTag(object, other, objTag);
9095     }
9096     if (!isLoose) {
9097       var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
9098           othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
9099
9100       if (objIsWrapped || othIsWrapped) {
9101         return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB);
9102       }
9103     }
9104     if (!isSameTag) {
9105       return false;
9106     }
9107     // Assume cyclic values are equal.
9108     // For more information on detecting circular references see https://es5.github.io/#JO.
9109     stackA || (stackA = []);
9110     stackB || (stackB = []);
9111
9112     var length = stackA.length;
9113     while (length--) {
9114       if (stackA[length] == object) {
9115         return stackB[length] == other;
9116       }
9117     }
9118     // Add `object` and `other` to the stack of traversed objects.
9119     stackA.push(object);
9120     stackB.push(other);
9121
9122     var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB);
9123
9124     stackA.pop();
9125     stackB.pop();
9126
9127     return result;
9128   }
9129
9130   /**
9131    * The base implementation of `_.isMatch` without support for callback
9132    * shorthands and `this` binding.
9133    *
9134    * @private
9135    * @param {Object} object The object to inspect.
9136    * @param {Array} matchData The propery names, values, and compare flags to match.
9137    * @param {Function} [customizer] The function to customize comparing objects.
9138    * @returns {boolean} Returns `true` if `object` is a match, else `false`.
9139    */
9140   function baseIsMatch(object, matchData, customizer) {
9141     var index = matchData.length,
9142         length = index,
9143         noCustomizer = !customizer;
9144
9145     if (object == null) {
9146       return !length;
9147     }
9148     object = toObject(object);
9149     while (index--) {
9150       var data = matchData[index];
9151       if ((noCustomizer && data[2])
9152             ? data[1] !== object[data[0]]
9153             : !(data[0] in object)
9154           ) {
9155         return false;
9156       }
9157     }
9158     while (++index < length) {
9159       data = matchData[index];
9160       var key = data[0],
9161           objValue = object[key],
9162           srcValue = data[1];
9163
9164       if (noCustomizer && data[2]) {
9165         if (objValue === undefined && !(key in object)) {
9166           return false;
9167         }
9168       } else {
9169         var result = customizer ? customizer(objValue, srcValue, key) : undefined;
9170         if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, true) : result)) {
9171           return false;
9172         }
9173       }
9174     }
9175     return true;
9176   }
9177
9178   /**
9179    * The base implementation of `_.map` without support for callback shorthands
9180    * and `this` binding.
9181    *
9182    * @private
9183    * @param {Array|Object|string} collection The collection to iterate over.
9184    * @param {Function} iteratee The function invoked per iteration.
9185    * @returns {Array} Returns the new mapped array.
9186    */
9187   function baseMap(collection, iteratee) {
9188     var index = -1,
9189         result = isArrayLike(collection) ? Array(collection.length) : [];
9190
9191     baseEach(collection, function(value, key, collection) {
9192       result[++index] = iteratee(value, key, collection);
9193     });
9194     return result;
9195   }
9196
9197   /**
9198    * The base implementation of `_.matches` which does not clone `source`.
9199    *
9200    * @private
9201    * @param {Object} source The object of property values to match.
9202    * @returns {Function} Returns the new function.
9203    */
9204   function baseMatches(source) {
9205     var matchData = getMatchData(source);
9206     if (matchData.length == 1 && matchData[0][2]) {
9207       var key = matchData[0][0],
9208           value = matchData[0][1];
9209
9210       return function(object) {
9211         if (object == null) {
9212           return false;
9213         }
9214         object = toObject(object);
9215         return object[key] === value && (value !== undefined || (key in object));
9216       };
9217     }
9218     return function(object) {
9219       return baseIsMatch(object, matchData);
9220     };
9221   }
9222
9223   /**
9224    * The base implementation of `_.matchesProperty` which does not clone `srcValue`.
9225    *
9226    * @private
9227    * @param {string} path The path of the property to get.
9228    * @param {*} srcValue The value to compare.
9229    * @returns {Function} Returns the new function.
9230    */
9231   function baseMatchesProperty(path, srcValue) {
9232     var isArr = isArray(path),
9233         isCommon = isKey(path) && isStrictComparable(srcValue),
9234         pathKey = (path + '');
9235
9236     path = toPath(path);
9237     return function(object) {
9238       if (object == null) {
9239         return false;
9240       }
9241       var key = pathKey;
9242       object = toObject(object);
9243       if ((isArr || !isCommon) && !(key in object)) {
9244         object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
9245         if (object == null) {
9246           return false;
9247         }
9248         key = last(path);
9249         object = toObject(object);
9250       }
9251       return object[key] === srcValue
9252         ? (srcValue !== undefined || (key in object))
9253         : baseIsEqual(srcValue, object[key], undefined, true);
9254     };
9255   }
9256
9257   /**
9258    * The base implementation of `_.merge` without support for argument juggling,
9259    * multiple sources, and `this` binding `customizer` functions.
9260    *
9261    * @private
9262    * @param {Object} object The destination object.
9263    * @param {Object} source The source object.
9264    * @param {Function} [customizer] The function to customize merging properties.
9265    * @param {Array} [stackA=[]] Tracks traversed source objects.
9266    * @param {Array} [stackB=[]] Associates values with source counterparts.
9267    * @returns {Object} Returns `object`.
9268    */
9269   function baseMerge(object, source, customizer, stackA, stackB) {
9270     if (!isObject(object)) {
9271       return object;
9272     }
9273     var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)),
9274         props = isSrcArr ? null : keys(source);
9275
9276     arrayEach(props || source, function(srcValue, key) {
9277       if (props) {
9278         key = srcValue;
9279         srcValue = source[key];
9280       }
9281       if (isObjectLike(srcValue)) {
9282         stackA || (stackA = []);
9283         stackB || (stackB = []);
9284         baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
9285       }
9286       else {
9287         var value = object[key],
9288             result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
9289             isCommon = result === undefined;
9290
9291         if (isCommon) {
9292           result = srcValue;
9293         }
9294         if ((result !== undefined || (isSrcArr && !(key in object))) &&
9295             (isCommon || (result === result ? (result !== value) : (value === value)))) {
9296           object[key] = result;
9297         }
9298       }
9299     });
9300     return object;
9301   }
9302
9303   /**
9304    * A specialized version of `baseMerge` for arrays and objects which performs
9305    * deep merges and tracks traversed objects enabling objects with circular
9306    * references to be merged.
9307    *
9308    * @private
9309    * @param {Object} object The destination object.
9310    * @param {Object} source The source object.
9311    * @param {string} key The key of the value to merge.
9312    * @param {Function} mergeFunc The function to merge values.
9313    * @param {Function} [customizer] The function to customize merging properties.
9314    * @param {Array} [stackA=[]] Tracks traversed source objects.
9315    * @param {Array} [stackB=[]] Associates values with source counterparts.
9316    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9317    */
9318   function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) {
9319     var length = stackA.length,
9320         srcValue = source[key];
9321
9322     while (length--) {
9323       if (stackA[length] == srcValue) {
9324         object[key] = stackB[length];
9325         return;
9326       }
9327     }
9328     var value = object[key],
9329         result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
9330         isCommon = result === undefined;
9331
9332     if (isCommon) {
9333       result = srcValue;
9334       if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) {
9335         result = isArray(value)
9336           ? value
9337           : (isArrayLike(value) ? arrayCopy(value) : []);
9338       }
9339       else if (isPlainObject(srcValue) || isArguments(srcValue)) {
9340         result = isArguments(value)
9341           ? toPlainObject(value)
9342           : (isPlainObject(value) ? value : {});
9343       }
9344       else {
9345         isCommon = false;
9346       }
9347     }
9348     // Add the source value to the stack of traversed objects and associate
9349     // it with its merged value.
9350     stackA.push(srcValue);
9351     stackB.push(result);
9352
9353     if (isCommon) {
9354       // Recursively merge objects and arrays (susceptible to call stack limits).
9355       object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB);
9356     } else if (result === result ? (result !== value) : (value === value)) {
9357       object[key] = result;
9358     }
9359   }
9360
9361   /**
9362    * The base implementation of `_.property` without support for deep paths.
9363    *
9364    * @private
9365    * @param {string} key The key of the property to get.
9366    * @returns {Function} Returns the new function.
9367    */
9368   function baseProperty(key) {
9369     return function(object) {
9370       return object == null ? undefined : toObject(object)[key];
9371     };
9372   }
9373
9374   /**
9375    * A specialized version of `baseProperty` which supports deep paths.
9376    *
9377    * @private
9378    * @param {Array|string} path The path of the property to get.
9379    * @returns {Function} Returns the new function.
9380    */
9381   function basePropertyDeep(path) {
9382     var pathKey = (path + '');
9383     path = toPath(path);
9384     return function(object) {
9385       return baseGet(object, path, pathKey);
9386     };
9387   }
9388
9389   /**
9390    * The base implementation of `_.reduce` and `_.reduceRight` without support
9391    * for callback shorthands and `this` binding, which iterates over `collection`
9392    * using the provided `eachFunc`.
9393    *
9394    * @private
9395    * @param {Array|Object|string} collection The collection to iterate over.
9396    * @param {Function} iteratee The function invoked per iteration.
9397    * @param {*} accumulator The initial value.
9398    * @param {boolean} initFromCollection Specify using the first or last element
9399    *  of `collection` as the initial value.
9400    * @param {Function} eachFunc The function to iterate over `collection`.
9401    * @returns {*} Returns the accumulated value.
9402    */
9403   function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) {
9404     eachFunc(collection, function(value, index, collection) {
9405       accumulator = initFromCollection
9406         ? (initFromCollection = false, value)
9407         : iteratee(accumulator, value, index, collection);
9408     });
9409     return accumulator;
9410   }
9411
9412   /**
9413    * The base implementation of `setData` without support for hot loop detection.
9414    *
9415    * @private
9416    * @param {Function} func The function to associate metadata with.
9417    * @param {*} data The metadata.
9418    * @returns {Function} Returns `func`.
9419    */
9420   var baseSetData = !metaMap ? identity : function(func, data) {
9421     metaMap.set(func, data);
9422     return func;
9423   };
9424
9425   /**
9426    * The base implementation of `_.slice` without an iteratee call guard.
9427    *
9428    * @private
9429    * @param {Array} array The array to slice.
9430    * @param {number} [start=0] The start position.
9431    * @param {number} [end=array.length] The end position.
9432    * @returns {Array} Returns the slice of `array`.
9433    */
9434   function baseSlice(array, start, end) {
9435     var index = -1,
9436         length = array.length;
9437
9438     start = start == null ? 0 : (+start || 0);
9439     if (start < 0) {
9440       start = -start > length ? 0 : (length + start);
9441     }
9442     end = (end === undefined || end > length) ? length : (+end || 0);
9443     if (end < 0) {
9444       end += length;
9445     }
9446     length = start > end ? 0 : ((end - start) >>> 0);
9447     start >>>= 0;
9448
9449     var result = Array(length);
9450     while (++index < length) {
9451       result[index] = array[index + start];
9452     }
9453     return result;
9454   }
9455
9456   /**
9457    * The base implementation of `_.some` without support for callback shorthands
9458    * and `this` binding.
9459    *
9460    * @private
9461    * @param {Array|Object|string} collection The collection to iterate over.
9462    * @param {Function} predicate The function invoked per iteration.
9463    * @returns {boolean} Returns `true` if any element passes the predicate check,
9464    *  else `false`.
9465    */
9466   function baseSome(collection, predicate) {
9467     var result;
9468
9469     baseEach(collection, function(value, index, collection) {
9470       result = predicate(value, index, collection);
9471       return !result;
9472     });
9473     return !!result;
9474   }
9475
9476   /**
9477    * The base implementation of `_.uniq` without support for callback shorthands
9478    * and `this` binding.
9479    *
9480    * @private
9481    * @param {Array} array The array to inspect.
9482    * @param {Function} [iteratee] The function invoked per iteration.
9483    * @returns {Array} Returns the new duplicate-value-free array.
9484    */
9485   function baseUniq(array, iteratee) {
9486     var index = -1,
9487         indexOf = getIndexOf(),
9488         length = array.length,
9489         isCommon = indexOf == baseIndexOf,
9490         isLarge = isCommon && length >= 200,
9491         seen = isLarge ? createCache() : null,
9492         result = [];
9493
9494     if (seen) {
9495       indexOf = cacheIndexOf;
9496       isCommon = false;
9497     } else {
9498       isLarge = false;
9499       seen = iteratee ? [] : result;
9500     }
9501     outer:
9502     while (++index < length) {
9503       var value = array[index],
9504           computed = iteratee ? iteratee(value, index, array) : value;
9505
9506       if (isCommon && value === value) {
9507         var seenIndex = seen.length;
9508         while (seenIndex--) {
9509           if (seen[seenIndex] === computed) {
9510             continue outer;
9511           }
9512         }
9513         if (iteratee) {
9514           seen.push(computed);
9515         }
9516         result.push(value);
9517       }
9518       else if (indexOf(seen, computed, 0) < 0) {
9519         if (iteratee || isLarge) {
9520           seen.push(computed);
9521         }
9522         result.push(value);
9523       }
9524     }
9525     return result;
9526   }
9527
9528   /**
9529    * The base implementation of `_.values` and `_.valuesIn` which creates an
9530    * array of `object` property values corresponding to the property names
9531    * of `props`.
9532    *
9533    * @private
9534    * @param {Object} object The object to query.
9535    * @param {Array} props The property names to get values for.
9536    * @returns {Object} Returns the array of property values.
9537    */
9538   function baseValues(object, props) {
9539     var index = -1,
9540         length = props.length,
9541         result = Array(length);
9542
9543     while (++index < length) {
9544       result[index] = object[props[index]];
9545     }
9546     return result;
9547   }
9548
9549   /**
9550    * The base implementation of `wrapperValue` which returns the result of
9551    * performing a sequence of actions on the unwrapped `value`, where each
9552    * successive action is supplied the return value of the previous.
9553    *
9554    * @private
9555    * @param {*} value The unwrapped value.
9556    * @param {Array} actions Actions to peform to resolve the unwrapped value.
9557    * @returns {*} Returns the resolved value.
9558    */
9559   function baseWrapperValue(value, actions) {
9560     var result = value;
9561     if (result instanceof LazyWrapper) {
9562       result = result.value();
9563     }
9564     var index = -1,
9565         length = actions.length;
9566
9567     while (++index < length) {
9568       var args = [result],
9569           action = actions[index];
9570
9571       push.apply(args, action.args);
9572       result = action.func.apply(action.thisArg, args);
9573     }
9574     return result;
9575   }
9576
9577   /**
9578    * Performs a binary search of `array` to determine the index at which `value`
9579    * should be inserted into `array` in order to maintain its sort order.
9580    *
9581    * @private
9582    * @param {Array} array The sorted array to inspect.
9583    * @param {*} value The value to evaluate.
9584    * @param {boolean} [retHighest] Specify returning the highest qualified index.
9585    * @returns {number} Returns the index at which `value` should be inserted
9586    *  into `array`.
9587    */
9588   function binaryIndex(array, value, retHighest) {
9589     var low = 0,
9590         high = array ? array.length : low;
9591
9592     if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
9593       while (low < high) {
9594         var mid = (low + high) >>> 1,
9595             computed = array[mid];
9596
9597         if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) {
9598           low = mid + 1;
9599         } else {
9600           high = mid;
9601         }
9602       }
9603       return high;
9604     }
9605     return binaryIndexBy(array, value, identity, retHighest);
9606   }
9607
9608   /**
9609    * This function is like `binaryIndex` except that it invokes `iteratee` for
9610    * `value` and each element of `array` to compute their sort ranking. The
9611    * iteratee is invoked with one argument; (value).
9612    *
9613    * @private
9614    * @param {Array} array The sorted array to inspect.
9615    * @param {*} value The value to evaluate.
9616    * @param {Function} iteratee The function invoked per iteration.
9617    * @param {boolean} [retHighest] Specify returning the highest qualified index.
9618    * @returns {number} Returns the index at which `value` should be inserted
9619    *  into `array`.
9620    */
9621   function binaryIndexBy(array, value, iteratee, retHighest) {
9622     value = iteratee(value);
9623
9624     var low = 0,
9625         high = array ? array.length : 0,
9626         valIsNaN = value !== value,
9627         valIsNull = value === null,
9628         valIsUndef = value === undefined;
9629
9630     while (low < high) {
9631       var mid = floor((low + high) / 2),
9632           computed = iteratee(array[mid]),
9633           isDef = computed !== undefined,
9634           isReflexive = computed === computed;
9635
9636       if (valIsNaN) {
9637         var setLow = isReflexive || retHighest;
9638       } else if (valIsNull) {
9639         setLow = isReflexive && isDef && (retHighest || computed != null);
9640       } else if (valIsUndef) {
9641         setLow = isReflexive && (retHighest || isDef);
9642       } else if (computed == null) {
9643         setLow = false;
9644       } else {
9645         setLow = retHighest ? (computed <= value) : (computed < value);
9646       }
9647       if (setLow) {
9648         low = mid + 1;
9649       } else {
9650         high = mid;
9651       }
9652     }
9653     return nativeMin(high, MAX_ARRAY_INDEX);
9654   }
9655
9656   /**
9657    * A specialized version of `baseCallback` which only supports `this` binding
9658    * and specifying the number of arguments to provide to `func`.
9659    *
9660    * @private
9661    * @param {Function} func The function to bind.
9662    * @param {*} thisArg The `this` binding of `func`.
9663    * @param {number} [argCount] The number of arguments to provide to `func`.
9664    * @returns {Function} Returns the callback.
9665    */
9666   function bindCallback(func, thisArg, argCount) {
9667     if (typeof func != 'function') {
9668       return identity;
9669     }
9670     if (thisArg === undefined) {
9671       return func;
9672     }
9673     switch (argCount) {
9674       case 1: return function(value) {
9675         return func.call(thisArg, value);
9676       };
9677       case 3: return function(value, index, collection) {
9678         return func.call(thisArg, value, index, collection);
9679       };
9680       case 4: return function(accumulator, value, index, collection) {
9681         return func.call(thisArg, accumulator, value, index, collection);
9682       };
9683       case 5: return function(value, other, key, object, source) {
9684         return func.call(thisArg, value, other, key, object, source);
9685       };
9686     }
9687     return function() {
9688       return func.apply(thisArg, arguments);
9689     };
9690   }
9691
9692   /**
9693    * Creates a clone of the given array buffer.
9694    *
9695    * @private
9696    * @param {ArrayBuffer} buffer The array buffer to clone.
9697    * @returns {ArrayBuffer} Returns the cloned array buffer.
9698    */
9699   function bufferClone(buffer) {
9700     return bufferSlice.call(buffer, 0);
9701   }
9702   if (!bufferSlice) {
9703     // PhantomJS has `ArrayBuffer` and `Uint8Array` but not `Float64Array`.
9704     bufferClone = !(ArrayBuffer && Uint8Array) ? constant(null) : function(buffer) {
9705       var byteLength = buffer.byteLength,
9706           floatLength = Float64Array ? floor(byteLength / FLOAT64_BYTES_PER_ELEMENT) : 0,
9707           offset = floatLength * FLOAT64_BYTES_PER_ELEMENT,
9708           result = new ArrayBuffer(byteLength);
9709
9710       if (floatLength) {
9711         var view = new Float64Array(result, 0, floatLength);
9712         view.set(new Float64Array(buffer, 0, floatLength));
9713       }
9714       if (byteLength != offset) {
9715         view = new Uint8Array(result, offset);
9716         view.set(new Uint8Array(buffer, offset));
9717       }
9718       return result;
9719     };
9720   }
9721
9722   /**
9723    * Creates an array that is the composition of partially applied arguments,
9724    * placeholders, and provided arguments into a single array of arguments.
9725    *
9726    * @private
9727    * @param {Array|Object} args The provided arguments.
9728    * @param {Array} partials The arguments to prepend to those provided.
9729    * @param {Array} holders The `partials` placeholder indexes.
9730    * @returns {Array} Returns the new array of composed arguments.
9731    */
9732   function composeArgs(args, partials, holders) {
9733     var holdersLength = holders.length,
9734         argsIndex = -1,
9735         argsLength = nativeMax(args.length - holdersLength, 0),
9736         leftIndex = -1,
9737         leftLength = partials.length,
9738         result = Array(argsLength + leftLength);
9739
9740     while (++leftIndex < leftLength) {
9741       result[leftIndex] = partials[leftIndex];
9742     }
9743     while (++argsIndex < holdersLength) {
9744       result[holders[argsIndex]] = args[argsIndex];
9745     }
9746     while (argsLength--) {
9747       result[leftIndex++] = args[argsIndex++];
9748     }
9749     return result;
9750   }
9751
9752   /**
9753    * This function is like `composeArgs` except that the arguments composition
9754    * is tailored for `_.partialRight`.
9755    *
9756    * @private
9757    * @param {Array|Object} args The provided arguments.
9758    * @param {Array} partials The arguments to append to those provided.
9759    * @param {Array} holders The `partials` placeholder indexes.
9760    * @returns {Array} Returns the new array of composed arguments.
9761    */
9762   function composeArgsRight(args, partials, holders) {
9763     var holdersIndex = -1,
9764         holdersLength = holders.length,
9765         argsIndex = -1,
9766         argsLength = nativeMax(args.length - holdersLength, 0),
9767         rightIndex = -1,
9768         rightLength = partials.length,
9769         result = Array(argsLength + rightLength);
9770
9771     while (++argsIndex < argsLength) {
9772       result[argsIndex] = args[argsIndex];
9773     }
9774     var offset = argsIndex;
9775     while (++rightIndex < rightLength) {
9776       result[offset + rightIndex] = partials[rightIndex];
9777     }
9778     while (++holdersIndex < holdersLength) {
9779       result[offset + holders[holdersIndex]] = args[argsIndex++];
9780     }
9781     return result;
9782   }
9783
9784   /**
9785    * Creates a function that aggregates a collection, creating an accumulator
9786    * object composed from the results of running each element in the collection
9787    * through an iteratee.
9788    *
9789    * **Note:** This function is used to create `_.countBy`, `_.groupBy`, `_.indexBy`,
9790    * and `_.partition`.
9791    *
9792    * @private
9793    * @param {Function} setter The function to set keys and values of the accumulator object.
9794    * @param {Function} [initializer] The function to initialize the accumulator object.
9795    * @returns {Function} Returns the new aggregator function.
9796    */
9797   function createAggregator(setter, initializer) {
9798     return function(collection, iteratee, thisArg) {
9799       var result = initializer ? initializer() : {};
9800       iteratee = getCallback(iteratee, thisArg, 3);
9801
9802       if (isArray(collection)) {
9803         var index = -1,
9804             length = collection.length;
9805
9806         while (++index < length) {
9807           var value = collection[index];
9808           setter(result, value, iteratee(value, index, collection), collection);
9809         }
9810       } else {
9811         baseEach(collection, function(value, key, collection) {
9812           setter(result, value, iteratee(value, key, collection), collection);
9813         });
9814       }
9815       return result;
9816     };
9817   }
9818
9819   /**
9820    * Creates a function that assigns properties of source object(s) to a given
9821    * destination object.
9822    *
9823    * **Note:** This function is used to create `_.assign`, `_.defaults`, and `_.merge`.
9824    *
9825    * @private
9826    * @param {Function} assigner The function to assign values.
9827    * @returns {Function} Returns the new assigner function.
9828    */
9829   function createAssigner(assigner) {
9830     return restParam(function(object, sources) {
9831       var index = -1,
9832           length = object == null ? 0 : sources.length,
9833           customizer = length > 2 ? sources[length - 2] : undefined,
9834           guard = length > 2 ? sources[2] : undefined,
9835           thisArg = length > 1 ? sources[length - 1] : undefined;
9836
9837       if (typeof customizer == 'function') {
9838         customizer = bindCallback(customizer, thisArg, 5);
9839         length -= 2;
9840       } else {
9841         customizer = typeof thisArg == 'function' ? thisArg : undefined;
9842         length -= (customizer ? 1 : 0);
9843       }
9844       if (guard && isIterateeCall(sources[0], sources[1], guard)) {
9845         customizer = length < 3 ? undefined : customizer;
9846         length = 1;
9847       }
9848       while (++index < length) {
9849         var source = sources[index];
9850         if (source) {
9851           assigner(object, source, customizer);
9852         }
9853       }
9854       return object;
9855     });
9856   }
9857
9858   /**
9859    * Creates a `baseEach` or `baseEachRight` function.
9860    *
9861    * @private
9862    * @param {Function} eachFunc The function to iterate over a collection.
9863    * @param {boolean} [fromRight] Specify iterating from right to left.
9864    * @returns {Function} Returns the new base function.
9865    */
9866   function createBaseEach(eachFunc, fromRight) {
9867     return function(collection, iteratee) {
9868       var length = collection ? getLength(collection) : 0;
9869       if (!isLength(length)) {
9870         return eachFunc(collection, iteratee);
9871       }
9872       var index = fromRight ? length : -1,
9873           iterable = toObject(collection);
9874
9875       while ((fromRight ? index-- : ++index < length)) {
9876         if (iteratee(iterable[index], index, iterable) === false) {
9877           break;
9878         }
9879       }
9880       return collection;
9881     };
9882   }
9883
9884   /**
9885    * Creates a base function for `_.forIn` or `_.forInRight`.
9886    *
9887    * @private
9888    * @param {boolean} [fromRight] Specify iterating from right to left.
9889    * @returns {Function} Returns the new base function.
9890    */
9891   function createBaseFor(fromRight) {
9892     return function(object, iteratee, keysFunc) {
9893       var iterable = toObject(object),
9894           props = keysFunc(object),
9895           length = props.length,
9896           index = fromRight ? length : -1;
9897
9898       while ((fromRight ? index-- : ++index < length)) {
9899         var key = props[index];
9900         if (iteratee(iterable[key], key, iterable) === false) {
9901           break;
9902         }
9903       }
9904       return object;
9905     };
9906   }
9907
9908   /**
9909    * Creates a function that wraps `func` and invokes it with the `this`
9910    * binding of `thisArg`.
9911    *
9912    * @private
9913    * @param {Function} func The function to bind.
9914    * @param {*} [thisArg] The `this` binding of `func`.
9915    * @returns {Function} Returns the new bound function.
9916    */
9917   function createBindWrapper(func, thisArg) {
9918     var Ctor = createCtorWrapper(func);
9919
9920     function wrapper() {
9921       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
9922       return fn.apply(thisArg, arguments);
9923     }
9924     return wrapper;
9925   }
9926
9927   /**
9928    * Creates a `Set` cache object to optimize linear searches of large arrays.
9929    *
9930    * @private
9931    * @param {Array} [values] The values to cache.
9932    * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`.
9933    */
9934   var createCache = !(nativeCreate && Set) ? constant(null) : function(values) {
9935     return new SetCache(values);
9936   };
9937
9938   /**
9939    * Creates a function that produces an instance of `Ctor` regardless of
9940    * whether it was invoked as part of a `new` expression or by `call` or `apply`.
9941    *
9942    * @private
9943    * @param {Function} Ctor The constructor to wrap.
9944    * @returns {Function} Returns the new wrapped function.
9945    */
9946   function createCtorWrapper(Ctor) {
9947     return function() {
9948       // Use a `switch` statement to work with class constructors.
9949       // See https://people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-function-objects-call-thisargument-argumentslist
9950       // for more details.
9951       var args = arguments;
9952       switch (args.length) {
9953         case 0: return new Ctor;
9954         case 1: return new Ctor(args[0]);
9955         case 2: return new Ctor(args[0], args[1]);
9956         case 3: return new Ctor(args[0], args[1], args[2]);
9957         case 4: return new Ctor(args[0], args[1], args[2], args[3]);
9958         case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
9959       }
9960       var thisBinding = baseCreate(Ctor.prototype),
9961           result = Ctor.apply(thisBinding, args);
9962
9963       // Mimic the constructor's `return` behavior.
9964       // See https://es5.github.io/#x13.2.2 for more details.
9965       return isObject(result) ? result : thisBinding;
9966     };
9967   }
9968
9969   /**
9970    * Creates a `_.find` or `_.findLast` function.
9971    *
9972    * @private
9973    * @param {Function} eachFunc The function to iterate over a collection.
9974    * @param {boolean} [fromRight] Specify iterating from right to left.
9975    * @returns {Function} Returns the new find function.
9976    */
9977   function createFind(eachFunc, fromRight) {
9978     return function(collection, predicate, thisArg) {
9979       predicate = getCallback(predicate, thisArg, 3);
9980       if (isArray(collection)) {
9981         var index = baseFindIndex(collection, predicate, fromRight);
9982         return index > -1 ? collection[index] : undefined;
9983       }
9984       return baseFind(collection, predicate, eachFunc);
9985     };
9986   }
9987
9988   /**
9989    * Creates a function for `_.forEach` or `_.forEachRight`.
9990    *
9991    * @private
9992    * @param {Function} arrayFunc The function to iterate over an array.
9993    * @param {Function} eachFunc The function to iterate over a collection.
9994    * @returns {Function} Returns the new each function.
9995    */
9996   function createForEach(arrayFunc, eachFunc) {
9997     return function(collection, iteratee, thisArg) {
9998       return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
9999         ? arrayFunc(collection, iteratee)
10000         : eachFunc(collection, bindCallback(iteratee, thisArg, 3));
10001     };
10002   }
10003
10004   /**
10005    * Creates a function for `_.forOwn` or `_.forOwnRight`.
10006    *
10007    * @private
10008    * @param {Function} objectFunc The function to iterate over an object.
10009    * @returns {Function} Returns the new each function.
10010    */
10011   function createForOwn(objectFunc) {
10012     return function(object, iteratee, thisArg) {
10013       if (typeof iteratee != 'function' || thisArg !== undefined) {
10014         iteratee = bindCallback(iteratee, thisArg, 3);
10015       }
10016       return objectFunc(object, iteratee);
10017     };
10018   }
10019
10020   /**
10021    * Creates a function for `_.reduce` or `_.reduceRight`.
10022    *
10023    * @private
10024    * @param {Function} arrayFunc The function to iterate over an array.
10025    * @param {Function} eachFunc The function to iterate over a collection.
10026    * @returns {Function} Returns the new each function.
10027    */
10028   function createReduce(arrayFunc, eachFunc) {
10029     return function(collection, iteratee, accumulator, thisArg) {
10030       var initFromArray = arguments.length < 3;
10031       return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
10032         ? arrayFunc(collection, iteratee, accumulator, initFromArray)
10033         : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc);
10034     };
10035   }
10036
10037   /**
10038    * Creates a function that wraps `func` and invokes it with optional `this`
10039    * binding of, partial application, and currying.
10040    *
10041    * @private
10042    * @param {Function|string} func The function or method name to reference.
10043    * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
10044    * @param {*} [thisArg] The `this` binding of `func`.
10045    * @param {Array} [partials] The arguments to prepend to those provided to the new function.
10046    * @param {Array} [holders] The `partials` placeholder indexes.
10047    * @param {Array} [partialsRight] The arguments to append to those provided to the new function.
10048    * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
10049    * @param {Array} [argPos] The argument positions of the new function.
10050    * @param {number} [ary] The arity cap of `func`.
10051    * @param {number} [arity] The arity of `func`.
10052    * @returns {Function} Returns the new wrapped function.
10053    */
10054   function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
10055     var isAry = bitmask & ARY_FLAG,
10056         isBind = bitmask & BIND_FLAG,
10057         isBindKey = bitmask & BIND_KEY_FLAG,
10058         isCurry = bitmask & CURRY_FLAG,
10059         isCurryBound = bitmask & CURRY_BOUND_FLAG,
10060         isCurryRight = bitmask & CURRY_RIGHT_FLAG,
10061         Ctor = isBindKey ? null : createCtorWrapper(func);
10062
10063     function wrapper() {
10064       // Avoid `arguments` object use disqualifying optimizations by
10065       // converting it to an array before providing it to other functions.
10066       var length = arguments.length,
10067           index = length,
10068           args = Array(length);
10069
10070       while (index--) {
10071         args[index] = arguments[index];
10072       }
10073       if (partials) {
10074         args = composeArgs(args, partials, holders);
10075       }
10076       if (partialsRight) {
10077         args = composeArgsRight(args, partialsRight, holdersRight);
10078       }
10079       if (isCurry || isCurryRight) {
10080         var placeholder = wrapper.placeholder,
10081             argsHolders = replaceHolders(args, placeholder);
10082
10083         length -= argsHolders.length;
10084         if (length < arity) {
10085           var newArgPos = argPos ? arrayCopy(argPos) : null,
10086               newArity = nativeMax(arity - length, 0),
10087               newsHolders = isCurry ? argsHolders : null,
10088               newHoldersRight = isCurry ? null : argsHolders,
10089               newPartials = isCurry ? args : null,
10090               newPartialsRight = isCurry ? null : args;
10091
10092           bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
10093           bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
10094
10095           if (!isCurryBound) {
10096             bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
10097           }
10098           var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity],
10099               result = createHybridWrapper.apply(undefined, newData);
10100
10101           if (isLaziable(func)) {
10102             setData(result, newData);
10103           }
10104           result.placeholder = placeholder;
10105           return result;
10106         }
10107       }
10108       var thisBinding = isBind ? thisArg : this,
10109           fn = isBindKey ? thisBinding[func] : func;
10110
10111       if (argPos) {
10112         args = reorder(args, argPos);
10113       }
10114       if (isAry && ary < args.length) {
10115         args.length = ary;
10116       }
10117       if (this && this !== root && this instanceof wrapper) {
10118         fn = Ctor || createCtorWrapper(func);
10119       }
10120       return fn.apply(thisBinding, args);
10121     }
10122     return wrapper;
10123   }
10124
10125   /**
10126    * Creates a function that wraps `func` and invokes it with the optional `this`
10127    * binding of `thisArg` and the `partials` prepended to those provided to
10128    * the wrapper.
10129    *
10130    * @private
10131    * @param {Function} func The function to partially apply arguments to.
10132    * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
10133    * @param {*} thisArg The `this` binding of `func`.
10134    * @param {Array} partials The arguments to prepend to those provided to the new function.
10135    * @returns {Function} Returns the new bound function.
10136    */
10137   function createPartialWrapper(func, bitmask, thisArg, partials) {
10138     var isBind = bitmask & BIND_FLAG,
10139         Ctor = createCtorWrapper(func);
10140
10141     function wrapper() {
10142       // Avoid `arguments` object use disqualifying optimizations by
10143       // converting it to an array before providing it `func`.
10144       var argsIndex = -1,
10145           argsLength = arguments.length,
10146           leftIndex = -1,
10147           leftLength = partials.length,
10148           args = Array(argsLength + leftLength);
10149
10150       while (++leftIndex < leftLength) {
10151         args[leftIndex] = partials[leftIndex];
10152       }
10153       while (argsLength--) {
10154         args[leftIndex++] = arguments[++argsIndex];
10155       }
10156       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10157       return fn.apply(isBind ? thisArg : this, args);
10158     }
10159     return wrapper;
10160   }
10161
10162   /**
10163    * Creates a function that either curries or invokes `func` with optional
10164    * `this` binding and partially applied arguments.
10165    *
10166    * @private
10167    * @param {Function|string} func The function or method name to reference.
10168    * @param {number} bitmask The bitmask of flags.
10169    *  The bitmask may be composed of the following flags:
10170    *     1 - `_.bind`
10171    *     2 - `_.bindKey`
10172    *     4 - `_.curry` or `_.curryRight` of a bound function
10173    *     8 - `_.curry`
10174    *    16 - `_.curryRight`
10175    *    32 - `_.partial`
10176    *    64 - `_.partialRight`
10177    *   128 - `_.rearg`
10178    *   256 - `_.ary`
10179    * @param {*} [thisArg] The `this` binding of `func`.
10180    * @param {Array} [partials] The arguments to be partially applied.
10181    * @param {Array} [holders] The `partials` placeholder indexes.
10182    * @param {Array} [argPos] The argument positions of the new function.
10183    * @param {number} [ary] The arity cap of `func`.
10184    * @param {number} [arity] The arity of `func`.
10185    * @returns {Function} Returns the new wrapped function.
10186    */
10187   function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
10188     var isBindKey = bitmask & BIND_KEY_FLAG;
10189     if (!isBindKey && typeof func != 'function') {
10190       throw new TypeError(FUNC_ERROR_TEXT);
10191     }
10192     var length = partials ? partials.length : 0;
10193     if (!length) {
10194       bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
10195       partials = holders = null;
10196     }
10197     length -= (holders ? holders.length : 0);
10198     if (bitmask & PARTIAL_RIGHT_FLAG) {
10199       var partialsRight = partials,
10200           holdersRight = holders;
10201
10202       partials = holders = null;
10203     }
10204     var data = isBindKey ? null : getData(func),
10205         newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
10206
10207     if (data) {
10208       mergeData(newData, data);
10209       bitmask = newData[1];
10210       arity = newData[9];
10211     }
10212     newData[9] = arity == null
10213       ? (isBindKey ? 0 : func.length)
10214       : (nativeMax(arity - length, 0) || 0);
10215
10216     if (bitmask == BIND_FLAG) {
10217       var result = createBindWrapper(newData[0], newData[2]);
10218     } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) {
10219       result = createPartialWrapper.apply(undefined, newData);
10220     } else {
10221       result = createHybridWrapper.apply(undefined, newData);
10222     }
10223     var setter = data ? baseSetData : setData;
10224     return setter(result, newData);
10225   }
10226
10227   /**
10228    * A specialized version of `baseIsEqualDeep` for arrays with support for
10229    * partial deep comparisons.
10230    *
10231    * @private
10232    * @param {Array} array The array to compare.
10233    * @param {Array} other The other array to compare.
10234    * @param {Function} equalFunc The function to determine equivalents of values.
10235    * @param {Function} [customizer] The function to customize comparing arrays.
10236    * @param {boolean} [isLoose] Specify performing partial comparisons.
10237    * @param {Array} [stackA] Tracks traversed `value` objects.
10238    * @param {Array} [stackB] Tracks traversed `other` objects.
10239    * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
10240    */
10241   function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) {
10242     var index = -1,
10243         arrLength = array.length,
10244         othLength = other.length;
10245
10246     if (arrLength != othLength && !(isLoose && othLength > arrLength)) {
10247       return false;
10248     }
10249     // Ignore non-index properties.
10250     while (++index < arrLength) {
10251       var arrValue = array[index],
10252           othValue = other[index],
10253           result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined;
10254
10255       if (result !== undefined) {
10256         if (result) {
10257           continue;
10258         }
10259         return false;
10260       }
10261       // Recursively compare arrays (susceptible to call stack limits).
10262       if (isLoose) {
10263         if (!arraySome(other, function(othValue) {
10264               return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB);
10265             })) {
10266           return false;
10267         }
10268       } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) {
10269         return false;
10270       }
10271     }
10272     return true;
10273   }
10274
10275   /**
10276    * A specialized version of `baseIsEqualDeep` for comparing objects of
10277    * the same `toStringTag`.
10278    *
10279    * **Note:** This function only supports comparing values with tags of
10280    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
10281    *
10282    * @private
10283    * @param {Object} value The object to compare.
10284    * @param {Object} other The other object to compare.
10285    * @param {string} tag The `toStringTag` of the objects to compare.
10286    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
10287    */
10288   function equalByTag(object, other, tag) {
10289     switch (tag) {
10290       case boolTag:
10291       case dateTag:
10292         // Coerce dates and booleans to numbers, dates to milliseconds and booleans
10293         // to `1` or `0` treating invalid dates coerced to `NaN` as not equal.
10294         return +object == +other;
10295
10296       case errorTag:
10297         return object.name == other.name && object.message == other.message;
10298
10299       case numberTag:
10300         // Treat `NaN` vs. `NaN` as equal.
10301         return (object != +object)
10302           ? other != +other
10303           : object == +other;
10304
10305       case regexpTag:
10306       case stringTag:
10307         // Coerce regexes to strings and treat strings primitives and string
10308         // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details.
10309         return object == (other + '');
10310     }
10311     return false;
10312   }
10313
10314   /**
10315    * A specialized version of `baseIsEqualDeep` for objects with support for
10316    * partial deep comparisons.
10317    *
10318    * @private
10319    * @param {Object} object The object to compare.
10320    * @param {Object} other The other object to compare.
10321    * @param {Function} equalFunc The function to determine equivalents of values.
10322    * @param {Function} [customizer] The function to customize comparing values.
10323    * @param {boolean} [isLoose] Specify performing partial comparisons.
10324    * @param {Array} [stackA] Tracks traversed `value` objects.
10325    * @param {Array} [stackB] Tracks traversed `other` objects.
10326    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
10327    */
10328   function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
10329     var objProps = keys(object),
10330         objLength = objProps.length,
10331         othProps = keys(other),
10332         othLength = othProps.length;
10333
10334     if (objLength != othLength && !isLoose) {
10335       return false;
10336     }
10337     var index = objLength;
10338     while (index--) {
10339       var key = objProps[index];
10340       if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) {
10341         return false;
10342       }
10343     }
10344     var skipCtor = isLoose;
10345     while (++index < objLength) {
10346       key = objProps[index];
10347       var objValue = object[key],
10348           othValue = other[key],
10349           result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined;
10350
10351       // Recursively compare objects (susceptible to call stack limits).
10352       if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) {
10353         return false;
10354       }
10355       skipCtor || (skipCtor = key == 'constructor');
10356     }
10357     if (!skipCtor) {
10358       var objCtor = object.constructor,
10359           othCtor = other.constructor;
10360
10361       // Non `Object` object instances with different constructors are not equal.
10362       if (objCtor != othCtor &&
10363           ('constructor' in object && 'constructor' in other) &&
10364           !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
10365             typeof othCtor == 'function' && othCtor instanceof othCtor)) {
10366         return false;
10367       }
10368     }
10369     return true;
10370   }
10371
10372   /**
10373    * Gets the appropriate "callback" function. If the `_.callback` method is
10374    * customized this function returns the custom method, otherwise it returns
10375    * the `baseCallback` function. If arguments are provided the chosen function
10376    * is invoked with them and its result is returned.
10377    *
10378    * @private
10379    * @returns {Function} Returns the chosen function or its result.
10380    */
10381   function getCallback(func, thisArg, argCount) {
10382     var result = lodash.callback || callback;
10383     result = result === callback ? baseCallback : result;
10384     return argCount ? result(func, thisArg, argCount) : result;
10385   }
10386
10387   /**
10388    * Gets metadata for `func`.
10389    *
10390    * @private
10391    * @param {Function} func The function to query.
10392    * @returns {*} Returns the metadata for `func`.
10393    */
10394   var getData = !metaMap ? noop : function(func) {
10395     return metaMap.get(func);
10396   };
10397
10398   /**
10399    * Gets the name of `func`.
10400    *
10401    * @private
10402    * @param {Function} func The function to query.
10403    * @returns {string} Returns the function name.
10404    */
10405   function getFuncName(func) {
10406     var result = func.name,
10407         array = realNames[result],
10408         length = array ? array.length : 0;
10409
10410     while (length--) {
10411       var data = array[length],
10412           otherFunc = data.func;
10413       if (otherFunc == null || otherFunc == func) {
10414         return data.name;
10415       }
10416     }
10417     return result;
10418   }
10419
10420   /**
10421    * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
10422    * customized this function returns the custom method, otherwise it returns
10423    * the `baseIndexOf` function. If arguments are provided the chosen function
10424    * is invoked with them and its result is returned.
10425    *
10426    * @private
10427    * @returns {Function|number} Returns the chosen function or its result.
10428    */
10429   function getIndexOf(collection, target, fromIndex) {
10430     var result = lodash.indexOf || indexOf;
10431     result = result === indexOf ? baseIndexOf : result;
10432     return collection ? result(collection, target, fromIndex) : result;
10433   }
10434
10435   /**
10436    * Gets the "length" property value of `object`.
10437    *
10438    * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792)
10439    * that affects Safari on at least iOS 8.1-8.3 ARM64.
10440    *
10441    * @private
10442    * @param {Object} object The object to query.
10443    * @returns {*} Returns the "length" value.
10444    */
10445   var getLength = baseProperty('length');
10446
10447   /**
10448    * Gets the propery names, values, and compare flags of `object`.
10449    *
10450    * @private
10451    * @param {Object} object The object to query.
10452    * @returns {Array} Returns the match data of `object`.
10453    */
10454   function getMatchData(object) {
10455     var result = pairs(object),
10456         length = result.length;
10457
10458     while (length--) {
10459       result[length][2] = isStrictComparable(result[length][1]);
10460     }
10461     return result;
10462   }
10463
10464   /**
10465    * Gets the native function at `key` of `object`.
10466    *
10467    * @private
10468    * @param {Object} object The object to query.
10469    * @param {string} key The key of the method to get.
10470    * @returns {*} Returns the function if it's native, else `undefined`.
10471    */
10472   function getNative(object, key) {
10473     var value = object == null ? undefined : object[key];
10474     return isNative(value) ? value : undefined;
10475   }
10476
10477   /**
10478    * Gets the view, applying any `transforms` to the `start` and `end` positions.
10479    *
10480    * @private
10481    * @param {number} start The start of the view.
10482    * @param {number} end The end of the view.
10483    * @param {Array} [transforms] The transformations to apply to the view.
10484    * @returns {Object} Returns an object containing the `start` and `end`
10485    *  positions of the view.
10486    */
10487   function getView(start, end, transforms) {
10488     var index = -1,
10489         length = transforms ? transforms.length : 0;
10490
10491     while (++index < length) {
10492       var data = transforms[index],
10493           size = data.size;
10494
10495       switch (data.type) {
10496         case 'drop':      start += size; break;
10497         case 'dropRight': end -= size; break;
10498         case 'take':      end = nativeMin(end, start + size); break;
10499         case 'takeRight': start = nativeMax(start, end - size); break;
10500       }
10501     }
10502     return { 'start': start, 'end': end };
10503   }
10504
10505   /**
10506    * Initializes an array clone.
10507    *
10508    * @private
10509    * @param {Array} array The array to clone.
10510    * @returns {Array} Returns the initialized clone.
10511    */
10512   function initCloneArray(array) {
10513     var length = array.length,
10514         result = new array.constructor(length);
10515
10516     // Add array properties assigned by `RegExp#exec`.
10517     if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
10518       result.index = array.index;
10519       result.input = array.input;
10520     }
10521     return result;
10522   }
10523
10524   /**
10525    * Initializes an object clone.
10526    *
10527    * @private
10528    * @param {Object} object The object to clone.
10529    * @returns {Object} Returns the initialized clone.
10530    */
10531   function initCloneObject(object) {
10532     var Ctor = object.constructor;
10533     if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) {
10534       Ctor = Object;
10535     }
10536     return new Ctor;
10537   }
10538
10539   /**
10540    * Initializes an object clone based on its `toStringTag`.
10541    *
10542    * **Note:** This function only supports cloning values with tags of
10543    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
10544    *
10545    * @private
10546    * @param {Object} object The object to clone.
10547    * @param {string} tag The `toStringTag` of the object to clone.
10548    * @param {boolean} [isDeep] Specify a deep clone.
10549    * @returns {Object} Returns the initialized clone.
10550    */
10551   function initCloneByTag(object, tag, isDeep) {
10552     var Ctor = object.constructor;
10553     switch (tag) {
10554       case arrayBufferTag:
10555         return bufferClone(object);
10556
10557       case boolTag:
10558       case dateTag:
10559         return new Ctor(+object);
10560
10561       case float32Tag: case float64Tag:
10562       case int8Tag: case int16Tag: case int32Tag:
10563       case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
10564         // Safari 5 mobile incorrectly has `Object` as the constructor of typed arrays.
10565         if (Ctor instanceof Ctor) {
10566           Ctor = ctorByTag[tag];
10567         }
10568         var buffer = object.buffer;
10569         return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length);
10570
10571       case numberTag:
10572       case stringTag:
10573         return new Ctor(object);
10574
10575       case regexpTag:
10576         var result = new Ctor(object.source, reFlags.exec(object));
10577         result.lastIndex = object.lastIndex;
10578     }
10579     return result;
10580   }
10581
10582   /**
10583    * Checks if `value` is array-like.
10584    *
10585    * @private
10586    * @param {*} value The value to check.
10587    * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
10588    */
10589   function isArrayLike(value) {
10590     return value != null && isLength(getLength(value));
10591   }
10592
10593   /**
10594    * Checks if `value` is a valid array-like index.
10595    *
10596    * @private
10597    * @param {*} value The value to check.
10598    * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
10599    * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
10600    */
10601   function isIndex(value, length) {
10602     value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1;
10603     length = length == null ? MAX_SAFE_INTEGER : length;
10604     return value > -1 && value % 1 == 0 && value < length;
10605   }
10606
10607   /**
10608    * Checks if the provided arguments are from an iteratee call.
10609    *
10610    * @private
10611    * @param {*} value The potential iteratee value argument.
10612    * @param {*} index The potential iteratee index or key argument.
10613    * @param {*} object The potential iteratee object argument.
10614    * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`.
10615    */
10616   function isIterateeCall(value, index, object) {
10617     if (!isObject(object)) {
10618       return false;
10619     }
10620     var type = typeof index;
10621     if (type == 'number'
10622         ? (isArrayLike(object) && isIndex(index, object.length))
10623         : (type == 'string' && index in object)) {
10624       var other = object[index];
10625       return value === value ? (value === other) : (other !== other);
10626     }
10627     return false;
10628   }
10629
10630   /**
10631    * Checks if `value` is a property name and not a property path.
10632    *
10633    * @private
10634    * @param {*} value The value to check.
10635    * @param {Object} [object] The object to query keys on.
10636    * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
10637    */
10638   function isKey(value, object) {
10639     var type = typeof value;
10640     if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') {
10641       return true;
10642     }
10643     if (isArray(value)) {
10644       return false;
10645     }
10646     var result = !reIsDeepProp.test(value);
10647     return result || (object != null && value in toObject(object));
10648   }
10649
10650   /**
10651    * Checks if `func` has a lazy counterpart.
10652    *
10653    * @private
10654    * @param {Function} func The function to check.
10655    * @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`.
10656    */
10657   function isLaziable(func) {
10658     var funcName = getFuncName(func);
10659     if (!(funcName in LazyWrapper.prototype)) {
10660       return false;
10661     }
10662     var other = lodash[funcName];
10663     if (func === other) {
10664       return true;
10665     }
10666     var data = getData(other);
10667     return !!data && func === data[0];
10668   }
10669
10670   /**
10671    * Checks if `value` is a valid array-like length.
10672    *
10673    * **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength).
10674    *
10675    * @private
10676    * @param {*} value The value to check.
10677    * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
10678    */
10679   function isLength(value) {
10680     return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
10681   }
10682
10683   /**
10684    * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
10685    *
10686    * @private
10687    * @param {*} value The value to check.
10688    * @returns {boolean} Returns `true` if `value` if suitable for strict
10689    *  equality comparisons, else `false`.
10690    */
10691   function isStrictComparable(value) {
10692     return value === value && !isObject(value);
10693   }
10694
10695   /**
10696    * Merges the function metadata of `source` into `data`.
10697    *
10698    * Merging metadata reduces the number of wrappers required to invoke a function.
10699    * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
10700    * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg`
10701    * augment function arguments, making the order in which they are executed important,
10702    * preventing the merging of metadata. However, we make an exception for a safe
10703    * common case where curried functions have `_.ary` and or `_.rearg` applied.
10704    *
10705    * @private
10706    * @param {Array} data The destination metadata.
10707    * @param {Array} source The source metadata.
10708    * @returns {Array} Returns `data`.
10709    */
10710   function mergeData(data, source) {
10711     var bitmask = data[1],
10712         srcBitmask = source[1],
10713         newBitmask = bitmask | srcBitmask,
10714         isCommon = newBitmask < ARY_FLAG;
10715
10716     var isCombo =
10717       (srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) ||
10718       (srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) ||
10719       (srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG);
10720
10721     // Exit early if metadata can't be merged.
10722     if (!(isCommon || isCombo)) {
10723       return data;
10724     }
10725     // Use source `thisArg` if available.
10726     if (srcBitmask & BIND_FLAG) {
10727       data[2] = source[2];
10728       // Set when currying a bound function.
10729       newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG;
10730     }
10731     // Compose partial arguments.
10732     var value = source[3];
10733     if (value) {
10734       var partials = data[3];
10735       data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value);
10736       data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]);
10737     }
10738     // Compose partial right arguments.
10739     value = source[5];
10740     if (value) {
10741       partials = data[5];
10742       data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value);
10743       data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]);
10744     }
10745     // Use source `argPos` if available.
10746     value = source[7];
10747     if (value) {
10748       data[7] = arrayCopy(value);
10749     }
10750     // Use source `ary` if it's smaller.
10751     if (srcBitmask & ARY_FLAG) {
10752       data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
10753     }
10754     // Use source `arity` if one is not provided.
10755     if (data[9] == null) {
10756       data[9] = source[9];
10757     }
10758     // Use source `func` and merge bitmasks.
10759     data[0] = source[0];
10760     data[1] = newBitmask;
10761
10762     return data;
10763   }
10764
10765   /**
10766    * A specialized version of `_.pick` which picks `object` properties specified
10767    * by `props`.
10768    *
10769    * @private
10770    * @param {Object} object The source object.
10771    * @param {string[]} props The property names to pick.
10772    * @returns {Object} Returns the new object.
10773    */
10774   function pickByArray(object, props) {
10775     object = toObject(object);
10776
10777     var index = -1,
10778         length = props.length,
10779         result = {};
10780
10781     while (++index < length) {
10782       var key = props[index];
10783       if (key in object) {
10784         result[key] = object[key];
10785       }
10786     }
10787     return result;
10788   }
10789
10790   /**
10791    * A specialized version of `_.pick` which picks `object` properties `predicate`
10792    * returns truthy for.
10793    *
10794    * @private
10795    * @param {Object} object The source object.
10796    * @param {Function} predicate The function invoked per iteration.
10797    * @returns {Object} Returns the new object.
10798    */
10799   function pickByCallback(object, predicate) {
10800     var result = {};
10801     baseForIn(object, function(value, key, object) {
10802       if (predicate(value, key, object)) {
10803         result[key] = value;
10804       }
10805     });
10806     return result;
10807   }
10808
10809   /**
10810    * Reorder `array` according to the specified indexes where the element at
10811    * the first index is assigned as the first element, the element at
10812    * the second index is assigned as the second element, and so on.
10813    *
10814    * @private
10815    * @param {Array} array The array to reorder.
10816    * @param {Array} indexes The arranged array indexes.
10817    * @returns {Array} Returns `array`.
10818    */
10819   function reorder(array, indexes) {
10820     var arrLength = array.length,
10821         length = nativeMin(indexes.length, arrLength),
10822         oldArray = arrayCopy(array);
10823
10824     while (length--) {
10825       var index = indexes[length];
10826       array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
10827     }
10828     return array;
10829   }
10830
10831   /**
10832    * Sets metadata for `func`.
10833    *
10834    * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
10835    * period of time, it will trip its breaker and transition to an identity function
10836    * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070)
10837    * for more details.
10838    *
10839    * @private
10840    * @param {Function} func The function to associate metadata with.
10841    * @param {*} data The metadata.
10842    * @returns {Function} Returns `func`.
10843    */
10844   var setData = (function() {
10845     var count = 0,
10846         lastCalled = 0;
10847
10848     return function(key, value) {
10849       var stamp = now(),
10850           remaining = HOT_SPAN - (stamp - lastCalled);
10851
10852       lastCalled = stamp;
10853       if (remaining > 0) {
10854         if (++count >= HOT_COUNT) {
10855           return key;
10856         }
10857       } else {
10858         count = 0;
10859       }
10860       return baseSetData(key, value);
10861     };
10862   }());
10863
10864   /**
10865    * A fallback implementation of `_.isPlainObject` which checks if `value`
10866    * is an object created by the `Object` constructor or has a `[[Prototype]]`
10867    * of `null`.
10868    *
10869    * @private
10870    * @param {*} value The value to check.
10871    * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
10872    */
10873   function shimIsPlainObject(value) {
10874     var Ctor,
10875         support = lodash.support;
10876
10877     // Exit early for non `Object` objects.
10878     if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isHostObject(value)) ||
10879         (!hasOwnProperty.call(value, 'constructor') &&
10880           (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor))) ||
10881         (!support.argsTag && isArguments(value))) {
10882       return false;
10883     }
10884     // IE < 9 iterates inherited properties before own properties. If the first
10885     // iterated property is an object's own property then there are no inherited
10886     // enumerable properties.
10887     var result;
10888     if (support.ownLast) {
10889       baseForIn(value, function(subValue, key, object) {
10890         result = hasOwnProperty.call(object, key);
10891         return false;
10892       });
10893       return result !== false;
10894     }
10895     // In most environments an object's own properties are iterated before
10896     // its inherited properties. If the last iterated property is an object's
10897     // own property then there are no inherited enumerable properties.
10898     baseForIn(value, function(subValue, key) {
10899       result = key;
10900     });
10901     return result === undefined || hasOwnProperty.call(value, result);
10902   }
10903
10904   /**
10905    * A fallback implementation of `Object.keys` which creates an array of the
10906    * own enumerable property names of `object`.
10907    *
10908    * @private
10909    * @param {Object} object The object to query.
10910    * @returns {Array} Returns the array of property names.
10911    */
10912   function shimKeys(object) {
10913     var props = keysIn(object),
10914         propsLength = props.length,
10915         length = propsLength && object.length;
10916
10917     var allowIndexes = !!length && isLength(length) &&
10918       (isArray(object) || isArguments(object) || isString(object));
10919
10920     var index = -1,
10921         result = [];
10922
10923     while (++index < propsLength) {
10924       var key = props[index];
10925       if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) {
10926         result.push(key);
10927       }
10928     }
10929     return result;
10930   }
10931
10932   /**
10933    * Converts `value` to an object if it's not one.
10934    *
10935    * @private
10936    * @param {*} value The value to process.
10937    * @returns {Object} Returns the object.
10938    */
10939   function toObject(value) {
10940     if (lodash.support.unindexedChars && isString(value)) {
10941       var index = -1,
10942           length = value.length,
10943           result = Object(value);
10944
10945       while (++index < length) {
10946         result[index] = value.charAt(index);
10947       }
10948       return result;
10949     }
10950     return isObject(value) ? value : Object(value);
10951   }
10952
10953   /**
10954    * Converts `value` to property path array if it's not one.
10955    *
10956    * @private
10957    * @param {*} value The value to process.
10958    * @returns {Array} Returns the property path array.
10959    */
10960   function toPath(value) {
10961     if (isArray(value)) {
10962       return value;
10963     }
10964     var result = [];
10965     baseToString(value).replace(rePropName, function(match, number, quote, string) {
10966       result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
10967     });
10968     return result;
10969   }
10970
10971   /**
10972    * Creates a clone of `wrapper`.
10973    *
10974    * @private
10975    * @param {Object} wrapper The wrapper to clone.
10976    * @returns {Object} Returns the cloned wrapper.
10977    */
10978   function wrapperClone(wrapper) {
10979     return wrapper instanceof LazyWrapper
10980       ? wrapper.clone()
10981       : new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__, arrayCopy(wrapper.__actions__));
10982   }
10983
10984   /*------------------------------------------------------------------------*/
10985
10986   /**
10987    * Creates an array of elements split into groups the length of `size`.
10988    * If `collection` can't be split evenly, the final chunk will be the remaining
10989    * elements.
10990    *
10991    * @static
10992    * @memberOf _
10993    * @category Array
10994    * @param {Array} array The array to process.
10995    * @param {number} [size=1] The length of each chunk.
10996    * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
10997    * @returns {Array} Returns the new array containing chunks.
10998    * @example
10999    *
11000    * _.chunk(['a', 'b', 'c', 'd'], 2);
11001    * // => [['a', 'b'], ['c', 'd']]
11002    *
11003    * _.chunk(['a', 'b', 'c', 'd'], 3);
11004    * // => [['a', 'b', 'c'], ['d']]
11005    */
11006   function chunk(array, size, guard) {
11007     if (guard ? isIterateeCall(array, size, guard) : size == null) {
11008       size = 1;
11009     } else {
11010       size = nativeMax(+size || 1, 1);
11011     }
11012     var index = 0,
11013         length = array ? array.length : 0,
11014         resIndex = -1,
11015         result = Array(ceil(length / size));
11016
11017     while (index < length) {
11018       result[++resIndex] = baseSlice(array, index, (index += size));
11019     }
11020     return result;
11021   }
11022
11023   /**
11024    * Creates an array with all falsey values removed. The values `false`, `null`,
11025    * `0`, `""`, `undefined`, and `NaN` are falsey.
11026    *
11027    * @static
11028    * @memberOf _
11029    * @category Array
11030    * @param {Array} array The array to compact.
11031    * @returns {Array} Returns the new array of filtered values.
11032    * @example
11033    *
11034    * _.compact([0, 1, false, 2, '', 3]);
11035    * // => [1, 2, 3]
11036    */
11037   function compact(array) {
11038     var index = -1,
11039         length = array ? array.length : 0,
11040         resIndex = -1,
11041         result = [];
11042
11043     while (++index < length) {
11044       var value = array[index];
11045       if (value) {
11046         result[++resIndex] = value;
11047       }
11048     }
11049     return result;
11050   }
11051
11052   /**
11053    * Creates an array of unique `array` values not included in the other
11054    * provided arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11055    * for equality comparisons.
11056    *
11057    * @static
11058    * @memberOf _
11059    * @category Array
11060    * @param {Array} array The array to inspect.
11061    * @param {...Array} [values] The arrays of values to exclude.
11062    * @returns {Array} Returns the new array of filtered values.
11063    * @example
11064    *
11065    * _.difference([1, 2, 3], [4, 2]);
11066    * // => [1, 3]
11067    */
11068   var difference = restParam(function(array, values) {
11069     return isArrayLike(array)
11070       ? baseDifference(array, baseFlatten(values, false, true))
11071       : [];
11072   });
11073
11074   /**
11075    * Gets the first element of `array`.
11076    *
11077    * @static
11078    * @memberOf _
11079    * @alias head
11080    * @category Array
11081    * @param {Array} array The array to query.
11082    * @returns {*} Returns the first element of `array`.
11083    * @example
11084    *
11085    * _.first([1, 2, 3]);
11086    * // => 1
11087    *
11088    * _.first([]);
11089    * // => undefined
11090    */
11091   function first(array) {
11092     return array ? array[0] : undefined;
11093   }
11094
11095   /**
11096    * Flattens a nested array. If `isDeep` is `true` the array is recursively
11097    * flattened, otherwise it is only flattened a single level.
11098    *
11099    * @static
11100    * @memberOf _
11101    * @category Array
11102    * @param {Array} array The array to flatten.
11103    * @param {boolean} [isDeep] Specify a deep flatten.
11104    * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
11105    * @returns {Array} Returns the new flattened array.
11106    * @example
11107    *
11108    * _.flatten([1, [2, 3, [4]]]);
11109    * // => [1, 2, 3, [4]]
11110    *
11111    * // using `isDeep`
11112    * _.flatten([1, [2, 3, [4]]], true);
11113    * // => [1, 2, 3, 4]
11114    */
11115   function flatten(array, isDeep, guard) {
11116     var length = array ? array.length : 0;
11117     if (guard && isIterateeCall(array, isDeep, guard)) {
11118       isDeep = false;
11119     }
11120     return length ? baseFlatten(array, isDeep) : [];
11121   }
11122
11123   /**
11124    * Gets the index at which the first occurrence of `value` is found in `array`
11125    * using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11126    * for equality comparisons. If `fromIndex` is negative, it is used as the offset
11127    * from the end of `array`. If `array` is sorted providing `true` for `fromIndex`
11128    * performs a faster binary search.
11129    *
11130    * @static
11131    * @memberOf _
11132    * @category Array
11133    * @param {Array} array The array to search.
11134    * @param {*} value The value to search for.
11135    * @param {boolean|number} [fromIndex=0] The index to search from or `true`
11136    *  to perform a binary search on a sorted array.
11137    * @returns {number} Returns the index of the matched value, else `-1`.
11138    * @example
11139    *
11140    * _.indexOf([1, 2, 1, 2], 2);
11141    * // => 1
11142    *
11143    * // using `fromIndex`
11144    * _.indexOf([1, 2, 1, 2], 2, 2);
11145    * // => 3
11146    *
11147    * // performing a binary search
11148    * _.indexOf([1, 1, 2, 2], 2, true);
11149    * // => 2
11150    */
11151   function indexOf(array, value, fromIndex) {
11152     var length = array ? array.length : 0;
11153     if (!length) {
11154       return -1;
11155     }
11156     if (typeof fromIndex == 'number') {
11157       fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex;
11158     } else if (fromIndex) {
11159       var index = binaryIndex(array, value),
11160           other = array[index];
11161
11162       if (value === value ? (value === other) : (other !== other)) {
11163         return index;
11164       }
11165       return -1;
11166     }
11167     return baseIndexOf(array, value, fromIndex || 0);
11168   }
11169
11170   /**
11171    * Creates an array of unique values that are included in all of the provided
11172    * arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11173    * for equality comparisons.
11174    *
11175    * @static
11176    * @memberOf _
11177    * @category Array
11178    * @param {...Array} [arrays] The arrays to inspect.
11179    * @returns {Array} Returns the new array of shared values.
11180    * @example
11181    * _.intersection([1, 2], [4, 2], [2, 1]);
11182    * // => [2]
11183    */
11184   var intersection = restParam(function(arrays) {
11185     var othLength = arrays.length,
11186         othIndex = othLength,
11187         caches = Array(length),
11188         indexOf = getIndexOf(),
11189         isCommon = indexOf == baseIndexOf,
11190         result = [];
11191
11192     while (othIndex--) {
11193       var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : [];
11194       caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null;
11195     }
11196     var array = arrays[0],
11197         index = -1,
11198         length = array ? array.length : 0,
11199         seen = caches[0];
11200
11201     outer:
11202     while (++index < length) {
11203       value = array[index];
11204       if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) {
11205         var othIndex = othLength;
11206         while (--othIndex) {
11207           var cache = caches[othIndex];
11208           if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) {
11209             continue outer;
11210           }
11211         }
11212         if (seen) {
11213           seen.push(value);
11214         }
11215         result.push(value);
11216       }
11217     }
11218     return result;
11219   });
11220
11221   /**
11222    * Gets the last element of `array`.
11223    *
11224    * @static
11225    * @memberOf _
11226    * @category Array
11227    * @param {Array} array The array to query.
11228    * @returns {*} Returns the last element of `array`.
11229    * @example
11230    *
11231    * _.last([1, 2, 3]);
11232    * // => 3
11233    */
11234   function last(array) {
11235     var length = array ? array.length : 0;
11236     return length ? array[length - 1] : undefined;
11237   }
11238
11239   /**
11240    * Creates an array of unique values, in order, from all of the provided arrays
11241    * using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11242    * for equality comparisons.
11243    *
11244    * @static
11245    * @memberOf _
11246    * @category Array
11247    * @param {...Array} [arrays] The arrays to inspect.
11248    * @returns {Array} Returns the new array of combined values.
11249    * @example
11250    *
11251    * _.union([1, 2], [4, 2], [2, 1]);
11252    * // => [1, 2, 4]
11253    */
11254   var union = restParam(function(arrays) {
11255     return baseUniq(baseFlatten(arrays, false, true));
11256   });
11257
11258   /**
11259    * Creates a duplicate-free version of an array, using
11260    * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11261    * for equality comparisons, in which only the first occurence of each element
11262    * is kept. Providing `true` for `isSorted` performs a faster search algorithm
11263    * for sorted arrays. If an iteratee function is provided it is invoked for
11264    * each element in the array to generate the criterion by which uniqueness
11265    * is computed. The `iteratee` is bound to `thisArg` and invoked with three
11266    * arguments: (value, index, array).
11267    *
11268    * If a property name is provided for `iteratee` the created `_.property`
11269    * style callback returns the property value of the given element.
11270    *
11271    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11272    * style callback returns `true` for elements that have a matching property
11273    * value, else `false`.
11274    *
11275    * If an object is provided for `iteratee` the created `_.matches` style
11276    * callback returns `true` for elements that have the properties of the given
11277    * object, else `false`.
11278    *
11279    * @static
11280    * @memberOf _
11281    * @alias unique
11282    * @category Array
11283    * @param {Array} array The array to inspect.
11284    * @param {boolean} [isSorted] Specify the array is sorted.
11285    * @param {Function|Object|string} [iteratee] The function invoked per iteration.
11286    * @param {*} [thisArg] The `this` binding of `iteratee`.
11287    * @returns {Array} Returns the new duplicate-value-free array.
11288    * @example
11289    *
11290    * _.uniq([2, 1, 2]);
11291    * // => [2, 1]
11292    *
11293    * // using `isSorted`
11294    * _.uniq([1, 1, 2], true);
11295    * // => [1, 2]
11296    *
11297    * // using an iteratee function
11298    * _.uniq([1, 2.5, 1.5, 2], function(n) {
11299    *   return this.floor(n);
11300    * }, Math);
11301    * // => [1, 2.5]
11302    *
11303    * // using the `_.property` callback shorthand
11304    * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
11305    * // => [{ 'x': 1 }, { 'x': 2 }]
11306    */
11307   function uniq(array, isSorted, iteratee, thisArg) {
11308     var length = array ? array.length : 0;
11309     if (!length) {
11310       return [];
11311     }
11312     if (isSorted != null && typeof isSorted != 'boolean') {
11313       thisArg = iteratee;
11314       iteratee = isIterateeCall(array, isSorted, thisArg) ? null : isSorted;
11315       isSorted = false;
11316     }
11317     var callback = getCallback();
11318     if (!(iteratee == null && callback === baseCallback)) {
11319       iteratee = callback(iteratee, thisArg, 3);
11320     }
11321     return (isSorted && getIndexOf() == baseIndexOf)
11322       ? sortedUniq(array, iteratee)
11323       : baseUniq(array, iteratee);
11324   }
11325
11326   /**
11327    * Creates an array excluding all provided values using
11328    * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11329    * for equality comparisons.
11330    *
11331    * @static
11332    * @memberOf _
11333    * @category Array
11334    * @param {Array} array The array to filter.
11335    * @param {...*} [values] The values to exclude.
11336    * @returns {Array} Returns the new array of filtered values.
11337    * @example
11338    *
11339    * _.without([1, 2, 1, 3], 1, 2);
11340    * // => [3]
11341    */
11342   var without = restParam(function(array, values) {
11343     return isArrayLike(array)
11344       ? baseDifference(array, values)
11345       : [];
11346   });
11347
11348   /*------------------------------------------------------------------------*/
11349
11350   /**
11351    * Creates a `lodash` object that wraps `value` with explicit method
11352    * chaining enabled.
11353    *
11354    * @static
11355    * @memberOf _
11356    * @category Chain
11357    * @param {*} value The value to wrap.
11358    * @returns {Object} Returns the new `lodash` wrapper instance.
11359    * @example
11360    *
11361    * var users = [
11362    *   { 'user': 'barney',  'age': 36 },
11363    *   { 'user': 'fred',    'age': 40 },
11364    *   { 'user': 'pebbles', 'age': 1 }
11365    * ];
11366    *
11367    * var youngest = _.chain(users)
11368    *   .sortBy('age')
11369    *   .map(function(chr) {
11370    *     return chr.user + ' is ' + chr.age;
11371    *   })
11372    *   .first()
11373    *   .value();
11374    * // => 'pebbles is 1'
11375    */
11376   function chain(value) {
11377     var result = lodash(value);
11378     result.__chain__ = true;
11379     return result;
11380   }
11381
11382   /**
11383    * This method invokes `interceptor` and returns `value`. The interceptor is
11384    * bound to `thisArg` and invoked with one argument; (value). The purpose of
11385    * this method is to "tap into" a method chain in order to perform operations
11386    * on intermediate results within the chain.
11387    *
11388    * @static
11389    * @memberOf _
11390    * @category Chain
11391    * @param {*} value The value to provide to `interceptor`.
11392    * @param {Function} interceptor The function to invoke.
11393    * @param {*} [thisArg] The `this` binding of `interceptor`.
11394    * @returns {*} Returns `value`.
11395    * @example
11396    *
11397    * _([1, 2, 3])
11398    *  .tap(function(array) {
11399    *    array.pop();
11400    *  })
11401    *  .reverse()
11402    *  .value();
11403    * // => [2, 1]
11404    */
11405   function tap(value, interceptor, thisArg) {
11406     interceptor.call(thisArg, value);
11407     return value;
11408   }
11409
11410   /**
11411    * This method is like `_.tap` except that it returns the result of `interceptor`.
11412    *
11413    * @static
11414    * @memberOf _
11415    * @category Chain
11416    * @param {*} value The value to provide to `interceptor`.
11417    * @param {Function} interceptor The function to invoke.
11418    * @param {*} [thisArg] The `this` binding of `interceptor`.
11419    * @returns {*} Returns the result of `interceptor`.
11420    * @example
11421    *
11422    * _('  abc  ')
11423    *  .chain()
11424    *  .trim()
11425    *  .thru(function(value) {
11426    *    return [value];
11427    *  })
11428    *  .value();
11429    * // => ['abc']
11430    */
11431   function thru(value, interceptor, thisArg) {
11432     return interceptor.call(thisArg, value);
11433   }
11434
11435   /**
11436    * Enables explicit method chaining on the wrapper object.
11437    *
11438    * @name chain
11439    * @memberOf _
11440    * @category Chain
11441    * @returns {Object} Returns the new `lodash` wrapper instance.
11442    * @example
11443    *
11444    * var users = [
11445    *   { 'user': 'barney', 'age': 36 },
11446    *   { 'user': 'fred',   'age': 40 }
11447    * ];
11448    *
11449    * // without explicit chaining
11450    * _(users).first();
11451    * // => { 'user': 'barney', 'age': 36 }
11452    *
11453    * // with explicit chaining
11454    * _(users).chain()
11455    *   .first()
11456    *   .pick('user')
11457    *   .value();
11458    * // => { 'user': 'barney' }
11459    */
11460   function wrapperChain() {
11461     return chain(this);
11462   }
11463
11464   /**
11465    * Executes the chained sequence and returns the wrapped result.
11466    *
11467    * @name commit
11468    * @memberOf _
11469    * @category Chain
11470    * @returns {Object} Returns the new `lodash` wrapper instance.
11471    * @example
11472    *
11473    * var array = [1, 2];
11474    * var wrapper = _(array).push(3);
11475    *
11476    * console.log(array);
11477    * // => [1, 2]
11478    *
11479    * wrapper = wrapper.commit();
11480    * console.log(array);
11481    * // => [1, 2, 3]
11482    *
11483    * wrapper.last();
11484    * // => 3
11485    *
11486    * console.log(array);
11487    * // => [1, 2, 3]
11488    */
11489   function wrapperCommit() {
11490     return new LodashWrapper(this.value(), this.__chain__);
11491   }
11492
11493   /**
11494    * Creates a clone of the chained sequence planting `value` as the wrapped value.
11495    *
11496    * @name plant
11497    * @memberOf _
11498    * @category Chain
11499    * @returns {Object} Returns the new `lodash` wrapper instance.
11500    * @example
11501    *
11502    * var array = [1, 2];
11503    * var wrapper = _(array).map(function(value) {
11504    *   return Math.pow(value, 2);
11505    * });
11506    *
11507    * var other = [3, 4];
11508    * var otherWrapper = wrapper.plant(other);
11509    *
11510    * otherWrapper.value();
11511    * // => [9, 16]
11512    *
11513    * wrapper.value();
11514    * // => [1, 4]
11515    */
11516   function wrapperPlant(value) {
11517     var result,
11518         parent = this;
11519
11520     while (parent instanceof baseLodash) {
11521       var clone = wrapperClone(parent);
11522       if (result) {
11523         previous.__wrapped__ = clone;
11524       } else {
11525         result = clone;
11526       }
11527       var previous = clone;
11528       parent = parent.__wrapped__;
11529     }
11530     previous.__wrapped__ = value;
11531     return result;
11532   }
11533
11534   /**
11535    * Reverses the wrapped array so the first element becomes the last, the
11536    * second element becomes the second to last, and so on.
11537    *
11538    * **Note:** This method mutates the wrapped array.
11539    *
11540    * @name reverse
11541    * @memberOf _
11542    * @category Chain
11543    * @returns {Object} Returns the new reversed `lodash` wrapper instance.
11544    * @example
11545    *
11546    * var array = [1, 2, 3];
11547    *
11548    * _(array).reverse().value()
11549    * // => [3, 2, 1]
11550    *
11551    * console.log(array);
11552    * // => [3, 2, 1]
11553    */
11554   function wrapperReverse() {
11555     var value = this.__wrapped__;
11556     if (value instanceof LazyWrapper) {
11557       if (this.__actions__.length) {
11558         value = new LazyWrapper(this);
11559       }
11560       return new LodashWrapper(value.reverse(), this.__chain__);
11561     }
11562     return this.thru(function(value) {
11563       return value.reverse();
11564     });
11565   }
11566
11567   /**
11568    * Produces the result of coercing the unwrapped value to a string.
11569    *
11570    * @name toString
11571    * @memberOf _
11572    * @category Chain
11573    * @returns {string} Returns the coerced string value.
11574    * @example
11575    *
11576    * _([1, 2, 3]).toString();
11577    * // => '1,2,3'
11578    */
11579   function wrapperToString() {
11580     return (this.value() + '');
11581   }
11582
11583   /**
11584    * Executes the chained sequence to extract the unwrapped value.
11585    *
11586    * @name value
11587    * @memberOf _
11588    * @alias run, toJSON, valueOf
11589    * @category Chain
11590    * @returns {*} Returns the resolved unwrapped value.
11591    * @example
11592    *
11593    * _([1, 2, 3]).value();
11594    * // => [1, 2, 3]
11595    */
11596   function wrapperValue() {
11597     return baseWrapperValue(this.__wrapped__, this.__actions__);
11598   }
11599
11600   /*------------------------------------------------------------------------*/
11601
11602   /**
11603    * Checks if `predicate` returns truthy for **all** elements of `collection`.
11604    * The predicate is bound to `thisArg` and invoked with three arguments:
11605    * (value, index|key, collection).
11606    *
11607    * If a property name is provided for `predicate` the created `_.property`
11608    * style callback returns the property value of the given element.
11609    *
11610    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11611    * style callback returns `true` for elements that have a matching property
11612    * value, else `false`.
11613    *
11614    * If an object is provided for `predicate` the created `_.matches` style
11615    * callback returns `true` for elements that have the properties of the given
11616    * object, else `false`.
11617    *
11618    * @static
11619    * @memberOf _
11620    * @alias all
11621    * @category Collection
11622    * @param {Array|Object|string} collection The collection to iterate over.
11623    * @param {Function|Object|string} [predicate=_.identity] The function invoked
11624    *  per iteration.
11625    * @param {*} [thisArg] The `this` binding of `predicate`.
11626    * @returns {boolean} Returns `true` if all elements pass the predicate check,
11627    *  else `false`.
11628    * @example
11629    *
11630    * _.every([true, 1, null, 'yes'], Boolean);
11631    * // => false
11632    *
11633    * var users = [
11634    *   { 'user': 'barney', 'active': false },
11635    *   { 'user': 'fred',   'active': false }
11636    * ];
11637    *
11638    * // using the `_.matches` callback shorthand
11639    * _.every(users, { 'user': 'barney', 'active': false });
11640    * // => false
11641    *
11642    * // using the `_.matchesProperty` callback shorthand
11643    * _.every(users, 'active', false);
11644    * // => true
11645    *
11646    * // using the `_.property` callback shorthand
11647    * _.every(users, 'active');
11648    * // => false
11649    */
11650   function every(collection, predicate, thisArg) {
11651     var func = isArray(collection) ? arrayEvery : baseEvery;
11652     if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
11653       predicate = null;
11654     }
11655     if (typeof predicate != 'function' || thisArg !== undefined) {
11656       predicate = getCallback(predicate, thisArg, 3);
11657     }
11658     return func(collection, predicate);
11659   }
11660
11661   /**
11662    * Iterates over elements of `collection`, returning an array of all elements
11663    * `predicate` returns truthy for. The predicate is bound to `thisArg` and
11664    * invoked with three arguments: (value, index|key, collection).
11665    *
11666    * If a property name is provided for `predicate` the created `_.property`
11667    * style callback returns the property value of the given element.
11668    *
11669    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11670    * style callback returns `true` for elements that have a matching property
11671    * value, else `false`.
11672    *
11673    * If an object is provided for `predicate` the created `_.matches` style
11674    * callback returns `true` for elements that have the properties of the given
11675    * object, else `false`.
11676    *
11677    * @static
11678    * @memberOf _
11679    * @alias select
11680    * @category Collection
11681    * @param {Array|Object|string} collection The collection to iterate over.
11682    * @param {Function|Object|string} [predicate=_.identity] The function invoked
11683    *  per iteration.
11684    * @param {*} [thisArg] The `this` binding of `predicate`.
11685    * @returns {Array} Returns the new filtered array.
11686    * @example
11687    *
11688    * _.filter([4, 5, 6], function(n) {
11689    *   return n % 2 == 0;
11690    * });
11691    * // => [4, 6]
11692    *
11693    * var users = [
11694    *   { 'user': 'barney', 'age': 36, 'active': true },
11695    *   { 'user': 'fred',   'age': 40, 'active': false }
11696    * ];
11697    *
11698    * // using the `_.matches` callback shorthand
11699    * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user');
11700    * // => ['barney']
11701    *
11702    * // using the `_.matchesProperty` callback shorthand
11703    * _.pluck(_.filter(users, 'active', false), 'user');
11704    * // => ['fred']
11705    *
11706    * // using the `_.property` callback shorthand
11707    * _.pluck(_.filter(users, 'active'), 'user');
11708    * // => ['barney']
11709    */
11710   function filter(collection, predicate, thisArg) {
11711     var func = isArray(collection) ? arrayFilter : baseFilter;
11712     predicate = getCallback(predicate, thisArg, 3);
11713     return func(collection, predicate);
11714   }
11715
11716   /**
11717    * Iterates over elements of `collection`, returning the first element
11718    * `predicate` returns truthy for. The predicate is bound to `thisArg` and
11719    * invoked with three arguments: (value, index|key, collection).
11720    *
11721    * If a property name is provided for `predicate` the created `_.property`
11722    * style callback returns the property value of the given element.
11723    *
11724    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11725    * style callback returns `true` for elements that have a matching property
11726    * value, else `false`.
11727    *
11728    * If an object is provided for `predicate` the created `_.matches` style
11729    * callback returns `true` for elements that have the properties of the given
11730    * object, else `false`.
11731    *
11732    * @static
11733    * @memberOf _
11734    * @alias detect
11735    * @category Collection
11736    * @param {Array|Object|string} collection The collection to search.
11737    * @param {Function|Object|string} [predicate=_.identity] The function invoked
11738    *  per iteration.
11739    * @param {*} [thisArg] The `this` binding of `predicate`.
11740    * @returns {*} Returns the matched element, else `undefined`.
11741    * @example
11742    *
11743    * var users = [
11744    *   { 'user': 'barney',  'age': 36, 'active': true },
11745    *   { 'user': 'fred',    'age': 40, 'active': false },
11746    *   { 'user': 'pebbles', 'age': 1,  'active': true }
11747    * ];
11748    *
11749    * _.result(_.find(users, function(chr) {
11750    *   return chr.age < 40;
11751    * }), 'user');
11752    * // => 'barney'
11753    *
11754    * // using the `_.matches` callback shorthand
11755    * _.result(_.find(users, { 'age': 1, 'active': true }), 'user');
11756    * // => 'pebbles'
11757    *
11758    * // using the `_.matchesProperty` callback shorthand
11759    * _.result(_.find(users, 'active', false), 'user');
11760    * // => 'fred'
11761    *
11762    * // using the `_.property` callback shorthand
11763    * _.result(_.find(users, 'active'), 'user');
11764    * // => 'barney'
11765    */
11766   var find = createFind(baseEach);
11767
11768   /**
11769    * Iterates over elements of `collection` invoking `iteratee` for each element.
11770    * The `iteratee` is bound to `thisArg` and invoked with three arguments:
11771    * (value, index|key, collection). Iteratee functions may exit iteration early
11772    * by explicitly returning `false`.
11773    *
11774    * **Note:** As with other "Collections" methods, objects with a "length" property
11775    * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
11776    * may be used for object iteration.
11777    *
11778    * @static
11779    * @memberOf _
11780    * @alias each
11781    * @category Collection
11782    * @param {Array|Object|string} collection The collection to iterate over.
11783    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
11784    * @param {*} [thisArg] The `this` binding of `iteratee`.
11785    * @returns {Array|Object|string} Returns `collection`.
11786    * @example
11787    *
11788    * _([1, 2]).forEach(function(n) {
11789    *   console.log(n);
11790    * }).value();
11791    * // => logs each value from left to right and returns the array
11792    *
11793    * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) {
11794    *   console.log(n, key);
11795    * });
11796    * // => logs each value-key pair and returns the object (iteration order is not guaranteed)
11797    */
11798   var forEach = createForEach(arrayEach, baseEach);
11799
11800   /**
11801    * Creates an object composed of keys generated from the results of running
11802    * each element of `collection` through `iteratee`. The corresponding value
11803    * of each key is an array of the elements responsible for generating the key.
11804    * The `iteratee` is bound to `thisArg` and invoked with three arguments:
11805    * (value, index|key, collection).
11806    *
11807    * If a property name is provided for `iteratee` the created `_.property`
11808    * style callback returns the property value of the given element.
11809    *
11810    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11811    * style callback returns `true` for elements that have a matching property
11812    * value, else `false`.
11813    *
11814    * If an object is provided for `iteratee` the created `_.matches` style
11815    * callback returns `true` for elements that have the properties of the given
11816    * object, else `false`.
11817    *
11818    * @static
11819    * @memberOf _
11820    * @category Collection
11821    * @param {Array|Object|string} collection The collection to iterate over.
11822    * @param {Function|Object|string} [iteratee=_.identity] The function invoked
11823    *  per iteration.
11824    * @param {*} [thisArg] The `this` binding of `iteratee`.
11825    * @returns {Object} Returns the composed aggregate object.
11826    * @example
11827    *
11828    * _.groupBy([4.2, 6.1, 6.4], function(n) {
11829    *   return Math.floor(n);
11830    * });
11831    * // => { '4': [4.2], '6': [6.1, 6.4] }
11832    *
11833    * _.groupBy([4.2, 6.1, 6.4], function(n) {
11834    *   return this.floor(n);
11835    * }, Math);
11836    * // => { '4': [4.2], '6': [6.1, 6.4] }
11837    *
11838    * // using the `_.property` callback shorthand
11839    * _.groupBy(['one', 'two', 'three'], 'length');
11840    * // => { '3': ['one', 'two'], '5': ['three'] }
11841    */
11842   var groupBy = createAggregator(function(result, value, key) {
11843     if (hasOwnProperty.call(result, key)) {
11844       result[key].push(value);
11845     } else {
11846       result[key] = [value];
11847     }
11848   });
11849
11850   /**
11851    * Checks if `value` is in `collection` using
11852    * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11853    * for equality comparisons. If `fromIndex` is negative, it is used as the offset
11854    * from the end of `collection`.
11855    *
11856    * @static
11857    * @memberOf _
11858    * @alias contains, include
11859    * @category Collection
11860    * @param {Array|Object|string} collection The collection to search.
11861    * @param {*} target The value to search for.
11862    * @param {number} [fromIndex=0] The index to search from.
11863    * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`.
11864    * @returns {boolean} Returns `true` if a matching element is found, else `false`.
11865    * @example
11866    *
11867    * _.includes([1, 2, 3], 1);
11868    * // => true
11869    *
11870    * _.includes([1, 2, 3], 1, 2);
11871    * // => false
11872    *
11873    * _.includes({ 'user': 'fred', 'age': 40 }, 'fred');
11874    * // => true
11875    *
11876    * _.includes('pebbles', 'eb');
11877    * // => true
11878    */
11879   function includes(collection, target, fromIndex, guard) {
11880     var length = collection ? getLength(collection) : 0;
11881     if (!isLength(length)) {
11882       collection = values(collection);
11883       length = collection.length;
11884     }
11885     if (!length) {
11886       return false;
11887     }
11888     if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) {
11889       fromIndex = 0;
11890     } else {
11891       fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
11892     }
11893     return (typeof collection == 'string' || !isArray(collection) && isString(collection))
11894       ? (fromIndex < length && collection.indexOf(target, fromIndex) > -1)
11895       : (getIndexOf(collection, target, fromIndex) > -1);
11896   }
11897
11898   /**
11899    * Creates an array of values by running each element in `collection` through
11900    * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three
11901    * arguments: (value, index|key, collection).
11902    *
11903    * If a property name is provided for `iteratee` the created `_.property`
11904    * style callback returns the property value of the given element.
11905    *
11906    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11907    * style callback returns `true` for elements that have a matching property
11908    * value, else `false`.
11909    *
11910    * If an object is provided for `iteratee` the created `_.matches` style
11911    * callback returns `true` for elements that have the properties of the given
11912    * object, else `false`.
11913    *
11914    * Many lodash methods are guarded to work as iteratees for methods like
11915    * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
11916    *
11917    * The guarded methods are:
11918    * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`,
11919    * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`,
11920    * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`,
11921    * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`,
11922    * `sum`, `uniq`, and `words`
11923    *
11924    * @static
11925    * @memberOf _
11926    * @alias collect
11927    * @category Collection
11928    * @param {Array|Object|string} collection The collection to iterate over.
11929    * @param {Function|Object|string} [iteratee=_.identity] The function invoked
11930    *  per iteration.
11931    * @param {*} [thisArg] The `this` binding of `iteratee`.
11932    * @returns {Array} Returns the new mapped array.
11933    * @example
11934    *
11935    * function timesThree(n) {
11936    *   return n * 3;
11937    * }
11938    *
11939    * _.map([1, 2], timesThree);
11940    * // => [3, 6]
11941    *
11942    * _.map({ 'a': 1, 'b': 2 }, timesThree);
11943    * // => [3, 6] (iteration order is not guaranteed)
11944    *
11945    * var users = [
11946    *   { 'user': 'barney' },
11947    *   { 'user': 'fred' }
11948    * ];
11949    *
11950    * // using the `_.property` callback shorthand
11951    * _.map(users, 'user');
11952    * // => ['barney', 'fred']
11953    */
11954   function map(collection, iteratee, thisArg) {
11955     var func = isArray(collection) ? arrayMap : baseMap;
11956     iteratee = getCallback(iteratee, thisArg, 3);
11957     return func(collection, iteratee);
11958   }
11959
11960   /**
11961    * Gets the property value of `path` from all elements in `collection`.
11962    *
11963    * @static
11964    * @memberOf _
11965    * @category Collection
11966    * @param {Array|Object|string} collection The collection to iterate over.
11967    * @param {Array|string} path The path of the property to pluck.
11968    * @returns {Array} Returns the property values.
11969    * @example
11970    *
11971    * var users = [
11972    *   { 'user': 'barney', 'age': 36 },
11973    *   { 'user': 'fred',   'age': 40 }
11974    * ];
11975    *
11976    * _.pluck(users, 'user');
11977    * // => ['barney', 'fred']
11978    *
11979    * var userIndex = _.indexBy(users, 'user');
11980    * _.pluck(userIndex, 'age');
11981    * // => [36, 40] (iteration order is not guaranteed)
11982    */
11983   function pluck(collection, path) {
11984     return map(collection, property(path));
11985   }
11986
11987   /**
11988    * Reduces `collection` to a value which is the accumulated result of running
11989    * each element in `collection` through `iteratee`, where each successive
11990    * invocation is supplied the return value of the previous. If `accumulator`
11991    * is not provided the first element of `collection` is used as the initial
11992    * value. The `iteratee` is bound to `thisArg` and invoked with four arguments:
11993    * (accumulator, value, index|key, collection).
11994    *
11995    * Many lodash methods are guarded to work as iteratees for methods like
11996    * `_.reduce`, `_.reduceRight`, and `_.transform`.
11997    *
11998    * The guarded methods are:
11999    * `assign`, `defaults`, `includes`, `merge`, `sortByAll`, and `sortByOrder`
12000    *
12001    * @static
12002    * @memberOf _
12003    * @alias foldl, inject
12004    * @category Collection
12005    * @param {Array|Object|string} collection The collection to iterate over.
12006    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12007    * @param {*} [accumulator] The initial value.
12008    * @param {*} [thisArg] The `this` binding of `iteratee`.
12009    * @returns {*} Returns the accumulated value.
12010    * @example
12011    *
12012    * _.reduce([1, 2], function(total, n) {
12013    *   return total + n;
12014    * });
12015    * // => 3
12016    *
12017    * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) {
12018    *   result[key] = n * 3;
12019    *   return result;
12020    * }, {});
12021    * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed)
12022    */
12023   var reduce = createReduce(arrayReduce, baseEach);
12024
12025   /**
12026    * The opposite of `_.filter`; this method returns the elements of `collection`
12027    * that `predicate` does **not** return truthy for.
12028    *
12029    * @static
12030    * @memberOf _
12031    * @category Collection
12032    * @param {Array|Object|string} collection The collection to iterate over.
12033    * @param {Function|Object|string} [predicate=_.identity] The function invoked
12034    *  per iteration.
12035    * @param {*} [thisArg] The `this` binding of `predicate`.
12036    * @returns {Array} Returns the new filtered array.
12037    * @example
12038    *
12039    * _.reject([1, 2, 3, 4], function(n) {
12040    *   return n % 2 == 0;
12041    * });
12042    * // => [1, 3]
12043    *
12044    * var users = [
12045    *   { 'user': 'barney', 'age': 36, 'active': false },
12046    *   { 'user': 'fred',   'age': 40, 'active': true }
12047    * ];
12048    *
12049    * // using the `_.matches` callback shorthand
12050    * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user');
12051    * // => ['barney']
12052    *
12053    * // using the `_.matchesProperty` callback shorthand
12054    * _.pluck(_.reject(users, 'active', false), 'user');
12055    * // => ['fred']
12056    *
12057    * // using the `_.property` callback shorthand
12058    * _.pluck(_.reject(users, 'active'), 'user');
12059    * // => ['barney']
12060    */
12061   function reject(collection, predicate, thisArg) {
12062     var func = isArray(collection) ? arrayFilter : baseFilter;
12063     predicate = getCallback(predicate, thisArg, 3);
12064     return func(collection, function(value, index, collection) {
12065       return !predicate(value, index, collection);
12066     });
12067   }
12068
12069   /**
12070    * Checks if `predicate` returns truthy for **any** element of `collection`.
12071    * The function returns as soon as it finds a passing value and does not iterate
12072    * over the entire collection. The predicate is bound to `thisArg` and invoked
12073    * with three arguments: (value, index|key, collection).
12074    *
12075    * If a property name is provided for `predicate` the created `_.property`
12076    * style callback returns the property value of the given element.
12077    *
12078    * If a value is also provided for `thisArg` the created `_.matchesProperty`
12079    * style callback returns `true` for elements that have a matching property
12080    * value, else `false`.
12081    *
12082    * If an object is provided for `predicate` the created `_.matches` style
12083    * callback returns `true` for elements that have the properties of the given
12084    * object, else `false`.
12085    *
12086    * @static
12087    * @memberOf _
12088    * @alias any
12089    * @category Collection
12090    * @param {Array|Object|string} collection The collection to iterate over.
12091    * @param {Function|Object|string} [predicate=_.identity] The function invoked
12092    *  per iteration.
12093    * @param {*} [thisArg] The `this` binding of `predicate`.
12094    * @returns {boolean} Returns `true` if any element passes the predicate check,
12095    *  else `false`.
12096    * @example
12097    *
12098    * _.some([null, 0, 'yes', false], Boolean);
12099    * // => true
12100    *
12101    * var users = [
12102    *   { 'user': 'barney', 'active': true },
12103    *   { 'user': 'fred',   'active': false }
12104    * ];
12105    *
12106    * // using the `_.matches` callback shorthand
12107    * _.some(users, { 'user': 'barney', 'active': false });
12108    * // => false
12109    *
12110    * // using the `_.matchesProperty` callback shorthand
12111    * _.some(users, 'active', false);
12112    * // => true
12113    *
12114    * // using the `_.property` callback shorthand
12115    * _.some(users, 'active');
12116    * // => true
12117    */
12118   function some(collection, predicate, thisArg) {
12119     var func = isArray(collection) ? arraySome : baseSome;
12120     if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
12121       predicate = null;
12122     }
12123     if (typeof predicate != 'function' || thisArg !== undefined) {
12124       predicate = getCallback(predicate, thisArg, 3);
12125     }
12126     return func(collection, predicate);
12127   }
12128
12129   /*------------------------------------------------------------------------*/
12130
12131   /**
12132    * Gets the number of milliseconds that have elapsed since the Unix epoch
12133    * (1 January 1970 00:00:00 UTC).
12134    *
12135    * @static
12136    * @memberOf _
12137    * @category Date
12138    * @example
12139    *
12140    * _.defer(function(stamp) {
12141    *   console.log(_.now() - stamp);
12142    * }, _.now());
12143    * // => logs the number of milliseconds it took for the deferred function to be invoked
12144    */
12145   var now = nativeNow || function() {
12146     return new Date().getTime();
12147   };
12148
12149   /*------------------------------------------------------------------------*/
12150
12151   /**
12152    * Creates a function that invokes `func` with the `this` binding of `thisArg`
12153    * and prepends any additional `_.bind` arguments to those provided to the
12154    * bound function.
12155    *
12156    * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
12157    * may be used as a placeholder for partially applied arguments.
12158    *
12159    * **Note:** Unlike native `Function#bind` this method does not set the "length"
12160    * property of bound functions.
12161    *
12162    * @static
12163    * @memberOf _
12164    * @category Function
12165    * @param {Function} func The function to bind.
12166    * @param {*} thisArg The `this` binding of `func`.
12167    * @param {...*} [partials] The arguments to be partially applied.
12168    * @returns {Function} Returns the new bound function.
12169    * @example
12170    *
12171    * var greet = function(greeting, punctuation) {
12172    *   return greeting + ' ' + this.user + punctuation;
12173    * };
12174    *
12175    * var object = { 'user': 'fred' };
12176    *
12177    * var bound = _.bind(greet, object, 'hi');
12178    * bound('!');
12179    * // => 'hi fred!'
12180    *
12181    * // using placeholders
12182    * var bound = _.bind(greet, object, _, '!');
12183    * bound('hi');
12184    * // => 'hi fred!'
12185    */
12186   var bind = restParam(function(func, thisArg, partials) {
12187     var bitmask = BIND_FLAG;
12188     if (partials.length) {
12189       var holders = replaceHolders(partials, bind.placeholder);
12190       bitmask |= PARTIAL_FLAG;
12191     }
12192     return createWrapper(func, bitmask, thisArg, partials, holders);
12193   });
12194
12195   /**
12196    * Creates a debounced function that delays invoking `func` until after `wait`
12197    * milliseconds have elapsed since the last time the debounced function was
12198    * invoked. The debounced function comes with a `cancel` method to cancel
12199    * delayed invocations. Provide an options object to indicate that `func`
12200    * should be invoked on the leading and/or trailing edge of the `wait` timeout.
12201    * Subsequent calls to the debounced function return the result of the last
12202    * `func` invocation.
12203    *
12204    * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
12205    * on the trailing edge of the timeout only if the the debounced function is
12206    * invoked more than once during the `wait` timeout.
12207    *
12208    * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
12209    * for details over the differences between `_.debounce` and `_.throttle`.
12210    *
12211    * @static
12212    * @memberOf _
12213    * @category Function
12214    * @param {Function} func The function to debounce.
12215    * @param {number} [wait=0] The number of milliseconds to delay.
12216    * @param {Object} [options] The options object.
12217    * @param {boolean} [options.leading=false] Specify invoking on the leading
12218    *  edge of the timeout.
12219    * @param {number} [options.maxWait] The maximum time `func` is allowed to be
12220    *  delayed before it is invoked.
12221    * @param {boolean} [options.trailing=true] Specify invoking on the trailing
12222    *  edge of the timeout.
12223    * @returns {Function} Returns the new debounced function.
12224    * @example
12225    *
12226    * // avoid costly calculations while the window size is in flux
12227    * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
12228    *
12229    * // invoke `sendMail` when the click event is fired, debouncing subsequent calls
12230    * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
12231    *   'leading': true,
12232    *   'trailing': false
12233    * }));
12234    *
12235    * // ensure `batchLog` is invoked once after 1 second of debounced calls
12236    * var source = new EventSource('/stream');
12237    * jQuery(source).on('message', _.debounce(batchLog, 250, {
12238    *   'maxWait': 1000
12239    * }));
12240    *
12241    * // cancel a debounced call
12242    * var todoChanges = _.debounce(batchLog, 1000);
12243    * Object.observe(models.todo, todoChanges);
12244    *
12245    * Object.observe(models, function(changes) {
12246    *   if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
12247    *     todoChanges.cancel();
12248    *   }
12249    * }, ['delete']);
12250    *
12251    * // ...at some point `models.todo` is changed
12252    * models.todo.completed = true;
12253    *
12254    * // ...before 1 second has passed `models.todo` is deleted
12255    * // which cancels the debounced `todoChanges` call
12256    * delete models.todo;
12257    */
12258   function debounce(func, wait, options) {
12259     var args,
12260         maxTimeoutId,
12261         result,
12262         stamp,
12263         thisArg,
12264         timeoutId,
12265         trailingCall,
12266         lastCalled = 0,
12267         maxWait = false,
12268         trailing = true;
12269
12270     if (typeof func != 'function') {
12271       throw new TypeError(FUNC_ERROR_TEXT);
12272     }
12273     wait = wait < 0 ? 0 : (+wait || 0);
12274     if (options === true) {
12275       var leading = true;
12276       trailing = false;
12277     } else if (isObject(options)) {
12278       leading = options.leading;
12279       maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
12280       trailing = 'trailing' in options ? options.trailing : trailing;
12281     }
12282
12283     function cancel() {
12284       if (timeoutId) {
12285         clearTimeout(timeoutId);
12286       }
12287       if (maxTimeoutId) {
12288         clearTimeout(maxTimeoutId);
12289       }
12290       maxTimeoutId = timeoutId = trailingCall = undefined;
12291     }
12292
12293     function delayed() {
12294       var remaining = wait - (now() - stamp);
12295       if (remaining <= 0 || remaining > wait) {
12296         if (maxTimeoutId) {
12297           clearTimeout(maxTimeoutId);
12298         }
12299         var isCalled = trailingCall;
12300         maxTimeoutId = timeoutId = trailingCall = undefined;
12301         if (isCalled) {
12302           lastCalled = now();
12303           result = func.apply(thisArg, args);
12304           if (!timeoutId && !maxTimeoutId) {
12305             args = thisArg = null;
12306           }
12307         }
12308       } else {
12309         timeoutId = setTimeout(delayed, remaining);
12310       }
12311     }
12312
12313     function maxDelayed() {
12314       if (timeoutId) {
12315         clearTimeout(timeoutId);
12316       }
12317       maxTimeoutId = timeoutId = trailingCall = undefined;
12318       if (trailing || (maxWait !== wait)) {
12319         lastCalled = now();
12320         result = func.apply(thisArg, args);
12321         if (!timeoutId && !maxTimeoutId) {
12322           args = thisArg = null;
12323         }
12324       }
12325     }
12326
12327     function debounced() {
12328       args = arguments;
12329       stamp = now();
12330       thisArg = this;
12331       trailingCall = trailing && (timeoutId || !leading);
12332
12333       if (maxWait === false) {
12334         var leadingCall = leading && !timeoutId;
12335       } else {
12336         if (!maxTimeoutId && !leading) {
12337           lastCalled = stamp;
12338         }
12339         var remaining = maxWait - (stamp - lastCalled),
12340             isCalled = remaining <= 0 || remaining > maxWait;
12341
12342         if (isCalled) {
12343           if (maxTimeoutId) {
12344             maxTimeoutId = clearTimeout(maxTimeoutId);
12345           }
12346           lastCalled = stamp;
12347           result = func.apply(thisArg, args);
12348         }
12349         else if (!maxTimeoutId) {
12350           maxTimeoutId = setTimeout(maxDelayed, remaining);
12351         }
12352       }
12353       if (isCalled && timeoutId) {
12354         timeoutId = clearTimeout(timeoutId);
12355       }
12356       else if (!timeoutId && wait !== maxWait) {
12357         timeoutId = setTimeout(delayed, wait);
12358       }
12359       if (leadingCall) {
12360         isCalled = true;
12361         result = func.apply(thisArg, args);
12362       }
12363       if (isCalled && !timeoutId && !maxTimeoutId) {
12364         args = thisArg = null;
12365       }
12366       return result;
12367     }
12368     debounced.cancel = cancel;
12369     return debounced;
12370   }
12371
12372   /**
12373    * Creates a function that invokes `func` with the `this` binding of the
12374    * created function and arguments from `start` and beyond provided as an array.
12375    *
12376    * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
12377    *
12378    * @static
12379    * @memberOf _
12380    * @category Function
12381    * @param {Function} func The function to apply a rest parameter to.
12382    * @param {number} [start=func.length-1] The start position of the rest parameter.
12383    * @returns {Function} Returns the new function.
12384    * @example
12385    *
12386    * var say = _.restParam(function(what, names) {
12387    *   return what + ' ' + _.initial(names).join(', ') +
12388    *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
12389    * });
12390    *
12391    * say('hello', 'fred', 'barney', 'pebbles');
12392    * // => 'hello fred, barney, & pebbles'
12393    */
12394   function restParam(func, start) {
12395     if (typeof func != 'function') {
12396       throw new TypeError(FUNC_ERROR_TEXT);
12397     }
12398     start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0);
12399     return function() {
12400       var args = arguments,
12401           index = -1,
12402           length = nativeMax(args.length - start, 0),
12403           rest = Array(length);
12404
12405       while (++index < length) {
12406         rest[index] = args[start + index];
12407       }
12408       switch (start) {
12409         case 0: return func.call(this, rest);
12410         case 1: return func.call(this, args[0], rest);
12411         case 2: return func.call(this, args[0], args[1], rest);
12412       }
12413       var otherArgs = Array(start + 1);
12414       index = -1;
12415       while (++index < start) {
12416         otherArgs[index] = args[index];
12417       }
12418       otherArgs[start] = rest;
12419       return func.apply(this, otherArgs);
12420     };
12421   }
12422
12423   /**
12424    * Creates a throttled function that only invokes `func` at most once per
12425    * every `wait` milliseconds. The throttled function comes with a `cancel`
12426    * method to cancel delayed invocations. Provide an options object to indicate
12427    * that `func` should be invoked on the leading and/or trailing edge of the
12428    * `wait` timeout. Subsequent calls to the throttled function return the
12429    * result of the last `func` call.
12430    *
12431    * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
12432    * on the trailing edge of the timeout only if the the throttled function is
12433    * invoked more than once during the `wait` timeout.
12434    *
12435    * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
12436    * for details over the differences between `_.throttle` and `_.debounce`.
12437    *
12438    * @static
12439    * @memberOf _
12440    * @category Function
12441    * @param {Function} func The function to throttle.
12442    * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
12443    * @param {Object} [options] The options object.
12444    * @param {boolean} [options.leading=true] Specify invoking on the leading
12445    *  edge of the timeout.
12446    * @param {boolean} [options.trailing=true] Specify invoking on the trailing
12447    *  edge of the timeout.
12448    * @returns {Function} Returns the new throttled function.
12449    * @example
12450    *
12451    * // avoid excessively updating the position while scrolling
12452    * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
12453    *
12454    * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes
12455    * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
12456    *   'trailing': false
12457    * }));
12458    *
12459    * // cancel a trailing throttled call
12460    * jQuery(window).on('popstate', throttled.cancel);
12461    */
12462   function throttle(func, wait, options) {
12463     var leading = true,
12464         trailing = true;
12465
12466     if (typeof func != 'function') {
12467       throw new TypeError(FUNC_ERROR_TEXT);
12468     }
12469     if (options === false) {
12470       leading = false;
12471     } else if (isObject(options)) {
12472       leading = 'leading' in options ? !!options.leading : leading;
12473       trailing = 'trailing' in options ? !!options.trailing : trailing;
12474     }
12475     debounceOptions.leading = leading;
12476     debounceOptions.maxWait = +wait;
12477     debounceOptions.trailing = trailing;
12478     return debounce(func, wait, debounceOptions);
12479   }
12480
12481   /*------------------------------------------------------------------------*/
12482
12483   /**
12484    * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned,
12485    * otherwise they are assigned by reference. If `customizer` is provided it is
12486    * invoked to produce the cloned values. If `customizer` returns `undefined`
12487    * cloning is handled by the method instead. The `customizer` is bound to
12488    * `thisArg` and invoked with two argument; (value [, index|key, object]).
12489    *
12490    * **Note:** This method is loosely based on the
12491    * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
12492    * The enumerable properties of `arguments` objects and objects created by
12493    * constructors other than `Object` are cloned to plain `Object` objects. An
12494    * empty object is returned for uncloneable values such as functions, DOM nodes,
12495    * Maps, Sets, and WeakMaps.
12496    *
12497    * @static
12498    * @memberOf _
12499    * @category Lang
12500    * @param {*} value The value to clone.
12501    * @param {boolean} [isDeep] Specify a deep clone.
12502    * @param {Function} [customizer] The function to customize cloning values.
12503    * @param {*} [thisArg] The `this` binding of `customizer`.
12504    * @returns {*} Returns the cloned value.
12505    * @example
12506    *
12507    * var users = [
12508    *   { 'user': 'barney' },
12509    *   { 'user': 'fred' }
12510    * ];
12511    *
12512    * var shallow = _.clone(users);
12513    * shallow[0] === users[0];
12514    * // => true
12515    *
12516    * var deep = _.clone(users, true);
12517    * deep[0] === users[0];
12518    * // => false
12519    *
12520    * // using a customizer callback
12521    * var el = _.clone(document.body, function(value) {
12522    *   if (_.isElement(value)) {
12523    *     return value.cloneNode(false);
12524    *   }
12525    * });
12526    *
12527    * el === document.body
12528    * // => false
12529    * el.nodeName
12530    * // => BODY
12531    * el.childNodes.length;
12532    * // => 0
12533    */
12534   function clone(value, isDeep, customizer, thisArg) {
12535     if (isDeep && typeof isDeep != 'boolean' && isIterateeCall(value, isDeep, customizer)) {
12536       isDeep = false;
12537     }
12538     else if (typeof isDeep == 'function') {
12539       thisArg = customizer;
12540       customizer = isDeep;
12541       isDeep = false;
12542     }
12543     return typeof customizer == 'function'
12544       ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 1))
12545       : baseClone(value, isDeep);
12546   }
12547
12548   /**
12549    * Creates a deep clone of `value`. If `customizer` is provided it is invoked
12550    * to produce the cloned values. If `customizer` returns `undefined` cloning
12551    * is handled by the method instead. The `customizer` is bound to `thisArg`
12552    * and invoked with two argument; (value [, index|key, object]).
12553    *
12554    * **Note:** This method is loosely based on the
12555    * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
12556    * The enumerable properties of `arguments` objects and objects created by
12557    * constructors other than `Object` are cloned to plain `Object` objects. An
12558    * empty object is returned for uncloneable values such as functions, DOM nodes,
12559    * Maps, Sets, and WeakMaps.
12560    *
12561    * @static
12562    * @memberOf _
12563    * @category Lang
12564    * @param {*} value The value to deep clone.
12565    * @param {Function} [customizer] The function to customize cloning values.
12566    * @param {*} [thisArg] The `this` binding of `customizer`.
12567    * @returns {*} Returns the deep cloned value.
12568    * @example
12569    *
12570    * var users = [
12571    *   { 'user': 'barney' },
12572    *   { 'user': 'fred' }
12573    * ];
12574    *
12575    * var deep = _.cloneDeep(users);
12576    * deep[0] === users[0];
12577    * // => false
12578    *
12579    * // using a customizer callback
12580    * var el = _.cloneDeep(document.body, function(value) {
12581    *   if (_.isElement(value)) {
12582    *     return value.cloneNode(true);
12583    *   }
12584    * });
12585    *
12586    * el === document.body
12587    * // => false
12588    * el.nodeName
12589    * // => BODY
12590    * el.childNodes.length;
12591    * // => 20
12592    */
12593   function cloneDeep(value, customizer, thisArg) {
12594     return typeof customizer == 'function'
12595       ? baseClone(value, true, bindCallback(customizer, thisArg, 1))
12596       : baseClone(value, true);
12597   }
12598
12599   /**
12600    * Checks if `value` is classified as an `arguments` object.
12601    *
12602    * @static
12603    * @memberOf _
12604    * @category Lang
12605    * @param {*} value The value to check.
12606    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12607    * @example
12608    *
12609    * _.isArguments(function() { return arguments; }());
12610    * // => true
12611    *
12612    * _.isArguments([1, 2, 3]);
12613    * // => false
12614    */
12615   function isArguments(value) {
12616     return isObjectLike(value) && isArrayLike(value) && objToString.call(value) == argsTag;
12617   }
12618   // Fallback for environments without a `toStringTag` for `arguments` objects.
12619   if (!support.argsTag) {
12620     isArguments = function(value) {
12621       return isObjectLike(value) && isArrayLike(value) &&
12622         hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');
12623     };
12624   }
12625
12626   /**
12627    * Checks if `value` is classified as an `Array` object.
12628    *
12629    * @static
12630    * @memberOf _
12631    * @category Lang
12632    * @param {*} value The value to check.
12633    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12634    * @example
12635    *
12636    * _.isArray([1, 2, 3]);
12637    * // => true
12638    *
12639    * _.isArray(function() { return arguments; }());
12640    * // => false
12641    */
12642   var isArray = nativeIsArray || function(value) {
12643     return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag;
12644   };
12645
12646   /**
12647    * Checks if `value` is empty. A value is considered empty unless it is an
12648    * `arguments` object, array, string, or jQuery-like collection with a length
12649    * greater than `0` or an object with own enumerable properties.
12650    *
12651    * @static
12652    * @memberOf _
12653    * @category Lang
12654    * @param {Array|Object|string} value The value to inspect.
12655    * @returns {boolean} Returns `true` if `value` is empty, else `false`.
12656    * @example
12657    *
12658    * _.isEmpty(null);
12659    * // => true
12660    *
12661    * _.isEmpty(true);
12662    * // => true
12663    *
12664    * _.isEmpty(1);
12665    * // => true
12666    *
12667    * _.isEmpty([1, 2, 3]);
12668    * // => false
12669    *
12670    * _.isEmpty({ 'a': 1 });
12671    * // => false
12672    */
12673   function isEmpty(value) {
12674     if (value == null) {
12675       return true;
12676     }
12677     if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) ||
12678         (isObjectLike(value) && isFunction(value.splice)))) {
12679       return !value.length;
12680     }
12681     return !keys(value).length;
12682   }
12683
12684   /**
12685    * Performs a deep comparison between two values to determine if they are
12686    * equivalent. If `customizer` is provided it is invoked to compare values.
12687    * If `customizer` returns `undefined` comparisons are handled by the method
12688    * instead. The `customizer` is bound to `thisArg` and invoked with three
12689    * arguments: (value, other [, index|key]).
12690    *
12691    * **Note:** This method supports comparing arrays, booleans, `Date` objects,
12692    * numbers, `Object` objects, regexes, and strings. Objects are compared by
12693    * their own, not inherited, enumerable properties. Functions and DOM nodes
12694    * are **not** supported. Provide a customizer function to extend support
12695    * for comparing other values.
12696    *
12697    * @static
12698    * @memberOf _
12699    * @alias eq
12700    * @category Lang
12701    * @param {*} value The value to compare.
12702    * @param {*} other The other value to compare.
12703    * @param {Function} [customizer] The function to customize value comparisons.
12704    * @param {*} [thisArg] The `this` binding of `customizer`.
12705    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
12706    * @example
12707    *
12708    * var object = { 'user': 'fred' };
12709    * var other = { 'user': 'fred' };
12710    *
12711    * object == other;
12712    * // => false
12713    *
12714    * _.isEqual(object, other);
12715    * // => true
12716    *
12717    * // using a customizer callback
12718    * var array = ['hello', 'goodbye'];
12719    * var other = ['hi', 'goodbye'];
12720    *
12721    * _.isEqual(array, other, function(value, other) {
12722    *   if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) {
12723    *     return true;
12724    *   }
12725    * });
12726    * // => true
12727    */
12728   function isEqual(value, other, customizer, thisArg) {
12729     customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined;
12730     var result = customizer ? customizer(value, other) : undefined;
12731     return  result === undefined ? baseIsEqual(value, other, customizer) : !!result;
12732   }
12733
12734   /**
12735    * Checks if `value` is classified as a `Function` object.
12736    *
12737    * @static
12738    * @memberOf _
12739    * @category Lang
12740    * @param {*} value The value to check.
12741    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12742    * @example
12743    *
12744    * _.isFunction(_);
12745    * // => true
12746    *
12747    * _.isFunction(/abc/);
12748    * // => false
12749    */
12750   var isFunction = !(baseIsFunction(/x/) || (Uint8Array && !baseIsFunction(Uint8Array))) ? baseIsFunction : function(value) {
12751     // The use of `Object#toString` avoids issues with the `typeof` operator
12752     // in older versions of Chrome and Safari which return 'function' for regexes
12753     // and Safari 8 equivalents which return 'object' for typed array constructors.
12754     return objToString.call(value) == funcTag;
12755   };
12756
12757   /**
12758    * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
12759    * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
12760    *
12761    * @static
12762    * @memberOf _
12763    * @category Lang
12764    * @param {*} value The value to check.
12765    * @returns {boolean} Returns `true` if `value` is an object, else `false`.
12766    * @example
12767    *
12768    * _.isObject({});
12769    * // => true
12770    *
12771    * _.isObject([1, 2, 3]);
12772    * // => true
12773    *
12774    * _.isObject(1);
12775    * // => false
12776    */
12777   function isObject(value) {
12778     // Avoid a V8 JIT bug in Chrome 19-20.
12779     // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
12780     var type = typeof value;
12781     return !!value && (type == 'object' || type == 'function');
12782   }
12783
12784   /**
12785    * Checks if `value` is a native function.
12786    *
12787    * @static
12788    * @memberOf _
12789    * @category Lang
12790    * @param {*} value The value to check.
12791    * @returns {boolean} Returns `true` if `value` is a native function, else `false`.
12792    * @example
12793    *
12794    * _.isNative(Array.prototype.push);
12795    * // => true
12796    *
12797    * _.isNative(_);
12798    * // => false
12799    */
12800   function isNative(value) {
12801     if (value == null) {
12802       return false;
12803     }
12804     if (objToString.call(value) == funcTag) {
12805       return reIsNative.test(fnToString.call(value));
12806     }
12807     return isObjectLike(value) && (isHostObject(value) ? reIsNative : reIsHostCtor).test(value);
12808   }
12809
12810   /**
12811    * Checks if `value` is a plain object, that is, an object created by the
12812    * `Object` constructor or one with a `[[Prototype]]` of `null`.
12813    *
12814    * **Note:** This method assumes objects created by the `Object` constructor
12815    * have no inherited enumerable properties.
12816    *
12817    * @static
12818    * @memberOf _
12819    * @category Lang
12820    * @param {*} value The value to check.
12821    * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
12822    * @example
12823    *
12824    * function Foo() {
12825    *   this.a = 1;
12826    * }
12827    *
12828    * _.isPlainObject(new Foo);
12829    * // => false
12830    *
12831    * _.isPlainObject([1, 2, 3]);
12832    * // => false
12833    *
12834    * _.isPlainObject({ 'x': 0, 'y': 0 });
12835    * // => true
12836    *
12837    * _.isPlainObject(Object.create(null));
12838    * // => true
12839    */
12840   var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
12841     if (!(value && objToString.call(value) == objectTag) || (!lodash.support.argsTag && isArguments(value))) {
12842       return false;
12843     }
12844     var valueOf = getNative(value, 'valueOf'),
12845         objProto = valueOf && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
12846
12847     return objProto
12848       ? (value == objProto || getPrototypeOf(value) == objProto)
12849       : shimIsPlainObject(value);
12850   };
12851
12852   /**
12853    * Checks if `value` is classified as a `String` primitive or object.
12854    *
12855    * @static
12856    * @memberOf _
12857    * @category Lang
12858    * @param {*} value The value to check.
12859    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12860    * @example
12861    *
12862    * _.isString('abc');
12863    * // => true
12864    *
12865    * _.isString(1);
12866    * // => false
12867    */
12868   function isString(value) {
12869     return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag);
12870   }
12871
12872   /**
12873    * Checks if `value` is classified as a typed array.
12874    *
12875    * @static
12876    * @memberOf _
12877    * @category Lang
12878    * @param {*} value The value to check.
12879    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12880    * @example
12881    *
12882    * _.isTypedArray(new Uint8Array);
12883    * // => true
12884    *
12885    * _.isTypedArray([]);
12886    * // => false
12887    */
12888   function isTypedArray(value) {
12889     return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
12890   }
12891
12892   /**
12893    * Converts `value` to a plain object flattening inherited enumerable
12894    * properties of `value` to own properties of the plain object.
12895    *
12896    * @static
12897    * @memberOf _
12898    * @category Lang
12899    * @param {*} value The value to convert.
12900    * @returns {Object} Returns the converted plain object.
12901    * @example
12902    *
12903    * function Foo() {
12904    *   this.b = 2;
12905    * }
12906    *
12907    * Foo.prototype.c = 3;
12908    *
12909    * _.assign({ 'a': 1 }, new Foo);
12910    * // => { 'a': 1, 'b': 2 }
12911    *
12912    * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
12913    * // => { 'a': 1, 'b': 2, 'c': 3 }
12914    */
12915   function toPlainObject(value) {
12916     return baseCopy(value, keysIn(value));
12917   }
12918
12919   /*------------------------------------------------------------------------*/
12920
12921   /**
12922    * Assigns own enumerable properties of source object(s) to the destination
12923    * object. Subsequent sources overwrite property assignments of previous sources.
12924    * If `customizer` is provided it is invoked to produce the assigned values.
12925    * The `customizer` is bound to `thisArg` and invoked with five arguments:
12926    * (objectValue, sourceValue, key, object, source).
12927    *
12928    * **Note:** This method mutates `object` and is based on
12929    * [`Object.assign`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign).
12930    *
12931    * @static
12932    * @memberOf _
12933    * @alias extend
12934    * @category Object
12935    * @param {Object} object The destination object.
12936    * @param {...Object} [sources] The source objects.
12937    * @param {Function} [customizer] The function to customize assigned values.
12938    * @param {*} [thisArg] The `this` binding of `customizer`.
12939    * @returns {Object} Returns `object`.
12940    * @example
12941    *
12942    * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' });
12943    * // => { 'user': 'fred', 'age': 40 }
12944    *
12945    * // using a customizer callback
12946    * var defaults = _.partialRight(_.assign, function(value, other) {
12947    *   return _.isUndefined(value) ? other : value;
12948    * });
12949    *
12950    * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
12951    * // => { 'user': 'barney', 'age': 36 }
12952    */
12953   var assign = createAssigner(function(object, source, customizer) {
12954     return customizer
12955       ? assignWith(object, source, customizer)
12956       : baseAssign(object, source);
12957   });
12958
12959   /**
12960    * Iterates over own enumerable properties of an object invoking `iteratee`
12961    * for each property. The `iteratee` is bound to `thisArg` and invoked with
12962    * three arguments: (value, key, object). Iteratee functions may exit iteration
12963    * early by explicitly returning `false`.
12964    *
12965    * @static
12966    * @memberOf _
12967    * @category Object
12968    * @param {Object} object The object to iterate over.
12969    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12970    * @param {*} [thisArg] The `this` binding of `iteratee`.
12971    * @returns {Object} Returns `object`.
12972    * @example
12973    *
12974    * function Foo() {
12975    *   this.a = 1;
12976    *   this.b = 2;
12977    * }
12978    *
12979    * Foo.prototype.c = 3;
12980    *
12981    * _.forOwn(new Foo, function(value, key) {
12982    *   console.log(key);
12983    * });
12984    * // => logs 'a' and 'b' (iteration order is not guaranteed)
12985    */
12986   var forOwn = createForOwn(baseForOwn);
12987
12988   /**
12989    * Creates an array of the own enumerable property names of `object`.
12990    *
12991    * **Note:** Non-object values are coerced to objects. See the
12992    * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys)
12993    * for more details.
12994    *
12995    * @static
12996    * @memberOf _
12997    * @category Object
12998    * @param {Object} object The object to query.
12999    * @returns {Array} Returns the array of property names.
13000    * @example
13001    *
13002    * function Foo() {
13003    *   this.a = 1;
13004    *   this.b = 2;
13005    * }
13006    *
13007    * Foo.prototype.c = 3;
13008    *
13009    * _.keys(new Foo);
13010    * // => ['a', 'b'] (iteration order is not guaranteed)
13011    *
13012    * _.keys('hi');
13013    * // => ['0', '1']
13014    */
13015   var keys = !nativeKeys ? shimKeys : function(object) {
13016     var Ctor = object == null ? null : object.constructor;
13017     if ((typeof Ctor == 'function' && Ctor.prototype === object) ||
13018         (typeof object == 'function' ? lodash.support.enumPrototypes : isArrayLike(object))) {
13019       return shimKeys(object);
13020     }
13021     return isObject(object) ? nativeKeys(object) : [];
13022   };
13023
13024   /**
13025    * Creates an array of the own and inherited enumerable property names of `object`.
13026    *
13027    * **Note:** Non-object values are coerced to objects.
13028    *
13029    * @static
13030    * @memberOf _
13031    * @category Object
13032    * @param {Object} object The object to query.
13033    * @returns {Array} Returns the array of property names.
13034    * @example
13035    *
13036    * function Foo() {
13037    *   this.a = 1;
13038    *   this.b = 2;
13039    * }
13040    *
13041    * Foo.prototype.c = 3;
13042    *
13043    * _.keysIn(new Foo);
13044    * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
13045    */
13046   function keysIn(object) {
13047     if (object == null) {
13048       return [];
13049     }
13050     if (!isObject(object)) {
13051       object = Object(object);
13052     }
13053     var length = object.length,
13054         support = lodash.support;
13055
13056     length = (length && isLength(length) &&
13057       (isArray(object) || isArguments(object) || isString(object)) && length) || 0;
13058
13059     var Ctor = object.constructor,
13060         index = -1,
13061         proto = (isFunction(Ctor) && Ctor.prototype) || objectProto,
13062         isProto = proto === object,
13063         result = Array(length),
13064         skipIndexes = length > 0,
13065         skipErrorProps = support.enumErrorProps && (object === errorProto || object instanceof Error),
13066         skipProto = support.enumPrototypes && isFunction(object);
13067
13068     while (++index < length) {
13069       result[index] = (index + '');
13070     }
13071     // lodash skips the `constructor` property when it infers it is iterating
13072     // over a `prototype` object because IE < 9 can't set the `[[Enumerable]]`
13073     // attribute of an existing property and the `constructor` property of a
13074     // prototype defaults to non-enumerable.
13075     for (var key in object) {
13076       if (!(skipProto && key == 'prototype') &&
13077           !(skipErrorProps && (key == 'message' || key == 'name')) &&
13078           !(skipIndexes && isIndex(key, length)) &&
13079           !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
13080         result.push(key);
13081       }
13082     }
13083     if (support.nonEnumShadows && object !== objectProto) {
13084       var tag = object === stringProto ? stringTag : (object === errorProto ? errorTag : objToString.call(object)),
13085           nonEnums = nonEnumProps[tag] || nonEnumProps[objectTag];
13086
13087       if (tag == objectTag) {
13088         proto = objectProto;
13089       }
13090       length = shadowProps.length;
13091       while (length--) {
13092         key = shadowProps[length];
13093         var nonEnum = nonEnums[key];
13094         if (!(isProto && nonEnum) &&
13095             (nonEnum ? hasOwnProperty.call(object, key) : object[key] !== proto[key])) {
13096           result.push(key);
13097         }
13098       }
13099     }
13100     return result;
13101   }
13102
13103   /**
13104    * Recursively merges own enumerable properties of the source object(s), that
13105    * don't resolve to `undefined` into the destination object. Subsequent sources
13106    * overwrite property assignments of previous sources. If `customizer` is
13107    * provided it is invoked to produce the merged values of the destination and
13108    * source properties. If `customizer` returns `undefined` merging is handled
13109    * by the method instead. The `customizer` is bound to `thisArg` and invoked
13110    * with five arguments: (objectValue, sourceValue, key, object, source).
13111    *
13112    * @static
13113    * @memberOf _
13114    * @category Object
13115    * @param {Object} object The destination object.
13116    * @param {...Object} [sources] The source objects.
13117    * @param {Function} [customizer] The function to customize assigned values.
13118    * @param {*} [thisArg] The `this` binding of `customizer`.
13119    * @returns {Object} Returns `object`.
13120    * @example
13121    *
13122    * var users = {
13123    *   'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
13124    * };
13125    *
13126    * var ages = {
13127    *   'data': [{ 'age': 36 }, { 'age': 40 }]
13128    * };
13129    *
13130    * _.merge(users, ages);
13131    * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
13132    *
13133    * // using a customizer callback
13134    * var object = {
13135    *   'fruits': ['apple'],
13136    *   'vegetables': ['beet']
13137    * };
13138    *
13139    * var other = {
13140    *   'fruits': ['banana'],
13141    *   'vegetables': ['carrot']
13142    * };
13143    *
13144    * _.merge(object, other, function(a, b) {
13145    *   if (_.isArray(a)) {
13146    *     return a.concat(b);
13147    *   }
13148    * });
13149    * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
13150    */
13151   var merge = createAssigner(baseMerge);
13152
13153   /**
13154    * The opposite of `_.pick`; this method creates an object composed of the
13155    * own and inherited enumerable properties of `object` that are not omitted.
13156    *
13157    * @static
13158    * @memberOf _
13159    * @category Object
13160    * @param {Object} object The source object.
13161    * @param {Function|...(string|string[])} [predicate] The function invoked per
13162    *  iteration or property names to omit, specified as individual property
13163    *  names or arrays of property names.
13164    * @param {*} [thisArg] The `this` binding of `predicate`.
13165    * @returns {Object} Returns the new object.
13166    * @example
13167    *
13168    * var object = { 'user': 'fred', 'age': 40 };
13169    *
13170    * _.omit(object, 'age');
13171    * // => { 'user': 'fred' }
13172    *
13173    * _.omit(object, _.isNumber);
13174    * // => { 'user': 'fred' }
13175    */
13176   var omit = restParam(function(object, props) {
13177     if (object == null) {
13178       return {};
13179     }
13180     if (typeof props[0] != 'function') {
13181       var props = arrayMap(baseFlatten(props), String);
13182       return pickByArray(object, baseDifference(keysIn(object), props));
13183     }
13184     var predicate = bindCallback(props[0], props[1], 3);
13185     return pickByCallback(object, function(value, key, object) {
13186       return !predicate(value, key, object);
13187     });
13188   });
13189
13190   /**
13191    * Creates a two dimensional array of the key-value pairs for `object`,
13192    * e.g. `[[key1, value1], [key2, value2]]`.
13193    *
13194    * @static
13195    * @memberOf _
13196    * @category Object
13197    * @param {Object} object The object to query.
13198    * @returns {Array} Returns the new array of key-value pairs.
13199    * @example
13200    *
13201    * _.pairs({ 'barney': 36, 'fred': 40 });
13202    * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed)
13203    */
13204   function pairs(object) {
13205     object = toObject(object);
13206
13207     var index = -1,
13208         props = keys(object),
13209         length = props.length,
13210         result = Array(length);
13211
13212     while (++index < length) {
13213       var key = props[index];
13214       result[index] = [key, object[key]];
13215     }
13216     return result;
13217   }
13218
13219   /**
13220    * Creates an object composed of the picked `object` properties. Property
13221    * names may be specified as individual arguments or as arrays of property
13222    * names. If `predicate` is provided it is invoked for each property of `object`
13223    * picking the properties `predicate` returns truthy for. The predicate is
13224    * bound to `thisArg` and invoked with three arguments: (value, key, object).
13225    *
13226    * @static
13227    * @memberOf _
13228    * @category Object
13229    * @param {Object} object The source object.
13230    * @param {Function|...(string|string[])} [predicate] The function invoked per
13231    *  iteration or property names to pick, specified as individual property
13232    *  names or arrays of property names.
13233    * @param {*} [thisArg] The `this` binding of `predicate`.
13234    * @returns {Object} Returns the new object.
13235    * @example
13236    *
13237    * var object = { 'user': 'fred', 'age': 40 };
13238    *
13239    * _.pick(object, 'user');
13240    * // => { 'user': 'fred' }
13241    *
13242    * _.pick(object, _.isString);
13243    * // => { 'user': 'fred' }
13244    */
13245   var pick = restParam(function(object, props) {
13246     if (object == null) {
13247       return {};
13248     }
13249     return typeof props[0] == 'function'
13250       ? pickByCallback(object, bindCallback(props[0], props[1], 3))
13251       : pickByArray(object, baseFlatten(props));
13252   });
13253
13254   /**
13255    * Creates an array of the own enumerable property values of `object`.
13256    *
13257    * **Note:** Non-object values are coerced to objects.
13258    *
13259    * @static
13260    * @memberOf _
13261    * @category Object
13262    * @param {Object} object The object to query.
13263    * @returns {Array} Returns the array of property values.
13264    * @example
13265    *
13266    * function Foo() {
13267    *   this.a = 1;
13268    *   this.b = 2;
13269    * }
13270    *
13271    * Foo.prototype.c = 3;
13272    *
13273    * _.values(new Foo);
13274    * // => [1, 2] (iteration order is not guaranteed)
13275    *
13276    * _.values('hi');
13277    * // => ['h', 'i']
13278    */
13279   function values(object) {
13280     return baseValues(object, keys(object));
13281   }
13282
13283   /*------------------------------------------------------------------------*/
13284
13285   /**
13286    * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?",
13287    * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`.
13288    *
13289    * @static
13290    * @memberOf _
13291    * @category String
13292    * @param {string} [string=''] The string to escape.
13293    * @returns {string} Returns the escaped string.
13294    * @example
13295    *
13296    * _.escapeRegExp('[lodash](https://lodash.com/)');
13297    * // => '\[lodash\]\(https:\/\/lodash\.com\/\)'
13298    */
13299   function escapeRegExp(string) {
13300     string = baseToString(string);
13301     return (string && reHasRegExpChars.test(string))
13302       ? string.replace(reRegExpChars, '\\$&')
13303       : string;
13304   }
13305
13306   /*------------------------------------------------------------------------*/
13307
13308   /**
13309    * Creates a function that invokes `func` with the `this` binding of `thisArg`
13310    * and arguments of the created function. If `func` is a property name the
13311    * created callback returns the property value for a given element. If `func`
13312    * is an object the created callback returns `true` for elements that contain
13313    * the equivalent object properties, otherwise it returns `false`.
13314    *
13315    * @static
13316    * @memberOf _
13317    * @alias iteratee
13318    * @category Utility
13319    * @param {*} [func=_.identity] The value to convert to a callback.
13320    * @param {*} [thisArg] The `this` binding of `func`.
13321    * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
13322    * @returns {Function} Returns the callback.
13323    * @example
13324    *
13325    * var users = [
13326    *   { 'user': 'barney', 'age': 36 },
13327    *   { 'user': 'fred',   'age': 40 }
13328    * ];
13329    *
13330    * // wrap to create custom callback shorthands
13331    * _.callback = _.wrap(_.callback, function(callback, func, thisArg) {
13332    *   var match = /^(.+?)__([gl]t)(.+)$/.exec(func);
13333    *   if (!match) {
13334    *     return callback(func, thisArg);
13335    *   }
13336    *   return function(object) {
13337    *     return match[2] == 'gt'
13338    *       ? object[match[1]] > match[3]
13339    *       : object[match[1]] < match[3];
13340    *   };
13341    * });
13342    *
13343    * _.filter(users, 'age__gt36');
13344    * // => [{ 'user': 'fred', 'age': 40 }]
13345    */
13346   function callback(func, thisArg, guard) {
13347     if (guard && isIterateeCall(func, thisArg, guard)) {
13348       thisArg = null;
13349     }
13350     return isObjectLike(func)
13351       ? matches(func)
13352       : baseCallback(func, thisArg);
13353   }
13354
13355   /**
13356    * Creates a function that returns `value`.
13357    *
13358    * @static
13359    * @memberOf _
13360    * @category Utility
13361    * @param {*} value The value to return from the new function.
13362    * @returns {Function} Returns the new function.
13363    * @example
13364    *
13365    * var object = { 'user': 'fred' };
13366    * var getter = _.constant(object);
13367    *
13368    * getter() === object;
13369    * // => true
13370    */
13371   function constant(value) {
13372     return function() {
13373       return value;
13374     };
13375   }
13376
13377   /**
13378    * This method returns the first argument provided to it.
13379    *
13380    * @static
13381    * @memberOf _
13382    * @category Utility
13383    * @param {*} value Any value.
13384    * @returns {*} Returns `value`.
13385    * @example
13386    *
13387    * var object = { 'user': 'fred' };
13388    *
13389    * _.identity(object) === object;
13390    * // => true
13391    */
13392   function identity(value) {
13393     return value;
13394   }
13395
13396   /**
13397    * Creates a function that performs a deep comparison between a given object
13398    * and `source`, returning `true` if the given object has equivalent property
13399    * values, else `false`.
13400    *
13401    * **Note:** This method supports comparing arrays, booleans, `Date` objects,
13402    * numbers, `Object` objects, regexes, and strings. Objects are compared by
13403    * their own, not inherited, enumerable properties. For comparing a single
13404    * own or inherited property value see `_.matchesProperty`.
13405    *
13406    * @static
13407    * @memberOf _
13408    * @category Utility
13409    * @param {Object} source The object of property values to match.
13410    * @returns {Function} Returns the new function.
13411    * @example
13412    *
13413    * var users = [
13414    *   { 'user': 'barney', 'age': 36, 'active': true },
13415    *   { 'user': 'fred',   'age': 40, 'active': false }
13416    * ];
13417    *
13418    * _.filter(users, _.matches({ 'age': 40, 'active': false }));
13419    * // => [{ 'user': 'fred', 'age': 40, 'active': false }]
13420    */
13421   function matches(source) {
13422     return baseMatches(baseClone(source, true));
13423   }
13424
13425   /**
13426    * Adds all own enumerable function properties of a source object to the
13427    * destination object. If `object` is a function then methods are added to
13428    * its prototype as well.
13429    *
13430    * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
13431    * avoid conflicts caused by modifying the original.
13432    *
13433    * @static
13434    * @memberOf _
13435    * @category Utility
13436    * @param {Function|Object} [object=lodash] The destination object.
13437    * @param {Object} source The object of functions to add.
13438    * @param {Object} [options] The options object.
13439    * @param {boolean} [options.chain=true] Specify whether the functions added
13440    *  are chainable.
13441    * @returns {Function|Object} Returns `object`.
13442    * @example
13443    *
13444    * function vowels(string) {
13445    *   return _.filter(string, function(v) {
13446    *     return /[aeiou]/i.test(v);
13447    *   });
13448    * }
13449    *
13450    * _.mixin({ 'vowels': vowels });
13451    * _.vowels('fred');
13452    * // => ['e']
13453    *
13454    * _('fred').vowels().value();
13455    * // => ['e']
13456    *
13457    * _.mixin({ 'vowels': vowels }, { 'chain': false });
13458    * _('fred').vowels();
13459    * // => ['e']
13460    */
13461   function mixin(object, source, options) {
13462     if (options == null) {
13463       var isObj = isObject(source),
13464           props = isObj ? keys(source) : null,
13465           methodNames = (props && props.length) ? baseFunctions(source, props) : null;
13466
13467       if (!(methodNames ? methodNames.length : isObj)) {
13468         methodNames = false;
13469         options = source;
13470         source = object;
13471         object = this;
13472       }
13473     }
13474     if (!methodNames) {
13475       methodNames = baseFunctions(source, keys(source));
13476     }
13477     var chain = true,
13478         index = -1,
13479         isFunc = isFunction(object),
13480         length = methodNames.length;
13481
13482     if (options === false) {
13483       chain = false;
13484     } else if (isObject(options) && 'chain' in options) {
13485       chain = options.chain;
13486     }
13487     while (++index < length) {
13488       var methodName = methodNames[index],
13489           func = source[methodName];
13490
13491       object[methodName] = func;
13492       if (isFunc) {
13493         object.prototype[methodName] = (function(func) {
13494           return function() {
13495             var chainAll = this.__chain__;
13496             if (chain || chainAll) {
13497               var result = object(this.__wrapped__),
13498                   actions = result.__actions__ = arrayCopy(this.__actions__);
13499
13500               actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
13501               result.__chain__ = chainAll;
13502               return result;
13503             }
13504             var args = [this.value()];
13505             push.apply(args, arguments);
13506             return func.apply(object, args);
13507           };
13508         }(func));
13509       }
13510     }
13511     return object;
13512   }
13513
13514   /**
13515    * A no-operation function that returns `undefined` regardless of the
13516    * arguments it receives.
13517    *
13518    * @static
13519    * @memberOf _
13520    * @category Utility
13521    * @example
13522    *
13523    * var object = { 'user': 'fred' };
13524    *
13525    * _.noop(object) === undefined;
13526    * // => true
13527    */
13528   function noop() {
13529     // No operation performed.
13530   }
13531
13532   /**
13533    * Creates a function that returns the property value at `path` on a
13534    * given object.
13535    *
13536    * @static
13537    * @memberOf _
13538    * @category Utility
13539    * @param {Array|string} path The path of the property to get.
13540    * @returns {Function} Returns the new function.
13541    * @example
13542    *
13543    * var objects = [
13544    *   { 'a': { 'b': { 'c': 2 } } },
13545    *   { 'a': { 'b': { 'c': 1 } } }
13546    * ];
13547    *
13548    * _.map(objects, _.property('a.b.c'));
13549    * // => [2, 1]
13550    *
13551    * _.pluck(_.sortBy(objects, _.property(['a', 'b', 'c'])), 'a.b.c');
13552    * // => [1, 2]
13553    */
13554   function property(path) {
13555     return isKey(path) ? baseProperty(path) : basePropertyDeep(path);
13556   }
13557
13558   /*------------------------------------------------------------------------*/
13559
13560   // Ensure wrappers are instances of `baseLodash`.
13561   lodash.prototype = baseLodash.prototype;
13562
13563   LodashWrapper.prototype = baseCreate(baseLodash.prototype);
13564   LodashWrapper.prototype.constructor = LodashWrapper;
13565
13566   LazyWrapper.prototype = baseCreate(baseLodash.prototype);
13567   LazyWrapper.prototype.constructor = LazyWrapper;
13568
13569   // Add functions to the `Set` cache.
13570   SetCache.prototype.push = cachePush;
13571
13572   // Add functions that return wrapped values when chaining.
13573   lodash.assign = assign;
13574   lodash.bind = bind;
13575   lodash.callback = callback;
13576   lodash.chain = chain;
13577   lodash.chunk = chunk;
13578   lodash.compact = compact;
13579   lodash.constant = constant;
13580   lodash.debounce = debounce;
13581   lodash.difference = difference;
13582   lodash.filter = filter;
13583   lodash.flatten = flatten;
13584   lodash.forEach = forEach;
13585   lodash.forOwn = forOwn;
13586   lodash.groupBy = groupBy;
13587   lodash.intersection = intersection;
13588   lodash.keys = keys;
13589   lodash.keysIn = keysIn;
13590   lodash.map = map;
13591   lodash.matches = matches;
13592   lodash.merge = merge;
13593   lodash.mixin = mixin;
13594   lodash.omit = omit;
13595   lodash.pairs = pairs;
13596   lodash.pick = pick;
13597   lodash.pluck = pluck;
13598   lodash.property = property;
13599   lodash.reject = reject;
13600   lodash.restParam = restParam;
13601   lodash.tap = tap;
13602   lodash.throttle = throttle;
13603   lodash.thru = thru;
13604   lodash.toPlainObject = toPlainObject;
13605   lodash.union = union;
13606   lodash.uniq = uniq;
13607   lodash.values = values;
13608   lodash.without = without;
13609
13610   // Add aliases.
13611   lodash.collect = map;
13612   lodash.each = forEach;
13613   lodash.extend = assign;
13614   lodash.iteratee = callback;
13615   lodash.select = filter;
13616   lodash.unique = uniq;
13617
13618   // Add functions to `lodash.prototype`.
13619   mixin(lodash, lodash);
13620
13621   /*------------------------------------------------------------------------*/
13622
13623   // Add functions that return unwrapped values when chaining.
13624   lodash.clone = clone;
13625   lodash.cloneDeep = cloneDeep;
13626   lodash.escapeRegExp = escapeRegExp;
13627   lodash.every = every;
13628   lodash.find = find;
13629   lodash.first = first;
13630   lodash.identity = identity;
13631   lodash.includes = includes;
13632   lodash.indexOf = indexOf;
13633   lodash.isArguments = isArguments;
13634   lodash.isArray = isArray;
13635   lodash.isEmpty = isEmpty;
13636   lodash.isEqual = isEqual;
13637   lodash.isFunction = isFunction;
13638   lodash.isNative = isNative;
13639   lodash.isObject = isObject;
13640   lodash.isPlainObject = isPlainObject;
13641   lodash.isString = isString;
13642   lodash.isTypedArray = isTypedArray;
13643   lodash.last = last;
13644   lodash.noop = noop;
13645   lodash.now = now;
13646   lodash.reduce = reduce;
13647   lodash.some = some;
13648
13649   // Add aliases.
13650   lodash.all = every;
13651   lodash.any = some;
13652   lodash.contains = includes;
13653   lodash.eq = isEqual;
13654   lodash.detect = find;
13655   lodash.foldl = reduce;
13656   lodash.head = first;
13657   lodash.include = includes;
13658   lodash.inject = reduce;
13659
13660   mixin(lodash, (function() {
13661     var source = {};
13662     baseForOwn(lodash, function(func, methodName) {
13663       if (!lodash.prototype[methodName]) {
13664         source[methodName] = func;
13665       }
13666     });
13667     return source;
13668   }()), false);
13669
13670   /*------------------------------------------------------------------------*/
13671
13672   lodash.prototype.sample = function(n) {
13673     if (!this.__chain__ && n == null) {
13674       return sample(this.value());
13675     }
13676     return this.thru(function(value) {
13677       return sample(value, n);
13678     });
13679   };
13680
13681   /*------------------------------------------------------------------------*/
13682
13683   /**
13684    * The semantic version number.
13685    *
13686    * @static
13687    * @memberOf _
13688    * @type string
13689    */
13690   lodash.VERSION = VERSION;
13691
13692   // Assign default placeholders.
13693   bind.placeholder = lodash;
13694
13695   // Add `LazyWrapper` methods that accept an `iteratee` value.
13696   arrayEach(['dropWhile', 'filter', 'map', 'takeWhile'], function(methodName, type) {
13697     var isFilter = type != LAZY_MAP_FLAG,
13698         isDropWhile = type == LAZY_DROP_WHILE_FLAG;
13699
13700     LazyWrapper.prototype[methodName] = function(iteratee, thisArg) {
13701       var filtered = this.__filtered__,
13702           result = (filtered && isDropWhile) ? new LazyWrapper(this) : this.clone(),
13703           iteratees = result.__iteratees__ || (result.__iteratees__ = []);
13704
13705       iteratees.push({
13706         'done': false,
13707         'count': 0,
13708         'index': 0,
13709         'iteratee': getCallback(iteratee, thisArg, 1),
13710         'limit': -1,
13711         'type': type
13712       });
13713
13714       result.__filtered__ = filtered || isFilter;
13715       return result;
13716     };
13717   });
13718
13719   // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
13720   arrayEach(['drop', 'take'], function(methodName, index) {
13721     var whileName = methodName + 'While';
13722
13723     LazyWrapper.prototype[methodName] = function(n) {
13724       var filtered = this.__filtered__,
13725           result = (filtered && !index) ? this.dropWhile() : this.clone();
13726
13727       n = n == null ? 1 : nativeMax(floor(n) || 0, 0);
13728       if (filtered) {
13729         if (index) {
13730           result.__takeCount__ = nativeMin(result.__takeCount__, n);
13731         } else {
13732           last(result.__iteratees__).limit = n;
13733         }
13734       } else {
13735         var views = result.__views__ || (result.__views__ = []);
13736         views.push({ 'size': n, 'type': methodName + (result.__dir__ < 0 ? 'Right' : '') });
13737       }
13738       return result;
13739     };
13740
13741     LazyWrapper.prototype[methodName + 'Right'] = function(n) {
13742       return this.reverse()[methodName](n).reverse();
13743     };
13744
13745     LazyWrapper.prototype[methodName + 'RightWhile'] = function(predicate, thisArg) {
13746       return this.reverse()[whileName](predicate, thisArg).reverse();
13747     };
13748   });
13749
13750   // Add `LazyWrapper` methods for `_.first` and `_.last`.
13751   arrayEach(['first', 'last'], function(methodName, index) {
13752     var takeName = 'take' + (index ? 'Right' : '');
13753
13754     LazyWrapper.prototype[methodName] = function() {
13755       return this[takeName](1).value()[0];
13756     };
13757   });
13758
13759   // Add `LazyWrapper` methods for `_.initial` and `_.rest`.
13760   arrayEach(['initial', 'rest'], function(methodName, index) {
13761     var dropName = 'drop' + (index ? '' : 'Right');
13762
13763     LazyWrapper.prototype[methodName] = function() {
13764       return this[dropName](1);
13765     };
13766   });
13767
13768   // Add `LazyWrapper` methods for `_.pluck` and `_.where`.
13769   arrayEach(['pluck', 'where'], function(methodName, index) {
13770     var operationName = index ? 'filter' : 'map',
13771         createCallback = index ? baseMatches : property;
13772
13773     LazyWrapper.prototype[methodName] = function(value) {
13774       return this[operationName](createCallback(value));
13775     };
13776   });
13777
13778   LazyWrapper.prototype.compact = function() {
13779     return this.filter(identity);
13780   };
13781
13782   LazyWrapper.prototype.reject = function(predicate, thisArg) {
13783     predicate = getCallback(predicate, thisArg, 1);
13784     return this.filter(function(value) {
13785       return !predicate(value);
13786     });
13787   };
13788
13789   LazyWrapper.prototype.slice = function(start, end) {
13790     start = start == null ? 0 : (+start || 0);
13791
13792     var result = this;
13793     if (start < 0) {
13794       result = this.takeRight(-start);
13795     } else if (start) {
13796       result = this.drop(start);
13797     }
13798     if (end !== undefined) {
13799       end = (+end || 0);
13800       result = end < 0 ? result.dropRight(-end) : result.take(end - start);
13801     }
13802     return result;
13803   };
13804
13805   LazyWrapper.prototype.toArray = function() {
13806     return this.drop(0);
13807   };
13808
13809   // Add `LazyWrapper` methods to `lodash.prototype`.
13810   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
13811     var lodashFunc = lodash[methodName];
13812     if (!lodashFunc) {
13813       return;
13814     }
13815     var checkIteratee = /^(?:filter|map|reject)|While$/.test(methodName),
13816         retUnwrapped = /^(?:first|last)$/.test(methodName);
13817
13818     lodash.prototype[methodName] = function() {
13819       var args = arguments,
13820           chainAll = this.__chain__,
13821           value = this.__wrapped__,
13822           isHybrid = !!this.__actions__.length,
13823           isLazy = value instanceof LazyWrapper,
13824           iteratee = args[0],
13825           useLazy = isLazy || isArray(value);
13826
13827       if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
13828         // avoid lazy use if the iteratee has a "length" value other than `1`
13829         isLazy = useLazy = false;
13830       }
13831       var onlyLazy = isLazy && !isHybrid;
13832       if (retUnwrapped && !chainAll) {
13833         return onlyLazy
13834           ? func.call(value)
13835           : lodashFunc.call(lodash, this.value());
13836       }
13837       var interceptor = function(value) {
13838         var otherArgs = [value];
13839         push.apply(otherArgs, args);
13840         return lodashFunc.apply(lodash, otherArgs);
13841       };
13842       if (useLazy) {
13843         var wrapper = onlyLazy ? value : new LazyWrapper(this),
13844             result = func.apply(wrapper, args);
13845
13846         if (!retUnwrapped && (isHybrid || result.__actions__)) {
13847           var actions = result.__actions__ || (result.__actions__ = []);
13848           actions.push({ 'func': thru, 'args': [interceptor], 'thisArg': lodash });
13849         }
13850         return new LodashWrapper(result, chainAll);
13851       }
13852       return this.thru(interceptor);
13853     };
13854   });
13855
13856   // Add `Array` and `String` methods to `lodash.prototype`.
13857   arrayEach(['concat', 'join', 'pop', 'push', 'replace', 'shift', 'sort', 'splice', 'split', 'unshift'], function(methodName) {
13858     var protoFunc = (/^(?:replace|split)$/.test(methodName) ? stringProto : arrayProto)[methodName],
13859         chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
13860         fixObjects = !support.spliceObjects && /^(?:pop|shift|splice)$/.test(methodName),
13861         retUnwrapped = /^(?:join|pop|replace|shift)$/.test(methodName);
13862
13863     // Avoid array-like object bugs with `Array#shift` and `Array#splice` in
13864     // IE < 9, Firefox < 10, and RingoJS.
13865     var func = !fixObjects ? protoFunc : function() {
13866       var result = protoFunc.apply(this, arguments);
13867       if (this.length === 0) {
13868         delete this[0];
13869       }
13870       return result;
13871     };
13872
13873     lodash.prototype[methodName] = function() {
13874       var args = arguments;
13875       if (retUnwrapped && !this.__chain__) {
13876         return func.apply(this.value(), args);
13877       }
13878       return this[chainName](function(value) {
13879         return func.apply(value, args);
13880       });
13881     };
13882   });
13883
13884   // Map minified function names to their real names.
13885   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
13886     var lodashFunc = lodash[methodName];
13887     if (lodashFunc) {
13888       var key = lodashFunc.name,
13889           names = realNames[key] || (realNames[key] = []);
13890
13891       names.push({ 'name': methodName, 'func': lodashFunc });
13892     }
13893   });
13894
13895   realNames[createHybridWrapper(null, BIND_KEY_FLAG).name] = [{ 'name': 'wrapper', 'func': null }];
13896
13897   // Add functions to the lazy wrapper.
13898   LazyWrapper.prototype.clone = lazyClone;
13899   LazyWrapper.prototype.reverse = lazyReverse;
13900   LazyWrapper.prototype.value = lazyValue;
13901
13902   // Add chaining functions to the `lodash` wrapper.
13903   lodash.prototype.chain = wrapperChain;
13904   lodash.prototype.commit = wrapperCommit;
13905   lodash.prototype.plant = wrapperPlant;
13906   lodash.prototype.reverse = wrapperReverse;
13907   lodash.prototype.toString = wrapperToString;
13908   lodash.prototype.run = lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
13909
13910   // Add function aliases to the `lodash` wrapper.
13911   lodash.prototype.collect = lodash.prototype.map;
13912   lodash.prototype.head = lodash.prototype.first;
13913   lodash.prototype.select = lodash.prototype.filter;
13914   lodash.prototype.tail = lodash.prototype.rest;
13915
13916   /*--------------------------------------------------------------------------*/
13917
13918   if (freeExports && freeModule) {
13919     // Export for Node.js or RingoJS.
13920     if (moduleExports) {
13921       (freeModule.exports = lodash)._ = lodash;
13922     }
13923   }
13924   else {
13925     // Export for a browser or Rhino.
13926     root._ = lodash;
13927   }
13928 }.call(this));
13929 (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;
13930 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){
13931 'use strict';
13932
13933 var ohauth = require('ohauth'),
13934     xtend = require('xtend'),
13935     store = require('store');
13936
13937 // # osm-auth
13938 //
13939 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
13940 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
13941 // does not support custom headers, which this uses everywhere.
13942 module.exports = function(o) {
13943
13944     var oauth = {};
13945
13946     // authenticated users will also have a request token secret, but it's
13947     // not used in transactions with the server
13948     oauth.authenticated = function() {
13949         return !!(token('oauth_token') && token('oauth_token_secret'));
13950     };
13951
13952     oauth.logout = function() {
13953         token('oauth_token', '');
13954         token('oauth_token_secret', '');
13955         token('oauth_request_token_secret', '');
13956         return oauth;
13957     };
13958
13959     // TODO: detect lack of click event
13960     oauth.authenticate = function(callback) {
13961         if (oauth.authenticated()) return callback();
13962
13963         oauth.logout();
13964
13965         // ## Getting a request token
13966         var params = timenonce(getAuth(o)),
13967             url = o.url + '/oauth/request_token',
13968             timer;
13969
13970         params.oauth_signature = ohauth.signature(
13971             o.oauth_secret, '',
13972             ohauth.baseString('POST', url, params));
13973
13974         if (!o.singlepage) {
13975             // Create a 600x550 popup window in the center of the screen
13976             var w = 600, h = 550,
13977                 settings = [
13978                     ['width', w], ['height', h],
13979                     ['left', screen.width / 2 - w / 2],
13980                     ['top', screen.height / 2 - h / 2]].map(function(x) {
13981                         return x.join('=');
13982                     }).join(','),
13983                 popup = window.open('about:blank', 'oauth_window', settings);
13984
13985
13986             timer = setInterval(function() {
13987                 if (popup.closed) {
13988                     o.done();
13989                     clearInterval(timer);
13990                     callback('not authenticated', null);
13991                 }
13992             }, 100);
13993         }
13994
13995         // Request a request token. When this is complete, the popup
13996         // window is redirected to OSM's authorization page.
13997         ohauth.xhr('POST', url, params, null, {}, reqTokenDone);
13998         o.loading();
13999
14000         function reqTokenDone(err, xhr) {
14001             o.done();
14002             if (err) {
14003                 if (timer) clearInterval(timer);
14004                 return callback(err);
14005             }
14006             var resp = ohauth.stringQs(xhr.response);
14007             token('oauth_request_token_secret', resp.oauth_token_secret);
14008             var authorize_url = o.url + '/oauth/authorize?' + ohauth.qsString({
14009                 oauth_token: resp.oauth_token,
14010                 oauth_callback: location.href.replace('index.html', '')
14011                     .replace(/#.*/, '') + o.landing
14012             });
14013
14014             if (o.singlepage) {
14015                 location.href = authorize_url;
14016             } else {
14017                 popup.location = authorize_url;
14018             }
14019         }
14020
14021         // Called by a function in a landing page, in the popup window. The
14022         // window closes itself.
14023         window.authComplete = function(token) {
14024             if (timer) clearInterval(timer);
14025             var oauth_token = ohauth.stringQs(token.split('?')[1]);
14026             get_access_token(oauth_token.oauth_token);
14027             delete window.authComplete;
14028         };
14029
14030         // ## Getting an request token
14031         //
14032         // At this point we have an `oauth_token`, brought in from a function
14033         // call on a landing page popup.
14034         function get_access_token(oauth_token) {
14035             var url = o.url + '/oauth/access_token',
14036                 params = timenonce(getAuth(o)),
14037                 request_token_secret = token('oauth_request_token_secret');
14038             params.oauth_token = oauth_token;
14039             params.oauth_signature = ohauth.signature(
14040                 o.oauth_secret,
14041                 request_token_secret,
14042                 ohauth.baseString('POST', url, params));
14043
14044             // ## Getting an access token
14045             //
14046             // The final token required for authentication. At this point
14047             // we have a `request token secret`
14048             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
14049             o.loading();
14050         }
14051
14052         function accessTokenDone(err, xhr) {
14053             o.done();
14054             if (timer) clearInterval(timer);
14055             if (err) return callback(err);
14056             var access_token = ohauth.stringQs(xhr.response);
14057             token('oauth_token', access_token.oauth_token);
14058             token('oauth_token_secret', access_token.oauth_token_secret);
14059             callback(null, oauth);
14060         }
14061     };
14062
14063     oauth.bootstrapToken = function(oauth_token, callback) {
14064         // ## Getting an request token
14065         // At this point we have an `oauth_token`, brought in from a function
14066         // call on a landing page popup.
14067         function get_access_token(oauth_token) {
14068             var url = o.url + '/oauth/access_token',
14069                 params = timenonce(getAuth(o)),
14070                 request_token_secret = token('oauth_request_token_secret');
14071             params.oauth_token = oauth_token;
14072             params.oauth_signature = ohauth.signature(
14073                 o.oauth_secret,
14074                 request_token_secret,
14075                 ohauth.baseString('POST', url, params));
14076
14077             // ## Getting an access token
14078             // The final token required for authentication. At this point
14079             // we have a `request token secret`
14080             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
14081             o.loading();
14082         }
14083
14084         function accessTokenDone(err, xhr) {
14085             o.done();
14086             if (err) return callback(err);
14087             var access_token = ohauth.stringQs(xhr.response);
14088             token('oauth_token', access_token.oauth_token);
14089             token('oauth_token_secret', access_token.oauth_token_secret);
14090             callback(null, oauth);
14091         }
14092
14093         get_access_token(oauth_token);
14094     };
14095
14096     // # xhr
14097     //
14098     // A single XMLHttpRequest wrapper that does authenticated calls if the
14099     // user has logged in.
14100     oauth.xhr = function(options, callback) {
14101         if (!oauth.authenticated()) {
14102             if (o.auto) return oauth.authenticate(run);
14103             else return callback('not authenticated', null);
14104         } else return run();
14105
14106         function run() {
14107             var params = timenonce(getAuth(o)),
14108                 url = o.url + options.path,
14109                 oauth_token_secret = token('oauth_token_secret');
14110
14111             // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
14112             if ((!options.options || !options.options.header ||
14113                 options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
14114                 options.content) {
14115                 params = xtend(params, ohauth.stringQs(options.content));
14116             }
14117
14118             params.oauth_token = token('oauth_token');
14119             params.oauth_signature = ohauth.signature(
14120                 o.oauth_secret,
14121                 oauth_token_secret,
14122                 ohauth.baseString(options.method, url, params));
14123
14124             ohauth.xhr(options.method,
14125                 url, params, options.content, options.options, done);
14126         }
14127
14128         function done(err, xhr) {
14129             if (err) return callback(err);
14130             else if (xhr.responseXML) return callback(err, xhr.responseXML);
14131             else return callback(err, xhr.response);
14132         }
14133     };
14134
14135     // pre-authorize this object, if we can just get a token and token_secret
14136     // from the start
14137     oauth.preauth = function(c) {
14138         if (!c) return;
14139         if (c.oauth_token) token('oauth_token', c.oauth_token);
14140         if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
14141         return oauth;
14142     };
14143
14144     oauth.options = function(_) {
14145         if (!arguments.length) return o;
14146
14147         o = _;
14148
14149         o.url = o.url || 'http://www.openstreetmap.org';
14150         o.landing = o.landing || 'land.html';
14151
14152         o.singlepage = o.singlepage || false;
14153
14154         // Optional loading and loading-done functions for nice UI feedback.
14155         // by default, no-ops
14156         o.loading = o.loading || function() {};
14157         o.done = o.done || function() {};
14158
14159         return oauth.preauth(o);
14160     };
14161
14162     // 'stamp' an authentication object from `getAuth()`
14163     // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
14164     // and timestamp
14165     function timenonce(o) {
14166         o.oauth_timestamp = ohauth.timestamp();
14167         o.oauth_nonce = ohauth.nonce();
14168         return o;
14169     }
14170
14171     // get/set tokens. These are prefixed with the base URL so that `osm-auth`
14172     // can be used with multiple APIs and the keys in `localStorage`
14173     // will not clash
14174     var token;
14175
14176     if (store.enabled) {
14177         token = function (x, y) {
14178             if (arguments.length === 1) return store.get(o.url + x);
14179             else if (arguments.length === 2) return store.set(o.url + x, y);
14180         };
14181     } else {
14182         var storage = {};
14183         token = function (x, y) {
14184             if (arguments.length === 1) return storage[o.url + x];
14185             else if (arguments.length === 2) return storage[o.url + x] = y;
14186         };
14187     }
14188
14189     // Get an authentication object. If you just add and remove properties
14190     // from a single object, you'll need to use `delete` to make sure that
14191     // it doesn't contain undesired properties for authentication
14192     function getAuth(o) {
14193         return {
14194             oauth_consumer_key: o.oauth_consumer_key,
14195             oauth_signature_method: "HMAC-SHA1"
14196         };
14197     }
14198
14199     // potentially pre-authorize
14200     oauth.options(o);
14201
14202     return oauth;
14203 };
14204
14205 },{"ohauth":2,"store":3,"xtend":4}],3:[function(require,module,exports){
14206 (function(global){;(function(win){
14207         var store = {},
14208                 doc = win.document,
14209                 localStorageName = 'localStorage',
14210                 storage
14211
14212         store.disabled = false
14213         store.set = function(key, value) {}
14214         store.get = function(key) {}
14215         store.remove = function(key) {}
14216         store.clear = function() {}
14217         store.transact = function(key, defaultVal, transactionFn) {
14218                 var val = store.get(key)
14219                 if (transactionFn == null) {
14220                         transactionFn = defaultVal
14221                         defaultVal = null
14222                 }
14223                 if (typeof val == 'undefined') { val = defaultVal || {} }
14224                 transactionFn(val)
14225                 store.set(key, val)
14226         }
14227         store.getAll = function() {}
14228         store.forEach = function() {}
14229
14230         store.serialize = function(value) {
14231                 return JSON.stringify(value)
14232         }
14233         store.deserialize = function(value) {
14234                 if (typeof value != 'string') { return undefined }
14235                 try { return JSON.parse(value) }
14236                 catch(e) { return value || undefined }
14237         }
14238
14239         // Functions to encapsulate questionable FireFox 3.6.13 behavior
14240         // when about.config::dom.storage.enabled === false
14241         // See https://github.com/marcuswestin/store.js/issues#issue/13
14242         function isLocalStorageNameSupported() {
14243                 try { return (localStorageName in win && win[localStorageName]) }
14244                 catch(err) { return false }
14245         }
14246
14247         if (isLocalStorageNameSupported()) {
14248                 storage = win[localStorageName]
14249                 store.set = function(key, val) {
14250                         if (val === undefined) { return store.remove(key) }
14251                         storage.setItem(key, store.serialize(val))
14252                         return val
14253                 }
14254                 store.get = function(key) { return store.deserialize(storage.getItem(key)) }
14255                 store.remove = function(key) { storage.removeItem(key) }
14256                 store.clear = function() { storage.clear() }
14257                 store.getAll = function() {
14258                         var ret = {}
14259                         store.forEach(function(key, val) {
14260                                 ret[key] = val
14261                         })
14262                         return ret
14263                 }
14264                 store.forEach = function(callback) {
14265                         for (var i=0; i<storage.length; i++) {
14266                                 var key = storage.key(i)
14267                                 callback(key, store.get(key))
14268                         }
14269                 }
14270         } else if (doc.documentElement.addBehavior) {
14271                 var storageOwner,
14272                         storageContainer
14273                 // Since #userData storage applies only to specific paths, we need to
14274                 // somehow link our data to a specific path.  We choose /favicon.ico
14275                 // as a pretty safe option, since all browsers already make a request to
14276                 // this URL anyway and being a 404 will not hurt us here.  We wrap an
14277                 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
14278                 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
14279                 // since the iframe access rules appear to allow direct access and
14280                 // manipulation of the document element, even for a 404 page.  This
14281                 // document can be used instead of the current document (which would
14282                 // have been limited to the current path) to perform #userData storage.
14283                 try {
14284                         storageContainer = new ActiveXObject('htmlfile')
14285                         storageContainer.open()
14286                         storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></iframe>')
14287                         storageContainer.close()
14288                         storageOwner = storageContainer.w.frames[0].document
14289                         storage = storageOwner.createElement('div')
14290                 } catch(e) {
14291                         // somehow ActiveXObject instantiation failed (perhaps some special
14292                         // security settings or otherwse), fall back to per-path storage
14293                         storage = doc.createElement('div')
14294                         storageOwner = doc.body
14295                 }
14296                 function withIEStorage(storeFunction) {
14297                         return function() {
14298                                 var args = Array.prototype.slice.call(arguments, 0)
14299                                 args.unshift(storage)
14300                                 // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
14301                                 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
14302                                 storageOwner.appendChild(storage)
14303                                 storage.addBehavior('#default#userData')
14304                                 storage.load(localStorageName)
14305                                 var result = storeFunction.apply(store, args)
14306                                 storageOwner.removeChild(storage)
14307                                 return result
14308                         }
14309                 }
14310
14311                 // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
14312                 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
14313                 function ieKeyFix(key) {
14314                         return key.replace(forbiddenCharsRegex, '___')
14315                 }
14316                 store.set = withIEStorage(function(storage, key, val) {
14317                         key = ieKeyFix(key)
14318                         if (val === undefined) { return store.remove(key) }
14319                         storage.setAttribute(key, store.serialize(val))
14320                         storage.save(localStorageName)
14321                         return val
14322                 })
14323                 store.get = withIEStorage(function(storage, key) {
14324                         key = ieKeyFix(key)
14325                         return store.deserialize(storage.getAttribute(key))
14326                 })
14327                 store.remove = withIEStorage(function(storage, key) {
14328                         key = ieKeyFix(key)
14329                         storage.removeAttribute(key)
14330                         storage.save(localStorageName)
14331                 })
14332                 store.clear = withIEStorage(function(storage) {
14333                         var attributes = storage.XMLDocument.documentElement.attributes
14334                         storage.load(localStorageName)
14335                         for (var i=0, attr; attr=attributes[i]; i++) {
14336                                 storage.removeAttribute(attr.name)
14337                         }
14338                         storage.save(localStorageName)
14339                 })
14340                 store.getAll = function(storage) {
14341                         var ret = {}
14342                         store.forEach(function(key, val) {
14343                                 ret[key] = val
14344                         })
14345                         return ret
14346                 }
14347                 store.forEach = withIEStorage(function(storage, callback) {
14348                         var attributes = storage.XMLDocument.documentElement.attributes
14349                         for (var i=0, attr; attr=attributes[i]; ++i) {
14350                                 callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
14351                         }
14352                 })
14353         }
14354
14355         try {
14356                 var testKey = '__storejs__'
14357                 store.set(testKey, testKey)
14358                 if (store.get(testKey) != testKey) { store.disabled = true }
14359                 store.remove(testKey)
14360         } catch(e) {
14361                 store.disabled = true
14362         }
14363         store.enabled = !store.disabled
14364
14365         if (typeof module != 'undefined' && module.exports) { module.exports = store }
14366         else if (typeof define === 'function' && define.amd) { define(store) }
14367         else { win.store = store }
14368
14369 })(this.window || global);
14370
14371 })(window)
14372 },{}],5:[function(require,module,exports){
14373 module.exports = hasKeys
14374
14375 function hasKeys(source) {
14376     return source !== null &&
14377         (typeof source === "object" ||
14378         typeof source === "function")
14379 }
14380
14381 },{}],4:[function(require,module,exports){
14382 var Keys = require("object-keys")
14383 var hasKeys = require("./has-keys")
14384
14385 module.exports = extend
14386
14387 function extend() {
14388     var target = {}
14389
14390     for (var i = 0; i < arguments.length; i++) {
14391         var source = arguments[i]
14392
14393         if (!hasKeys(source)) {
14394             continue
14395         }
14396
14397         var keys = Keys(source)
14398
14399         for (var j = 0; j < keys.length; j++) {
14400             var name = keys[j]
14401             target[name] = source[name]
14402         }
14403     }
14404
14405     return target
14406 }
14407
14408 },{"./has-keys":5,"object-keys":6}],7:[function(require,module,exports){
14409 (function(global){/**
14410  * jsHashes - A fast and independent hashing library pure JavaScript implemented (ES3 compliant) for both server and client side
14411  *
14412  * @class Hashes
14413  * @author Tomas Aparicio <tomas@rijndael-project.com>
14414  * @license New BSD (see LICENSE file)
14415  * @version 1.0.4
14416  *
14417  * Algorithms specification:
14418  *
14419  * MD5 <http://www.ietf.org/rfc/rfc1321.txt>
14420  * RIPEMD-160 <http://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
14421  * SHA1   <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14422  * SHA256 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14423  * SHA512 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14424  * HMAC <http://www.ietf.org/rfc/rfc2104.txt>
14425  *
14426  */
14427 (function(){
14428   var Hashes;
14429
14430   // private helper methods
14431   function utf8Encode(str) {
14432     var  x, y, output = '', i = -1, l;
14433
14434     if (str && str.length) {
14435       l = str.length;
14436       while ((i+=1) < l) {
14437         /* Decode utf-16 surrogate pairs */
14438         x = str.charCodeAt(i);
14439         y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
14440         if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
14441             x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
14442             i += 1;
14443         }
14444         /* Encode output as utf-8 */
14445         if (x <= 0x7F) {
14446             output += String.fromCharCode(x);
14447         } else if (x <= 0x7FF) {
14448             output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
14449                         0x80 | ( x & 0x3F));
14450         } else if (x <= 0xFFFF) {
14451             output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
14452                         0x80 | ((x >>> 6 ) & 0x3F),
14453                         0x80 | ( x & 0x3F));
14454         } else if (x <= 0x1FFFFF) {
14455             output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
14456                         0x80 | ((x >>> 12) & 0x3F),
14457                         0x80 | ((x >>> 6 ) & 0x3F),
14458                         0x80 | ( x & 0x3F));
14459         }
14460       }
14461     }
14462     return output;
14463   }
14464
14465   function utf8Decode(str) {
14466     var i, ac, c1, c2, c3, arr = [], l;
14467     i = ac = c1 = c2 = c3 = 0;
14468
14469     if (str && str.length) {
14470       l = str.length;
14471       str += '';
14472
14473       while (i < l) {
14474           c1 = str.charCodeAt(i);
14475           ac += 1;
14476           if (c1 < 128) {
14477               arr[ac] = String.fromCharCode(c1);
14478               i+=1;
14479           } else if (c1 > 191 && c1 < 224) {
14480               c2 = str.charCodeAt(i + 1);
14481               arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
14482               i += 2;
14483           } else {
14484               c2 = str.charCodeAt(i + 1);
14485               c3 = str.charCodeAt(i + 2);
14486               arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
14487               i += 3;
14488           }
14489       }
14490     }
14491     return arr.join('');
14492   }
14493
14494   /**
14495    * Add integers, wrapping at 2^32. This uses 16-bit operations internally
14496    * to work around bugs in some JS interpreters.
14497    */
14498   function safe_add(x, y) {
14499     var lsw = (x & 0xFFFF) + (y & 0xFFFF),
14500         msw = (x >> 16) + (y >> 16) + (lsw >> 16);
14501     return (msw << 16) | (lsw & 0xFFFF);
14502   }
14503
14504   /**
14505    * Bitwise rotate a 32-bit number to the left.
14506    */
14507   function bit_rol(num, cnt) {
14508     return (num << cnt) | (num >>> (32 - cnt));
14509   }
14510
14511   /**
14512    * Convert a raw string to a hex string
14513    */
14514   function rstr2hex(input, hexcase) {
14515     var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
14516         output = '', x, i = 0, l = input.length;
14517     for (; i < l; i+=1) {
14518       x = input.charCodeAt(i);
14519       output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
14520     }
14521     return output;
14522   }
14523
14524   /**
14525    * Encode a string as utf-16
14526    */
14527   function str2rstr_utf16le(input) {
14528     var i, l = input.length, output = '';
14529     for (i = 0; i < l; i+=1) {
14530       output += String.fromCharCode( input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF);
14531     }
14532     return output;
14533   }
14534
14535   function str2rstr_utf16be(input) {
14536     var i, l = input.length, output = '';
14537     for (i = 0; i < l; i+=1) {
14538       output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF);
14539     }
14540     return output;
14541   }
14542
14543   /**
14544    * Convert an array of big-endian words to a string
14545    */
14546   function binb2rstr(input) {
14547     var i, l = input.length * 32, output = '';
14548     for (i = 0; i < l; i += 8) {
14549         output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
14550     }
14551     return output;
14552   }
14553
14554   /**
14555    * Convert an array of little-endian words to a string
14556    */
14557   function binl2rstr(input) {
14558     var i, l = input.length * 32, output = '';
14559     for (i = 0;i < l; i += 8) {
14560       output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
14561     }
14562     return output;
14563   }
14564
14565   /**
14566    * Convert a raw string to an array of little-endian words
14567    * Characters >255 have their high-byte silently ignored.
14568    */
14569   function rstr2binl(input) {
14570     var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
14571     for (i = 0; i < lo; i+=1) {
14572       output[i] = 0;
14573     }
14574     for (i = 0; i < l; i += 8) {
14575       output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
14576     }
14577     return output;
14578   }
14579
14580   /**
14581    * Convert a raw string to an array of big-endian words
14582    * Characters >255 have their high-byte silently ignored.
14583    */
14584    function rstr2binb(input) {
14585       var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
14586       for (i = 0; i < lo; i+=1) {
14587             output[i] = 0;
14588         }
14589       for (i = 0; i < l; i += 8) {
14590             output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
14591         }
14592       return output;
14593    }
14594
14595   /**
14596    * Convert a raw string to an arbitrary string encoding
14597    */
14598   function rstr2any(input, encoding) {
14599     var divisor = encoding.length,
14600         remainders = Array(),
14601         i, q, x, ld, quotient, dividend, output, full_length;
14602
14603     /* Convert to an array of 16-bit big-endian values, forming the dividend */
14604     dividend = Array(Math.ceil(input.length / 2));
14605     ld = dividend.length;
14606     for (i = 0; i < ld; i+=1) {
14607       dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
14608     }
14609
14610     /**
14611      * Repeatedly perform a long division. The binary array forms the dividend,
14612      * the length of the encoding is the divisor. Once computed, the quotient
14613      * forms the dividend for the next step. We stop when the dividend is zerHashes.
14614      * All remainders are stored for later use.
14615      */
14616     while(dividend.length > 0) {
14617       quotient = Array();
14618       x = 0;
14619       for (i = 0; i < dividend.length; i+=1) {
14620         x = (x << 16) + dividend[i];
14621         q = Math.floor(x / divisor);
14622         x -= q * divisor;
14623         if (quotient.length > 0 || q > 0) {
14624           quotient[quotient.length] = q;
14625         }
14626       }
14627       remainders[remainders.length] = x;
14628       dividend = quotient;
14629     }
14630
14631     /* Convert the remainders to the output string */
14632     output = '';
14633     for (i = remainders.length - 1; i >= 0; i--) {
14634       output += encoding.charAt(remainders[i]);
14635     }
14636
14637     /* Append leading zero equivalents */
14638     full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
14639     for (i = output.length; i < full_length; i+=1) {
14640       output = encoding[0] + output;
14641     }
14642     return output;
14643   }
14644
14645   /**
14646    * Convert a raw string to a base-64 string
14647    */
14648   function rstr2b64(input, b64pad) {
14649     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
14650         output = '',
14651         len = input.length, i, j, triplet;
14652     b64pad= b64pad || '=';
14653     for (i = 0; i < len; i += 3) {
14654       triplet = (input.charCodeAt(i) << 16)
14655             | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
14656             | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
14657       for (j = 0; j < 4; j+=1) {
14658         if (i * 8 + j * 6 > input.length * 8) {
14659           output += b64pad;
14660         } else {
14661           output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
14662         }
14663        }
14664     }
14665     return output;
14666   }
14667
14668   Hashes = {
14669   /**
14670    * @property {String} version
14671    * @readonly
14672    */
14673   VERSION : '1.0.3',
14674   /**
14675    * @member Hashes
14676    * @class Base64
14677    * @constructor
14678    */
14679   Base64 : function () {
14680     // private properties
14681     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
14682         pad = '=', // default pad according with the RFC standard
14683         url = false, // URL encoding support @todo
14684         utf8 = true; // by default enable UTF-8 support encoding
14685
14686     // public method for encoding
14687     this.encode = function (input) {
14688       var i, j, triplet,
14689           output = '',
14690           len = input.length;
14691
14692       pad = pad || '=';
14693       input = (utf8) ? utf8Encode(input) : input;
14694
14695       for (i = 0; i < len; i += 3) {
14696         triplet = (input.charCodeAt(i) << 16)
14697               | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
14698               | (i + 2 < len ? input.charCodeAt(i+2) : 0);
14699         for (j = 0; j < 4; j+=1) {
14700           if (i * 8 + j * 6 > len * 8) {
14701               output += pad;
14702           } else {
14703               output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
14704           }
14705         }
14706       }
14707       return output;
14708     };
14709
14710     // public method for decoding
14711     this.decode = function (input) {
14712       // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
14713       var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
14714         dec = '',
14715         arr = [];
14716       if (!input) { return input; }
14717
14718       i = ac = 0;
14719       input = input.replace(new RegExp('\\'+pad,'gi'),''); // use '='
14720       //input += '';
14721
14722       do { // unpack four hexets into three octets using index points in b64
14723         h1 = tab.indexOf(input.charAt(i+=1));
14724         h2 = tab.indexOf(input.charAt(i+=1));
14725         h3 = tab.indexOf(input.charAt(i+=1));
14726         h4 = tab.indexOf(input.charAt(i+=1));
14727
14728         bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
14729
14730         o1 = bits >> 16 & 0xff;
14731         o2 = bits >> 8 & 0xff;
14732         o3 = bits & 0xff;
14733         ac += 1;
14734
14735         if (h3 === 64) {
14736           arr[ac] = String.fromCharCode(o1);
14737         } else if (h4 === 64) {
14738           arr[ac] = String.fromCharCode(o1, o2);
14739         } else {
14740           arr[ac] = String.fromCharCode(o1, o2, o3);
14741         }
14742       } while (i < input.length);
14743
14744       dec = arr.join('');
14745       dec = (utf8) ? utf8Decode(dec) : dec;
14746
14747       return dec;
14748     };
14749
14750     // set custom pad string
14751     this.setPad = function (str) {
14752         pad = str || pad;
14753         return this;
14754     };
14755     // set custom tab string characters
14756     this.setTab = function (str) {
14757         tab = str || tab;
14758         return this;
14759     };
14760     this.setUTF8 = function (bool) {
14761         if (typeof bool === 'boolean') {
14762           utf8 = bool;
14763         }
14764         return this;
14765     };
14766   },
14767
14768   /**
14769    * CRC-32 calculation
14770    * @member Hashes
14771    * @method CRC32
14772    * @static
14773    * @param {String} str Input String
14774    * @return {String}
14775    */
14776   CRC32 : function (str) {
14777     var crc = 0, x = 0, y = 0, table, i, iTop;
14778     str = utf8Encode(str);
14779
14780     table = [
14781         '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
14782         '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
14783         '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
14784         '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
14785         'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
14786         '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
14787         'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
14788         '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
14789         'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
14790         '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
14791         'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
14792         '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
14793         'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
14794         '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
14795         '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
14796         '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
14797         '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
14798         'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
14799         '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
14800         'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
14801         '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
14802         'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
14803         '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
14804         'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
14805         '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
14806         'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
14807     ].join('');
14808
14809     crc = crc ^ (-1);
14810     for (i = 0, iTop = str.length; i < iTop; i+=1 ) {
14811         y = ( crc ^ str.charCodeAt( i ) ) & 0xFF;
14812         x = '0x' + table.substr( y * 9, 8 );
14813         crc = ( crc >>> 8 ) ^ x;
14814     }
14815     // always return a positive number (that's what >>> 0 does)
14816     return (crc ^ (-1)) >>> 0;
14817   },
14818   /**
14819    * @member Hashes
14820    * @class MD5
14821    * @constructor
14822    * @param {Object} [config]
14823    *
14824    * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
14825    * Digest Algorithm, as defined in RFC 1321.
14826    * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
14827    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
14828    * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
14829    */
14830   MD5 : function (options) {
14831     /**
14832      * Private config properties. You may need to tweak these to be compatible with
14833      * the server-side, but the defaults work in most cases.
14834      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
14835      */
14836     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
14837         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
14838         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
14839
14840     // privileged (public) methods
14841     this.hex = function (s) {
14842       return rstr2hex(rstr(s, utf8), hexcase);
14843     };
14844     this.b64 = function (s) {
14845       return rstr2b64(rstr(s), b64pad);
14846     };
14847     this.any = function(s, e) {
14848       return rstr2any(rstr(s, utf8), e);
14849     };
14850     this.hex_hmac = function (k, d) {
14851       return rstr2hex(rstr_hmac(k, d), hexcase);
14852     };
14853     this.b64_hmac = function (k, d) {
14854       return rstr2b64(rstr_hmac(k,d), b64pad);
14855     };
14856     this.any_hmac = function (k, d, e) {
14857       return rstr2any(rstr_hmac(k, d), e);
14858     };
14859     /**
14860      * Perform a simple self-test to see if the VM is working
14861      * @return {String} Hexadecimal hash sample
14862      */
14863     this.vm_test = function () {
14864       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
14865     };
14866     /**
14867      * Enable/disable uppercase hexadecimal returned string
14868      * @param {Boolean}
14869      * @return {Object} this
14870      */
14871     this.setUpperCase = function (a) {
14872       if (typeof a === 'boolean' ) {
14873         hexcase = a;
14874       }
14875       return this;
14876     };
14877     /**
14878      * Defines a base64 pad string
14879      * @param {String} Pad
14880      * @return {Object} this
14881      */
14882     this.setPad = function (a) {
14883       b64pad = a || b64pad;
14884       return this;
14885     };
14886     /**
14887      * Defines a base64 pad string
14888      * @param {Boolean}
14889      * @return {Object} [this]
14890      */
14891     this.setUTF8 = function (a) {
14892       if (typeof a === 'boolean') {
14893         utf8 = a;
14894       }
14895       return this;
14896     };
14897
14898     // private methods
14899
14900     /**
14901      * Calculate the MD5 of a raw string
14902      */
14903     function rstr(s) {
14904       s = (utf8) ? utf8Encode(s): s;
14905       return binl2rstr(binl(rstr2binl(s), s.length * 8));
14906     }
14907
14908     /**
14909      * Calculate the HMAC-MD5, of a key and some data (raw strings)
14910      */
14911     function rstr_hmac(key, data) {
14912       var bkey, ipad, opad, hash, i;
14913
14914       key = (utf8) ? utf8Encode(key) : key;
14915       data = (utf8) ? utf8Encode(data) : data;
14916       bkey = rstr2binl(key);
14917       if (bkey.length > 16) {
14918         bkey = binl(bkey, key.length * 8);
14919       }
14920
14921       ipad = Array(16), opad = Array(16);
14922       for (i = 0; i < 16; i+=1) {
14923           ipad[i] = bkey[i] ^ 0x36363636;
14924           opad[i] = bkey[i] ^ 0x5C5C5C5C;
14925       }
14926       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
14927       return binl2rstr(binl(opad.concat(hash), 512 + 128));
14928     }
14929
14930     /**
14931      * Calculate the MD5 of an array of little-endian words, and a bit length.
14932      */
14933     function binl(x, len) {
14934       var i, olda, oldb, oldc, oldd,
14935           a =  1732584193,
14936           b = -271733879,
14937           c = -1732584194,
14938           d =  271733878;
14939
14940       /* append padding */
14941       x[len >> 5] |= 0x80 << ((len) % 32);
14942       x[(((len + 64) >>> 9) << 4) + 14] = len;
14943
14944       for (i = 0; i < x.length; i += 16) {
14945         olda = a;
14946         oldb = b;
14947         oldc = c;
14948         oldd = d;
14949
14950         a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
14951         d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
14952         c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
14953         b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
14954         a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
14955         d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
14956         c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
14957         b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
14958         a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
14959         d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
14960         c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
14961         b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
14962         a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
14963         d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
14964         c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
14965         b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
14966
14967         a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
14968         d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
14969         c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
14970         b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
14971         a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
14972         d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
14973         c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
14974         b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
14975         a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
14976         d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
14977         c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
14978         b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
14979         a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
14980         d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
14981         c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
14982         b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
14983
14984         a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
14985         d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
14986         c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
14987         b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
14988         a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
14989         d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
14990         c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
14991         b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
14992         a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
14993         d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
14994         c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
14995         b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
14996         a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
14997         d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
14998         c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
14999         b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
15000
15001         a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
15002         d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
15003         c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
15004         b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
15005         a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
15006         d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
15007         c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
15008         b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
15009         a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
15010         d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
15011         c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
15012         b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
15013         a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
15014         d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
15015         c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
15016         b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
15017
15018         a = safe_add(a, olda);
15019         b = safe_add(b, oldb);
15020         c = safe_add(c, oldc);
15021         d = safe_add(d, oldd);
15022       }
15023       return Array(a, b, c, d);
15024     }
15025
15026     /**
15027      * These functions implement the four basic operations the algorithm uses.
15028      */
15029     function md5_cmn(q, a, b, x, s, t) {
15030       return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
15031     }
15032     function md5_ff(a, b, c, d, x, s, t) {
15033       return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
15034     }
15035     function md5_gg(a, b, c, d, x, s, t) {
15036       return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
15037     }
15038     function md5_hh(a, b, c, d, x, s, t) {
15039       return md5_cmn(b ^ c ^ d, a, b, x, s, t);
15040     }
15041     function md5_ii(a, b, c, d, x, s, t) {
15042       return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
15043     }
15044   },
15045   /**
15046    * @member Hashes
15047    * @class Hashes.SHA1
15048    * @param {Object} [config]
15049    * @constructor
15050    *
15051    * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
15052    * Version 2.2 Copyright Paul Johnston 2000 - 2009.
15053    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15054    * See http://pajhome.org.uk/crypt/md5 for details.
15055    */
15056   SHA1 : function (options) {
15057    /**
15058      * Private config properties. You may need to tweak these to be compatible with
15059      * the server-side, but the defaults work in most cases.
15060      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
15061      */
15062     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
15063         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
15064         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
15065
15066     // public methods
15067     this.hex = function (s) {
15068         return rstr2hex(rstr(s, utf8), hexcase);
15069     };
15070     this.b64 = function (s) {
15071         return rstr2b64(rstr(s, utf8), b64pad);
15072     };
15073     this.any = function (s, e) {
15074         return rstr2any(rstr(s, utf8), e);
15075     };
15076     this.hex_hmac = function (k, d) {
15077         return rstr2hex(rstr_hmac(k, d));
15078     };
15079     this.b64_hmac = function (k, d) {
15080         return rstr2b64(rstr_hmac(k, d), b64pad);
15081     };
15082     this.any_hmac = function (k, d, e) {
15083         return rstr2any(rstr_hmac(k, d), e);
15084     };
15085     /**
15086      * Perform a simple self-test to see if the VM is working
15087      * @return {String} Hexadecimal hash sample
15088      * @public
15089      */
15090     this.vm_test = function () {
15091       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15092     };
15093     /**
15094      * @description Enable/disable uppercase hexadecimal returned string
15095      * @param {boolean}
15096      * @return {Object} this
15097      * @public
15098      */
15099     this.setUpperCase = function (a) {
15100         if (typeof a === 'boolean') {
15101         hexcase = a;
15102       }
15103         return this;
15104     };
15105     /**
15106      * @description Defines a base64 pad string
15107      * @param {string} Pad
15108      * @return {Object} this
15109      * @public
15110      */
15111     this.setPad = function (a) {
15112       b64pad = a || b64pad;
15113         return this;
15114     };
15115     /**
15116      * @description Defines a base64 pad string
15117      * @param {boolean}
15118      * @return {Object} this
15119      * @public
15120      */
15121     this.setUTF8 = function (a) {
15122         if (typeof a === 'boolean') {
15123         utf8 = a;
15124       }
15125         return this;
15126     };
15127
15128     // private methods
15129
15130     /**
15131          * Calculate the SHA-512 of a raw string
15132          */
15133         function rstr(s) {
15134       s = (utf8) ? utf8Encode(s) : s;
15135       return binb2rstr(binb(rstr2binb(s), s.length * 8));
15136         }
15137
15138     /**
15139      * Calculate the HMAC-SHA1 of a key and some data (raw strings)
15140      */
15141     function rstr_hmac(key, data) {
15142         var bkey, ipad, opad, i, hash;
15143         key = (utf8) ? utf8Encode(key) : key;
15144         data = (utf8) ? utf8Encode(data) : data;
15145         bkey = rstr2binb(key);
15146
15147         if (bkey.length > 16) {
15148         bkey = binb(bkey, key.length * 8);
15149       }
15150         ipad = Array(16), opad = Array(16);
15151         for (i = 0; i < 16; i+=1) {
15152                 ipad[i] = bkey[i] ^ 0x36363636;
15153                 opad[i] = bkey[i] ^ 0x5C5C5C5C;
15154         }
15155         hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
15156         return binb2rstr(binb(opad.concat(hash), 512 + 160));
15157     }
15158
15159     /**
15160      * Calculate the SHA-1 of an array of big-endian words, and a bit length
15161      */
15162     function binb(x, len) {
15163       var i, j, t, olda, oldb, oldc, oldd, olde,
15164           w = Array(80),
15165           a =  1732584193,
15166           b = -271733879,
15167           c = -1732584194,
15168           d =  271733878,
15169           e = -1009589776;
15170
15171       /* append padding */
15172       x[len >> 5] |= 0x80 << (24 - len % 32);
15173       x[((len + 64 >> 9) << 4) + 15] = len;
15174
15175       for (i = 0; i < x.length; i += 16) {
15176         olda = a,
15177         oldb = b;
15178         oldc = c;
15179         oldd = d;
15180         olde = e;
15181
15182         for (j = 0; j < 80; j+=1)       {
15183           if (j < 16) {
15184             w[j] = x[i + j];
15185           } else {
15186             w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
15187           }
15188           t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
15189                                            safe_add(safe_add(e, w[j]), sha1_kt(j)));
15190           e = d;
15191           d = c;
15192           c = bit_rol(b, 30);
15193           b = a;
15194           a = t;
15195         }
15196
15197         a = safe_add(a, olda);
15198         b = safe_add(b, oldb);
15199         c = safe_add(c, oldc);
15200         d = safe_add(d, oldd);
15201         e = safe_add(e, olde);
15202       }
15203       return Array(a, b, c, d, e);
15204     }
15205
15206     /**
15207      * Perform the appropriate triplet combination function for the current
15208      * iteration
15209      */
15210     function sha1_ft(t, b, c, d) {
15211       if (t < 20) { return (b & c) | ((~b) & d); }
15212       if (t < 40) { return b ^ c ^ d; }
15213       if (t < 60) { return (b & c) | (b & d) | (c & d); }
15214       return b ^ c ^ d;
15215     }
15216
15217     /**
15218      * Determine the appropriate additive constant for the current iteration
15219      */
15220     function sha1_kt(t) {
15221       return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
15222                  (t < 60) ? -1894007588 : -899497514;
15223     }
15224   },
15225   /**
15226    * @class Hashes.SHA256
15227    * @param {config}
15228    *
15229    * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
15230    * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
15231    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15232    * See http://pajhome.org.uk/crypt/md5 for details.
15233    * Also http://anmar.eu.org/projects/jssha2/
15234    */
15235   SHA256 : function (options) {
15236     /**
15237      * Private properties configuration variables. You may need to tweak these to be compatible with
15238      * the server-side, but the defaults work in most cases.
15239      * @see this.setUpperCase() method
15240      * @see this.setPad() method
15241      */
15242     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase  */
15243               b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', /* base-64 pad character. Default '=' for strict RFC compliance   */
15244               utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15245               sha256_K;
15246
15247     /* privileged (public) methods */
15248     this.hex = function (s) {
15249       return rstr2hex(rstr(s, utf8));
15250     };
15251     this.b64 = function (s) {
15252       return rstr2b64(rstr(s, utf8), b64pad);
15253     };
15254     this.any = function (s, e) {
15255       return rstr2any(rstr(s, utf8), e);
15256     };
15257     this.hex_hmac = function (k, d) {
15258       return rstr2hex(rstr_hmac(k, d));
15259     };
15260     this.b64_hmac = function (k, d) {
15261       return rstr2b64(rstr_hmac(k, d), b64pad);
15262     };
15263     this.any_hmac = function (k, d, e) {
15264       return rstr2any(rstr_hmac(k, d), e);
15265     };
15266     /**
15267      * Perform a simple self-test to see if the VM is working
15268      * @return {String} Hexadecimal hash sample
15269      * @public
15270      */
15271     this.vm_test = function () {
15272       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15273     };
15274     /**
15275      * Enable/disable uppercase hexadecimal returned string
15276      * @param {boolean}
15277      * @return {Object} this
15278      * @public
15279      */
15280     this.setUpperCase = function (a) {
15281       if (typeof a === 'boolean') {
15282         hexcase = a;
15283       }
15284       return this;
15285     };
15286     /**
15287      * @description Defines a base64 pad string
15288      * @param {string} Pad
15289      * @return {Object} this
15290      * @public
15291      */
15292     this.setPad = function (a) {
15293       b64pad = a || b64pad;
15294       return this;
15295     };
15296     /**
15297      * Defines a base64 pad string
15298      * @param {boolean}
15299      * @return {Object} this
15300      * @public
15301      */
15302     this.setUTF8 = function (a) {
15303       if (typeof a === 'boolean') {
15304         utf8 = a;
15305       }
15306       return this;
15307     };
15308
15309     // private methods
15310
15311     /**
15312      * Calculate the SHA-512 of a raw string
15313      */
15314     function rstr(s, utf8) {
15315       s = (utf8) ? utf8Encode(s) : s;
15316       return binb2rstr(binb(rstr2binb(s), s.length * 8));
15317     }
15318
15319     /**
15320      * Calculate the HMAC-sha256 of a key and some data (raw strings)
15321      */
15322     function rstr_hmac(key, data) {
15323       key = (utf8) ? utf8Encode(key) : key;
15324       data = (utf8) ? utf8Encode(data) : data;
15325       var hash, i = 0,
15326           bkey = rstr2binb(key),
15327           ipad = Array(16),
15328           opad = Array(16);
15329
15330       if (bkey.length > 16) { bkey = binb(bkey, key.length * 8); }
15331
15332       for (; i < 16; i+=1) {
15333         ipad[i] = bkey[i] ^ 0x36363636;
15334         opad[i] = bkey[i] ^ 0x5C5C5C5C;
15335       }
15336
15337       hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
15338       return binb2rstr(binb(opad.concat(hash), 512 + 256));
15339     }
15340
15341     /*
15342      * Main sha256 function, with its support functions
15343      */
15344     function sha256_S (X, n) {return ( X >>> n ) | (X << (32 - n));}
15345     function sha256_R (X, n) {return ( X >>> n );}
15346     function sha256_Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
15347     function sha256_Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
15348     function sha256_Sigma0256(x) {return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));}
15349     function sha256_Sigma1256(x) {return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));}
15350     function sha256_Gamma0256(x) {return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));}
15351     function sha256_Gamma1256(x) {return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));}
15352     function sha256_Sigma0512(x) {return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39));}
15353     function sha256_Sigma1512(x) {return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41));}
15354     function sha256_Gamma0512(x) {return (sha256_S(x, 1)  ^ sha256_S(x, 8) ^ sha256_R(x, 7));}
15355     function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));}
15356
15357     sha256_K = [
15358       1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,
15359       -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
15360       1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
15361       264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
15362       -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
15363       113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
15364       1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885,
15365       -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
15366       430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
15367       1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,
15368       -1866530822, -1538233109, -1090935817, -965641998
15369     ];
15370
15371     function binb(m, l) {
15372       var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
15373                  1359893119, -1694144372, 528734635, 1541459225];
15374       var W = new Array(64);
15375       var a, b, c, d, e, f, g, h;
15376       var i, j, T1, T2;
15377
15378       /* append padding */
15379       m[l >> 5] |= 0x80 << (24 - l % 32);
15380       m[((l + 64 >> 9) << 4) + 15] = l;
15381
15382       for (i = 0; i < m.length; i += 16)
15383       {
15384       a = HASH[0];
15385       b = HASH[1];
15386       c = HASH[2];
15387       d = HASH[3];
15388       e = HASH[4];
15389       f = HASH[5];
15390       g = HASH[6];
15391       h = HASH[7];
15392
15393       for (j = 0; j < 64; j+=1)
15394       {
15395         if (j < 16) {
15396           W[j] = m[j + i];
15397         } else {
15398           W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
15399                           sha256_Gamma0256(W[j - 15])), W[j - 16]);
15400         }
15401
15402         T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
15403                                   sha256_K[j]), W[j]);
15404         T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
15405         h = g;
15406         g = f;
15407         f = e;
15408         e = safe_add(d, T1);
15409         d = c;
15410         c = b;
15411         b = a;
15412         a = safe_add(T1, T2);
15413       }
15414
15415       HASH[0] = safe_add(a, HASH[0]);
15416       HASH[1] = safe_add(b, HASH[1]);
15417       HASH[2] = safe_add(c, HASH[2]);
15418       HASH[3] = safe_add(d, HASH[3]);
15419       HASH[4] = safe_add(e, HASH[4]);
15420       HASH[5] = safe_add(f, HASH[5]);
15421       HASH[6] = safe_add(g, HASH[6]);
15422       HASH[7] = safe_add(h, HASH[7]);
15423       }
15424       return HASH;
15425     }
15426
15427   },
15428
15429   /**
15430    * @class Hashes.SHA512
15431    * @param {config}
15432    *
15433    * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
15434    * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
15435    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15436    * See http://pajhome.org.uk/crypt/md5 for details.
15437    */
15438   SHA512 : function (options) {
15439     /**
15440      * Private properties configuration variables. You may need to tweak these to be compatible with
15441      * the server-side, but the defaults work in most cases.
15442      * @see this.setUpperCase() method
15443      * @see this.setPad() method
15444      */
15445     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false , /* hexadecimal output case format. false - lowercase; true - uppercase  */
15446         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
15447         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15448         sha512_k;
15449
15450     /* privileged (public) methods */
15451     this.hex = function (s) {
15452       return rstr2hex(rstr(s));
15453     };
15454     this.b64 = function (s) {
15455       return rstr2b64(rstr(s), b64pad);
15456     };
15457     this.any = function (s, e) {
15458       return rstr2any(rstr(s), e);
15459     };
15460     this.hex_hmac = function (k, d) {
15461       return rstr2hex(rstr_hmac(k, d));
15462     };
15463     this.b64_hmac = function (k, d) {
15464       return rstr2b64(rstr_hmac(k, d), b64pad);
15465     };
15466     this.any_hmac = function (k, d, e) {
15467       return rstr2any(rstr_hmac(k, d), e);
15468     };
15469     /**
15470      * Perform a simple self-test to see if the VM is working
15471      * @return {String} Hexadecimal hash sample
15472      * @public
15473      */
15474     this.vm_test = function () {
15475       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15476     };
15477     /**
15478      * @description Enable/disable uppercase hexadecimal returned string
15479      * @param {boolean}
15480      * @return {Object} this
15481      * @public
15482      */
15483     this.setUpperCase = function (a) {
15484       if (typeof a === 'boolean') {
15485         hexcase = a;
15486       }
15487       return this;
15488     };
15489     /**
15490      * @description Defines a base64 pad string
15491      * @param {string} Pad
15492      * @return {Object} this
15493      * @public
15494      */
15495     this.setPad = function (a) {
15496       b64pad = a || b64pad;
15497       return this;
15498     };
15499     /**
15500      * @description Defines a base64 pad string
15501      * @param {boolean}
15502      * @return {Object} this
15503      * @public
15504      */
15505     this.setUTF8 = function (a) {
15506       if (typeof a === 'boolean') {
15507         utf8 = a;
15508       }
15509       return this;
15510     };
15511
15512     /* private methods */
15513
15514     /**
15515      * Calculate the SHA-512 of a raw string
15516      */
15517     function rstr(s) {
15518       s = (utf8) ? utf8Encode(s) : s;
15519       return binb2rstr(binb(rstr2binb(s), s.length * 8));
15520     }
15521     /*
15522      * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
15523      */
15524     function rstr_hmac(key, data) {
15525       key = (utf8) ? utf8Encode(key) : key;
15526       data = (utf8) ? utf8Encode(data) : data;
15527
15528       var hash, i = 0,
15529           bkey = rstr2binb(key),
15530           ipad = Array(32), opad = Array(32);
15531
15532       if (bkey.length > 32) { bkey = binb(bkey, key.length * 8); }
15533
15534       for (; i < 32; i+=1) {
15535         ipad[i] = bkey[i] ^ 0x36363636;
15536         opad[i] = bkey[i] ^ 0x5C5C5C5C;
15537       }
15538
15539       hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
15540       return binb2rstr(binb(opad.concat(hash), 1024 + 512));
15541     }
15542
15543     /**
15544      * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
15545      */
15546     function binb(x, len) {
15547       var j, i, l,
15548           W = new Array(80),
15549           hash = new Array(16),
15550           //Initial hash values
15551           H = [
15552             new int64(0x6a09e667, -205731576),
15553             new int64(-1150833019, -2067093701),
15554             new int64(0x3c6ef372, -23791573),
15555             new int64(-1521486534, 0x5f1d36f1),
15556             new int64(0x510e527f, -1377402159),
15557             new int64(-1694144372, 0x2b3e6c1f),
15558             new int64(0x1f83d9ab, -79577749),
15559             new int64(0x5be0cd19, 0x137e2179)
15560           ],
15561           T1 = new int64(0, 0),
15562           T2 = new int64(0, 0),
15563           a = new int64(0,0),
15564           b = new int64(0,0),
15565           c = new int64(0,0),
15566           d = new int64(0,0),
15567           e = new int64(0,0),
15568           f = new int64(0,0),
15569           g = new int64(0,0),
15570           h = new int64(0,0),
15571           //Temporary variables not specified by the document
15572           s0 = new int64(0, 0),
15573           s1 = new int64(0, 0),
15574           Ch = new int64(0, 0),
15575           Maj = new int64(0, 0),
15576           r1 = new int64(0, 0),
15577           r2 = new int64(0, 0),
15578           r3 = new int64(0, 0);
15579
15580       if (sha512_k === undefined) {
15581           //SHA512 constants
15582           sha512_k = [
15583             new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
15584             new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
15585             new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
15586             new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
15587             new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
15588             new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
15589             new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
15590             new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
15591             new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
15592             new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
15593             new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
15594             new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
15595             new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
15596             new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
15597             new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
15598             new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
15599             new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
15600             new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
15601             new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
15602             new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
15603             new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
15604             new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
15605             new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
15606             new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
15607             new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
15608             new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
15609             new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
15610             new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
15611             new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
15612             new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
15613             new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
15614             new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
15615             new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
15616             new int64(-354779690, -840897762), new int64(-176337025, -294727304),
15617             new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
15618             new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
15619             new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
15620             new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
15621             new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
15622             new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
15623           ];
15624       }
15625
15626       for (i=0; i<80; i+=1) {
15627         W[i] = new int64(0, 0);
15628       }
15629
15630       // append padding to the source string. The format is described in the FIPS.
15631       x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
15632       x[((len + 128 >> 10)<< 5) + 31] = len;
15633       l = x.length;
15634       for (i = 0; i<l; i+=32) { //32 dwords is the block size
15635         int64copy(a, H[0]);
15636         int64copy(b, H[1]);
15637         int64copy(c, H[2]);
15638         int64copy(d, H[3]);
15639         int64copy(e, H[4]);
15640         int64copy(f, H[5]);
15641         int64copy(g, H[6]);
15642         int64copy(h, H[7]);
15643
15644         for (j=0; j<16; j+=1) {
15645           W[j].h = x[i + 2*j];
15646           W[j].l = x[i + 2*j + 1];
15647         }
15648
15649         for (j=16; j<80; j+=1) {
15650           //sigma1
15651           int64rrot(r1, W[j-2], 19);
15652           int64revrrot(r2, W[j-2], 29);
15653           int64shr(r3, W[j-2], 6);
15654           s1.l = r1.l ^ r2.l ^ r3.l;
15655           s1.h = r1.h ^ r2.h ^ r3.h;
15656           //sigma0
15657           int64rrot(r1, W[j-15], 1);
15658           int64rrot(r2, W[j-15], 8);
15659           int64shr(r3, W[j-15], 7);
15660           s0.l = r1.l ^ r2.l ^ r3.l;
15661           s0.h = r1.h ^ r2.h ^ r3.h;
15662
15663           int64add4(W[j], s1, W[j-7], s0, W[j-16]);
15664         }
15665
15666         for (j = 0; j < 80; j+=1) {
15667           //Ch
15668           Ch.l = (e.l & f.l) ^ (~e.l & g.l);
15669           Ch.h = (e.h & f.h) ^ (~e.h & g.h);
15670
15671           //Sigma1
15672           int64rrot(r1, e, 14);
15673           int64rrot(r2, e, 18);
15674           int64revrrot(r3, e, 9);
15675           s1.l = r1.l ^ r2.l ^ r3.l;
15676           s1.h = r1.h ^ r2.h ^ r3.h;
15677
15678           //Sigma0
15679           int64rrot(r1, a, 28);
15680           int64revrrot(r2, a, 2);
15681           int64revrrot(r3, a, 7);
15682           s0.l = r1.l ^ r2.l ^ r3.l;
15683           s0.h = r1.h ^ r2.h ^ r3.h;
15684
15685           //Maj
15686           Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
15687           Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
15688
15689           int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
15690           int64add(T2, s0, Maj);
15691
15692           int64copy(h, g);
15693           int64copy(g, f);
15694           int64copy(f, e);
15695           int64add(e, d, T1);
15696           int64copy(d, c);
15697           int64copy(c, b);
15698           int64copy(b, a);
15699           int64add(a, T1, T2);
15700         }
15701         int64add(H[0], H[0], a);
15702         int64add(H[1], H[1], b);
15703         int64add(H[2], H[2], c);
15704         int64add(H[3], H[3], d);
15705         int64add(H[4], H[4], e);
15706         int64add(H[5], H[5], f);
15707         int64add(H[6], H[6], g);
15708         int64add(H[7], H[7], h);
15709       }
15710
15711       //represent the hash as an array of 32-bit dwords
15712       for (i=0; i<8; i+=1) {
15713         hash[2*i] = H[i].h;
15714         hash[2*i + 1] = H[i].l;
15715       }
15716       return hash;
15717     }
15718
15719     //A constructor for 64-bit numbers
15720     function int64(h, l) {
15721       this.h = h;
15722       this.l = l;
15723       //this.toString = int64toString;
15724     }
15725
15726     //Copies src into dst, assuming both are 64-bit numbers
15727     function int64copy(dst, src) {
15728       dst.h = src.h;
15729       dst.l = src.l;
15730     }
15731
15732     //Right-rotates a 64-bit number by shift
15733     //Won't handle cases of shift>=32
15734     //The function revrrot() is for that
15735     function int64rrot(dst, x, shift) {
15736       dst.l = (x.l >>> shift) | (x.h << (32-shift));
15737       dst.h = (x.h >>> shift) | (x.l << (32-shift));
15738     }
15739
15740     //Reverses the dwords of the source and then rotates right by shift.
15741     //This is equivalent to rotation by 32+shift
15742     function int64revrrot(dst, x, shift) {
15743       dst.l = (x.h >>> shift) | (x.l << (32-shift));
15744       dst.h = (x.l >>> shift) | (x.h << (32-shift));
15745     }
15746
15747     //Bitwise-shifts right a 64-bit number by shift
15748     //Won't handle shift>=32, but it's never needed in SHA512
15749     function int64shr(dst, x, shift) {
15750       dst.l = (x.l >>> shift) | (x.h << (32-shift));
15751       dst.h = (x.h >>> shift);
15752     }
15753
15754     //Adds two 64-bit numbers
15755     //Like the original implementation, does not rely on 32-bit operations
15756     function int64add(dst, x, y) {
15757        var w0 = (x.l & 0xffff) + (y.l & 0xffff);
15758        var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
15759        var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
15760        var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
15761        dst.l = (w0 & 0xffff) | (w1 << 16);
15762        dst.h = (w2 & 0xffff) | (w3 << 16);
15763     }
15764
15765     //Same, except with 4 addends. Works faster than adding them one by one.
15766     function int64add4(dst, a, b, c, d) {
15767        var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
15768        var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
15769        var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
15770        var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
15771        dst.l = (w0 & 0xffff) | (w1 << 16);
15772        dst.h = (w2 & 0xffff) | (w3 << 16);
15773     }
15774
15775     //Same, except with 5 addends
15776     function int64add5(dst, a, b, c, d, e) {
15777       var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
15778           w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
15779           w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
15780           w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
15781        dst.l = (w0 & 0xffff) | (w1 << 16);
15782        dst.h = (w2 & 0xffff) | (w3 << 16);
15783     }
15784   },
15785   /**
15786    * @class Hashes.RMD160
15787    * @constructor
15788    * @param {Object} [config]
15789    *
15790    * A JavaScript implementation of the RIPEMD-160 Algorithm
15791    * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
15792    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15793    * See http://pajhome.org.uk/crypt/md5 for details.
15794    * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
15795    */
15796   RMD160 : function (options) {
15797     /**
15798      * Private properties configuration variables. You may need to tweak these to be compatible with
15799      * the server-side, but the defaults work in most cases.
15800      * @see this.setUpperCase() method
15801      * @see this.setPad() method
15802      */
15803     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,   /* hexadecimal output case format. false - lowercase; true - uppercase  */
15804         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
15805         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15806         rmd160_r1 = [
15807            0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
15808            7,  4, 13,  1, 10,  6, 15,  3, 12,  0,  9,  5,  2, 14, 11,  8,
15809            3, 10, 14,  4,  9, 15,  8,  1,  2,  7,  0,  6, 13, 11,  5, 12,
15810            1,  9, 11, 10,  0,  8, 12,  4, 13,  3,  7, 15, 14,  5,  6,  2,
15811            4,  0,  5,  9,  7, 12,  2, 10, 14,  1,  3,  8, 11,  6, 15, 13
15812         ],
15813         rmd160_r2 = [
15814            5, 14,  7,  0,  9,  2, 11,  4, 13,  6, 15,  8,  1, 10,  3, 12,
15815            6, 11,  3,  7,  0, 13,  5, 10, 14, 15,  8, 12,  4,  9,  1,  2,
15816           15,  5,  1,  3,  7, 14,  6,  9, 11,  8, 12,  2, 10,  0,  4, 13,
15817            8,  6,  4,  1,  3, 11, 15,  0,  5, 12,  2, 13,  9,  7, 10, 14,
15818           12, 15, 10,  4,  1,  5,  8,  7,  6,  2, 13, 14,  0,  3,  9, 11
15819         ],
15820         rmd160_s1 = [
15821           11, 14, 15, 12,  5,  8,  7,  9, 11, 13, 14, 15,  6,  7,  9,  8,
15822            7,  6,  8, 13, 11,  9,  7, 15,  7, 12, 15,  9, 11,  7, 13, 12,
15823           11, 13,  6,  7, 14,  9, 13, 15, 14,  8, 13,  6,  5, 12,  7,  5,
15824           11, 12, 14, 15, 14, 15,  9,  8,  9, 14,  5,  6,  8,  6,  5, 12,
15825            9, 15,  5, 11,  6,  8, 13, 12,  5, 12, 13, 14, 11,  8,  5,  6
15826         ],
15827         rmd160_s2 = [
15828            8,  9,  9, 11, 13, 15, 15,  5,  7,  7,  8, 11, 14, 14, 12,  6,
15829            9, 13, 15,  7, 12,  8,  9, 11,  7,  7, 12,  7,  6, 15, 13, 11,
15830            9,  7, 15, 11,  8,  6,  6, 14, 12, 13,  5, 14, 13, 13,  7,  5,
15831           15,  5,  8, 11, 14, 14,  6, 14,  6,  9, 12,  9, 12,  5, 15,  8,
15832            8,  5, 12,  9, 12,  5, 14,  6,  8, 13,  6,  5, 15, 13, 11, 11
15833         ];
15834
15835     /* privileged (public) methods */
15836     this.hex = function (s) {
15837       return rstr2hex(rstr(s, utf8));
15838     };
15839     this.b64 = function (s) {
15840       return rstr2b64(rstr(s, utf8), b64pad);
15841     };
15842     this.any = function (s, e) {
15843       return rstr2any(rstr(s, utf8), e);
15844     };
15845     this.hex_hmac = function (k, d) {
15846       return rstr2hex(rstr_hmac(k, d));
15847     };
15848     this.b64_hmac = function (k, d) {
15849       return rstr2b64(rstr_hmac(k, d), b64pad);
15850     };
15851     this.any_hmac = function (k, d, e) {
15852       return rstr2any(rstr_hmac(k, d), e);
15853     };
15854     /**
15855      * Perform a simple self-test to see if the VM is working
15856      * @return {String} Hexadecimal hash sample
15857      * @public
15858      */
15859     this.vm_test = function () {
15860       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15861     };
15862     /**
15863      * @description Enable/disable uppercase hexadecimal returned string
15864      * @param {boolean}
15865      * @return {Object} this
15866      * @public
15867      */
15868     this.setUpperCase = function (a) {
15869       if (typeof a === 'boolean' ) { hexcase = a; }
15870       return this;
15871     };
15872     /**
15873      * @description Defines a base64 pad string
15874      * @param {string} Pad
15875      * @return {Object} this
15876      * @public
15877      */
15878     this.setPad = function (a) {
15879       if (typeof a !== 'undefined' ) { b64pad = a; }
15880       return this;
15881     };
15882     /**
15883      * @description Defines a base64 pad string
15884      * @param {boolean}
15885      * @return {Object} this
15886      * @public
15887      */
15888     this.setUTF8 = function (a) {
15889       if (typeof a === 'boolean') { utf8 = a; }
15890       return this;
15891     };
15892
15893     /* private methods */
15894
15895     /**
15896      * Calculate the rmd160 of a raw string
15897      */
15898     function rstr(s) {
15899       s = (utf8) ? utf8Encode(s) : s;
15900       return binl2rstr(binl(rstr2binl(s), s.length * 8));
15901     }
15902
15903     /**
15904      * Calculate the HMAC-rmd160 of a key and some data (raw strings)
15905      */
15906     function rstr_hmac(key, data) {
15907       key = (utf8) ? utf8Encode(key) : key;
15908       data = (utf8) ? utf8Encode(data) : data;
15909       var i, hash,
15910           bkey = rstr2binl(key),
15911           ipad = Array(16), opad = Array(16);
15912
15913       if (bkey.length > 16) {
15914         bkey = binl(bkey, key.length * 8);
15915       }
15916
15917       for (i = 0; i < 16; i+=1) {
15918         ipad[i] = bkey[i] ^ 0x36363636;
15919         opad[i] = bkey[i] ^ 0x5C5C5C5C;
15920       }
15921       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
15922       return binl2rstr(binl(opad.concat(hash), 512 + 160));
15923     }
15924
15925     /**
15926      * Convert an array of little-endian words to a string
15927      */
15928     function binl2rstr(input) {
15929       var i, output = '', l = input.length * 32;
15930       for (i = 0; i < l; i += 8) {
15931         output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
15932       }
15933       return output;
15934     }
15935
15936     /**
15937      * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
15938      */
15939     function binl(x, len) {
15940       var T, j, i, l,
15941           h0 = 0x67452301,
15942           h1 = 0xefcdab89,
15943           h2 = 0x98badcfe,
15944           h3 = 0x10325476,
15945           h4 = 0xc3d2e1f0,
15946           A1, B1, C1, D1, E1,
15947           A2, B2, C2, D2, E2;
15948
15949       /* append padding */
15950       x[len >> 5] |= 0x80 << (len % 32);
15951       x[(((len + 64) >>> 9) << 4) + 14] = len;
15952       l = x.length;
15953
15954       for (i = 0; i < l; i+=16) {
15955         A1 = A2 = h0; B1 = B2 = h1; C1 = C2 = h2; D1 = D2 = h3; E1 = E2 = h4;
15956         for (j = 0; j <= 79; j+=1) {
15957           T = safe_add(A1, rmd160_f(j, B1, C1, D1));
15958           T = safe_add(T, x[i + rmd160_r1[j]]);
15959           T = safe_add(T, rmd160_K1(j));
15960           T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
15961           A1 = E1; E1 = D1; D1 = bit_rol(C1, 10); C1 = B1; B1 = T;
15962           T = safe_add(A2, rmd160_f(79-j, B2, C2, D2));
15963           T = safe_add(T, x[i + rmd160_r2[j]]);
15964           T = safe_add(T, rmd160_K2(j));
15965           T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
15966           A2 = E2; E2 = D2; D2 = bit_rol(C2, 10); C2 = B2; B2 = T;
15967         }
15968
15969         T = safe_add(h1, safe_add(C1, D2));
15970         h1 = safe_add(h2, safe_add(D1, E2));
15971         h2 = safe_add(h3, safe_add(E1, A2));
15972         h3 = safe_add(h4, safe_add(A1, B2));
15973         h4 = safe_add(h0, safe_add(B1, C2));
15974         h0 = T;
15975       }
15976       return [h0, h1, h2, h3, h4];
15977     }
15978
15979     // specific algorithm methods
15980     function rmd160_f(j, x, y, z) {
15981       return ( 0 <= j && j <= 15) ? (x ^ y ^ z) :
15982          (16 <= j && j <= 31) ? (x & y) | (~x & z) :
15983          (32 <= j && j <= 47) ? (x | ~y) ^ z :
15984          (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
15985          (64 <= j && j <= 79) ? x ^ (y | ~z) :
15986          'rmd160_f: j out of range';
15987     }
15988
15989     function rmd160_K1(j) {
15990       return ( 0 <= j && j <= 15) ? 0x00000000 :
15991          (16 <= j && j <= 31) ? 0x5a827999 :
15992          (32 <= j && j <= 47) ? 0x6ed9eba1 :
15993          (48 <= j && j <= 63) ? 0x8f1bbcdc :
15994          (64 <= j && j <= 79) ? 0xa953fd4e :
15995          'rmd160_K1: j out of range';
15996     }
15997
15998     function rmd160_K2(j){
15999       return ( 0 <= j && j <= 15) ? 0x50a28be6 :
16000          (16 <= j && j <= 31) ? 0x5c4dd124 :
16001          (32 <= j && j <= 47) ? 0x6d703ef3 :
16002          (48 <= j && j <= 63) ? 0x7a6d76e9 :
16003          (64 <= j && j <= 79) ? 0x00000000 :
16004          'rmd160_K2: j out of range';
16005     }
16006   }
16007 };
16008
16009   // exposes Hashes
16010   (function( window, undefined ) {
16011     var freeExports = false;
16012     if (typeof exports === 'object' ) {
16013       freeExports = exports;
16014       if (exports && typeof global === 'object' && global && global === global.global ) { window = global; }
16015     }
16016
16017     if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
16018       // define as an anonymous module, so, through path mapping, it can be aliased
16019       define(function () { return Hashes; });
16020     }
16021     else if ( freeExports ) {
16022       // in Node.js or RingoJS v0.8.0+
16023       if ( typeof module === 'object' && module && module.exports === freeExports ) {
16024         module.exports = Hashes;
16025       }
16026       // in Narwhal or RingoJS v0.7.0-
16027       else {
16028         freeExports.Hashes = Hashes;
16029       }
16030     }
16031     else {
16032       // in a browser or Rhino
16033       window.Hashes = Hashes;
16034     }
16035   }( this ));
16036 }()); // IIFE
16037
16038 })(window)
16039 },{}],2:[function(require,module,exports){
16040 'use strict';
16041
16042 var hashes = require('jshashes'),
16043     xtend = require('xtend'),
16044     sha1 = new hashes.SHA1();
16045
16046 var ohauth = {};
16047
16048 ohauth.qsString = function(obj) {
16049     return Object.keys(obj).sort().map(function(key) {
16050         return ohauth.percentEncode(key) + '=' +
16051             ohauth.percentEncode(obj[key]);
16052     }).join('&');
16053 };
16054
16055 ohauth.stringQs = function(str) {
16056     return str.split('&').reduce(function(obj, pair){
16057         var parts = pair.split('=');
16058         obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
16059             '' : decodeURIComponent(parts[1]);
16060         return obj;
16061     }, {});
16062 };
16063
16064 ohauth.rawxhr = function(method, url, data, headers, callback) {
16065     var xhr = new XMLHttpRequest(),
16066         twoHundred = /^20\d$/;
16067     xhr.onreadystatechange = function() {
16068         if (4 == xhr.readyState && 0 !== xhr.status) {
16069             if (twoHundred.test(xhr.status)) callback(null, xhr);
16070             else return callback(xhr, null);
16071         }
16072     };
16073     xhr.onerror = function(e) { return callback(e, null); };
16074     xhr.open(method, url, true);
16075     for (var h in headers) xhr.setRequestHeader(h, headers[h]);
16076     xhr.send(data);
16077 };
16078
16079 ohauth.xhr = function(method, url, auth, data, options, callback) {
16080     var headers = (options && options.header) || {
16081         'Content-Type': 'application/x-www-form-urlencoded'
16082     };
16083     headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
16084     ohauth.rawxhr(method, url, data, headers, callback);
16085 };
16086
16087 ohauth.nonce = function() {
16088     for (var o = ''; o.length < 6;) {
16089         o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
16090     }
16091     return o;
16092 };
16093
16094 ohauth.authHeader = function(obj) {
16095     return Object.keys(obj).sort().map(function(key) {
16096         return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
16097     }).join(', ');
16098 };
16099
16100 ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
16101
16102 ohauth.percentEncode = function(s) {
16103     return encodeURIComponent(s)
16104         .replace(/\!/g, '%21').replace(/\'/g, '%27')
16105         .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
16106 };
16107
16108 ohauth.baseString = function(method, url, params) {
16109     if (params.oauth_signature) delete params.oauth_signature;
16110     return [
16111         method,
16112         ohauth.percentEncode(url),
16113         ohauth.percentEncode(ohauth.qsString(params))].join('&');
16114 };
16115
16116 ohauth.signature = function(oauth_secret, token_secret, baseString) {
16117     return sha1.b64_hmac(
16118         ohauth.percentEncode(oauth_secret) + '&' +
16119         ohauth.percentEncode(token_secret),
16120         baseString);
16121 };
16122
16123 /**
16124  * Takes an options object for configuration (consumer_key,
16125  * consumer_secret, version, signature_method, token) and returns a
16126  * function that generates the Authorization header for given data.
16127  *
16128  * The returned function takes these parameters:
16129  * - method: GET/POST/...
16130  * - uri: full URI with protocol, port, path and query string
16131  * - extra_params: any extra parameters (that are passed in the POST data),
16132  *   can be an object or a from-urlencoded string.
16133  *
16134  * Returned function returns full OAuth header with "OAuth" string in it.
16135  */
16136
16137 ohauth.headerGenerator = function(options) {
16138     options = options || {};
16139     var consumer_key = options.consumer_key || '',
16140         consumer_secret = options.consumer_secret || '',
16141         signature_method = options.signature_method || 'HMAC-SHA1',
16142         version = options.version || '1.0',
16143         token = options.token || '';
16144
16145     return function(method, uri, extra_params) {
16146         method = method.toUpperCase();
16147         if (typeof extra_params === 'string' && extra_params.length > 0) {
16148             extra_params = ohauth.stringQs(extra_params);
16149         }
16150
16151         var uri_parts = uri.split('?', 2),
16152         base_uri = uri_parts[0];
16153
16154         var query_params = uri_parts.length === 2 ?
16155             ohauth.stringQs(uri_parts[1]) : {};
16156
16157         var oauth_params = {
16158             oauth_consumer_key: consumer_key,
16159             oauth_signature_method: signature_method,
16160             oauth_version: version,
16161             oauth_timestamp: ohauth.timestamp(),
16162             oauth_nonce: ohauth.nonce()
16163         };
16164
16165         if (token) oauth_params.oauth_token = token;
16166
16167         var all_params = xtend({}, oauth_params, query_params, extra_params),
16168             base_str = ohauth.baseString(method, base_uri, all_params);
16169
16170         oauth_params.oauth_signature = ohauth.signature(consumer_secret, token, base_str);
16171
16172         return 'OAuth ' + ohauth.authHeader(oauth_params);
16173     };
16174 };
16175
16176 module.exports = ohauth;
16177
16178 },{"jshashes":7,"xtend":4}],6:[function(require,module,exports){
16179 module.exports = Object.keys || require('./shim');
16180
16181
16182 },{"./shim":8}],8:[function(require,module,exports){
16183 (function () {
16184         "use strict";
16185
16186         // modified from https://github.com/kriskowal/es5-shim
16187         var has = Object.prototype.hasOwnProperty,
16188                 is = require('is'),
16189                 forEach = require('foreach'),
16190                 hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'),
16191                 dontEnums = [
16192                         "toString",
16193                         "toLocaleString",
16194                         "valueOf",
16195                         "hasOwnProperty",
16196                         "isPrototypeOf",
16197                         "propertyIsEnumerable",
16198                         "constructor"
16199                 ],
16200                 keysShim;
16201
16202         keysShim = function keys(object) {
16203                 if (!is.object(object) && !is.array(object)) {
16204                         throw new TypeError("Object.keys called on a non-object");
16205                 }
16206
16207                 var name, theKeys = [];
16208                 for (name in object) {
16209                         if (has.call(object, name)) {
16210                                 theKeys.push(name);
16211                         }
16212                 }
16213
16214                 if (hasDontEnumBug) {
16215                         forEach(dontEnums, function (dontEnum) {
16216                                 if (has.call(object, dontEnum)) {
16217                                         theKeys.push(dontEnum);
16218                                 }
16219                         });
16220                 }
16221                 return theKeys;
16222         };
16223
16224         module.exports = keysShim;
16225 }());
16226
16227
16228 },{"is":9,"foreach":10}],9:[function(require,module,exports){
16229
16230 /**!
16231  * is
16232  * the definitive JavaScript type testing library
16233  *
16234  * @copyright 2013 Enrico Marino
16235  * @license MIT
16236  */
16237
16238 var objProto = Object.prototype;
16239 var owns = objProto.hasOwnProperty;
16240 var toString = objProto.toString;
16241 var isActualNaN = function (value) {
16242   return value !== value;
16243 };
16244 var NON_HOST_TYPES = {
16245   "boolean": 1,
16246   "number": 1,
16247   "string": 1,
16248   "undefined": 1
16249 };
16250
16251 /**
16252  * Expose `is`
16253  */
16254
16255 var is = module.exports = {};
16256
16257 /**
16258  * Test general.
16259  */
16260
16261 /**
16262  * is.type
16263  * Test if `value` is a type of `type`.
16264  *
16265  * @param {Mixed} value value to test
16266  * @param {String} type type
16267  * @return {Boolean} true if `value` is a type of `type`, false otherwise
16268  * @api public
16269  */
16270
16271 is.a =
16272 is.type = function (value, type) {
16273   return typeof value === type;
16274 };
16275
16276 /**
16277  * is.defined
16278  * Test if `value` is defined.
16279  *
16280  * @param {Mixed} value value to test
16281  * @return {Boolean} true if 'value' is defined, false otherwise
16282  * @api public
16283  */
16284
16285 is.defined = function (value) {
16286   return value !== undefined;
16287 };
16288
16289 /**
16290  * is.empty
16291  * Test if `value` is empty.
16292  *
16293  * @param {Mixed} value value to test
16294  * @return {Boolean} true if `value` is empty, false otherwise
16295  * @api public
16296  */
16297
16298 is.empty = function (value) {
16299   var type = toString.call(value);
16300   var key;
16301
16302   if ('[object Array]' === type || '[object Arguments]' === type) {
16303     return value.length === 0;
16304   }
16305
16306   if ('[object Object]' === type) {
16307     for (key in value) if (owns.call(value, key)) return false;
16308     return true;
16309   }
16310
16311   if ('[object String]' === type) {
16312     return '' === value;
16313   }
16314
16315   return false;
16316 };
16317
16318 /**
16319  * is.equal
16320  * Test if `value` is equal to `other`.
16321  *
16322  * @param {Mixed} value value to test
16323  * @param {Mixed} other value to compare with
16324  * @return {Boolean} true if `value` is equal to `other`, false otherwise
16325  */
16326
16327 is.equal = function (value, other) {
16328   var type = toString.call(value)
16329   var key;
16330
16331   if (type !== toString.call(other)) {
16332     return false;
16333   }
16334
16335   if ('[object Object]' === type) {
16336     for (key in value) {
16337       if (!is.equal(value[key], other[key])) {
16338         return false;
16339       }
16340     }
16341     return true;
16342   }
16343
16344   if ('[object Array]' === type) {
16345     key = value.length;
16346     if (key !== other.length) {
16347       return false;
16348     }
16349     while (--key) {
16350       if (!is.equal(value[key], other[key])) {
16351         return false;
16352       }
16353     }
16354     return true;
16355   }
16356
16357   if ('[object Function]' === type) {
16358     return value.prototype === other.prototype;
16359   }
16360
16361   if ('[object Date]' === type) {
16362     return value.getTime() === other.getTime();
16363   }
16364
16365   return value === other;
16366 };
16367
16368 /**
16369  * is.hosted
16370  * Test if `value` is hosted by `host`.
16371  *
16372  * @param {Mixed} value to test
16373  * @param {Mixed} host host to test with
16374  * @return {Boolean} true if `value` is hosted by `host`, false otherwise
16375  * @api public
16376  */
16377
16378 is.hosted = function (value, host) {
16379   var type = typeof host[value];
16380   return type === 'object' ? !!host[value] : !NON_HOST_TYPES[type];
16381 };
16382
16383 /**
16384  * is.instance
16385  * Test if `value` is an instance of `constructor`.
16386  *
16387  * @param {Mixed} value value to test
16388  * @return {Boolean} true if `value` is an instance of `constructor`
16389  * @api public
16390  */
16391
16392 is.instance = is['instanceof'] = function (value, constructor) {
16393   return value instanceof constructor;
16394 };
16395
16396 /**
16397  * is.null
16398  * Test if `value` is null.
16399  *
16400  * @param {Mixed} value value to test
16401  * @return {Boolean} true if `value` is null, false otherwise
16402  * @api public
16403  */
16404
16405 is['null'] = function (value) {
16406   return value === null;
16407 };
16408
16409 /**
16410  * is.undefined
16411  * Test if `value` is undefined.
16412  *
16413  * @param {Mixed} value value to test
16414  * @return {Boolean} true if `value` is undefined, false otherwise
16415  * @api public
16416  */
16417
16418 is.undefined = function (value) {
16419   return value === undefined;
16420 };
16421
16422 /**
16423  * Test arguments.
16424  */
16425
16426 /**
16427  * is.arguments
16428  * Test if `value` is an arguments object.
16429  *
16430  * @param {Mixed} value value to test
16431  * @return {Boolean} true if `value` is an arguments object, false otherwise
16432  * @api public
16433  */
16434
16435 is.arguments = function (value) {
16436   var isStandardArguments = '[object Arguments]' === toString.call(value);
16437   var isOldArguments = !is.array(value) && is.arraylike(value) && is.object(value) && is.fn(value.callee);
16438   return isStandardArguments || isOldArguments;
16439 };
16440
16441 /**
16442  * Test array.
16443  */
16444
16445 /**
16446  * is.array
16447  * Test if 'value' is an array.
16448  *
16449  * @param {Mixed} value value to test
16450  * @return {Boolean} true if `value` is an array, false otherwise
16451  * @api public
16452  */
16453
16454 is.array = function (value) {
16455   return '[object Array]' === toString.call(value);
16456 };
16457
16458 /**
16459  * is.arguments.empty
16460  * Test if `value` is an empty arguments object.
16461  *
16462  * @param {Mixed} value value to test
16463  * @return {Boolean} true if `value` is an empty arguments object, false otherwise
16464  * @api public
16465  */
16466 is.arguments.empty = function (value) {
16467   return is.arguments(value) && value.length === 0;
16468 };
16469
16470 /**
16471  * is.array.empty
16472  * Test if `value` is an empty array.
16473  *
16474  * @param {Mixed} value value to test
16475  * @return {Boolean} true if `value` is an empty array, false otherwise
16476  * @api public
16477  */
16478 is.array.empty = function (value) {
16479   return is.array(value) && value.length === 0;
16480 };
16481
16482 /**
16483  * is.arraylike
16484  * Test if `value` is an arraylike object.
16485  *
16486  * @param {Mixed} value value to test
16487  * @return {Boolean} true if `value` is an arguments object, false otherwise
16488  * @api public
16489  */
16490
16491 is.arraylike = function (value) {
16492   return !!value && !is.boolean(value)
16493     && owns.call(value, 'length')
16494     && isFinite(value.length)
16495     && is.number(value.length)
16496     && value.length >= 0;
16497 };
16498
16499 /**
16500  * Test boolean.
16501  */
16502
16503 /**
16504  * is.boolean
16505  * Test if `value` is a boolean.
16506  *
16507  * @param {Mixed} value value to test
16508  * @return {Boolean} true if `value` is a boolean, false otherwise
16509  * @api public
16510  */
16511
16512 is.boolean = function (value) {
16513   return '[object Boolean]' === toString.call(value);
16514 };
16515
16516 /**
16517  * is.false
16518  * Test if `value` is false.
16519  *
16520  * @param {Mixed} value value to test
16521  * @return {Boolean} true if `value` is false, false otherwise
16522  * @api public
16523  */
16524
16525 is['false'] = function (value) {
16526   return is.boolean(value) && (value === false || value.valueOf() === false);
16527 };
16528
16529 /**
16530  * is.true
16531  * Test if `value` is true.
16532  *
16533  * @param {Mixed} value value to test
16534  * @return {Boolean} true if `value` is true, false otherwise
16535  * @api public
16536  */
16537
16538 is['true'] = function (value) {
16539   return is.boolean(value) && (value === true || value.valueOf() === true);
16540 };
16541
16542 /**
16543  * Test date.
16544  */
16545
16546 /**
16547  * is.date
16548  * Test if `value` is a date.
16549  *
16550  * @param {Mixed} value value to test
16551  * @return {Boolean} true if `value` is a date, false otherwise
16552  * @api public
16553  */
16554
16555 is.date = function (value) {
16556   return '[object Date]' === toString.call(value);
16557 };
16558
16559 /**
16560  * Test element.
16561  */
16562
16563 /**
16564  * is.element
16565  * Test if `value` is an html element.
16566  *
16567  * @param {Mixed} value value to test
16568  * @return {Boolean} true if `value` is an HTML Element, false otherwise
16569  * @api public
16570  */
16571
16572 is.element = function (value) {
16573   return value !== undefined
16574     && typeof HTMLElement !== 'undefined'
16575     && value instanceof HTMLElement
16576     && value.nodeType === 1;
16577 };
16578
16579 /**
16580  * Test error.
16581  */
16582
16583 /**
16584  * is.error
16585  * Test if `value` is an error object.
16586  *
16587  * @param {Mixed} value value to test
16588  * @return {Boolean} true if `value` is an error object, false otherwise
16589  * @api public
16590  */
16591
16592 is.error = function (value) {
16593   return '[object Error]' === toString.call(value);
16594 };
16595
16596 /**
16597  * Test function.
16598  */
16599
16600 /**
16601  * is.fn / is.function (deprecated)
16602  * Test if `value` is a function.
16603  *
16604  * @param {Mixed} value value to test
16605  * @return {Boolean} true if `value` is a function, false otherwise
16606  * @api public
16607  */
16608
16609 is.fn = is['function'] = function (value) {
16610   var isAlert = typeof window !== 'undefined' && value === window.alert;
16611   return isAlert || '[object Function]' === toString.call(value);
16612 };
16613
16614 /**
16615  * Test number.
16616  */
16617
16618 /**
16619  * is.number
16620  * Test if `value` is a number.
16621  *
16622  * @param {Mixed} value value to test
16623  * @return {Boolean} true if `value` is a number, false otherwise
16624  * @api public
16625  */
16626
16627 is.number = function (value) {
16628   return '[object Number]' === toString.call(value);
16629 };
16630
16631 /**
16632  * is.infinite
16633  * Test if `value` is positive or negative infinity.
16634  *
16635  * @param {Mixed} value value to test
16636  * @return {Boolean} true if `value` is positive or negative Infinity, false otherwise
16637  * @api public
16638  */
16639 is.infinite = function (value) {
16640   return value === Infinity || value === -Infinity;
16641 };
16642
16643 /**
16644  * is.decimal
16645  * Test if `value` is a decimal number.
16646  *
16647  * @param {Mixed} value value to test
16648  * @return {Boolean} true if `value` is a decimal number, false otherwise
16649  * @api public
16650  */
16651
16652 is.decimal = function (value) {
16653   return is.number(value) && !isActualNaN(value) && value % 1 !== 0;
16654 };
16655
16656 /**
16657  * is.divisibleBy
16658  * Test if `value` is divisible by `n`.
16659  *
16660  * @param {Number} value value to test
16661  * @param {Number} n dividend
16662  * @return {Boolean} true if `value` is divisible by `n`, false otherwise
16663  * @api public
16664  */
16665
16666 is.divisibleBy = function (value, n) {
16667   var isDividendInfinite = is.infinite(value);
16668   var isDivisorInfinite = is.infinite(n);
16669   var isNonZeroNumber = is.number(value) && !isActualNaN(value) && is.number(n) && !isActualNaN(n) && n !== 0;
16670   return isDividendInfinite || isDivisorInfinite || (isNonZeroNumber && value % n === 0);
16671 };
16672
16673 /**
16674  * is.int
16675  * Test if `value` is an integer.
16676  *
16677  * @param value to test
16678  * @return {Boolean} true if `value` is an integer, false otherwise
16679  * @api public
16680  */
16681
16682 is.int = function (value) {
16683   return is.number(value) && !isActualNaN(value) && value % 1 === 0;
16684 };
16685
16686 /**
16687  * is.maximum
16688  * Test if `value` is greater than 'others' values.
16689  *
16690  * @param {Number} value value to test
16691  * @param {Array} others values to compare with
16692  * @return {Boolean} true if `value` is greater than `others` values
16693  * @api public
16694  */
16695
16696 is.maximum = function (value, others) {
16697   if (isActualNaN(value)) {
16698     throw new TypeError('NaN is not a valid value');
16699   } else if (!is.arraylike(others)) {
16700     throw new TypeError('second argument must be array-like');
16701   }
16702   var len = others.length;
16703
16704   while (--len >= 0) {
16705     if (value < others[len]) {
16706       return false;
16707     }
16708   }
16709
16710   return true;
16711 };
16712
16713 /**
16714  * is.minimum
16715  * Test if `value` is less than `others` values.
16716  *
16717  * @param {Number} value value to test
16718  * @param {Array} others values to compare with
16719  * @return {Boolean} true if `value` is less than `others` values
16720  * @api public
16721  */
16722
16723 is.minimum = function (value, others) {
16724   if (isActualNaN(value)) {
16725     throw new TypeError('NaN is not a valid value');
16726   } else if (!is.arraylike(others)) {
16727     throw new TypeError('second argument must be array-like');
16728   }
16729   var len = others.length;
16730
16731   while (--len >= 0) {
16732     if (value > others[len]) {
16733       return false;
16734     }
16735   }
16736
16737   return true;
16738 };
16739
16740 /**
16741  * is.nan
16742  * Test if `value` is not a number.
16743  *
16744  * @param {Mixed} value value to test
16745  * @return {Boolean} true if `value` is not a number, false otherwise
16746  * @api public
16747  */
16748
16749 is.nan = function (value) {
16750   return !is.number(value) || value !== value;
16751 };
16752
16753 /**
16754  * is.even
16755  * Test if `value` is an even number.
16756  *
16757  * @param {Number} value value to test
16758  * @return {Boolean} true if `value` is an even number, false otherwise
16759  * @api public
16760  */
16761
16762 is.even = function (value) {
16763   return is.infinite(value) || (is.number(value) && value === value && value % 2 === 0);
16764 };
16765
16766 /**
16767  * is.odd
16768  * Test if `value` is an odd number.
16769  *
16770  * @param {Number} value value to test
16771  * @return {Boolean} true if `value` is an odd number, false otherwise
16772  * @api public
16773  */
16774
16775 is.odd = function (value) {
16776   return is.infinite(value) || (is.number(value) && value === value && value % 2 !== 0);
16777 };
16778
16779 /**
16780  * is.ge
16781  * Test if `value` is greater than or equal to `other`.
16782  *
16783  * @param {Number} value value to test
16784  * @param {Number} other value to compare with
16785  * @return {Boolean}
16786  * @api public
16787  */
16788
16789 is.ge = function (value, other) {
16790   if (isActualNaN(value) || isActualNaN(other)) {
16791     throw new TypeError('NaN is not a valid value');
16792   }
16793   return !is.infinite(value) && !is.infinite(other) && value >= other;
16794 };
16795
16796 /**
16797  * is.gt
16798  * Test if `value` is greater than `other`.
16799  *
16800  * @param {Number} value value to test
16801  * @param {Number} other value to compare with
16802  * @return {Boolean}
16803  * @api public
16804  */
16805
16806 is.gt = function (value, other) {
16807   if (isActualNaN(value) || isActualNaN(other)) {
16808     throw new TypeError('NaN is not a valid value');
16809   }
16810   return !is.infinite(value) && !is.infinite(other) && value > other;
16811 };
16812
16813 /**
16814  * is.le
16815  * Test if `value` is less than or equal to `other`.
16816  *
16817  * @param {Number} value value to test
16818  * @param {Number} other value to compare with
16819  * @return {Boolean} if 'value' is less than or equal to 'other'
16820  * @api public
16821  */
16822
16823 is.le = function (value, other) {
16824   if (isActualNaN(value) || isActualNaN(other)) {
16825     throw new TypeError('NaN is not a valid value');
16826   }
16827   return !is.infinite(value) && !is.infinite(other) && value <= other;
16828 };
16829
16830 /**
16831  * is.lt
16832  * Test if `value` is less than `other`.
16833  *
16834  * @param {Number} value value to test
16835  * @param {Number} other value to compare with
16836  * @return {Boolean} if `value` is less than `other`
16837  * @api public
16838  */
16839
16840 is.lt = function (value, other) {
16841   if (isActualNaN(value) || isActualNaN(other)) {
16842     throw new TypeError('NaN is not a valid value');
16843   }
16844   return !is.infinite(value) && !is.infinite(other) && value < other;
16845 };
16846
16847 /**
16848  * is.within
16849  * Test if `value` is within `start` and `finish`.
16850  *
16851  * @param {Number} value value to test
16852  * @param {Number} start lower bound
16853  * @param {Number} finish upper bound
16854  * @return {Boolean} true if 'value' is is within 'start' and 'finish'
16855  * @api public
16856  */
16857 is.within = function (value, start, finish) {
16858   if (isActualNaN(value) || isActualNaN(start) || isActualNaN(finish)) {
16859     throw new TypeError('NaN is not a valid value');
16860   } else if (!is.number(value) || !is.number(start) || !is.number(finish)) {
16861     throw new TypeError('all arguments must be numbers');
16862   }
16863   var isAnyInfinite = is.infinite(value) || is.infinite(start) || is.infinite(finish);
16864   return isAnyInfinite || (value >= start && value <= finish);
16865 };
16866
16867 /**
16868  * Test object.
16869  */
16870
16871 /**
16872  * is.object
16873  * Test if `value` is an object.
16874  *
16875  * @param {Mixed} value value to test
16876  * @return {Boolean} true if `value` is an object, false otherwise
16877  * @api public
16878  */
16879
16880 is.object = function (value) {
16881   return value && '[object Object]' === toString.call(value);
16882 };
16883
16884 /**
16885  * is.hash
16886  * Test if `value` is a hash - a plain object literal.
16887  *
16888  * @param {Mixed} value value to test
16889  * @return {Boolean} true if `value` is a hash, false otherwise
16890  * @api public
16891  */
16892
16893 is.hash = function (value) {
16894   return is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval;
16895 };
16896
16897 /**
16898  * Test regexp.
16899  */
16900
16901 /**
16902  * is.regexp
16903  * Test if `value` is a regular expression.
16904  *
16905  * @param {Mixed} value value to test
16906  * @return {Boolean} true if `value` is a regexp, false otherwise
16907  * @api public
16908  */
16909
16910 is.regexp = function (value) {
16911   return '[object RegExp]' === toString.call(value);
16912 };
16913
16914 /**
16915  * Test string.
16916  */
16917
16918 /**
16919  * is.string
16920  * Test if `value` is a string.
16921  *
16922  * @param {Mixed} value value to test
16923  * @return {Boolean} true if 'value' is a string, false otherwise
16924  * @api public
16925  */
16926
16927 is.string = function (value) {
16928   return '[object String]' === toString.call(value);
16929 };
16930
16931
16932 },{}],10:[function(require,module,exports){
16933
16934 var hasOwn = Object.prototype.hasOwnProperty;
16935 var toString = Object.prototype.toString;
16936
16937 module.exports = function forEach (obj, fn, ctx) {
16938     if (toString.call(fn) !== '[object Function]') {
16939         throw new TypeError('iterator must be a function');
16940     }
16941     var l = obj.length;
16942     if (l === +l) {
16943         for (var i = 0; i < l; i++) {
16944             fn.call(ctx, obj[i], i, obj);
16945         }
16946     } else {
16947         for (var k in obj) {
16948             if (hasOwn.call(obj, k)) {
16949                 fn.call(ctx, obj[k], k, obj);
16950             }
16951         }
16952     }
16953 };
16954
16955
16956 },{}]},{},[1])(1)
16957 });
16958 ;
16959 /*
16960  (c) 2015, Vladimir Agafonkin
16961  RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
16962  https://github.com/mourner/rbush
16963 */
16964
16965 (function () {
16966 'use strict';
16967
16968 function rbush(maxEntries, format) {
16969
16970     // jshint newcap: false, validthis: true
16971     if (!(this instanceof rbush)) return new rbush(maxEntries, format);
16972
16973     // max entries in a node is 9 by default; min node fill is 40% for best performance
16974     this._maxEntries = Math.max(4, maxEntries || 9);
16975     this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
16976
16977     if (format) {
16978         this._initFormat(format);
16979     }
16980
16981     this.clear();
16982 }
16983
16984 rbush.prototype = {
16985
16986     all: function () {
16987         return this._all(this.data, []);
16988     },
16989
16990     search: function (bbox) {
16991
16992         var node = this.data,
16993             result = [],
16994             toBBox = this.toBBox;
16995
16996         if (!intersects(bbox, node.bbox)) return result;
16997
16998         var nodesToSearch = [],
16999             i, len, child, childBBox;
17000
17001         while (node) {
17002             for (i = 0, len = node.children.length; i < len; i++) {
17003
17004                 child = node.children[i];
17005                 childBBox = node.leaf ? toBBox(child) : child.bbox;
17006
17007                 if (intersects(bbox, childBBox)) {
17008                     if (node.leaf) result.push(child);
17009                     else if (contains(bbox, childBBox)) this._all(child, result);
17010                     else nodesToSearch.push(child);
17011                 }
17012             }
17013             node = nodesToSearch.pop();
17014         }
17015
17016         return result;
17017     },
17018
17019     collides: function (bbox) {
17020
17021         var node = this.data,
17022             toBBox = this.toBBox;
17023
17024         if (!intersects(bbox, node.bbox)) return false;
17025
17026         var nodesToSearch = [],
17027             i, len, child, childBBox;
17028
17029         while (node) {
17030             for (i = 0, len = node.children.length; i < len; i++) {
17031
17032                 child = node.children[i];
17033                 childBBox = node.leaf ? toBBox(child) : child.bbox;
17034
17035                 if (intersects(bbox, childBBox)) {
17036                     if (node.leaf || contains(bbox, childBBox)) return true;
17037                     nodesToSearch.push(child);
17038                 }
17039             }
17040             node = nodesToSearch.pop();
17041         }
17042
17043         return false;
17044     },
17045
17046     load: function (data) {
17047         if (!(data && data.length)) return this;
17048
17049         if (data.length < this._minEntries) {
17050             for (var i = 0, len = data.length; i < len; i++) {
17051                 this.insert(data[i]);
17052             }
17053             return this;
17054         }
17055
17056         // recursively build the tree with the given data from stratch using OMT algorithm
17057         var node = this._build(data.slice(), 0, data.length - 1, 0);
17058
17059         if (!this.data.children.length) {
17060             // save as is if tree is empty
17061             this.data = node;
17062
17063         } else if (this.data.height === node.height) {
17064             // split root if trees have the same height
17065             this._splitRoot(this.data, node);
17066
17067         } else {
17068             if (this.data.height < node.height) {
17069                 // swap trees if inserted one is bigger
17070                 var tmpNode = this.data;
17071                 this.data = node;
17072                 node = tmpNode;
17073             }
17074
17075             // insert the small tree into the large tree at appropriate level
17076             this._insert(node, this.data.height - node.height - 1, true);
17077         }
17078
17079         return this;
17080     },
17081
17082     insert: function (item) {
17083         if (item) this._insert(item, this.data.height - 1);
17084         return this;
17085     },
17086
17087     clear: function () {
17088         this.data = {
17089             children: [],
17090             height: 1,
17091             bbox: empty(),
17092             leaf: true
17093         };
17094         return this;
17095     },
17096
17097     remove: function (item) {
17098         if (!item) return this;
17099
17100         var node = this.data,
17101             bbox = this.toBBox(item),
17102             path = [],
17103             indexes = [],
17104             i, parent, index, goingUp;
17105
17106         // depth-first iterative tree traversal
17107         while (node || path.length) {
17108
17109             if (!node) { // go up
17110                 node = path.pop();
17111                 parent = path[path.length - 1];
17112                 i = indexes.pop();
17113                 goingUp = true;
17114             }
17115
17116             if (node.leaf) { // check current node
17117                 index = node.children.indexOf(item);
17118
17119                 if (index !== -1) {
17120                     // item found, remove the item and condense tree upwards
17121                     node.children.splice(index, 1);
17122                     path.push(node);
17123                     this._condense(path);
17124                     return this;
17125                 }
17126             }
17127
17128             if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
17129                 path.push(node);
17130                 indexes.push(i);
17131                 i = 0;
17132                 parent = node;
17133                 node = node.children[0];
17134
17135             } else if (parent) { // go right
17136                 i++;
17137                 node = parent.children[i];
17138                 goingUp = false;
17139
17140             } else node = null; // nothing found
17141         }
17142
17143         return this;
17144     },
17145
17146     toBBox: function (item) { return item; },
17147
17148     compareMinX: function (a, b) { return a[0] - b[0]; },
17149     compareMinY: function (a, b) { return a[1] - b[1]; },
17150
17151     toJSON: function () { return this.data; },
17152
17153     fromJSON: function (data) {
17154         this.data = data;
17155         return this;
17156     },
17157
17158     _all: function (node, result) {
17159         var nodesToSearch = [];
17160         while (node) {
17161             if (node.leaf) result.push.apply(result, node.children);
17162             else nodesToSearch.push.apply(nodesToSearch, node.children);
17163
17164             node = nodesToSearch.pop();
17165         }
17166         return result;
17167     },
17168
17169     _build: function (items, left, right, height) {
17170
17171         var N = right - left + 1,
17172             M = this._maxEntries,
17173             node;
17174
17175         if (N <= M) {
17176             // reached leaf level; return leaf
17177             node = {
17178                 children: items.slice(left, right + 1),
17179                 height: 1,
17180                 bbox: null,
17181                 leaf: true
17182             };
17183             calcBBox(node, this.toBBox);
17184             return node;
17185         }
17186
17187         if (!height) {
17188             // target height of the bulk-loaded tree
17189             height = Math.ceil(Math.log(N) / Math.log(M));
17190
17191             // target number of root entries to maximize storage utilization
17192             M = Math.ceil(N / Math.pow(M, height - 1));
17193         }
17194
17195         node = {
17196             children: [],
17197             height: height,
17198             bbox: null,
17199             leaf: false
17200         };
17201
17202         // split the items into M mostly square tiles
17203
17204         var N2 = Math.ceil(N / M),
17205             N1 = N2 * Math.ceil(Math.sqrt(M)),
17206             i, j, right2, right3;
17207
17208         multiSelect(items, left, right, N1, this.compareMinX);
17209
17210         for (i = left; i <= right; i += N1) {
17211
17212             right2 = Math.min(i + N1 - 1, right);
17213
17214             multiSelect(items, i, right2, N2, this.compareMinY);
17215
17216             for (j = i; j <= right2; j += N2) {
17217
17218                 right3 = Math.min(j + N2 - 1, right2);
17219
17220                 // pack each entry recursively
17221                 node.children.push(this._build(items, j, right3, height - 1));
17222             }
17223         }
17224
17225         calcBBox(node, this.toBBox);
17226
17227         return node;
17228     },
17229
17230     _chooseSubtree: function (bbox, node, level, path) {
17231
17232         var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
17233
17234         while (true) {
17235             path.push(node);
17236
17237             if (node.leaf || path.length - 1 === level) break;
17238
17239             minArea = minEnlargement = Infinity;
17240
17241             for (i = 0, len = node.children.length; i < len; i++) {
17242                 child = node.children[i];
17243                 area = bboxArea(child.bbox);
17244                 enlargement = enlargedArea(bbox, child.bbox) - area;
17245
17246                 // choose entry with the least area enlargement
17247                 if (enlargement < minEnlargement) {
17248                     minEnlargement = enlargement;
17249                     minArea = area < minArea ? area : minArea;
17250                     targetNode = child;
17251
17252                 } else if (enlargement === minEnlargement) {
17253                     // otherwise choose one with the smallest area
17254                     if (area < minArea) {
17255                         minArea = area;
17256                         targetNode = child;
17257                     }
17258                 }
17259             }
17260
17261             node = targetNode;
17262         }
17263
17264         return node;
17265     },
17266
17267     _insert: function (item, level, isNode) {
17268
17269         var toBBox = this.toBBox,
17270             bbox = isNode ? item.bbox : toBBox(item),
17271             insertPath = [];
17272
17273         // find the best node for accommodating the item, saving all nodes along the path too
17274         var node = this._chooseSubtree(bbox, this.data, level, insertPath);
17275
17276         // put the item into the node
17277         node.children.push(item);
17278         extend(node.bbox, bbox);
17279
17280         // split on node overflow; propagate upwards if necessary
17281         while (level >= 0) {
17282             if (insertPath[level].children.length > this._maxEntries) {
17283                 this._split(insertPath, level);
17284                 level--;
17285             } else break;
17286         }
17287
17288         // adjust bboxes along the insertion path
17289         this._adjustParentBBoxes(bbox, insertPath, level);
17290     },
17291
17292     // split overflowed node into two
17293     _split: function (insertPath, level) {
17294
17295         var node = insertPath[level],
17296             M = node.children.length,
17297             m = this._minEntries;
17298
17299         this._chooseSplitAxis(node, m, M);
17300
17301         var splitIndex = this._chooseSplitIndex(node, m, M);
17302
17303         var newNode = {
17304             children: node.children.splice(splitIndex, node.children.length - splitIndex),
17305             height: node.height,
17306             bbox: null,
17307             leaf: false
17308         };
17309
17310         if (node.leaf) newNode.leaf = true;
17311
17312         calcBBox(node, this.toBBox);
17313         calcBBox(newNode, this.toBBox);
17314
17315         if (level) insertPath[level - 1].children.push(newNode);
17316         else this._splitRoot(node, newNode);
17317     },
17318
17319     _splitRoot: function (node, newNode) {
17320         // split root node
17321         this.data = {
17322             children: [node, newNode],
17323             height: node.height + 1,
17324             bbox: null,
17325             leaf: false
17326         };
17327         calcBBox(this.data, this.toBBox);
17328     },
17329
17330     _chooseSplitIndex: function (node, m, M) {
17331
17332         var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
17333
17334         minOverlap = minArea = Infinity;
17335
17336         for (i = m; i <= M - m; i++) {
17337             bbox1 = distBBox(node, 0, i, this.toBBox);
17338             bbox2 = distBBox(node, i, M, this.toBBox);
17339
17340             overlap = intersectionArea(bbox1, bbox2);
17341             area = bboxArea(bbox1) + bboxArea(bbox2);
17342
17343             // choose distribution with minimum overlap
17344             if (overlap < minOverlap) {
17345                 minOverlap = overlap;
17346                 index = i;
17347
17348                 minArea = area < minArea ? area : minArea;
17349
17350             } else if (overlap === minOverlap) {
17351                 // otherwise choose distribution with minimum area
17352                 if (area < minArea) {
17353                     minArea = area;
17354                     index = i;
17355                 }
17356             }
17357         }
17358
17359         return index;
17360     },
17361
17362     // sorts node children by the best axis for split
17363     _chooseSplitAxis: function (node, m, M) {
17364
17365         var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
17366             compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
17367             xMargin = this._allDistMargin(node, m, M, compareMinX),
17368             yMargin = this._allDistMargin(node, m, M, compareMinY);
17369
17370         // if total distributions margin value is minimal for x, sort by minX,
17371         // otherwise it's already sorted by minY
17372         if (xMargin < yMargin) node.children.sort(compareMinX);
17373     },
17374
17375     // total margin of all possible split distributions where each node is at least m full
17376     _allDistMargin: function (node, m, M, compare) {
17377
17378         node.children.sort(compare);
17379
17380         var toBBox = this.toBBox,
17381             leftBBox = distBBox(node, 0, m, toBBox),
17382             rightBBox = distBBox(node, M - m, M, toBBox),
17383             margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
17384             i, child;
17385
17386         for (i = m; i < M - m; i++) {
17387             child = node.children[i];
17388             extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
17389             margin += bboxMargin(leftBBox);
17390         }
17391
17392         for (i = M - m - 1; i >= m; i--) {
17393             child = node.children[i];
17394             extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
17395             margin += bboxMargin(rightBBox);
17396         }
17397
17398         return margin;
17399     },
17400
17401     _adjustParentBBoxes: function (bbox, path, level) {
17402         // adjust bboxes along the given tree path
17403         for (var i = level; i >= 0; i--) {
17404             extend(path[i].bbox, bbox);
17405         }
17406     },
17407
17408     _condense: function (path) {
17409         // go through the path, removing empty nodes and updating bboxes
17410         for (var i = path.length - 1, siblings; i >= 0; i--) {
17411             if (path[i].children.length === 0) {
17412                 if (i > 0) {
17413                     siblings = path[i - 1].children;
17414                     siblings.splice(siblings.indexOf(path[i]), 1);
17415
17416                 } else this.clear();
17417
17418             } else calcBBox(path[i], this.toBBox);
17419         }
17420     },
17421
17422     _initFormat: function (format) {
17423         // data format (minX, minY, maxX, maxY accessors)
17424
17425         // uses eval-type function compilation instead of just accepting a toBBox function
17426         // because the algorithms are very sensitive to sorting functions performance,
17427         // so they should be dead simple and without inner calls
17428
17429         // jshint evil: true
17430
17431         var compareArr = ['return a', ' - b', ';'];
17432
17433         this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
17434         this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
17435
17436         this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
17437     }
17438 };
17439
17440
17441 // calculate node's bbox from bboxes of its children
17442 function calcBBox(node, toBBox) {
17443     node.bbox = distBBox(node, 0, node.children.length, toBBox);
17444 }
17445
17446 // min bounding rectangle of node children from k to p-1
17447 function distBBox(node, k, p, toBBox) {
17448     var bbox = empty();
17449
17450     for (var i = k, child; i < p; i++) {
17451         child = node.children[i];
17452         extend(bbox, node.leaf ? toBBox(child) : child.bbox);
17453     }
17454
17455     return bbox;
17456 }
17457
17458 function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
17459
17460 function extend(a, b) {
17461     a[0] = Math.min(a[0], b[0]);
17462     a[1] = Math.min(a[1], b[1]);
17463     a[2] = Math.max(a[2], b[2]);
17464     a[3] = Math.max(a[3], b[3]);
17465     return a;
17466 }
17467
17468 function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
17469 function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
17470
17471 function bboxArea(a)   { return (a[2] - a[0]) * (a[3] - a[1]); }
17472 function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
17473
17474 function enlargedArea(a, b) {
17475     return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
17476            (Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
17477 }
17478
17479 function intersectionArea(a, b) {
17480     var minX = Math.max(a[0], b[0]),
17481         minY = Math.max(a[1], b[1]),
17482         maxX = Math.min(a[2], b[2]),
17483         maxY = Math.min(a[3], b[3]);
17484
17485     return Math.max(0, maxX - minX) *
17486            Math.max(0, maxY - minY);
17487 }
17488
17489 function contains(a, b) {
17490     return a[0] <= b[0] &&
17491            a[1] <= b[1] &&
17492            b[2] <= a[2] &&
17493            b[3] <= a[3];
17494 }
17495
17496 function intersects(a, b) {
17497     return b[0] <= a[2] &&
17498            b[1] <= a[3] &&
17499            b[2] >= a[0] &&
17500            b[3] >= a[1];
17501 }
17502
17503 // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
17504 // combines selection algorithm with binary divide & conquer approach
17505
17506 function multiSelect(arr, left, right, n, compare) {
17507     var stack = [left, right],
17508         mid;
17509
17510     while (stack.length) {
17511         right = stack.pop();
17512         left = stack.pop();
17513
17514         if (right - left <= n) continue;
17515
17516         mid = left + Math.ceil((right - left) / n / 2) * n;
17517         select(arr, left, right, mid, compare);
17518
17519         stack.push(left, mid, mid, right);
17520     }
17521 }
17522
17523 // Floyd-Rivest selection algorithm:
17524 // sort an array between left and right (inclusive) so that the smallest k elements come first (unordered)
17525 function select(arr, left, right, k, compare) {
17526     var n, i, z, s, sd, newLeft, newRight, t, j;
17527
17528     while (right > left) {
17529         if (right - left > 600) {
17530             n = right - left + 1;
17531             i = k - left + 1;
17532             z = Math.log(n);
17533             s = 0.5 * Math.exp(2 * z / 3);
17534             sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1);
17535             newLeft = Math.max(left, Math.floor(k - i * s / n + sd));
17536             newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd));
17537             select(arr, newLeft, newRight, k, compare);
17538         }
17539
17540         t = arr[k];
17541         i = left;
17542         j = right;
17543
17544         swap(arr, left, k);
17545         if (compare(arr[right], t) > 0) swap(arr, left, right);
17546
17547         while (i < j) {
17548             swap(arr, i, j);
17549             i++;
17550             j--;
17551             while (compare(arr[i], t) < 0) i++;
17552             while (compare(arr[j], t) > 0) j--;
17553         }
17554
17555         if (compare(arr[left], t) === 0) swap(arr, left, j);
17556         else {
17557             j++;
17558             swap(arr, j, right);
17559         }
17560
17561         if (j <= k) left = j + 1;
17562         if (k <= j) right = j - 1;
17563     }
17564 }
17565
17566 function swap(arr, i, j) {
17567     var tmp = arr[i];
17568     arr[i] = arr[j];
17569     arr[j] = tmp;
17570 }
17571
17572
17573 // export as AMD/CommonJS module or global variable
17574 if (typeof define === 'function' && define.amd) define('rbush', function () { return rbush; });
17575 else if (typeof module !== 'undefined') module.exports = rbush;
17576 else if (typeof self !== 'undefined') self.rbush = rbush;
17577 else window.rbush = rbush;
17578
17579 })();
17580 (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;
17581 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){
17582 module.exports = element;
17583 module.exports.pair = pair;
17584 module.exports.format = format;
17585 module.exports.formatPair = formatPair;
17586
17587 function element(x, dims) {
17588     return search(x, dims).val;
17589 }
17590
17591 function formatPair(x) {
17592     return format(x.lat, 'lat') + ' ' + format(x.lon, 'lon');
17593 }
17594
17595 // Is 0 North or South?
17596 function format(x, dim) {
17597     var dirs = {
17598             lat: ['N', 'S'],
17599             lon: ['E', 'W']
17600         }[dim] || '',
17601         dir = dirs[x >= 0 ? 0 : 1],
17602         abs = Math.abs(x),
17603         whole = Math.floor(abs),
17604         fraction = abs - whole,
17605         fractionMinutes = fraction * 60,
17606         minutes = Math.floor(fractionMinutes),
17607         seconds = Math.floor((fractionMinutes - minutes) * 60);
17608
17609     return whole + '° ' +
17610         (minutes ? minutes + "' " : '') +
17611         (seconds ? seconds + '" ' : '') + dir;
17612 }
17613
17614 function search(x, dims, r) {
17615     if (!dims) dims = 'NSEW';
17616     if (typeof x !== 'string') return { val: null, regex: r };
17617     r = r || /[\s\,]*([\-|\—|\―]?[0-9.]+)°? *(?:([0-9.]+)['’′‘] *)?(?:([0-9.]+)(?:''|"|”|″) *)?([NSEW])?/gi;
17618     var m = r.exec(x);
17619     if (!m) return { val: null, regex: r };
17620     else if (m[4] && dims.indexOf(m[4]) === -1) return { val: null, regex: r };
17621     else return {
17622         val: (((m[1]) ? parseFloat(m[1]) : 0) +
17623             ((m[2] ? parseFloat(m[2]) / 60 : 0)) +
17624             ((m[3] ? parseFloat(m[3]) / 3600 : 0))) *
17625             ((m[4] && m[4] === 'S' || m[4] === 'W') ? -1 : 1),
17626         regex: r,
17627         raw: m[0],
17628         dim: m[4]
17629     };
17630 }
17631
17632 function pair(x, dims) {
17633     x = x.trim();
17634     var one = search(x, dims);
17635     if (one.val === null) return null;
17636     var two = search(x, dims, one.regex);
17637     if (two.val === null) return null;
17638     // null if one/two are not contiguous.
17639     if (one.raw + two.raw !== x) return null;
17640     if (one.dim) return swapdim(one.val, two.val, one.dim);
17641     else return [one.val, two.val];
17642 }
17643
17644 function swapdim(a, b, dim) {
17645     if (dim == 'N' || dim == 'S') return [a, b];
17646     if (dim == 'W' || dim == 'E') return [b, a];
17647 }
17648
17649 },{}]},{},[1])
17650 (1)
17651 });
17652 ;toGeoJSON = (function() {
17653     'use strict';
17654
17655     var removeSpace = (/\s*/g),
17656         trimSpace = (/^\s*|\s*$/g),
17657         splitSpace = (/\s+/);
17658     // generate a short, numeric hash of a string
17659     function okhash(x) {
17660         if (!x || !x.length) return 0;
17661         for (var i = 0, h = 0; i < x.length; i++) {
17662             h = ((h << 5) - h) + x.charCodeAt(i) | 0;
17663         } return h;
17664     }
17665     // all Y children of X
17666     function get(x, y) { return x.getElementsByTagName(y); }
17667     function attr(x, y) { return x.getAttribute(y); }
17668     function attrf(x, y) { return parseFloat(attr(x, y)); }
17669     // one Y child of X, if any, otherwise null
17670     function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
17671     // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
17672     function norm(el) { if (el.normalize) { el.normalize(); } return el; }
17673     // cast array x into numbers
17674     function numarray(x) {
17675         for (var j = 0, o = []; j < x.length; j++) o[j] = parseFloat(x[j]);
17676         return o;
17677     }
17678     function clean(x) {
17679         var o = {};
17680         for (var i in x) if (x[i]) o[i] = x[i];
17681         return o;
17682     }
17683     // get the content of a text node, if any
17684     function nodeVal(x) { if (x) {norm(x);} return x && x.firstChild && x.firstChild.nodeValue; }
17685     // get one coordinate from a coordinate array, if any
17686     function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
17687     // get all coordinates from a coordinate array as [[],[]]
17688     function coord(v) {
17689         var coords = v.replace(trimSpace, '').split(splitSpace),
17690             o = [];
17691         for (var i = 0; i < coords.length; i++) {
17692             o.push(coord1(coords[i]));
17693         }
17694         return o;
17695     }
17696     function coordPair(x) { return [attrf(x, 'lon'), attrf(x, 'lat')]; }
17697
17698     // create a new feature collection parent object
17699     function fc() {
17700         return {
17701             type: 'FeatureCollection',
17702             features: []
17703         };
17704     }
17705
17706     var styleSupport = false;
17707     if (typeof XMLSerializer !== 'undefined') {
17708         var serializer = new XMLSerializer();
17709         styleSupport = true;
17710     }
17711     function xml2str(str) { return serializer.serializeToString(str); }
17712
17713     var t = {
17714         kml: function(doc, o) {
17715             o = o || {};
17716
17717             var gj = fc(),
17718                 // styleindex keeps track of hashed styles in order to match features
17719                 styleIndex = {},
17720                 // atomic geospatial types supported by KML - MultiGeometry is
17721                 // handled separately
17722                 geotypes = ['Polygon', 'LineString', 'Point', 'Track'],
17723                 // all root placemarks in the file
17724                 placemarks = get(doc, 'Placemark'),
17725                 styles = get(doc, 'Style');
17726
17727             if (styleSupport) for (var k = 0; k < styles.length; k++) {
17728                 styleIndex['#' + attr(styles[k], 'id')] = okhash(xml2str(styles[k])).toString(16);
17729             }
17730             for (var j = 0; j < placemarks.length; j++) {
17731                 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
17732             }
17733             function gxCoord(v) { return numarray(v.split(' ')); }
17734             function gxCoords(root) {
17735                 var elems = get(root, 'coord', 'gx'), coords = [];
17736                 for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
17737                 return coords;
17738             }
17739             function getGeometry(root) {
17740                 var geomNode, geomNodes, i, j, k, geoms = [];
17741                 if (get1(root, 'MultiGeometry')) return getGeometry(get1(root, 'MultiGeometry'));
17742                 if (get1(root, 'MultiTrack')) return getGeometry(get1(root, 'MultiTrack'));
17743                 for (i = 0; i < geotypes.length; i++) {
17744                     geomNodes = get(root, geotypes[i]);
17745                     if (geomNodes) {
17746                         for (j = 0; j < geomNodes.length; j++) {
17747                             geomNode = geomNodes[j];
17748                             if (geotypes[i] == 'Point') {
17749                                 geoms.push({
17750                                     type: 'Point',
17751                                     coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
17752                                 });
17753                             } else if (geotypes[i] == 'LineString') {
17754                                 geoms.push({
17755                                     type: 'LineString',
17756                                     coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
17757                                 });
17758                             } else if (geotypes[i] == 'Polygon') {
17759                                 var rings = get(geomNode, 'LinearRing'),
17760                                     coords = [];
17761                                 for (k = 0; k < rings.length; k++) {
17762                                     coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
17763                                 }
17764                                 geoms.push({
17765                                     type: 'Polygon',
17766                                     coordinates: coords
17767                                 });
17768                             } else if (geotypes[i] == 'Track') {
17769                                 geoms.push({
17770                                     type: 'LineString',
17771                                     coordinates: gxCoords(geomNode)
17772                                 });
17773                             }
17774                         }
17775                     }
17776                 }
17777                 return geoms;
17778             }
17779             function getPlacemark(root) {
17780                 var geoms = getGeometry(root), i, properties = {},
17781                     name = nodeVal(get1(root, 'name')),
17782                     styleUrl = nodeVal(get1(root, 'styleUrl')),
17783                     description = nodeVal(get1(root, 'description')),
17784                     extendedData = get1(root, 'ExtendedData');
17785
17786                 if (!geoms.length) return [];
17787                 if (name) properties.name = name;
17788                 if (styleUrl && styleIndex[styleUrl]) {
17789                     properties.styleUrl = styleUrl;
17790                     properties.styleHash = styleIndex[styleUrl];
17791                 }
17792                 if (description) properties.description = description;
17793                 if (extendedData) {
17794                     var datas = get(extendedData, 'Data'),
17795                         simpleDatas = get(extendedData, 'SimpleData');
17796
17797                     for (i = 0; i < datas.length; i++) {
17798                         properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
17799                     }
17800                     for (i = 0; i < simpleDatas.length; i++) {
17801                         properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
17802                     }
17803                 }
17804                 return [{
17805                     type: 'Feature',
17806                     geometry: (geoms.length === 1) ? geoms[0] : {
17807                         type: 'GeometryCollection',
17808                         geometries: geoms
17809                     },
17810                     properties: properties
17811                 }];
17812             }
17813             return gj;
17814         },
17815         gpx: function(doc, o) {
17816             var i,
17817                 tracks = get(doc, 'trk'),
17818                 routes = get(doc, 'rte'),
17819                 waypoints = get(doc, 'wpt'),
17820                 // a feature collection
17821                 gj = fc();
17822             for (i = 0; i < tracks.length; i++) {
17823                 gj.features.push(getLinestring(tracks[i], 'trkpt'));
17824             }
17825             for (i = 0; i < routes.length; i++) {
17826                 gj.features.push(getLinestring(routes[i], 'rtept'));
17827             }
17828             for (i = 0; i < waypoints.length; i++) {
17829                 gj.features.push(getPoint(waypoints[i]));
17830             }
17831             function getLinestring(node, pointname) {
17832                 var j, pts = get(node, pointname), line = [];
17833                 for (j = 0; j < pts.length; j++) {
17834                     line.push(coordPair(pts[j]));
17835                 }
17836                 return {
17837                     type: 'Feature',
17838                     properties: getProperties(node),
17839                     geometry: {
17840                         type: 'LineString',
17841                         coordinates: line
17842                     }
17843                 };
17844             }
17845             function getPoint(node) {
17846                 var prop = getProperties(node);
17847                 prop.ele = nodeVal(get1(node, 'ele'));
17848                 prop.sym = nodeVal(get1(node, 'sym'));
17849                 return {
17850                     type: 'Feature',
17851                     properties: prop,
17852                     geometry: {
17853                         type: 'Point',
17854                         coordinates: coordPair(node)
17855                     }
17856                 };
17857             }
17858             function getProperties(node) {
17859                 var meta = ['name', 'desc', 'author', 'copyright', 'link',
17860                             'time', 'keywords'],
17861                     prop = {},
17862                     k;
17863                 for (k = 0; k < meta.length; k++) {
17864                     prop[meta[k]] = nodeVal(get1(node, meta[k]));
17865                 }
17866                 return clean(prop);
17867             }
17868             return gj;
17869         }
17870     };
17871     return t;
17872 })();
17873
17874 if (typeof module !== 'undefined') module.exports = toGeoJSON;
17875 /**
17876  * marked - a markdown parser
17877  * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
17878  * https://github.com/chjj/marked
17879  */
17880
17881 ;(function() {
17882
17883 /**
17884  * Block-Level Grammar
17885  */
17886
17887 var block = {
17888   newline: /^\n+/,
17889   code: /^( {4}[^\n]+\n*)+/,
17890   fences: noop,
17891   hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17892   heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17893   nptable: noop,
17894   lheading: /^([^\n]+)\n *(=|-){3,} *\n*/,
17895   blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
17896   list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17897   html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
17898   def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17899   table: noop,
17900   paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17901   text: /^[^\n]+/
17902 };
17903
17904 block.bullet = /(?:[*+-]|\d+\.)/;
17905 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17906 block.item = replace(block.item, 'gm')
17907   (/bull/g, block.bullet)
17908   ();
17909
17910 block.list = replace(block.list)
17911   (/bull/g, block.bullet)
17912   ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)
17913   ();
17914
17915 block._tag = '(?!(?:'
17916   + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17917   + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17918   + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b';
17919
17920 block.html = replace(block.html)
17921   ('comment', /<!--[\s\S]*?-->/)
17922   ('closed', /<(tag)[\s\S]+?<\/\1>/)
17923   ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17924   (/tag/g, block._tag)
17925   ();
17926
17927 block.paragraph = replace(block.paragraph)
17928   ('hr', block.hr)
17929   ('heading', block.heading)
17930   ('lheading', block.lheading)
17931   ('blockquote', block.blockquote)
17932   ('tag', '<' + block._tag)
17933   ('def', block.def)
17934   ();
17935
17936 /**
17937  * Normal Block Grammar
17938  */
17939
17940 block.normal = merge({}, block);
17941
17942 /**
17943  * GFM Block Grammar
17944  */
17945
17946 block.gfm = merge({}, block.normal, {
17947   fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
17948   paragraph: /^/
17949 });
17950
17951 block.gfm.paragraph = replace(block.paragraph)
17952   ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|')
17953   ();
17954
17955 /**
17956  * GFM + Tables Block Grammar
17957  */
17958
17959 block.tables = merge({}, block.gfm, {
17960   nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17961   table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17962 });
17963
17964 /**
17965  * Block Lexer
17966  */
17967
17968 function Lexer(options) {
17969   this.tokens = [];
17970   this.tokens.links = {};
17971   this.options = options || marked.defaults;
17972   this.rules = block.normal;
17973
17974   if (this.options.gfm) {
17975     if (this.options.tables) {
17976       this.rules = block.tables;
17977     } else {
17978       this.rules = block.gfm;
17979     }
17980   }
17981 }
17982
17983 /**
17984  * Expose Block Rules
17985  */
17986
17987 Lexer.rules = block;
17988
17989 /**
17990  * Static Lex Method
17991  */
17992
17993 Lexer.lex = function(src, options) {
17994   var lexer = new Lexer(options);
17995   return lexer.lex(src);
17996 };
17997
17998 /**
17999  * Preprocessing
18000  */
18001
18002 Lexer.prototype.lex = function(src) {
18003   src = src
18004     .replace(/\r\n|\r/g, '\n')
18005     .replace(/\t/g, '    ')
18006     .replace(/\u00a0/g, ' ')
18007     .replace(/\u2424/g, '\n');
18008
18009   return this.token(src, true);
18010 };
18011
18012 /**
18013  * Lexing
18014  */
18015
18016 Lexer.prototype.token = function(src, top) {
18017   var src = src.replace(/^ +$/gm, '')
18018     , next
18019     , loose
18020     , cap
18021     , bull
18022     , b
18023     , item
18024     , space
18025     , i
18026     , l;
18027
18028   while (src) {
18029     // newline
18030     if (cap = this.rules.newline.exec(src)) {
18031       src = src.substring(cap[0].length);
18032       if (cap[0].length > 1) {
18033         this.tokens.push({
18034           type: 'space'
18035         });
18036       }
18037     }
18038
18039     // code
18040     if (cap = this.rules.code.exec(src)) {
18041       src = src.substring(cap[0].length);
18042       cap = cap[0].replace(/^ {4}/gm, '');
18043       this.tokens.push({
18044         type: 'code',
18045         text: !this.options.pedantic
18046           ? cap.replace(/\n+$/, '')
18047           : cap
18048       });
18049       continue;
18050     }
18051
18052     // fences (gfm)
18053     if (cap = this.rules.fences.exec(src)) {
18054       src = src.substring(cap[0].length);
18055       this.tokens.push({
18056         type: 'code',
18057         lang: cap[2],
18058         text: cap[3]
18059       });
18060       continue;
18061     }
18062
18063     // heading
18064     if (cap = this.rules.heading.exec(src)) {
18065       src = src.substring(cap[0].length);
18066       this.tokens.push({
18067         type: 'heading',
18068         depth: cap[1].length,
18069         text: cap[2]
18070       });
18071       continue;
18072     }
18073
18074     // table no leading pipe (gfm)
18075     if (top && (cap = this.rules.nptable.exec(src))) {
18076       src = src.substring(cap[0].length);
18077
18078       item = {
18079         type: 'table',
18080         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18081         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18082         cells: cap[3].replace(/\n$/, '').split('\n')
18083       };
18084
18085       for (i = 0; i < item.align.length; i++) {
18086         if (/^ *-+: *$/.test(item.align[i])) {
18087           item.align[i] = 'right';
18088         } else if (/^ *:-+: *$/.test(item.align[i])) {
18089           item.align[i] = 'center';
18090         } else if (/^ *:-+ *$/.test(item.align[i])) {
18091           item.align[i] = 'left';
18092         } else {
18093           item.align[i] = null;
18094         }
18095       }
18096
18097       for (i = 0; i < item.cells.length; i++) {
18098         item.cells[i] = item.cells[i].split(/ *\| */);
18099       }
18100
18101       this.tokens.push(item);
18102
18103       continue;
18104     }
18105
18106     // lheading
18107     if (cap = this.rules.lheading.exec(src)) {
18108       src = src.substring(cap[0].length);
18109       this.tokens.push({
18110         type: 'heading',
18111         depth: cap[2] === '=' ? 1 : 2,
18112         text: cap[1]
18113       });
18114       continue;
18115     }
18116
18117     // hr
18118     if (cap = this.rules.hr.exec(src)) {
18119       src = src.substring(cap[0].length);
18120       this.tokens.push({
18121         type: 'hr'
18122       });
18123       continue;
18124     }
18125
18126     // blockquote
18127     if (cap = this.rules.blockquote.exec(src)) {
18128       src = src.substring(cap[0].length);
18129
18130       this.tokens.push({
18131         type: 'blockquote_start'
18132       });
18133
18134       cap = cap[0].replace(/^ *> ?/gm, '');
18135
18136       // Pass `top` to keep the current
18137       // "toplevel" state. This is exactly
18138       // how markdown.pl works.
18139       this.token(cap, top);
18140
18141       this.tokens.push({
18142         type: 'blockquote_end'
18143       });
18144
18145       continue;
18146     }
18147
18148     // list
18149     if (cap = this.rules.list.exec(src)) {
18150       src = src.substring(cap[0].length);
18151       bull = cap[2];
18152
18153       this.tokens.push({
18154         type: 'list_start',
18155         ordered: bull.length > 1
18156       });
18157
18158       // Get each top-level item.
18159       cap = cap[0].match(this.rules.item);
18160
18161       next = false;
18162       l = cap.length;
18163       i = 0;
18164
18165       for (; i < l; i++) {
18166         item = cap[i];
18167
18168         // Remove the list item's bullet
18169         // so it is seen as the next token.
18170         space = item.length;
18171         item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18172
18173         // Outdent whatever the
18174         // list item contains. Hacky.
18175         if (~item.indexOf('\n ')) {
18176           space -= item.length;
18177           item = !this.options.pedantic
18178             ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18179             : item.replace(/^ {1,4}/gm, '');
18180         }
18181
18182         // Determine whether the next list item belongs here.
18183         // Backpedal if it does not belong in this list.
18184         if (this.options.smartLists && i !== l - 1) {
18185           b = block.bullet.exec(cap[i+1])[0];
18186           if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18187             src = cap.slice(i + 1).join('\n') + src;
18188             i = l - 1;
18189           }
18190         }
18191
18192         // Determine whether item is loose or not.
18193         // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18194         // for discount behavior.
18195         loose = next || /\n\n(?!\s*$)/.test(item);
18196         if (i !== l - 1) {
18197           next = item[item.length-1] === '\n';
18198           if (!loose) loose = next;
18199         }
18200
18201         this.tokens.push({
18202           type: loose
18203             ? 'loose_item_start'
18204             : 'list_item_start'
18205         });
18206
18207         // Recurse.
18208         this.token(item, false);
18209
18210         this.tokens.push({
18211           type: 'list_item_end'
18212         });
18213       }
18214
18215       this.tokens.push({
18216         type: 'list_end'
18217       });
18218
18219       continue;
18220     }
18221
18222     // html
18223     if (cap = this.rules.html.exec(src)) {
18224       src = src.substring(cap[0].length);
18225       this.tokens.push({
18226         type: this.options.sanitize
18227           ? 'paragraph'
18228           : 'html',
18229         pre: cap[1] === 'pre' || cap[1] === 'script',
18230         text: cap[0]
18231       });
18232       continue;
18233     }
18234
18235     // def
18236     if (top && (cap = this.rules.def.exec(src))) {
18237       src = src.substring(cap[0].length);
18238       this.tokens.links[cap[1].toLowerCase()] = {
18239         href: cap[2],
18240         title: cap[3]
18241       };
18242       continue;
18243     }
18244
18245     // table (gfm)
18246     if (top && (cap = this.rules.table.exec(src))) {
18247       src = src.substring(cap[0].length);
18248
18249       item = {
18250         type: 'table',
18251         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18252         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18253         cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18254       };
18255
18256       for (i = 0; i < item.align.length; i++) {
18257         if (/^ *-+: *$/.test(item.align[i])) {
18258           item.align[i] = 'right';
18259         } else if (/^ *:-+: *$/.test(item.align[i])) {
18260           item.align[i] = 'center';
18261         } else if (/^ *:-+ *$/.test(item.align[i])) {
18262           item.align[i] = 'left';
18263         } else {
18264           item.align[i] = null;
18265         }
18266       }
18267
18268       for (i = 0; i < item.cells.length; i++) {
18269         item.cells[i] = item.cells[i]
18270           .replace(/^ *\| *| *\| *$/g, '')
18271           .split(/ *\| */);
18272       }
18273
18274       this.tokens.push(item);
18275
18276       continue;
18277     }
18278
18279     // top-level paragraph
18280     if (top && (cap = this.rules.paragraph.exec(src))) {
18281       src = src.substring(cap[0].length);
18282       this.tokens.push({
18283         type: 'paragraph',
18284         text: cap[1][cap[1].length-1] === '\n'
18285           ? cap[1].slice(0, -1)
18286           : cap[1]
18287       });
18288       continue;
18289     }
18290
18291     // text
18292     if (cap = this.rules.text.exec(src)) {
18293       // Top-level should never reach here.
18294       src = src.substring(cap[0].length);
18295       this.tokens.push({
18296         type: 'text',
18297         text: cap[0]
18298       });
18299       continue;
18300     }
18301
18302     if (src) {
18303       throw new
18304         Error('Infinite loop on byte: ' + src.charCodeAt(0));
18305     }
18306   }
18307
18308   return this.tokens;
18309 };
18310
18311 /**
18312  * Inline-Level Grammar
18313  */
18314
18315 var inline = {
18316   escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18317   autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18318   url: noop,
18319   tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18320   link: /^!?\[(inside)\]\(href\)/,
18321   reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18322   nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18323   strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18324   em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18325   code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18326   br: /^ {2,}\n(?!\s*$)/,
18327   del: noop,
18328   text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18329 };
18330
18331 inline._inside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
18332 inline._href = /\s*<?([^\s]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18333
18334 inline.link = replace(inline.link)
18335   ('inside', inline._inside)
18336   ('href', inline._href)
18337   ();
18338
18339 inline.reflink = replace(inline.reflink)
18340   ('inside', inline._inside)
18341   ();
18342
18343 /**
18344  * Normal Inline Grammar
18345  */
18346
18347 inline.normal = merge({}, inline);
18348
18349 /**
18350  * Pedantic Inline Grammar
18351  */
18352
18353 inline.pedantic = merge({}, inline.normal, {
18354   strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18355   em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18356 });
18357
18358 /**
18359  * GFM Inline Grammar
18360  */
18361
18362 inline.gfm = merge({}, inline.normal, {
18363   escape: replace(inline.escape)('])', '~|])')(),
18364   url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18365   del: /^~~(?=\S)([\s\S]*?\S)~~/,
18366   text: replace(inline.text)
18367     (']|', '~]|')
18368     ('|', '|https?://|')
18369     ()
18370 });
18371
18372 /**
18373  * GFM + Line Breaks Inline Grammar
18374  */
18375
18376 inline.breaks = merge({}, inline.gfm, {
18377   br: replace(inline.br)('{2,}', '*')(),
18378   text: replace(inline.gfm.text)('{2,}', '*')()
18379 });
18380
18381 /**
18382  * Inline Lexer & Compiler
18383  */
18384
18385 function InlineLexer(links, options) {
18386   this.options = options || marked.defaults;
18387   this.links = links;
18388   this.rules = inline.normal;
18389
18390   if (!this.links) {
18391     throw new
18392       Error('Tokens array requires a `links` property.');
18393   }
18394
18395   if (this.options.gfm) {
18396     if (this.options.breaks) {
18397       this.rules = inline.breaks;
18398     } else {
18399       this.rules = inline.gfm;
18400     }
18401   } else if (this.options.pedantic) {
18402     this.rules = inline.pedantic;
18403   }
18404 }
18405
18406 /**
18407  * Expose Inline Rules
18408  */
18409
18410 InlineLexer.rules = inline;
18411
18412 /**
18413  * Static Lexing/Compiling Method
18414  */
18415
18416 InlineLexer.output = function(src, links, options) {
18417   var inline = new InlineLexer(links, options);
18418   return inline.output(src);
18419 };
18420
18421 /**
18422  * Lexing/Compiling
18423  */
18424
18425 InlineLexer.prototype.output = function(src) {
18426   var out = ''
18427     , link
18428     , text
18429     , href
18430     , cap;
18431
18432   while (src) {
18433     // escape
18434     if (cap = this.rules.escape.exec(src)) {
18435       src = src.substring(cap[0].length);
18436       out += cap[1];
18437       continue;
18438     }
18439
18440     // autolink
18441     if (cap = this.rules.autolink.exec(src)) {
18442       src = src.substring(cap[0].length);
18443       if (cap[2] === '@') {
18444         text = cap[1][6] === ':'
18445           ? this.mangle(cap[1].substring(7))
18446           : this.mangle(cap[1]);
18447         href = this.mangle('mailto:') + text;
18448       } else {
18449         text = escape(cap[1]);
18450         href = text;
18451       }
18452       out += '<a href="'
18453         + href
18454         + '">'
18455         + text
18456         + '</a>';
18457       continue;
18458     }
18459
18460     // url (gfm)
18461     if (cap = this.rules.url.exec(src)) {
18462       src = src.substring(cap[0].length);
18463       text = escape(cap[1]);
18464       href = text;
18465       out += '<a href="'
18466         + href
18467         + '">'
18468         + text
18469         + '</a>';
18470       continue;
18471     }
18472
18473     // tag
18474     if (cap = this.rules.tag.exec(src)) {
18475       src = src.substring(cap[0].length);
18476       out += this.options.sanitize
18477         ? escape(cap[0])
18478         : cap[0];
18479       continue;
18480     }
18481
18482     // link
18483     if (cap = this.rules.link.exec(src)) {
18484       src = src.substring(cap[0].length);
18485       out += this.outputLink(cap, {
18486         href: cap[2],
18487         title: cap[3]
18488       });
18489       continue;
18490     }
18491
18492     // reflink, nolink
18493     if ((cap = this.rules.reflink.exec(src))
18494         || (cap = this.rules.nolink.exec(src))) {
18495       src = src.substring(cap[0].length);
18496       link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18497       link = this.links[link.toLowerCase()];
18498       if (!link || !link.href) {
18499         out += cap[0][0];
18500         src = cap[0].substring(1) + src;
18501         continue;
18502       }
18503       out += this.outputLink(cap, link);
18504       continue;
18505     }
18506
18507     // strong
18508     if (cap = this.rules.strong.exec(src)) {
18509       src = src.substring(cap[0].length);
18510       out += '<strong>'
18511         + this.output(cap[2] || cap[1])
18512         + '</strong>';
18513       continue;
18514     }
18515
18516     // em
18517     if (cap = this.rules.em.exec(src)) {
18518       src = src.substring(cap[0].length);
18519       out += '<em>'
18520         + this.output(cap[2] || cap[1])
18521         + '</em>';
18522       continue;
18523     }
18524
18525     // code
18526     if (cap = this.rules.code.exec(src)) {
18527       src = src.substring(cap[0].length);
18528       out += '<code>'
18529         + escape(cap[2], true)
18530         + '</code>';
18531       continue;
18532     }
18533
18534     // br
18535     if (cap = this.rules.br.exec(src)) {
18536       src = src.substring(cap[0].length);
18537       out += '<br>';
18538       continue;
18539     }
18540
18541     // del (gfm)
18542     if (cap = this.rules.del.exec(src)) {
18543       src = src.substring(cap[0].length);
18544       out += '<del>'
18545         + this.output(cap[1])
18546         + '</del>';
18547       continue;
18548     }
18549
18550     // text
18551     if (cap = this.rules.text.exec(src)) {
18552       src = src.substring(cap[0].length);
18553       out += escape(cap[0]);
18554       continue;
18555     }
18556
18557     if (src) {
18558       throw new
18559         Error('Infinite loop on byte: ' + src.charCodeAt(0));
18560     }
18561   }
18562
18563   return out;
18564 };
18565
18566 /**
18567  * Compile Link
18568  */
18569
18570 InlineLexer.prototype.outputLink = function(cap, link) {
18571   if (cap[0][0] !== '!') {
18572     return '<a href="'
18573       + escape(link.href)
18574       + '"'
18575       + (link.title
18576       ? ' title="'
18577       + escape(link.title)
18578       + '"'
18579       : '')
18580       + '>'
18581       + this.output(cap[1])
18582       + '</a>';
18583   } else {
18584     return '<img src="'
18585       + escape(link.href)
18586       + '" alt="'
18587       + escape(cap[1])
18588       + '"'
18589       + (link.title
18590       ? ' title="'
18591       + escape(link.title)
18592       + '"'
18593       : '')
18594       + '>';
18595   }
18596 };
18597
18598 /**
18599  * Smartypants Transformations
18600  */
18601
18602 InlineLexer.prototype.smartypants = function(text) {
18603   if (!this.options.smartypants) return text;
18604   return text
18605     .replace(/--/g, '—')
18606     .replace(/'([^']*)'/g, '‘$1’')
18607     .replace(/"([^"]*)"/g, '“$1”')
18608     .replace(/\.{3}/g, '…');
18609 };
18610
18611 /**
18612  * Mangle Links
18613  */
18614
18615 InlineLexer.prototype.mangle = function(text) {
18616   var out = ''
18617     , l = text.length
18618     , i = 0
18619     , ch;
18620
18621   for (; i < l; i++) {
18622     ch = text.charCodeAt(i);
18623     if (Math.random() > 0.5) {
18624       ch = 'x' + ch.toString(16);
18625     }
18626     out += '&#' + ch + ';';
18627   }
18628
18629   return out;
18630 };
18631
18632 /**
18633  * Parsing & Compiling
18634  */
18635
18636 function Parser(options) {
18637   this.tokens = [];
18638   this.token = null;
18639   this.options = options || marked.defaults;
18640 }
18641
18642 /**
18643  * Static Parse Method
18644  */
18645
18646 Parser.parse = function(src, options) {
18647   var parser = new Parser(options);
18648   return parser.parse(src);
18649 };
18650
18651 /**
18652  * Parse Loop
18653  */
18654
18655 Parser.prototype.parse = function(src) {
18656   this.inline = new InlineLexer(src.links, this.options);
18657   this.tokens = src.reverse();
18658
18659   var out = '';
18660   while (this.next()) {
18661     out += this.tok();
18662   }
18663
18664   return out;
18665 };
18666
18667 /**
18668  * Next Token
18669  */
18670
18671 Parser.prototype.next = function() {
18672   return this.token = this.tokens.pop();
18673 };
18674
18675 /**
18676  * Preview Next Token
18677  */
18678
18679 Parser.prototype.peek = function() {
18680   return this.tokens[this.tokens.length-1] || 0;
18681 };
18682
18683 /**
18684  * Parse Text Tokens
18685  */
18686
18687 Parser.prototype.parseText = function() {
18688   var body = this.token.text;
18689
18690   while (this.peek().type === 'text') {
18691     body += '\n' + this.next().text;
18692   }
18693
18694   return this.inline.output(body);
18695 };
18696
18697 /**
18698  * Parse Current Token
18699  */
18700
18701 Parser.prototype.tok = function() {
18702   switch (this.token.type) {
18703     case 'space': {
18704       return '';
18705     }
18706     case 'hr': {
18707       return '<hr>\n';
18708     }
18709     case 'heading': {
18710       return '<h'
18711         + this.token.depth
18712         + '>'
18713         + this.inline.output(this.token.text)
18714         + '</h'
18715         + this.token.depth
18716         + '>\n';
18717     }
18718     case 'code': {
18719       if (this.options.highlight) {
18720         var code = this.options.highlight(this.token.text, this.token.lang);
18721         if (code != null && code !== this.token.text) {
18722           this.token.escaped = true;
18723           this.token.text = code;
18724         }
18725       }
18726
18727       if (!this.token.escaped) {
18728         this.token.text = escape(this.token.text, true);
18729       }
18730
18731       return '<pre><code'
18732         + (this.token.lang
18733         ? ' class="'
18734         + this.options.langPrefix
18735         + this.token.lang
18736         + '"'
18737         : '')
18738         + '>'
18739         + this.token.text
18740         + '</code></pre>\n';
18741     }
18742     case 'table': {
18743       var body = ''
18744         , heading
18745         , i
18746         , row
18747         , cell
18748         , j;
18749
18750       // header
18751       body += '<thead>\n<tr>\n';
18752       for (i = 0; i < this.token.header.length; i++) {
18753         heading = this.inline.output(this.token.header[i]);
18754         body += this.token.align[i]
18755           ? '<th align="' + this.token.align[i] + '">' + heading + '</th>\n'
18756           : '<th>' + heading + '</th>\n';
18757       }
18758       body += '</tr>\n</thead>\n';
18759
18760       // body
18761       body += '<tbody>\n'
18762       for (i = 0; i < this.token.cells.length; i++) {
18763         row = this.token.cells[i];
18764         body += '<tr>\n';
18765         for (j = 0; j < row.length; j++) {
18766           cell = this.inline.output(row[j]);
18767           body += this.token.align[j]
18768             ? '<td align="' + this.token.align[j] + '">' + cell + '</td>\n'
18769             : '<td>' + cell + '</td>\n';
18770         }
18771         body += '</tr>\n';
18772       }
18773       body += '</tbody>\n';
18774
18775       return '<table>\n'
18776         + body
18777         + '</table>\n';
18778     }
18779     case 'blockquote_start': {
18780       var body = '';
18781
18782       while (this.next().type !== 'blockquote_end') {
18783         body += this.tok();
18784       }
18785
18786       return '<blockquote>\n'
18787         + body
18788         + '</blockquote>\n';
18789     }
18790     case 'list_start': {
18791       var type = this.token.ordered ? 'ol' : 'ul'
18792         , body = '';
18793
18794       while (this.next().type !== 'list_end') {
18795         body += this.tok();
18796       }
18797
18798       return '<'
18799         + type
18800         + '>\n'
18801         + body
18802         + '</'
18803         + type
18804         + '>\n';
18805     }
18806     case 'list_item_start': {
18807       var body = '';
18808
18809       while (this.next().type !== 'list_item_end') {
18810         body += this.token.type === 'text'
18811           ? this.parseText()
18812           : this.tok();
18813       }
18814
18815       return '<li>'
18816         + body
18817         + '</li>\n';
18818     }
18819     case 'loose_item_start': {
18820       var body = '';
18821
18822       while (this.next().type !== 'list_item_end') {
18823         body += this.tok();
18824       }
18825
18826       return '<li>'
18827         + body
18828         + '</li>\n';
18829     }
18830     case 'html': {
18831       return !this.token.pre && !this.options.pedantic
18832         ? this.inline.output(this.token.text)
18833         : this.token.text;
18834     }
18835     case 'paragraph': {
18836       return '<p>'
18837         + this.inline.output(this.token.text)
18838         + '</p>\n';
18839     }
18840     case 'text': {
18841       return '<p>'
18842         + this.parseText()
18843         + '</p>\n';
18844     }
18845   }
18846 };
18847
18848 /**
18849  * Helpers
18850  */
18851
18852 function escape(html, encode) {
18853   return html
18854     .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18855     .replace(/</g, '&lt;')
18856     .replace(/>/g, '&gt;')
18857     .replace(/"/g, '&quot;')
18858     .replace(/'/g, '&#39;');
18859 }
18860
18861 function replace(regex, opt) {
18862   regex = regex.source;
18863   opt = opt || '';
18864   return function self(name, val) {
18865     if (!name) return new RegExp(regex, opt);
18866     val = val.source || val;
18867     val = val.replace(/(^|[^\[])\^/g, '$1');
18868     regex = regex.replace(name, val);
18869     return self;
18870   };
18871 }
18872
18873 function noop() {}
18874 noop.exec = noop;
18875
18876 function merge(obj) {
18877   var i = 1
18878     , target
18879     , key;
18880
18881   for (; i < arguments.length; i++) {
18882     target = arguments[i];
18883     for (key in target) {
18884       if (Object.prototype.hasOwnProperty.call(target, key)) {
18885         obj[key] = target[key];
18886       }
18887     }
18888   }
18889
18890   return obj;
18891 }
18892
18893 /**
18894  * Marked
18895  */
18896
18897 function marked(src, opt, callback) {
18898   if (callback || typeof opt === 'function') {
18899     if (!callback) {
18900       callback = opt;
18901       opt = null;
18902     }
18903
18904     if (opt) opt = merge({}, marked.defaults, opt);
18905
18906     var tokens = Lexer.lex(tokens, opt)
18907       , highlight = opt.highlight
18908       , pending = 0
18909       , l = tokens.length
18910       , i = 0;
18911
18912     if (!highlight || highlight.length < 3) {
18913       return callback(null, Parser.parse(tokens, opt));
18914     }
18915
18916     var done = function() {
18917       delete opt.highlight;
18918       var out = Parser.parse(tokens, opt);
18919       opt.highlight = highlight;
18920       return callback(null, out);
18921     };
18922
18923     for (; i < l; i++) {
18924       (function(token) {
18925         if (token.type !== 'code') return;
18926         pending++;
18927         return highlight(token.text, token.lang, function(err, code) {
18928           if (code == null || code === token.text) {
18929             return --pending || done();
18930           }
18931           token.text = code;
18932           token.escaped = true;
18933           --pending || done();
18934         });
18935       })(tokens[i]);
18936     }
18937
18938     return;
18939   }
18940   try {
18941     if (opt) opt = merge({}, marked.defaults, opt);
18942     return Parser.parse(Lexer.lex(src, opt), opt);
18943   } catch (e) {
18944     e.message += '\nPlease report this to https://github.com/chjj/marked.';
18945     if ((opt || marked.defaults).silent) {
18946       return '<p>An error occured:</p><pre>'
18947         + escape(e.message + '', true)
18948         + '</pre>';
18949     }
18950     throw e;
18951   }
18952 }
18953
18954 /**
18955  * Options
18956  */
18957
18958 marked.options =
18959 marked.setOptions = function(opt) {
18960   merge(marked.defaults, opt);
18961   return marked;
18962 };
18963
18964 marked.defaults = {
18965   gfm: true,
18966   tables: true,
18967   breaks: false,
18968   pedantic: false,
18969   sanitize: false,
18970   smartLists: false,
18971   silent: false,
18972   highlight: null,
18973   langPrefix: 'lang-'
18974 };
18975
18976 /**
18977  * Expose
18978  */
18979
18980 marked.Parser = Parser;
18981 marked.parser = Parser.parse;
18982
18983 marked.Lexer = Lexer;
18984 marked.lexer = Lexer.lex;
18985
18986 marked.InlineLexer = InlineLexer;
18987 marked.inlineLexer = InlineLexer.output;
18988
18989 marked.parse = marked;
18990
18991 if (typeof exports === 'object') {
18992   module.exports = marked;
18993 } else if (typeof define === 'function' && define.amd) {
18994   define(function() { return marked; });
18995 } else {
18996   this.marked = marked;
18997 }
18998
18999 }).call(function() {
19000   return this || (typeof window !== 'undefined' ? window : global);
19001 }());
19002 (function () {
19003 'use strict';
19004 window.iD = function () {
19005     window.locale.en = iD.data.en;
19006     window.locale.current('en');
19007
19008     var dispatch = d3.dispatch('enter', 'exit'),
19009         context = {};
19010
19011     // https://github.com/openstreetmap/iD/issues/772
19012     // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
19013     var storage;
19014     try { storage = localStorage; } catch (e) {}  // eslint-disable-line no-empty
19015     storage = storage || (function() {
19016         var s = {};
19017         return {
19018             getItem: function(k) { return s[k]; },
19019             setItem: function(k, v) { s[k] = v; },
19020             removeItem: function(k) { delete s[k]; }
19021         };
19022     })();
19023
19024     context.storage = function(k, v) {
19025         try {
19026             if (arguments.length === 1) return storage.getItem(k);
19027             else if (v === null) storage.removeItem(k);
19028             else storage.setItem(k, v);
19029         } catch(e) {
19030             // localstorage quota exceeded
19031             /* eslint-disable no-console */
19032             if (typeof console !== 'undefined') console.error('localStorage quota exceeded');
19033             /* eslint-enable no-console */
19034         }
19035     };
19036
19037
19038     /* Straight accessors. Avoid using these if you can. */
19039     var ui, connection, history;
19040     context.ui = function() { return ui; };
19041     context.connection = function() { return connection; };
19042     context.history = function() { return history; };
19043
19044
19045     /* Connection */
19046     function entitiesLoaded(err, result) {
19047         if (!err) history.merge(result.data, result.extent);
19048     }
19049
19050     context.preauth = function(options) {
19051         connection.switch(options);
19052         return context;
19053     };
19054
19055     context.loadTiles = function(projection, dimensions, callback) {
19056         function done(err, result) {
19057             entitiesLoaded(err, result);
19058             if (callback) callback(err, result);
19059         }
19060         connection.loadTiles(projection, dimensions, done);
19061     };
19062
19063     context.loadEntity = function(id, callback) {
19064         function done(err, result) {
19065             entitiesLoaded(err, result);
19066             if (callback) callback(err, result);
19067         }
19068         connection.loadEntity(id, done);
19069     };
19070
19071     context.zoomToEntity = function(id, zoomTo) {
19072         if (zoomTo !== false) {
19073             this.loadEntity(id, function(err, result) {
19074                 if (err) return;
19075                 var entity = _.find(result.data, function(e) { return e.id === id; });
19076                 if (entity) { map.zoomTo(entity); }
19077             });
19078         }
19079
19080         map.on('drawn.zoomToEntity', function() {
19081             if (!context.hasEntity(id)) return;
19082             map.on('drawn.zoomToEntity', null);
19083             context.on('enter.zoomToEntity', null);
19084             context.enter(iD.modes.Select(context, [id]));
19085         });
19086
19087         context.on('enter.zoomToEntity', function() {
19088             if (mode.id !== 'browse') {
19089                 map.on('drawn.zoomToEntity', null);
19090                 context.on('enter.zoomToEntity', null);
19091             }
19092         });
19093     };
19094
19095     var minEditableZoom = 16;
19096     context.minEditableZoom = function(_) {
19097         if (!arguments.length) return minEditableZoom;
19098         minEditableZoom = _;
19099         connection.tileZoom(_);
19100         return context;
19101     };
19102
19103
19104     /* History */
19105     var inIntro = false;
19106     context.inIntro = function(_) {
19107         if (!arguments.length) return inIntro;
19108         inIntro = _;
19109         return context;
19110     };
19111
19112     context.save = function() {
19113         if (inIntro || (mode && mode.id === 'save')) return;
19114         history.save();
19115         if (history.hasChanges()) return t('save.unsaved_changes');
19116     };
19117
19118     context.flush = function() {
19119         context.debouncedSave.cancel();
19120         connection.flush();
19121         features.reset();
19122         history.reset();
19123         _.each(iD.services, function(service) {
19124             var reset = service().reset;
19125             if (reset) reset(context);
19126         });
19127         return context;
19128     };
19129
19130
19131     /* Graph */
19132     context.hasEntity = function(id) {
19133         return history.graph().hasEntity(id);
19134     };
19135     context.entity = function(id) {
19136         return history.graph().entity(id);
19137     };
19138     context.childNodes = function(way) {
19139         return history.graph().childNodes(way);
19140     };
19141     context.geometry = function(id) {
19142         return context.entity(id).geometry(history.graph());
19143     };
19144
19145
19146     /* Modes */
19147     var mode;
19148     context.mode = function() {
19149         return mode;
19150     };
19151     context.enter = function(newMode) {
19152         if (mode) {
19153             mode.exit();
19154             dispatch.exit(mode);
19155         }
19156
19157         mode = newMode;
19158         mode.enter();
19159         dispatch.enter(mode);
19160     };
19161
19162     context.selectedIDs = function() {
19163         if (mode && mode.selectedIDs) {
19164             return mode.selectedIDs();
19165         } else {
19166             return [];
19167         }
19168     };
19169
19170
19171     /* Behaviors */
19172     context.install = function(behavior) {
19173         context.surface().call(behavior);
19174     };
19175     context.uninstall = function(behavior) {
19176         context.surface().call(behavior.off);
19177     };
19178
19179
19180     /* Copy/Paste */
19181     var copyIDs = [], copyGraph;
19182     context.copyGraph = function() { return copyGraph; };
19183     context.copyIDs = function(_) {
19184         if (!arguments.length) return copyIDs;
19185         copyIDs = _;
19186         copyGraph = history.graph();
19187         return context;
19188     };
19189
19190
19191     /* Background */
19192     var background;
19193     context.background = function() { return background; };
19194
19195
19196     /* Features */
19197     var features;
19198     context.features = function() { return features; };
19199     context.hasHiddenConnections = function(id) {
19200         var graph = history.graph(),
19201             entity = graph.entity(id);
19202         return features.hasHiddenConnections(entity, graph);
19203     };
19204
19205
19206     /* Map */
19207     var map;
19208     context.map = function() { return map; };
19209     context.layers = function() { return map.layers; };
19210     context.surface = function() { return map.surface; };
19211     context.editable = function() { return map.editable(); };
19212
19213     context.surfaceRect = function() {
19214         // Work around a bug in Firefox.
19215         //   http://stackoverflow.com/questions/18153989/
19216         //   https://bugzilla.mozilla.org/show_bug.cgi?id=530985
19217         return context.surface().node().parentNode.getBoundingClientRect();
19218     };
19219
19220
19221     /* Presets */
19222     var presets;
19223     context.presets = function(_) {
19224         if (!arguments.length) return presets;
19225         presets.load(_);
19226         iD.areaKeys = presets.areaKeys();
19227         return context;
19228     };
19229
19230
19231     /* Imagery */
19232     context.imagery = function(_) {
19233         background.load(_);
19234         return context;
19235     };
19236
19237
19238     /* Container */
19239     var container, embed;
19240     context.container = function(_) {
19241         if (!arguments.length) return container;
19242         container = _;
19243         container.classed('id-container', true);
19244         return context;
19245     };
19246     context.embed = function(_) {
19247         if (!arguments.length) return embed;
19248         embed = _;
19249         return context;
19250     };
19251
19252
19253     /* Taginfo */
19254     var taginfo;
19255     context.taginfo = function(_) {
19256         if (!arguments.length) return taginfo;
19257         taginfo = _;
19258         return context;
19259     };
19260
19261
19262     /* Assets */
19263     var assetPath = '';
19264     context.assetPath = function(_) {
19265         if (!arguments.length) return assetPath;
19266         assetPath = _;
19267         return context;
19268     };
19269
19270     var assetMap = {};
19271     context.assetMap = function(_) {
19272         if (!arguments.length) return assetMap;
19273         assetMap = _;
19274         return context;
19275     };
19276
19277     context.asset = function(_) {
19278         var filename = assetPath + _;
19279         return assetMap[filename] || filename;
19280     };
19281
19282     context.imagePath = function(_) {
19283         return context.asset('img/' + _);
19284     };
19285
19286     var locale, localePath;
19287     context.locale = function(loc, path) {
19288         locale = loc;
19289         localePath = path;
19290
19291         // Also set iD.detect().locale (unless we detected 'en-us' and openstreetmap wants 'en')..
19292         if (!(loc.toLowerCase() === 'en' && iD.detect().locale.toLowerCase() === 'en-us')) {
19293             iD.detect().locale = loc;
19294         }
19295
19296         return context;
19297     };
19298
19299     context.loadLocale = function(cb) {
19300         if (locale && locale !== 'en' && iD.data.locales.indexOf(locale) !== -1) {
19301             localePath = localePath || context.asset('locales/' + locale + '.json');
19302             d3.json(localePath, function(err, result) {
19303                 window.locale[locale] = result;
19304                 window.locale.current(locale);
19305                 cb();
19306             });
19307         } else {
19308             cb();
19309         }
19310     };
19311
19312
19313     /* Init */
19314
19315     context.projection = iD.geo.RawMercator();
19316
19317     locale = iD.detect().locale;
19318     if (locale && iD.data.locales.indexOf(locale) === -1) {
19319         locale = locale.split('-')[0];
19320     }
19321
19322     history = iD.History(context);
19323     context.graph = history.graph;
19324     context.changes = history.changes;
19325     context.intersects = history.intersects;
19326
19327     // Debounce save, since it's a synchronous localStorage write,
19328     // and history changes can happen frequently (e.g. when dragging).
19329     context.debouncedSave = _.debounce(context.save, 350);
19330     function withDebouncedSave(fn) {
19331         return function() {
19332             var result = fn.apply(history, arguments);
19333             context.debouncedSave();
19334             return result;
19335         };
19336     }
19337
19338     context.perform = withDebouncedSave(history.perform);
19339     context.replace = withDebouncedSave(history.replace);
19340     context.pop = withDebouncedSave(history.pop);
19341     context.overwrite = withDebouncedSave(history.overwrite);
19342     context.undo = withDebouncedSave(history.undo);
19343     context.redo = withDebouncedSave(history.redo);
19344
19345     ui = iD.ui(context);
19346
19347     connection = iD.Connection();
19348
19349     background = iD.Background(context);
19350
19351     features = iD.Features(context);
19352
19353     map = iD.Map(context);
19354     context.mouse = map.mouse;
19355     context.extent = map.extent;
19356     context.pan = map.pan;
19357     context.zoomIn = map.zoomIn;
19358     context.zoomOut = map.zoomOut;
19359     context.zoomInFurther = map.zoomInFurther;
19360     context.zoomOutFurther = map.zoomOutFurther;
19361     context.redrawEnable = map.redrawEnable;
19362
19363     presets = iD.presets();
19364
19365     return d3.rebind(context, dispatch, 'on');
19366 };
19367
19368
19369 iD.version = '1.9.1';
19370
19371 (function() {
19372     var detected = {};
19373
19374     var ua = navigator.userAgent,
19375         m = null;
19376
19377     m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i);   // Edge
19378     if (m !== null) {
19379         detected.browser = m[1];
19380         detected.version = m[2];
19381     }
19382     if (!detected.browser) {
19383         m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i);   // IE11
19384         if (m !== null) {
19385             detected.browser = 'msie';
19386             detected.version = m[1];
19387         }
19388     }
19389     if (!detected.browser) {
19390         m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i);   // Opera 15+
19391         if (m !== null) {
19392             detected.browser = 'Opera';
19393             detected.version = m[2];
19394         }
19395     }
19396     if (!detected.browser) {
19397         m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
19398         if (m !== null) {
19399             detected.browser = m[1];
19400             detected.version = m[2];
19401             m = ua.match(/version\/([\.\d]+)/i);
19402             if (m !== null) detected.version = m[1];
19403         }
19404     }
19405     if (!detected.browser) {
19406         detected.browser = navigator.appName;
19407         detected.version = navigator.appVersion;
19408     }
19409
19410     // keep major.minor version only..
19411     detected.version = detected.version.split(/\W/).slice(0,2).join('.');
19412
19413     if (detected.browser.toLowerCase() === 'msie') {
19414         detected.ie = true;
19415         detected.browser = 'Internet Explorer';
19416         detected.support = parseFloat(detected.version) >= 11;
19417     } else {
19418         detected.ie = false;
19419         detected.support = true;
19420     }
19421
19422     // Added due to incomplete svg style support. See #715
19423     detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 );
19424
19425     detected.locale = (navigator.languages && navigator.languages.length)
19426         ? navigator.languages[0] : (navigator.language || navigator.userLanguage || 'en-US');
19427
19428     detected.filedrop = (window.FileReader && 'ondrop' in window);
19429
19430     function nav(x) {
19431         return navigator.userAgent.indexOf(x) !== -1;
19432     }
19433
19434     if (nav('Win')) {
19435         detected.os = 'win';
19436         detected.platform = 'Windows';
19437     }
19438     else if (nav('Mac')) {
19439         detected.os = 'mac';
19440         detected.platform = 'Macintosh';
19441     }
19442     else if (nav('X11') || nav('Linux')) {
19443         detected.os = 'linux';
19444         detected.platform = 'Linux';
19445     }
19446     else {
19447         detected.os = 'win';
19448         detected.platform = 'Unknown';
19449     }
19450
19451     iD.detect = function() { return detected; };
19452 })();
19453 iD.services = {};
19454 iD.services.mapillary = function() {
19455     var mapillary = {},
19456         dispatch = d3.dispatch('loadedImages', 'loadedSigns'),
19457         apibase = 'https://a.mapillary.com/v2/',
19458         urlImage = 'https://www.mapillary.com/map/im/',
19459         urlThumb = 'https://d1cuyjsrcm0gby.cloudfront.net/',
19460         clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi',
19461         maxResults = 1000,
19462         tileZoom = 14;
19463
19464
19465     function loadSignStyles(context) {
19466         d3.select('head').selectAll('#traffico')
19467             .data([0])
19468             .enter()
19469             .append('link')
19470             .attr('id', 'traffico')
19471             .attr('rel', 'stylesheet')
19472             .attr('href', context.asset('traffico/stylesheets/traffico.css'));
19473     }
19474
19475     function loadSignDefs(context) {
19476         if (!iD.services.mapillary.sign_defs) {
19477             iD.services.mapillary.sign_defs = {};
19478             _.each(['au', 'br', 'ca', 'de', 'us'], function(region) {
19479                 d3.json(context.asset('traffico/string-maps/' + region + '-map.json'), function(err, data) {
19480                     if (err) return;
19481                     if (region === 'de') region = 'eu';
19482                     iD.services.mapillary.sign_defs[region] = data;
19483                 });
19484             });
19485         }
19486     }
19487
19488     function abortRequest(i) {
19489         i.abort();
19490     }
19491
19492     function getTiles(projection, dimensions) {
19493         var s = projection.scale() * 2 * Math.PI,
19494             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
19495             ts = 256 * Math.pow(2, z - tileZoom),
19496             origin = [
19497                 s / 2 - projection.translate()[0],
19498                 s / 2 - projection.translate()[1]];
19499
19500         return d3.geo.tile()
19501             .scaleExtent([tileZoom, tileZoom])
19502             .scale(s)
19503             .size(dimensions)
19504             .translate(projection.translate())()
19505             .map(function(tile) {
19506                 var x = tile[0] * ts - origin[0],
19507                     y = tile[1] * ts - origin[1];
19508
19509                 return {
19510                     id: tile.toString(),
19511                     extent: iD.geo.Extent(
19512                         projection.invert([x, y + ts]),
19513                         projection.invert([x + ts, y]))
19514                 };
19515             });
19516     }
19517
19518
19519     function loadTiles(which, url, projection, dimensions) {
19520         var tiles = getTiles(projection, dimensions);
19521
19522         _.filter(which.inflight, function(v, k) {
19523             var wanted = _.find(tiles, function(tile) { return k === (tile.id + ',0'); });
19524             if (!wanted) delete which.inflight[k];
19525             return !wanted;
19526         }).map(abortRequest);
19527
19528         tiles.forEach(function(tile) {
19529             loadTilePage(which, url, tile, 0);
19530         });
19531     }
19532
19533     function loadTilePage(which, url, tile, page) {
19534         var cache = iD.services.mapillary.cache[which],
19535             id = tile.id + ',' + String(page),
19536             rect = tile.extent.rectangle();
19537
19538         if (cache.loaded[id] || cache.inflight[id]) return;
19539
19540         cache.inflight[id] = d3.json(url +
19541             iD.util.qsString({
19542                 geojson: 'true',
19543                 limit: maxResults,
19544                 page: page,
19545                 client_id: clientId,
19546                 min_lon: rect[0],
19547                 min_lat: rect[1],
19548                 max_lon: rect[2],
19549                 max_lat: rect[3]
19550             }), function(err, data) {
19551                 cache.loaded[id] = true;
19552                 delete cache.inflight[id];
19553                 if (err || !data.features || !data.features.length) return;
19554
19555                 var features = [],
19556                     feature, loc, d;
19557
19558                 for (var i = 0; i < data.features.length; i++) {
19559                     feature = data.features[i];
19560                     loc = feature.geometry.coordinates;
19561                     d = { key: feature.properties.key, loc: loc };
19562                     if (which === 'images') d.ca = feature.properties.ca;
19563                     if (which === 'signs') d.signs = feature.properties.rects;
19564
19565                     features.push([loc[0], loc[1], loc[0], loc[1], d]);
19566                 }
19567
19568                 cache.rtree.load(features);
19569
19570                 if (which === 'images') dispatch.loadedImages();
19571                 if (which === 'signs') dispatch.loadedSigns();
19572
19573                 if (data.features.length === maxResults) {
19574                     loadTilePage(which, url, tile, ++page);
19575                 }
19576             }
19577         );
19578     }
19579
19580     mapillary.loadImages = function(projection, dimensions) {
19581         var url = apibase + 'search/im/geojson?';
19582         loadTiles('images', url, projection, dimensions);
19583     };
19584
19585     mapillary.loadSigns = function(context, projection, dimensions) {
19586         var url = apibase + 'search/im/geojson/or?';
19587         loadSignStyles(context);
19588         loadSignDefs(context);
19589         loadTiles('signs', url, projection, dimensions);
19590     };
19591
19592
19593     // partition viewport into `psize` x `psize` regions
19594     function partitionViewport(psize, projection, dimensions) {
19595         psize = psize || 16;
19596         var cols = d3.range(0, dimensions[0], psize),
19597             rows = d3.range(0, dimensions[1], psize),
19598             partitions = [];
19599
19600         rows.forEach(function(y) {
19601             cols.forEach(function(x) {
19602                 var min = [x, y + psize],
19603                     max = [x + psize, y];
19604                 partitions.push(
19605                     iD.geo.Extent(projection.invert(min), projection.invert(max)));
19606             });
19607         });
19608
19609         return partitions;
19610     }
19611
19612     // no more than `limit` results per partition.
19613     function searchLimited(psize, limit, projection, dimensions, rtree) {
19614         limit = limit || 3;
19615
19616         var partitions = partitionViewport(psize, projection, dimensions);
19617         return _.flatten(_.compact(_.map(partitions, function(extent) {
19618             return rtree.search(extent.rectangle())
19619                 .slice(0, limit)
19620                 .map(function(d) { return d[4]; });
19621         })));
19622     }
19623
19624     mapillary.images = function(projection, dimensions) {
19625         var psize = 16, limit = 3;
19626         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.images.rtree);
19627     };
19628
19629     mapillary.signs = function(projection, dimensions) {
19630         var psize = 32, limit = 3;
19631         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.signs.rtree);
19632     };
19633
19634     mapillary.signsSupported = function() {
19635         var detected = iD.detect();
19636         return (!(detected.ie || detected.browser.toLowerCase() === 'safari'));
19637     };
19638
19639     mapillary.signHTML = function(d) {
19640         if (!iD.services.mapillary.sign_defs) return;
19641
19642         var detectionPackage = d.signs[0].package,
19643             type = d.signs[0].type,
19644             country = detectionPackage.split('_')[1];
19645         return iD.services.mapillary.sign_defs[country][type];
19646     };
19647
19648     mapillary.showThumbnail = function(imageKey, position) {
19649         if (!imageKey) return;
19650
19651         var positionClass = {
19652             'ar': (position !== 'left'),
19653             'al': (position === 'left')
19654         };
19655
19656         var thumbnail = d3.select('#content').selectAll('.mapillary-image')
19657             .data([0]);
19658
19659         // Enter
19660         var enter = thumbnail.enter().append('div')
19661             .attr('class', 'mapillary-image ar');
19662
19663         enter.append('button')
19664             .on('click', function () {
19665                 mapillary.hideThumbnail();
19666             })
19667             .append('div')
19668             .call(iD.svg.Icon('#icon-close'));
19669
19670         enter.append('img');
19671
19672         enter.append('a')
19673             .attr('class', 'link ar')
19674             .attr('target', '_blank')
19675             .call(iD.svg.Icon('#icon-out-link', 'inline'))
19676             .append('span')
19677             .text(t('mapillary.view_on_mapillary'));
19678
19679         // Update
19680         thumbnail.selectAll('img')
19681             .attr('src', urlThumb + imageKey + '/thumb-320.jpg');
19682
19683         var link = thumbnail.selectAll('a')
19684             .attr('href', urlImage + imageKey);
19685
19686         if (position) {
19687             thumbnail.classed(positionClass);
19688             link.classed(positionClass);
19689         }
19690
19691         thumbnail
19692             .transition()
19693             .duration(200)
19694             .style('opacity', 1);
19695     };
19696
19697     mapillary.hideThumbnail = function() {
19698         if (iD.services.mapillary) {
19699             iD.services.mapillary.thumb = null;
19700         }
19701         d3.select('#content').selectAll('.mapillary-image')
19702             .transition()
19703             .duration(200)
19704             .style('opacity', 0)
19705             .remove();
19706     };
19707
19708     mapillary.selectedThumbnail = function(d) {
19709         if (!iD.services.mapillary) return null;
19710         if (!arguments.length) return iD.services.mapillary.thumb;
19711         iD.services.mapillary.thumb = d;
19712     };
19713
19714     mapillary.reset = function() {
19715         var cache = iD.services.mapillary.cache;
19716
19717         if (cache) {
19718             _.forEach(cache.images.inflight, abortRequest);
19719             _.forEach(cache.signs.inflight, abortRequest);
19720         }
19721
19722         iD.services.mapillary.cache = {
19723             images: { inflight: {}, loaded: {}, rtree: rbush() },
19724             signs:  { inflight: {}, loaded: {}, rtree: rbush() }
19725         };
19726
19727         iD.services.mapillary.thumb = null;
19728
19729         return mapillary;
19730     };
19731
19732
19733     if (!iD.services.mapillary.cache) {
19734         mapillary.reset();
19735     }
19736
19737     return d3.rebind(mapillary, dispatch, 'on');
19738 };
19739 iD.services.nominatim  = function() {
19740     var nominatim = {},
19741         endpoint = 'https://nominatim.openstreetmap.org/reverse?';
19742
19743
19744     nominatim.countryCode = function(location, callback) {
19745         var cache = iD.services.nominatim.cache,
19746             countryCodes = cache.search([location[0], location[1], location[0], location[1]]);
19747
19748         if (countryCodes.length > 0)
19749             return callback(null, countryCodes[0][4]);
19750
19751         d3.json(endpoint +
19752             iD.util.qsString({
19753                 format: 'json',
19754                 addressdetails: 1,
19755                 lat: location[1],
19756                 lon: location[0]
19757             }), function(err, result) {
19758                 if (err)
19759                     return callback(err);
19760                 else if (result && result.error)
19761                     return callback(result.error);
19762
19763                 var extent = iD.geo.Extent(location).padByMeters(1000);
19764
19765                 cache.insert(extent.rectangle().concat(result.address.country_code));
19766
19767                 callback(null, result.address.country_code);
19768             });
19769     };
19770
19771     nominatim.reset = function() {
19772         iD.services.nominatim.cache = rbush();
19773         return nominatim;
19774     };
19775
19776
19777     if (!iD.services.nominatim.cache) {
19778         nominatim.reset();
19779     }
19780
19781     return nominatim;
19782 };
19783 iD.services.taginfo = function() {
19784     var taginfo = {},
19785         endpoint = 'https://taginfo.openstreetmap.org/api/4/',
19786         tag_sorts = {
19787             point: 'count_nodes',
19788             vertex: 'count_nodes',
19789             area: 'count_ways',
19790             line: 'count_ways'
19791         },
19792         tag_filters = {
19793             point: 'nodes',
19794             vertex: 'nodes',
19795             area: 'ways',
19796             line: 'ways'
19797         };
19798
19799
19800     function sets(parameters, n, o) {
19801         if (parameters.geometry && o[parameters.geometry]) {
19802             parameters[n] = o[parameters.geometry];
19803         }
19804         return parameters;
19805     }
19806
19807     function setFilter(parameters) {
19808         return sets(parameters, 'filter', tag_filters);
19809     }
19810
19811     function setSort(parameters) {
19812         return sets(parameters, 'sortname', tag_sorts);
19813     }
19814
19815     function clean(parameters) {
19816         return _.omit(parameters, 'geometry', 'debounce');
19817     }
19818
19819     function popularKeys(parameters) {
19820         var pop_field = 'count_all';
19821         if (parameters.filter) pop_field = 'count_' + parameters.filter;
19822         return function(d) { return parseFloat(d[pop_field]) > 5000 || d.in_wiki; };
19823     }
19824
19825     function popularValues() {
19826         return function(d) { return parseFloat(d.fraction) > 0.01 || d.in_wiki; };
19827     }
19828
19829     function valKey(d) { return { value: d.key }; }
19830
19831     function valKeyDescription(d) {
19832         return {
19833             value: d.value,
19834             title: d.description
19835         };
19836     }
19837
19838     // sort keys with ':' lower than keys without ':'
19839     function sortKeys(a, b) {
19840         return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
19841             : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
19842             : 0;
19843     }
19844
19845     var debounced = _.debounce(d3.json, 100, true);
19846
19847     function request(url, debounce, callback) {
19848         var cache = iD.services.taginfo.cache;
19849
19850         if (cache[url]) {
19851             callback(null, cache[url]);
19852         } else if (debounce) {
19853             debounced(url, done);
19854         } else {
19855             d3.json(url, done);
19856         }
19857
19858         function done(err, data) {
19859             if (!err) cache[url] = data;
19860             callback(err, data);
19861         }
19862     }
19863
19864     taginfo.keys = function(parameters, callback) {
19865         var debounce = parameters.debounce;
19866         parameters = clean(setSort(parameters));
19867         request(endpoint + 'keys/all?' +
19868             iD.util.qsString(_.extend({
19869                 rp: 10,
19870                 sortname: 'count_all',
19871                 sortorder: 'desc',
19872                 page: 1
19873             }, parameters)), debounce, function(err, d) {
19874                 if (err) return callback(err);
19875                 callback(null, d.data.filter(popularKeys(parameters)).sort(sortKeys).map(valKey));
19876             });
19877     };
19878
19879     taginfo.values = function(parameters, callback) {
19880         var debounce = parameters.debounce;
19881         parameters = clean(setSort(setFilter(parameters)));
19882         request(endpoint + 'key/values?' +
19883             iD.util.qsString(_.extend({
19884                 rp: 25,
19885                 sortname: 'count_all',
19886                 sortorder: 'desc',
19887                 page: 1
19888             }, parameters)), debounce, function(err, d) {
19889                 if (err) return callback(err);
19890                 callback(null, d.data.filter(popularValues()).map(valKeyDescription), parameters);
19891             });
19892     };
19893
19894     taginfo.docs = function(parameters, callback) {
19895         var debounce = parameters.debounce;
19896         parameters = clean(setSort(parameters));
19897
19898         var path = 'key/wiki_pages?';
19899         if (parameters.value) path = 'tag/wiki_pages?';
19900         else if (parameters.rtype) path = 'relation/wiki_pages?';
19901
19902         request(endpoint + path + iD.util.qsString(parameters), debounce, function(err, d) {
19903             if (err) return callback(err);
19904             callback(null, d.data);
19905         });
19906     };
19907
19908     taginfo.endpoint = function(_) {
19909         if (!arguments.length) return endpoint;
19910         endpoint = _;
19911         return taginfo;
19912     };
19913
19914     taginfo.reset = function() {
19915         iD.services.taginfo.cache = {};
19916         return taginfo;
19917     };
19918
19919
19920     if (!iD.services.taginfo.cache) {
19921         taginfo.reset();
19922     }
19923
19924     return taginfo;
19925 };
19926 iD.services.wikipedia = function() {
19927     var wiki = {},
19928         endpoint = 'https://en.wikipedia.org/w/api.php?';
19929
19930     wiki.search = function(lang, query, callback) {
19931         lang = lang || 'en';
19932         d3.jsonp(endpoint.replace('en', lang) +
19933             iD.util.qsString({
19934                 action: 'query',
19935                 list: 'search',
19936                 srlimit: '10',
19937                 srinfo: 'suggestion',
19938                 format: 'json',
19939                 callback: '{callback}',
19940                 srsearch: query
19941             }), function(data) {
19942                 if (!data.query) return;
19943                 callback(query, data.query.search.map(function(d) {
19944                     return d.title;
19945                 }));
19946             });
19947     };
19948
19949     wiki.suggestions = function(lang, query, callback) {
19950         lang = lang || 'en';
19951         d3.jsonp(endpoint.replace('en', lang) +
19952             iD.util.qsString({
19953                 action: 'opensearch',
19954                 namespace: 0,
19955                 suggest: '',
19956                 format: 'json',
19957                 callback: '{callback}',
19958                 search: query
19959             }), function(d) {
19960                 callback(d[0], d[1]);
19961             });
19962     };
19963
19964     wiki.translations = function(lang, title, callback) {
19965         d3.jsonp(endpoint.replace('en', lang) +
19966             iD.util.qsString({
19967                 action: 'query',
19968                 prop: 'langlinks',
19969                 format: 'json',
19970                 callback: '{callback}',
19971                 lllimit: 500,
19972                 titles: title
19973             }), function(d) {
19974                 var list = d.query.pages[Object.keys(d.query.pages)[0]],
19975                     translations = {};
19976                 if (list && list.langlinks) {
19977                     list.langlinks.forEach(function(d) {
19978                         translations[d.lang] = d['*'];
19979                     });
19980                     callback(translations);
19981                 }
19982             });
19983     };
19984
19985     return wiki;
19986 };
19987 iD.util = {};
19988
19989 iD.util.tagText = function(entity) {
19990     return d3.entries(entity.tags).map(function(e) {
19991         return e.key + '=' + e.value;
19992     }).join(', ');
19993 };
19994
19995 iD.util.entitySelector = function(ids) {
19996     return ids.length ? '.' + ids.join(',.') : 'nothing';
19997 };
19998
19999 iD.util.entityOrMemberSelector = function(ids, graph) {
20000     var s = iD.util.entitySelector(ids);
20001
20002     ids.forEach(function(id) {
20003         var entity = graph.hasEntity(id);
20004         if (entity && entity.type === 'relation') {
20005             entity.members.forEach(function(member) {
20006                 s += ',.' + member.id;
20007             });
20008         }
20009     });
20010
20011     return s;
20012 };
20013
20014 iD.util.displayName = function(entity) {
20015     var localeName = 'name:' + iD.detect().locale.toLowerCase().split('-')[0];
20016     return entity.tags[localeName] || entity.tags.name || entity.tags.ref;
20017 };
20018
20019 iD.util.displayType = function(id) {
20020     return {
20021         n: t('inspector.node'),
20022         w: t('inspector.way'),
20023         r: t('inspector.relation')
20024     }[id.charAt(0)];
20025 };
20026
20027 iD.util.stringQs = function(str) {
20028     return str.split('&').reduce(function(obj, pair){
20029         var parts = pair.split('=');
20030         if (parts.length === 2) {
20031             obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
20032         }
20033         return obj;
20034     }, {});
20035 };
20036
20037 iD.util.qsString = function(obj, noencode) {
20038     function softEncode(s) {
20039       // encode everything except special characters used in certain hash parameters:
20040       // "/" in map states, ":", ",", {" and "}" in background
20041       return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
20042     }
20043     return Object.keys(obj).sort().map(function(key) {
20044         return encodeURIComponent(key) + '=' + (
20045             noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
20046     }).join('&');
20047 };
20048
20049 iD.util.prefixDOMProperty = function(property) {
20050     var prefixes = ['webkit', 'ms', 'moz', 'o'],
20051         i = -1,
20052         n = prefixes.length,
20053         s = document.body;
20054
20055     if (property in s)
20056         return property;
20057
20058     property = property.substr(0, 1).toUpperCase() + property.substr(1);
20059
20060     while (++i < n)
20061         if (prefixes[i] + property in s)
20062             return prefixes[i] + property;
20063
20064     return false;
20065 };
20066
20067 iD.util.prefixCSSProperty = function(property) {
20068     var prefixes = ['webkit', 'ms', 'Moz', 'O'],
20069         i = -1,
20070         n = prefixes.length,
20071         s = document.body.style;
20072
20073     if (property.toLowerCase() in s)
20074         return property.toLowerCase();
20075
20076     while (++i < n)
20077         if (prefixes[i] + property in s)
20078             return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
20079
20080     return false;
20081 };
20082
20083
20084 iD.util.setTransform = function(el, x, y, scale) {
20085     var prop = iD.util.transformProperty = iD.util.transformProperty || iD.util.prefixCSSProperty('Transform'),
20086         translate = iD.detect().opera ?
20087             'translate('   + x + 'px,' + y + 'px)' :
20088             'translate3d(' + x + 'px,' + y + 'px,0)';
20089     return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
20090 };
20091
20092 iD.util.getStyle = function(selector) {
20093     for (var i = 0; i < document.styleSheets.length; i++) {
20094         var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules || [];
20095         for (var k = 0; k < rules.length; k++) {
20096             var selectorText = rules[k].selectorText && rules[k].selectorText.split(', ');
20097             if (_.contains(selectorText, selector)) {
20098                 return rules[k];
20099             }
20100         }
20101     }
20102 };
20103
20104 iD.util.editDistance = function(a, b) {
20105     if (a.length === 0) return b.length;
20106     if (b.length === 0) return a.length;
20107     var matrix = [];
20108     for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
20109     for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
20110     for (i = 1; i <= b.length; i++) {
20111         for (j = 1; j <= a.length; j++) {
20112             if (b.charAt(i-1) === a.charAt(j-1)) {
20113                 matrix[i][j] = matrix[i-1][j-1];
20114             } else {
20115                 matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
20116                     Math.min(matrix[i][j-1] + 1, // insertion
20117                     matrix[i-1][j] + 1)); // deletion
20118             }
20119         }
20120     }
20121     return matrix[b.length][a.length];
20122 };
20123
20124 // a d3.mouse-alike which
20125 // 1. Only works on HTML elements, not SVG
20126 // 2. Does not cause style recalculation
20127 iD.util.fastMouse = function(container) {
20128     var rect = container.getBoundingClientRect(),
20129         rectLeft = rect.left,
20130         rectTop = rect.top,
20131         clientLeft = +container.clientLeft,
20132         clientTop = +container.clientTop;
20133     return function(e) {
20134         return [
20135             e.clientX - rectLeft - clientLeft,
20136             e.clientY - rectTop - clientTop];
20137     };
20138 };
20139
20140 /* eslint-disable no-proto */
20141 iD.util.getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; };
20142 /* eslint-enable no-proto */
20143
20144 iD.util.asyncMap = function(inputs, func, callback) {
20145     var remaining = inputs.length,
20146         results = [],
20147         errors = [];
20148
20149     inputs.forEach(function(d, i) {
20150         func(d, function done(err, data) {
20151             errors[i] = err;
20152             results[i] = data;
20153             remaining--;
20154             if (!remaining) callback(errors, results);
20155         });
20156     });
20157 };
20158
20159 // wraps an index to an interval [0..length-1]
20160 iD.util.wrap = function(index, length) {
20161     if (index < 0)
20162         index += Math.ceil(-index/length)*length;
20163     return index % length;
20164 };
20165 // A per-domain session mutex backed by a cookie and dead man's
20166 // switch. If the session crashes, the mutex will auto-release
20167 // after 5 seconds.
20168
20169 iD.util.SessionMutex = function(name) {
20170     var mutex = {},
20171         intervalID;
20172
20173     function renew() {
20174         var expires = new Date();
20175         expires.setSeconds(expires.getSeconds() + 5);
20176         document.cookie = name + '=1; expires=' + expires.toUTCString();
20177     }
20178
20179     mutex.lock = function() {
20180         if (intervalID) return true;
20181         var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
20182         if (cookie) return false;
20183         renew();
20184         intervalID = window.setInterval(renew, 4000);
20185         return true;
20186     };
20187
20188     mutex.unlock = function() {
20189         if (!intervalID) return;
20190         document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
20191         clearInterval(intervalID);
20192         intervalID = null;
20193     };
20194
20195     mutex.locked = function() {
20196         return !!intervalID;
20197     };
20198
20199     return mutex;
20200 };
20201 iD.util.SuggestNames = function(preset, suggestions) {
20202     preset = preset.id.split('/', 2);
20203     var k = preset[0],
20204         v = preset[1];
20205
20206     return function(value, callback) {
20207         var result = [];
20208         if (value && value.length > 2) {
20209             if (suggestions[k] && suggestions[k][v]) {
20210                 for (var sugg in suggestions[k][v]) {
20211                     var dist = iD.util.editDistance(value, sugg.substring(0, value.length));
20212                     if (dist < 3) {
20213                         result.push({
20214                             title: sugg,
20215                             value: sugg,
20216                             dist: dist
20217                         });
20218                     }
20219                 }
20220             }
20221             result.sort(function(a, b) {
20222                 return a.dist - b.dist;
20223             });
20224         }
20225         result = result.slice(0,3);
20226         callback(result);
20227     };
20228 };
20229 iD.geo = {};
20230
20231 iD.geo.roundCoords = function(c) {
20232     return [Math.floor(c[0]), Math.floor(c[1])];
20233 };
20234
20235 iD.geo.interp = function(p1, p2, t) {
20236     return [p1[0] + (p2[0] - p1[0]) * t,
20237             p1[1] + (p2[1] - p1[1]) * t];
20238 };
20239
20240 // 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product.
20241 // Returns a positive value, if OAB makes a counter-clockwise turn,
20242 // negative for clockwise turn, and zero if the points are collinear.
20243 iD.geo.cross = function(o, a, b) {
20244     return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
20245 };
20246
20247 // http://jsperf.com/id-dist-optimization
20248 iD.geo.euclideanDistance = function(a, b) {
20249     var x = a[0] - b[0], y = a[1] - b[1];
20250     return Math.sqrt((x * x) + (y * y));
20251 };
20252
20253 // using WGS84 polar radius (6356752.314245179 m)
20254 // const = 2 * PI * r / 360
20255 iD.geo.latToMeters = function(dLat) {
20256     return dLat * 110946.257617;
20257 };
20258
20259 // using WGS84 equatorial radius (6378137.0 m)
20260 // const = 2 * PI * r / 360
20261 iD.geo.lonToMeters = function(dLon, atLat) {
20262     return Math.abs(atLat) >= 90 ? 0 :
20263         dLon * 111319.490793 * Math.abs(Math.cos(atLat * (Math.PI/180)));
20264 };
20265
20266 // using WGS84 polar radius (6356752.314245179 m)
20267 // const = 2 * PI * r / 360
20268 iD.geo.metersToLat = function(m) {
20269     return m / 110946.257617;
20270 };
20271
20272 // using WGS84 equatorial radius (6378137.0 m)
20273 // const = 2 * PI * r / 360
20274 iD.geo.metersToLon = function(m, atLat) {
20275     return Math.abs(atLat) >= 90 ? 0 :
20276         m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180)));
20277 };
20278
20279 // Equirectangular approximation of spherical distances on Earth
20280 iD.geo.sphericalDistance = function(a, b) {
20281     var x = iD.geo.lonToMeters(a[0] - b[0], (a[1] + b[1]) / 2),
20282         y = iD.geo.latToMeters(a[1] - b[1]);
20283     return Math.sqrt((x * x) + (y * y));
20284 };
20285
20286 iD.geo.edgeEqual = function(a, b) {
20287     return (a[0] === b[0] && a[1] === b[1]) ||
20288         (a[0] === b[1] && a[1] === b[0]);
20289 };
20290
20291 // Return the counterclockwise angle in the range (-pi, pi)
20292 // between the positive X axis and the line intersecting a and b.
20293 iD.geo.angle = function(a, b, projection) {
20294     a = projection(a.loc);
20295     b = projection(b.loc);
20296     return Math.atan2(b[1] - a[1], b[0] - a[0]);
20297 };
20298
20299 // Choose the edge with the minimal distance from `point` to its orthogonal
20300 // projection onto that edge, if such a projection exists, or the distance to
20301 // the closest vertex on that edge. Returns an object with the `index` of the
20302 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
20303 iD.geo.chooseEdge = function(nodes, point, projection) {
20304     var dist = iD.geo.euclideanDistance,
20305         points = nodes.map(function(n) { return projection(n.loc); }),
20306         min = Infinity,
20307         idx, loc;
20308
20309     function dot(p, q) {
20310         return p[0] * q[0] + p[1] * q[1];
20311     }
20312
20313     for (var i = 0; i < points.length - 1; i++) {
20314         var o = points[i],
20315             s = [points[i + 1][0] - o[0],
20316                  points[i + 1][1] - o[1]],
20317             v = [point[0] - o[0],
20318                  point[1] - o[1]],
20319             proj = dot(v, s) / dot(s, s),
20320             p;
20321
20322         if (proj < 0) {
20323             p = o;
20324         } else if (proj > 1) {
20325             p = points[i + 1];
20326         } else {
20327             p = [o[0] + proj * s[0], o[1] + proj * s[1]];
20328         }
20329
20330         var d = dist(p, point);
20331         if (d < min) {
20332             min = d;
20333             idx = i + 1;
20334             loc = projection.invert(p);
20335         }
20336     }
20337
20338     return {
20339         index: idx,
20340         distance: min,
20341         loc: loc
20342     };
20343 };
20344
20345 // Return the intersection point of 2 line segments.
20346 // From https://github.com/pgkelley4/line-segments-intersect
20347 // This uses the vector cross product approach described below:
20348 //  http://stackoverflow.com/a/565282/786339
20349 iD.geo.lineIntersection = function(a, b) {
20350     function subtractPoints(point1, point2) {
20351         return [point1[0] - point2[0], point1[1] - point2[1]];
20352     }
20353     function crossProduct(point1, point2) {
20354         return point1[0] * point2[1] - point1[1] * point2[0];
20355     }
20356
20357     var p = [a[0][0], a[0][1]],
20358         p2 = [a[1][0], a[1][1]],
20359         q = [b[0][0], b[0][1]],
20360         q2 = [b[1][0], b[1][1]],
20361         r = subtractPoints(p2, p),
20362         s = subtractPoints(q2, q),
20363         uNumerator = crossProduct(subtractPoints(q, p), r),
20364         denominator = crossProduct(r, s);
20365
20366     if (uNumerator && denominator) {
20367         var u = uNumerator / denominator,
20368             t = crossProduct(subtractPoints(q, p), s) / denominator;
20369
20370         if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
20371             return iD.geo.interp(p, p2, t);
20372         }
20373     }
20374
20375     return null;
20376 };
20377
20378 iD.geo.pathIntersections = function(path1, path2) {
20379     var intersections = [];
20380     for (var i = 0; i < path1.length - 1; i++) {
20381         for (var j = 0; j < path2.length - 1; j++) {
20382             var a = [ path1[i], path1[i+1] ],
20383                 b = [ path2[j], path2[j+1] ],
20384                 hit = iD.geo.lineIntersection(a, b);
20385             if (hit) intersections.push(hit);
20386         }
20387     }
20388     return intersections;
20389 };
20390
20391 // Return whether point is contained in polygon.
20392 //
20393 // `point` should be a 2-item array of coordinates.
20394 // `polygon` should be an array of 2-item arrays of coordinates.
20395 //
20396 // From https://github.com/substack/point-in-polygon.
20397 // ray-casting algorithm based on
20398 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
20399 //
20400 iD.geo.pointInPolygon = function(point, polygon) {
20401     var x = point[0],
20402         y = point[1],
20403         inside = false;
20404
20405     for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
20406         var xi = polygon[i][0], yi = polygon[i][1];
20407         var xj = polygon[j][0], yj = polygon[j][1];
20408
20409         var intersect = ((yi > y) !== (yj > y)) &&
20410             (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
20411         if (intersect) inside = !inside;
20412     }
20413
20414     return inside;
20415 };
20416
20417 iD.geo.polygonContainsPolygon = function(outer, inner) {
20418     return _.every(inner, function(point) {
20419         return iD.geo.pointInPolygon(point, outer);
20420     });
20421 };
20422
20423 iD.geo.polygonIntersectsPolygon = function(outer, inner, checkSegments) {
20424     function testSegments(outer, inner) {
20425         for (var i = 0; i < outer.length - 1; i++) {
20426             for (var j = 0; j < inner.length - 1; j++) {
20427                 var a = [ outer[i], outer[i+1] ],
20428                     b = [ inner[j], inner[j+1] ];
20429                 if (iD.geo.lineIntersection(a, b)) return true;
20430             }
20431         }
20432         return false;
20433     }
20434
20435     function testPoints(outer, inner) {
20436         return _.some(inner, function(point) {
20437             return iD.geo.pointInPolygon(point, outer);
20438         });
20439     }
20440
20441    return testPoints(outer, inner) || (!!checkSegments && testSegments(outer, inner));
20442 };
20443
20444 iD.geo.pathLength = function(path) {
20445     var length = 0,
20446         dx, dy;
20447     for (var i = 0; i < path.length - 1; i++) {
20448         dx = path[i][0] - path[i + 1][0];
20449         dy = path[i][1] - path[i + 1][1];
20450         length += Math.sqrt(dx * dx + dy * dy);
20451     }
20452     return length;
20453 };
20454 iD.geo.Extent = function geoExtent(min, max) {
20455     if (!(this instanceof iD.geo.Extent)) return new iD.geo.Extent(min, max);
20456     if (min instanceof iD.geo.Extent) {
20457         return min;
20458     } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
20459         this[0] = min[0];
20460         this[1] = min[1];
20461     } else {
20462         this[0] = min        || [ Infinity,  Infinity];
20463         this[1] = max || min || [-Infinity, -Infinity];
20464     }
20465 };
20466
20467 iD.geo.Extent.prototype = new Array(2);
20468
20469 _.extend(iD.geo.Extent.prototype, {
20470     equals: function (obj) {
20471         return this[0][0] === obj[0][0] &&
20472             this[0][1] === obj[0][1] &&
20473             this[1][0] === obj[1][0] &&
20474             this[1][1] === obj[1][1];
20475     },
20476
20477     extend: function(obj) {
20478         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20479         return iD.geo.Extent([Math.min(obj[0][0], this[0][0]),
20480                               Math.min(obj[0][1], this[0][1])],
20481                              [Math.max(obj[1][0], this[1][0]),
20482                               Math.max(obj[1][1], this[1][1])]);
20483     },
20484
20485     _extend: function(extent) {
20486         this[0][0] = Math.min(extent[0][0], this[0][0]);
20487         this[0][1] = Math.min(extent[0][1], this[0][1]);
20488         this[1][0] = Math.max(extent[1][0], this[1][0]);
20489         this[1][1] = Math.max(extent[1][1], this[1][1]);
20490     },
20491
20492     area: function() {
20493         return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
20494     },
20495
20496     center: function() {
20497         return [(this[0][0] + this[1][0]) / 2,
20498                 (this[0][1] + this[1][1]) / 2];
20499     },
20500
20501     rectangle: function() {
20502         return [this[0][0], this[0][1], this[1][0], this[1][1]];
20503     },
20504
20505     polygon: function() {
20506         return [
20507             [this[0][0], this[0][1]],
20508             [this[0][0], this[1][1]],
20509             [this[1][0], this[1][1]],
20510             [this[1][0], this[0][1]],
20511             [this[0][0], this[0][1]]
20512         ];
20513     },
20514
20515     contains: function(obj) {
20516         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20517         return obj[0][0] >= this[0][0] &&
20518                obj[0][1] >= this[0][1] &&
20519                obj[1][0] <= this[1][0] &&
20520                obj[1][1] <= this[1][1];
20521     },
20522
20523     intersects: function(obj) {
20524         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20525         return obj[0][0] <= this[1][0] &&
20526                obj[0][1] <= this[1][1] &&
20527                obj[1][0] >= this[0][0] &&
20528                obj[1][1] >= this[0][1];
20529     },
20530
20531     intersection: function(obj) {
20532         if (!this.intersects(obj)) return new iD.geo.Extent();
20533         return new iD.geo.Extent([Math.max(obj[0][0], this[0][0]),
20534                                   Math.max(obj[0][1], this[0][1])],
20535                                  [Math.min(obj[1][0], this[1][0]),
20536                                   Math.min(obj[1][1], this[1][1])]);
20537     },
20538
20539     percentContainedIn: function(obj) {
20540         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20541         var a1 = this.intersection(obj).area(),
20542             a2 = this.area();
20543
20544         if (a1 === Infinity || a2 === Infinity || a1 === 0 || a2 === 0) {
20545             return 0;
20546         } else {
20547             return a1 / a2;
20548         }
20549     },
20550
20551     padByMeters: function(meters) {
20552         var dLat = iD.geo.metersToLat(meters),
20553             dLon = iD.geo.metersToLon(meters, this.center()[1]);
20554         return iD.geo.Extent(
20555                 [this[0][0] - dLon, this[0][1] - dLat],
20556                 [this[1][0] + dLon, this[1][1] + dLat]);
20557     },
20558
20559     toParam: function() {
20560         return this.rectangle().join(',');
20561     }
20562
20563 });
20564 iD.geo.Turn = function(turn) {
20565     if (!(this instanceof iD.geo.Turn))
20566         return new iD.geo.Turn(turn);
20567     _.extend(this, turn);
20568 };
20569
20570 iD.geo.Intersection = function(graph, vertexId) {
20571     var vertex = graph.entity(vertexId),
20572         parentWays = graph.parentWays(vertex),
20573         coincident = [],
20574         highways = {};
20575
20576     function addHighway(way, adjacentNodeId) {
20577         if (highways[adjacentNodeId]) {
20578             coincident.push(adjacentNodeId);
20579         } else {
20580             highways[adjacentNodeId] = way;
20581         }
20582     }
20583
20584     // Pre-split ways that would need to be split in
20585     // order to add a restriction. The real split will
20586     // happen when the restriction is added.
20587     parentWays.forEach(function(way) {
20588         if (!way.tags.highway || way.isArea() || way.isDegenerate())
20589             return;
20590
20591         var isFirst = (vertexId === way.first()),
20592             isLast = (vertexId === way.last()),
20593             isAffix = (isFirst || isLast),
20594             isClosingNode = (isFirst && isLast);
20595
20596         if (isAffix && !isClosingNode) {
20597             var index = (isFirst ? 1 : way.nodes.length - 2);
20598             addHighway(way, way.nodes[index]);
20599
20600         } else {
20601             var splitIndex, wayA, wayB, indexA, indexB;
20602             if (isClosingNode) {
20603                 splitIndex = Math.ceil(way.nodes.length / 2);  // split at midpoint
20604                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
20605                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
20606                 indexA = 1;
20607                 indexB = way.nodes.length - 2;
20608             } else {
20609                 splitIndex = _.indexOf(way.nodes, vertex.id, 1);  // split at vertexid
20610                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
20611                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
20612                 indexA = splitIndex - 1;
20613                 indexB = splitIndex + 1;
20614             }
20615             graph = graph.replace(wayA).replace(wayB);
20616             addHighway(wayA, way.nodes[indexA]);
20617             addHighway(wayB, way.nodes[indexB]);
20618         }
20619     });
20620
20621     // remove any ways from this intersection that are coincident
20622     // (i.e. any adjacent node used by more than one intersecting way)
20623     coincident.forEach(function (n) {
20624         delete highways[n];
20625     });
20626
20627
20628     var intersection = {
20629         highways: highways,
20630         ways: _.values(highways),
20631         graph: graph
20632     };
20633
20634     intersection.adjacentNodeId = function(fromWayId) {
20635         return _.find(_.keys(highways), function(k) {
20636             return highways[k].id === fromWayId;
20637         });
20638     };
20639
20640     intersection.turns = function(fromNodeId) {
20641         var start = highways[fromNodeId];
20642         if (!start)
20643             return [];
20644
20645         if (start.first() === vertex.id && start.tags.oneway === 'yes')
20646             return [];
20647         if (start.last() === vertex.id && start.tags.oneway === '-1')
20648             return [];
20649
20650         function withRestriction(turn) {
20651             graph.parentRelations(graph.entity(turn.from.way)).forEach(function(relation) {
20652                 if (relation.tags.type !== 'restriction')
20653                     return;
20654
20655                 var f = relation.memberByRole('from'),
20656                     t = relation.memberByRole('to'),
20657                     v = relation.memberByRole('via');
20658
20659                 if (f && f.id === turn.from.way &&
20660                     v && v.id === turn.via.node &&
20661                     t && t.id === turn.to.way) {
20662                     turn.restriction = relation.id;
20663                 } else if (/^only_/.test(relation.tags.restriction) &&
20664                     f && f.id === turn.from.way &&
20665                     v && v.id === turn.via.node &&
20666                     t && t.id !== turn.to.way) {
20667                     turn.restriction = relation.id;
20668                     turn.indirect_restriction = true;
20669                 }
20670             });
20671
20672             return iD.geo.Turn(turn);
20673         }
20674
20675         var from = {
20676                 node: fromNodeId,
20677                 way: start.id.split(/-(a|b)/)[0]
20678             },
20679             via = { node: vertex.id },
20680             turns = [];
20681
20682         _.each(highways, function(end, adjacentNodeId) {
20683             if (end === start)
20684                 return;
20685
20686             // backward
20687             if (end.first() !== vertex.id && end.tags.oneway !== 'yes') {
20688                 turns.push(withRestriction({
20689                     from: from,
20690                     via: via,
20691                     to: {
20692                         node: adjacentNodeId,
20693                         way: end.id.split(/-(a|b)/)[0]
20694                     }
20695                 }));
20696             }
20697
20698             // forward
20699             if (end.last() !== vertex.id && end.tags.oneway !== '-1') {
20700                 turns.push(withRestriction({
20701                     from: from,
20702                     via: via,
20703                     to: {
20704                         node: adjacentNodeId,
20705                         way: end.id.split(/-(a|b)/)[0]
20706                     }
20707                 }));
20708             }
20709
20710         });
20711
20712         // U-turn
20713         if (start.tags.oneway !== 'yes' && start.tags.oneway !== '-1') {
20714             turns.push(withRestriction({
20715                 from: from,
20716                 via: via,
20717                 to: from,
20718                 u: true
20719             }));
20720         }
20721
20722         return turns;
20723     };
20724
20725     return intersection;
20726 };
20727
20728
20729 iD.geo.inferRestriction = function(graph, from, via, to, projection) {
20730     var fromWay = graph.entity(from.way),
20731         fromNode = graph.entity(from.node),
20732         toWay = graph.entity(to.way),
20733         toNode = graph.entity(to.node),
20734         viaNode = graph.entity(via.node),
20735         fromOneWay = (fromWay.tags.oneway === 'yes' && fromWay.last() === via.node) ||
20736             (fromWay.tags.oneway === '-1' && fromWay.first() === via.node),
20737         toOneWay = (toWay.tags.oneway === 'yes' && toWay.first() === via.node) ||
20738             (toWay.tags.oneway === '-1' && toWay.last() === via.node),
20739         angle = iD.geo.angle(viaNode, fromNode, projection) -
20740                 iD.geo.angle(viaNode, toNode, projection);
20741
20742     angle = angle * 180 / Math.PI;
20743
20744     while (angle < 0)
20745         angle += 360;
20746
20747     if (fromNode === toNode)
20748         return 'no_u_turn';
20749     if ((angle < 23 || angle > 336) && fromOneWay && toOneWay)
20750         return 'no_u_turn';
20751     if (angle < 158)
20752         return 'no_right_turn';
20753     if (angle > 202)
20754         return 'no_left_turn';
20755
20756     return 'no_straight_on';
20757 };
20758 // For fixing up rendering of multipolygons with tags on the outer member.
20759 // https://github.com/openstreetmap/iD/issues/613
20760 iD.geo.isSimpleMultipolygonOuterMember = function(entity, graph) {
20761     if (entity.type !== 'way')
20762         return false;
20763
20764     var parents = graph.parentRelations(entity);
20765     if (parents.length !== 1)
20766         return false;
20767
20768     var parent = parents[0];
20769     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
20770         return false;
20771
20772     var members = parent.members, member;
20773     for (var i = 0; i < members.length; i++) {
20774         member = members[i];
20775         if (member.id === entity.id && member.role && member.role !== 'outer')
20776             return false; // Not outer member
20777         if (member.id !== entity.id && (!member.role || member.role === 'outer'))
20778             return false; // Not a simple multipolygon
20779     }
20780
20781     return parent;
20782 };
20783
20784 iD.geo.simpleMultipolygonOuterMember = function(entity, graph) {
20785     if (entity.type !== 'way')
20786         return false;
20787
20788     var parents = graph.parentRelations(entity);
20789     if (parents.length !== 1)
20790         return false;
20791
20792     var parent = parents[0];
20793     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
20794         return false;
20795
20796     var members = parent.members, member, outerMember;
20797     for (var i = 0; i < members.length; i++) {
20798         member = members[i];
20799         if (!member.role || member.role === 'outer') {
20800             if (outerMember)
20801                 return false; // Not a simple multipolygon
20802             outerMember = member;
20803         }
20804     }
20805
20806     return outerMember && graph.hasEntity(outerMember.id);
20807 };
20808
20809 // Join `array` into sequences of connecting ways.
20810 //
20811 // Segments which share identical start/end nodes will, as much as possible,
20812 // be connected with each other.
20813 //
20814 // The return value is a nested array. Each constituent array contains elements
20815 // of `array` which have been determined to connect. Each consitituent array
20816 // also has a `nodes` property whose value is an ordered array of member nodes,
20817 // with appropriate order reversal and start/end coordinate de-duplication.
20818 //
20819 // Members of `array` must have, at minimum, `type` and `id` properties.
20820 // Thus either an array of `iD.Way`s or a relation member array may be
20821 // used.
20822 //
20823 // If an member has a `tags` property, its tags will be reversed via
20824 // `iD.actions.Reverse` in the output.
20825 //
20826 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
20827 // false) and non-way members are ignored.
20828 //
20829 iD.geo.joinWays = function(array, graph) {
20830     var joined = [], member, current, nodes, first, last, i, how, what;
20831
20832     array = array.filter(function(member) {
20833         return member.type === 'way' && graph.hasEntity(member.id);
20834     });
20835
20836     function resolve(member) {
20837         return graph.childNodes(graph.entity(member.id));
20838     }
20839
20840     function reverse(member) {
20841         return member.tags ? iD.actions.Reverse(member.id, {reverseOneway: true})(graph).entity(member.id) : member;
20842     }
20843
20844     while (array.length) {
20845         member = array.shift();
20846         current = [member];
20847         current.nodes = nodes = resolve(member).slice();
20848         joined.push(current);
20849
20850         while (array.length && _.first(nodes) !== _.last(nodes)) {
20851             first = _.first(nodes);
20852             last  = _.last(nodes);
20853
20854             for (i = 0; i < array.length; i++) {
20855                 member = array[i];
20856                 what = resolve(member);
20857
20858                 if (last === _.first(what)) {
20859                     how  = nodes.push;
20860                     what = what.slice(1);
20861                     break;
20862                 } else if (last === _.last(what)) {
20863                     how  = nodes.push;
20864                     what = what.slice(0, -1).reverse();
20865                     member = reverse(member);
20866                     break;
20867                 } else if (first === _.last(what)) {
20868                     how  = nodes.unshift;
20869                     what = what.slice(0, -1);
20870                     break;
20871                 } else if (first === _.first(what)) {
20872                     how  = nodes.unshift;
20873                     what = what.slice(1).reverse();
20874                     member = reverse(member);
20875                     break;
20876                 } else {
20877                     what = how = null;
20878                 }
20879             }
20880
20881             if (!what)
20882                 break; // No more joinable ways.
20883
20884             how.apply(current, [member]);
20885             how.apply(nodes, what);
20886
20887             array.splice(i, 1);
20888         }
20889     }
20890
20891     return joined;
20892 };
20893 /*
20894     Bypasses features of D3's default projection stream pipeline that are unnecessary:
20895     * Antimeridian clipping
20896     * Spherical rotation
20897     * Resampling
20898 */
20899 iD.geo.RawMercator = function () {
20900     var project = d3.geo.mercator.raw,
20901         k = 512 / Math.PI, // scale
20902         x = 0, y = 0, // translate
20903         clipExtent = [[0, 0], [0, 0]];
20904
20905     function projection(point) {
20906         point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
20907         return [point[0] * k + x, y - point[1] * k];
20908     }
20909
20910     projection.invert = function(point) {
20911         point = project.invert((point[0] - x) / k, (y - point[1]) / k);
20912         return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
20913     };
20914
20915     projection.scale = function(_) {
20916         if (!arguments.length) return k;
20917         k = +_;
20918         return projection;
20919     };
20920
20921     projection.translate = function(_) {
20922         if (!arguments.length) return [x, y];
20923         x = +_[0];
20924         y = +_[1];
20925         return projection;
20926     };
20927
20928     projection.clipExtent = function(_) {
20929         if (!arguments.length) return clipExtent;
20930         clipExtent = _;
20931         return projection;
20932     };
20933
20934     projection.stream = d3.geo.transform({
20935         point: function(x, y) {
20936             x = projection([x, y]);
20937             this.stream.point(x[0], x[1]);
20938         }
20939     }).stream;
20940
20941     return projection;
20942 };
20943 iD.actions = {};
20944 iD.actions.AddEntity = function(way) {
20945     return function(graph) {
20946         return graph.replace(way);
20947     };
20948 };
20949 iD.actions.AddMember = function(relationId, member, memberIndex) {
20950     return function(graph) {
20951         var relation = graph.entity(relationId);
20952
20953         if (isNaN(memberIndex) && member.type === 'way') {
20954             var members = relation.indexedMembers();
20955             members.push(member);
20956
20957             var joined = iD.geo.joinWays(members, graph);
20958             for (var i = 0; i < joined.length; i++) {
20959                 var segment = joined[i];
20960                 for (var j = 0; j < segment.length && segment.length >= 2; j++) {
20961                     if (segment[j] !== member)
20962                         continue;
20963
20964                     if (j === 0) {
20965                         memberIndex = segment[j + 1].index;
20966                     } else if (j === segment.length - 1) {
20967                         memberIndex = segment[j - 1].index + 1;
20968                     } else {
20969                         memberIndex = Math.min(segment[j - 1].index + 1, segment[j + 1].index + 1);
20970                     }
20971                 }
20972             }
20973         }
20974
20975         return graph.replace(relation.addMember(member, memberIndex));
20976     };
20977 };
20978 iD.actions.AddMidpoint = function(midpoint, node) {
20979     return function(graph) {
20980         graph = graph.replace(node.move(midpoint.loc));
20981
20982         var parents = _.intersection(
20983             graph.parentWays(graph.entity(midpoint.edge[0])),
20984             graph.parentWays(graph.entity(midpoint.edge[1])));
20985
20986         parents.forEach(function(way) {
20987             for (var i = 0; i < way.nodes.length - 1; i++) {
20988                 if (iD.geo.edgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
20989                     graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
20990
20991                     // Add only one midpoint on doubled-back segments,
20992                     // turning them into self-intersections.
20993                     return;
20994                 }
20995             }
20996         });
20997
20998         return graph;
20999     };
21000 };
21001 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
21002 iD.actions.AddVertex = function(wayId, nodeId, index) {
21003     return function(graph) {
21004         return graph.replace(graph.entity(wayId).addNode(nodeId, index));
21005     };
21006 };
21007 iD.actions.ChangeMember = function(relationId, member, memberIndex) {
21008     return function(graph) {
21009         return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
21010     };
21011 };
21012 iD.actions.ChangePreset = function(entityId, oldPreset, newPreset) {
21013     return function(graph) {
21014         var entity = graph.entity(entityId),
21015             geometry = entity.geometry(graph),
21016             tags = entity.tags;
21017
21018         if (oldPreset) tags = oldPreset.removeTags(tags, geometry);
21019         if (newPreset) tags = newPreset.applyTags(tags, geometry);
21020
21021         return graph.replace(entity.update({tags: tags}));
21022     };
21023 };
21024 iD.actions.ChangeTags = function(entityId, tags) {
21025     return function(graph) {
21026         var entity = graph.entity(entityId);
21027         return graph.replace(entity.update({tags: tags}));
21028     };
21029 };
21030 iD.actions.Circularize = function(wayId, projection, maxAngle) {
21031     maxAngle = (maxAngle || 20) * Math.PI / 180;
21032
21033     var action = function(graph) {
21034         var way = graph.entity(wayId);
21035
21036         if (!way.isConvex(graph)) {
21037             graph = action.makeConvex(graph);
21038         }
21039
21040         var nodes = _.uniq(graph.childNodes(way)),
21041             keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }),
21042             points = nodes.map(function(n) { return projection(n.loc); }),
21043             keyPoints = keyNodes.map(function(n) { return projection(n.loc); }),
21044             centroid = (points.length === 2) ? iD.geo.interp(points[0], points[1], 0.5) : d3.geom.polygon(points).centroid(),
21045             radius = d3.median(points, function(p) { return iD.geo.euclideanDistance(centroid, p); }),
21046             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
21047             ids;
21048
21049         // we need atleast two key nodes for the algorithm to work
21050         if (!keyNodes.length) {
21051             keyNodes = [nodes[0]];
21052             keyPoints = [points[0]];
21053         }
21054
21055         if (keyNodes.length === 1) {
21056             var index = nodes.indexOf(keyNodes[0]),
21057                 oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
21058
21059             keyNodes.push(nodes[oppositeIndex]);
21060             keyPoints.push(points[oppositeIndex]);
21061         }
21062
21063         // key points and nodes are those connected to the ways,
21064         // they are projected onto the circle, inbetween nodes are moved
21065         // to constant intervals between key nodes, extra inbetween nodes are
21066         // added if necessary.
21067         for (var i = 0; i < keyPoints.length; i++) {
21068             var nextKeyNodeIndex = (i + 1) % keyNodes.length,
21069                 startNode = keyNodes[i],
21070                 endNode = keyNodes[nextKeyNodeIndex],
21071                 startNodeIndex = nodes.indexOf(startNode),
21072                 endNodeIndex = nodes.indexOf(endNode),
21073                 numberNewPoints = -1,
21074                 indexRange = endNodeIndex - startNodeIndex,
21075                 distance, totalAngle, eachAngle, startAngle, endAngle,
21076                 angle, loc, node, j,
21077                 inBetweenNodes = [];
21078
21079             if (indexRange < 0) {
21080                 indexRange += nodes.length;
21081             }
21082
21083             // position this key node
21084             distance = iD.geo.euclideanDistance(centroid, keyPoints[i]);
21085             if (distance === 0) { distance = 1e-4; }
21086             keyPoints[i] = [
21087                 centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
21088                 centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
21089             graph = graph.replace(keyNodes[i].move(projection.invert(keyPoints[i])));
21090
21091             // figure out the between delta angle we want to match to
21092             startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
21093             endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
21094             totalAngle = endAngle - startAngle;
21095
21096             // detects looping around -pi/pi
21097             if (totalAngle * sign > 0) {
21098                 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
21099             }
21100
21101             do {
21102                 numberNewPoints++;
21103                 eachAngle = totalAngle / (indexRange + numberNewPoints);
21104             } while (Math.abs(eachAngle) > maxAngle);
21105
21106             // move existing points
21107             for (j = 1; j < indexRange; j++) {
21108                 angle = startAngle + j * eachAngle;
21109                 loc = projection.invert([
21110                     centroid[0] + Math.cos(angle)*radius,
21111                     centroid[1] + Math.sin(angle)*radius]);
21112
21113                 node = nodes[(j + startNodeIndex) % nodes.length].move(loc);
21114                 graph = graph.replace(node);
21115             }
21116
21117             // add new inbetween nodes if necessary
21118             for (j = 0; j < numberNewPoints; j++) {
21119                 angle = startAngle + (indexRange + j) * eachAngle;
21120                 loc = projection.invert([
21121                     centroid[0] + Math.cos(angle) * radius,
21122                     centroid[1] + Math.sin(angle) * radius]);
21123
21124                 node = iD.Node({loc: loc});
21125                 graph = graph.replace(node);
21126
21127                 nodes.splice(endNodeIndex + j, 0, node);
21128                 inBetweenNodes.push(node.id);
21129             }
21130
21131             // Check for other ways that share these keyNodes..
21132             // If keyNodes are adjacent in both ways,
21133             // we can add inBetween nodes to that shared way too..
21134             if (indexRange === 1 && inBetweenNodes.length) {
21135                 var startIndex1 = way.nodes.lastIndexOf(startNode.id),
21136                     endIndex1 = way.nodes.lastIndexOf(endNode.id),
21137                     wayDirection1 = (endIndex1 - startIndex1);
21138                 if (wayDirection1 < -1) { wayDirection1 = 1; }
21139
21140                 /* eslint-disable no-loop-func */
21141                 _.each(_.without(graph.parentWays(keyNodes[i]), way), function(sharedWay) {
21142                     if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
21143                         var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id),
21144                             endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id),
21145                             wayDirection2 = (endIndex2 - startIndex2),
21146                             insertAt = endIndex2;
21147                         if (wayDirection2 < -1) { wayDirection2 = 1; }
21148
21149                         if (wayDirection1 !== wayDirection2) {
21150                             inBetweenNodes.reverse();
21151                             insertAt = startIndex2;
21152                         }
21153                         for (j = 0; j < inBetweenNodes.length; j++) {
21154                             sharedWay = sharedWay.addNode(inBetweenNodes[j], insertAt + j);
21155                         }
21156                         graph = graph.replace(sharedWay);
21157                     }
21158                 });
21159                 /* eslint-enable no-loop-func */
21160             }
21161
21162         }
21163
21164         // update the way to have all the new nodes
21165         ids = nodes.map(function(n) { return n.id; });
21166         ids.push(ids[0]);
21167
21168         way = way.update({nodes: ids});
21169         graph = graph.replace(way);
21170
21171         return graph;
21172     };
21173
21174     action.makeConvex = function(graph) {
21175         var way = graph.entity(wayId),
21176             nodes = _.uniq(graph.childNodes(way)),
21177             points = nodes.map(function(n) { return projection(n.loc); }),
21178             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
21179             hull = d3.geom.hull(points);
21180
21181         // D3 convex hulls go counterclockwise..
21182         if (sign === -1) {
21183             nodes.reverse();
21184             points.reverse();
21185         }
21186
21187         for (var i = 0; i < hull.length - 1; i++) {
21188             var startIndex = points.indexOf(hull[i]),
21189                 endIndex = points.indexOf(hull[i+1]),
21190                 indexRange = (endIndex - startIndex);
21191
21192             if (indexRange < 0) {
21193                 indexRange += nodes.length;
21194             }
21195
21196             // move interior nodes to the surface of the convex hull..
21197             for (var j = 1; j < indexRange; j++) {
21198                 var point = iD.geo.interp(hull[i], hull[i+1], j / indexRange),
21199                     node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
21200                 graph = graph.replace(node);
21201             }
21202         }
21203         return graph;
21204     };
21205
21206     action.disabled = function(graph) {
21207         if (!graph.entity(wayId).isClosed())
21208             return 'not_closed';
21209     };
21210
21211     return action;
21212 };
21213 // Connect the ways at the given nodes.
21214 //
21215 // The last node will survive. All other nodes will be replaced with
21216 // the surviving node in parent ways, and then removed.
21217 //
21218 // Tags and relation memberships of of non-surviving nodes are merged
21219 // to the survivor.
21220 //
21221 // This is the inverse of `iD.actions.Disconnect`.
21222 //
21223 // Reference:
21224 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
21225 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
21226 //
21227 iD.actions.Connect = function(nodeIds) {
21228     return function(graph) {
21229         var survivor = graph.entity(_.last(nodeIds));
21230
21231         for (var i = 0; i < nodeIds.length - 1; i++) {
21232             var node = graph.entity(nodeIds[i]);
21233
21234             /* eslint-disable no-loop-func */
21235             graph.parentWays(node).forEach(function(parent) {
21236                 if (!parent.areAdjacent(node.id, survivor.id)) {
21237                     graph = graph.replace(parent.replaceNode(node.id, survivor.id));
21238                 }
21239             });
21240
21241             graph.parentRelations(node).forEach(function(parent) {
21242                 graph = graph.replace(parent.replaceMember(node, survivor));
21243             });
21244             /* eslint-enable no-loop-func */
21245
21246             survivor = survivor.mergeTags(node.tags);
21247             graph = iD.actions.DeleteNode(node.id)(graph);
21248         }
21249
21250         graph = graph.replace(survivor);
21251
21252         return graph;
21253     };
21254 };
21255 iD.actions.CopyEntities = function(ids, fromGraph) {
21256     var copies = {};
21257
21258     var action = function(graph) {
21259         ids.forEach(function(id) {
21260             fromGraph.entity(id).copy(fromGraph, copies);
21261         });
21262
21263         for (var id in copies) {
21264             graph = graph.replace(copies[id]);
21265         }
21266
21267         return graph;
21268     };
21269
21270     action.copies = function() {
21271         return copies;
21272     };
21273
21274     return action;
21275 };
21276 iD.actions.DeleteMember = function(relationId, memberIndex) {
21277     return function(graph) {
21278         var relation = graph.entity(relationId)
21279             .removeMember(memberIndex);
21280
21281         graph = graph.replace(relation);
21282
21283         if (relation.isDegenerate())
21284             graph = iD.actions.DeleteRelation(relation.id)(graph);
21285
21286         return graph;
21287     };
21288 };
21289 iD.actions.DeleteMultiple = function(ids) {
21290     var actions = {
21291         way: iD.actions.DeleteWay,
21292         node: iD.actions.DeleteNode,
21293         relation: iD.actions.DeleteRelation
21294     };
21295
21296     var action = function(graph) {
21297         ids.forEach(function(id) {
21298             if (graph.hasEntity(id)) { // It may have been deleted aready.
21299                 graph = actions[graph.entity(id).type](id)(graph);
21300             }
21301         });
21302
21303         return graph;
21304     };
21305
21306     action.disabled = function(graph) {
21307         for (var i = 0; i < ids.length; i++) {
21308             var id = ids[i],
21309                 disabled = actions[graph.entity(id).type](id).disabled(graph);
21310             if (disabled) return disabled;
21311         }
21312     };
21313
21314     return action;
21315 };
21316 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
21317 iD.actions.DeleteNode = function(nodeId) {
21318     var action = function(graph) {
21319         var node = graph.entity(nodeId);
21320
21321         graph.parentWays(node)
21322             .forEach(function(parent) {
21323                 parent = parent.removeNode(nodeId);
21324                 graph = graph.replace(parent);
21325
21326                 if (parent.isDegenerate()) {
21327                     graph = iD.actions.DeleteWay(parent.id)(graph);
21328                 }
21329             });
21330
21331         graph.parentRelations(node)
21332             .forEach(function(parent) {
21333                 parent = parent.removeMembersWithID(nodeId);
21334                 graph = graph.replace(parent);
21335
21336                 if (parent.isDegenerate()) {
21337                     graph = iD.actions.DeleteRelation(parent.id)(graph);
21338                 }
21339             });
21340
21341         return graph.remove(node);
21342     };
21343
21344     action.disabled = function() {
21345         return false;
21346     };
21347
21348     return action;
21349 };
21350 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
21351 iD.actions.DeleteRelation = function(relationId) {
21352     function deleteEntity(entity, graph) {
21353         return !graph.parentWays(entity).length &&
21354             !graph.parentRelations(entity).length &&
21355             !entity.hasInterestingTags();
21356     }
21357
21358     var action = function(graph) {
21359         var relation = graph.entity(relationId);
21360
21361         graph.parentRelations(relation)
21362             .forEach(function(parent) {
21363                 parent = parent.removeMembersWithID(relationId);
21364                 graph = graph.replace(parent);
21365
21366                 if (parent.isDegenerate()) {
21367                     graph = iD.actions.DeleteRelation(parent.id)(graph);
21368                 }
21369             });
21370
21371         _.uniq(_.pluck(relation.members, 'id')).forEach(function(memberId) {
21372             graph = graph.replace(relation.removeMembersWithID(memberId));
21373
21374             var entity = graph.entity(memberId);
21375             if (deleteEntity(entity, graph)) {
21376                 graph = iD.actions.DeleteMultiple([memberId])(graph);
21377             }
21378         });
21379
21380         return graph.remove(relation);
21381     };
21382
21383     action.disabled = function(graph) {
21384         if (!graph.entity(relationId).isComplete(graph))
21385             return 'incomplete_relation';
21386     };
21387
21388     return action;
21389 };
21390 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
21391 iD.actions.DeleteWay = function(wayId) {
21392     function deleteNode(node, graph) {
21393         return !graph.parentWays(node).length &&
21394             !graph.parentRelations(node).length &&
21395             !node.hasInterestingTags();
21396     }
21397
21398     var action = function(graph) {
21399         var way = graph.entity(wayId);
21400
21401         graph.parentRelations(way)
21402             .forEach(function(parent) {
21403                 parent = parent.removeMembersWithID(wayId);
21404                 graph = graph.replace(parent);
21405
21406                 if (parent.isDegenerate()) {
21407                     graph = iD.actions.DeleteRelation(parent.id)(graph);
21408                 }
21409             });
21410
21411         _.uniq(way.nodes).forEach(function(nodeId) {
21412             graph = graph.replace(way.removeNode(nodeId));
21413
21414             var node = graph.entity(nodeId);
21415             if (deleteNode(node, graph)) {
21416                 graph = graph.remove(node);
21417             }
21418         });
21419
21420         return graph.remove(way);
21421     };
21422
21423     action.disabled = function(graph) {
21424         var disabled = false;
21425
21426         graph.parentRelations(graph.entity(wayId)).forEach(function(parent) {
21427             var type = parent.tags.type,
21428                 role = parent.memberById(wayId).role || 'outer';
21429             if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
21430                 disabled = 'part_of_relation';
21431             }
21432         });
21433
21434         return disabled;
21435     };
21436
21437     return action;
21438 };
21439 iD.actions.DeprecateTags = function(entityId) {
21440     return function(graph) {
21441         var entity = graph.entity(entityId),
21442             newtags = _.clone(entity.tags),
21443             change = false,
21444             rule;
21445
21446         // This handles deprecated tags with a single condition
21447         for (var i = 0; i < iD.data.deprecated.length; i++) {
21448
21449             rule = iD.data.deprecated[i];
21450             var match = _.pairs(rule.old)[0],
21451                 replacements = rule.replace ? _.pairs(rule.replace) : null;
21452
21453             if (entity.tags[match[0]] && match[1] === '*') {
21454
21455                 var value = entity.tags[match[0]];
21456                 if (replacements && !newtags[replacements[0][0]]) {
21457                     newtags[replacements[0][0]] = value;
21458                 }
21459                 delete newtags[match[0]];
21460                 change = true;
21461
21462             } else if (entity.tags[match[0]] === match[1]) {
21463                 newtags = _.assign({}, rule.replace || {}, _.omit(newtags, match[0]));
21464                 change = true;
21465             }
21466         }
21467
21468         if (change) {
21469             return graph.replace(entity.update({tags: newtags}));
21470         } else {
21471             return graph;
21472         }
21473     };
21474 };
21475 iD.actions.DiscardTags = function(difference) {
21476     return function(graph) {
21477         function discardTags(entity) {
21478             if (!_.isEmpty(entity.tags)) {
21479                 var tags = {};
21480                 _.each(entity.tags, function(v, k) {
21481                     if (v) tags[k] = v;
21482                 });
21483
21484                 graph = graph.replace(entity.update({
21485                     tags: _.omit(tags, iD.data.discarded)
21486                 }));
21487             }
21488         }
21489
21490         difference.modified().forEach(discardTags);
21491         difference.created().forEach(discardTags);
21492
21493         return graph;
21494     };
21495 };
21496 // Disconect the ways at the given node.
21497 //
21498 // Optionally, disconnect only the given ways.
21499 //
21500 // For testing convenience, accepts an ID to assign to the (first) new node.
21501 // Normally, this will be undefined and the way will automatically
21502 // be assigned a new ID.
21503 //
21504 // This is the inverse of `iD.actions.Connect`.
21505 //
21506 // Reference:
21507 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
21508 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
21509 //
21510 iD.actions.Disconnect = function(nodeId, newNodeId) {
21511     var wayIds;
21512
21513     var action = function(graph) {
21514         var node = graph.entity(nodeId),
21515             connections = action.connections(graph);
21516
21517         connections.forEach(function(connection) {
21518             var way = graph.entity(connection.wayID),
21519                 newNode = iD.Node({id: newNodeId, loc: node.loc, tags: node.tags});
21520
21521             graph = graph.replace(newNode);
21522             if (connection.index === 0 && way.isArea()) {
21523                 // replace shared node with shared node..
21524                 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
21525             } else {
21526                 // replace shared node with multiple new nodes..
21527                 graph = graph.replace(way.updateNode(newNode.id, connection.index));
21528             }
21529         });
21530
21531         return graph;
21532     };
21533
21534     action.connections = function(graph) {
21535         var candidates = [],
21536             keeping = false,
21537             parentWays = graph.parentWays(graph.entity(nodeId));
21538
21539         parentWays.forEach(function(way) {
21540             if (wayIds && wayIds.indexOf(way.id) === -1) {
21541                 keeping = true;
21542                 return;
21543             }
21544             if (way.isArea() && (way.nodes[0] === nodeId)) {
21545                 candidates.push({wayID: way.id, index: 0});
21546             } else {
21547                 way.nodes.forEach(function(waynode, index) {
21548                     if (waynode === nodeId) {
21549                         candidates.push({wayID: way.id, index: index});
21550                     }
21551                 });
21552             }
21553         });
21554
21555         return keeping ? candidates : candidates.slice(1);
21556     };
21557
21558     action.disabled = function(graph) {
21559         var connections = action.connections(graph);
21560         if (connections.length === 0 || (wayIds && wayIds.length !== connections.length))
21561             return 'not_connected';
21562
21563         var parentWays = graph.parentWays(graph.entity(nodeId)),
21564             seenRelationIds = {},
21565             sharedRelation;
21566
21567         parentWays.forEach(function(way) {
21568             if (wayIds && wayIds.indexOf(way.id) === -1)
21569                 return;
21570
21571             var relations = graph.parentRelations(way);
21572             relations.forEach(function(relation) {
21573                 if (relation.id in seenRelationIds) {
21574                     sharedRelation = relation;
21575                 } else {
21576                     seenRelationIds[relation.id] = true;
21577                 }
21578             });
21579         });
21580
21581         if (sharedRelation)
21582             return 'relation';
21583     };
21584
21585     action.limitWays = function(_) {
21586         if (!arguments.length) return wayIds;
21587         wayIds = _;
21588         return action;
21589     };
21590
21591     return action;
21592 };
21593 // Join ways at the end node they share.
21594 //
21595 // This is the inverse of `iD.actions.Split`.
21596 //
21597 // Reference:
21598 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
21599 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
21600 //
21601 iD.actions.Join = function(ids) {
21602
21603     function groupEntitiesByGeometry(graph) {
21604         var entities = ids.map(function(id) { return graph.entity(id); });
21605         return _.extend({line: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
21606     }
21607
21608     var action = function(graph) {
21609         var ways = ids.map(graph.entity, graph),
21610             survivor = ways[0];
21611
21612         // Prefer to keep an existing way.
21613         for (var i = 0; i < ways.length; i++) {
21614             if (!ways[i].isNew()) {
21615                 survivor = ways[i];
21616                 break;
21617             }
21618         }
21619
21620         var joined = iD.geo.joinWays(ways, graph)[0];
21621
21622         survivor = survivor.update({nodes: _.pluck(joined.nodes, 'id')});
21623         graph = graph.replace(survivor);
21624
21625         joined.forEach(function(way) {
21626             if (way.id === survivor.id)
21627                 return;
21628
21629             graph.parentRelations(way).forEach(function(parent) {
21630                 graph = graph.replace(parent.replaceMember(way, survivor));
21631             });
21632
21633             survivor = survivor.mergeTags(way.tags);
21634
21635             graph = graph.replace(survivor);
21636             graph = iD.actions.DeleteWay(way.id)(graph);
21637         });
21638
21639         return graph;
21640     };
21641
21642     action.disabled = function(graph) {
21643         var geometries = groupEntitiesByGeometry(graph);
21644         if (ids.length < 2 || ids.length !== geometries.line.length)
21645             return 'not_eligible';
21646
21647         var joined = iD.geo.joinWays(ids.map(graph.entity, graph), graph);
21648         if (joined.length > 1)
21649             return 'not_adjacent';
21650
21651         var nodeIds = _.pluck(joined[0].nodes, 'id').slice(1, -1),
21652             relation,
21653             tags = {},
21654             conflicting = false;
21655
21656         joined[0].forEach(function(way) {
21657             var parents = graph.parentRelations(way);
21658             parents.forEach(function(parent) {
21659                 if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; }))
21660                     relation = parent;
21661             });
21662
21663             for (var k in way.tags) {
21664                 if (!(k in tags)) {
21665                     tags[k] = way.tags[k];
21666                 } else if (tags[k] && iD.interestingTag(k) && tags[k] !== way.tags[k]) {
21667                     conflicting = true;
21668                 }
21669             }
21670         });
21671
21672         if (relation)
21673             return 'restriction';
21674
21675         if (conflicting)
21676             return 'conflicting_tags';
21677     };
21678
21679     return action;
21680 };
21681 iD.actions.Merge = function(ids) {
21682     function groupEntitiesByGeometry(graph) {
21683         var entities = ids.map(function(id) { return graph.entity(id); });
21684         return _.extend({point: [], area: [], line: [], relation: []},
21685             _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
21686     }
21687
21688     var action = function(graph) {
21689         var geometries = groupEntitiesByGeometry(graph),
21690             target = geometries.area[0] || geometries.line[0],
21691             points = geometries.point;
21692
21693         points.forEach(function(point) {
21694             target = target.mergeTags(point.tags);
21695
21696             graph.parentRelations(point).forEach(function(parent) {
21697                 graph = graph.replace(parent.replaceMember(point, target));
21698             });
21699
21700             graph = graph.remove(point);
21701         });
21702
21703         graph = graph.replace(target);
21704
21705         return graph;
21706     };
21707
21708     action.disabled = function(graph) {
21709         var geometries = groupEntitiesByGeometry(graph);
21710         if (geometries.point.length === 0 ||
21711             (geometries.area.length + geometries.line.length) !== 1 ||
21712             geometries.relation.length !== 0)
21713             return 'not_eligible';
21714     };
21715
21716     return action;
21717 };
21718 iD.actions.MergePolygon = function(ids, newRelationId) {
21719
21720     function groupEntities(graph) {
21721         var entities = ids.map(function (id) { return graph.entity(id); });
21722         return _.extend({
21723                 closedWay: [],
21724                 multipolygon: [],
21725                 other: []
21726             }, _.groupBy(entities, function(entity) {
21727                 if (entity.type === 'way' && entity.isClosed()) {
21728                     return 'closedWay';
21729                 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
21730                     return 'multipolygon';
21731                 } else {
21732                     return 'other';
21733                 }
21734             }));
21735     }
21736
21737     var action = function(graph) {
21738         var entities = groupEntities(graph);
21739
21740         // An array representing all the polygons that are part of the multipolygon.
21741         //
21742         // Each element is itself an array of objects with an id property, and has a
21743         // locs property which is an array of the locations forming the polygon.
21744         var polygons = entities.multipolygon.reduce(function(polygons, m) {
21745             return polygons.concat(iD.geo.joinWays(m.members, graph));
21746         }, []).concat(entities.closedWay.map(function(d) {
21747             var member = [{id: d.id}];
21748             member.nodes = graph.childNodes(d);
21749             return member;
21750         }));
21751
21752         // contained is an array of arrays of boolean values,
21753         // where contained[j][k] is true iff the jth way is
21754         // contained by the kth way.
21755         var contained = polygons.map(function(w, i) {
21756             return polygons.map(function(d, n) {
21757                 if (i === n) return null;
21758                 return iD.geo.polygonContainsPolygon(
21759                     _.pluck(d.nodes, 'loc'),
21760                     _.pluck(w.nodes, 'loc'));
21761             });
21762         });
21763
21764         // Sort all polygons as either outer or inner ways
21765         var members = [],
21766             outer = true;
21767
21768         while (polygons.length) {
21769             extractUncontained(polygons);
21770             polygons = polygons.filter(isContained);
21771             contained = contained.filter(isContained).map(filterContained);
21772         }
21773
21774         function isContained(d, i) {
21775             return _.any(contained[i]);
21776         }
21777
21778         function filterContained(d) {
21779             return d.filter(isContained);
21780         }
21781
21782         function extractUncontained(polygons) {
21783             polygons.forEach(function(d, i) {
21784                 if (!isContained(d, i)) {
21785                     d.forEach(function(member) {
21786                         members.push({
21787                             type: 'way',
21788                             id: member.id,
21789                             role: outer ? 'outer' : 'inner'
21790                         });
21791                     });
21792                 }
21793             });
21794             outer = !outer;
21795         }
21796
21797         // Move all tags to one relation
21798         var relation = entities.multipolygon[0] ||
21799             iD.Relation({ id: newRelationId, tags: { type: 'multipolygon' }});
21800
21801         entities.multipolygon.slice(1).forEach(function(m) {
21802             relation = relation.mergeTags(m.tags);
21803             graph = graph.remove(m);
21804         });
21805
21806         entities.closedWay.forEach(function(way) {
21807             function isThisOuter(m) {
21808                 return m.id === way.id && m.role !== 'inner';
21809             }
21810             if (members.some(isThisOuter)) {
21811                 relation = relation.mergeTags(way.tags);
21812                 graph = graph.replace(way.update({ tags: {} }));
21813             }
21814         });
21815
21816         return graph.replace(relation.update({
21817             members: members,
21818             tags: _.omit(relation.tags, 'area')
21819         }));
21820     };
21821
21822     action.disabled = function(graph) {
21823         var entities = groupEntities(graph);
21824         if (entities.other.length > 0 ||
21825             entities.closedWay.length + entities.multipolygon.length < 2)
21826             return 'not_eligible';
21827         if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); }))
21828             return 'incomplete_relation';
21829     };
21830
21831     return action;
21832 };
21833 iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser) {
21834     var option = 'safe',  // 'safe', 'force_local', 'force_remote'
21835         conflicts = [];
21836
21837     function user(d) {
21838         return _.isFunction(formatUser) ? formatUser(d) : d;
21839     }
21840
21841
21842     function mergeLocation(remote, target) {
21843         function pointEqual(a, b) {
21844             var epsilon = 1e-6;
21845             return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon);
21846         }
21847
21848         if (option === 'force_local' || pointEqual(target.loc, remote.loc)) {
21849             return target;
21850         }
21851         if (option === 'force_remote') {
21852             return target.update({loc: remote.loc});
21853         }
21854
21855         conflicts.push(t('merge_remote_changes.conflict.location', { user: user(remote.user) }));
21856         return target;
21857     }
21858
21859
21860     function mergeNodes(base, remote, target) {
21861         if (option === 'force_local' || _.isEqual(target.nodes, remote.nodes)) {
21862             return target;
21863         }
21864         if (option === 'force_remote') {
21865             return target.update({nodes: remote.nodes});
21866         }
21867
21868         var ccount = conflicts.length,
21869             o = base.nodes || [],
21870             a = target.nodes || [],
21871             b = remote.nodes || [],
21872             nodes = [],
21873             hunks = Diff3.diff3_merge(a, o, b, true);
21874
21875         for (var i = 0; i < hunks.length; i++) {
21876             var hunk = hunks[i];
21877             if (hunk.ok) {
21878                 nodes.push.apply(nodes, hunk.ok);
21879             } else {
21880                 // for all conflicts, we can assume c.a !== c.b
21881                 // because `diff3_merge` called with `true` option to exclude false conflicts..
21882                 var c = hunk.conflict;
21883                 if (_.isEqual(c.o, c.a)) {  // only changed remotely
21884                     nodes.push.apply(nodes, c.b);
21885                 } else if (_.isEqual(c.o, c.b)) {  // only changed locally
21886                     nodes.push.apply(nodes, c.a);
21887                 } else {       // changed both locally and remotely
21888                     conflicts.push(t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) }));
21889                     break;
21890                 }
21891             }
21892         }
21893
21894         return (conflicts.length === ccount) ? target.update({nodes: nodes}) : target;
21895     }
21896
21897
21898     function mergeChildren(targetWay, children, updates, graph) {
21899         function isUsed(node, targetWay) {
21900             var parentWays = _.pluck(graph.parentWays(node), 'id');
21901             return node.hasInterestingTags() ||
21902                 _.without(parentWays, targetWay.id).length > 0 ||
21903                 graph.parentRelations(node).length > 0;
21904         }
21905
21906         var ccount = conflicts.length;
21907
21908         for (var i = 0; i < children.length; i++) {
21909             var id = children[i],
21910                 node = graph.hasEntity(id);
21911
21912             // remove unused childNodes..
21913             if (targetWay.nodes.indexOf(id) === -1) {
21914                 if (node && !isUsed(node, targetWay)) {
21915                     updates.removeIds.push(id);
21916                 }
21917                 continue;
21918             }
21919
21920             // restore used childNodes..
21921             var local = localGraph.hasEntity(id),
21922                 remote = remoteGraph.hasEntity(id),
21923                 target;
21924
21925             if (option === 'force_remote' && remote && remote.visible) {
21926                 updates.replacements.push(remote);
21927
21928             } else if (option === 'force_local' && local) {
21929                 target = iD.Entity(local);
21930                 if (remote) {
21931                     target = target.update({ version: remote.version });
21932                 }
21933                 updates.replacements.push(target);
21934
21935             } else if (option === 'safe' && local && remote && local.version !== remote.version) {
21936                 target = iD.Entity(local, { version: remote.version });
21937                 if (remote.visible) {
21938                     target = mergeLocation(remote, target);
21939                 } else {
21940                     conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
21941                 }
21942
21943                 if (conflicts.length !== ccount) break;
21944                 updates.replacements.push(target);
21945             }
21946         }
21947
21948         return targetWay;
21949     }
21950
21951
21952     function updateChildren(updates, graph) {
21953         for (var i = 0; i < updates.replacements.length; i++) {
21954             graph = graph.replace(updates.replacements[i]);
21955         }
21956         if (updates.removeIds.length) {
21957             graph = iD.actions.DeleteMultiple(updates.removeIds)(graph);
21958         }
21959         return graph;
21960     }
21961
21962
21963     function mergeMembers(remote, target) {
21964         if (option === 'force_local' || _.isEqual(target.members, remote.members)) {
21965             return target;
21966         }
21967         if (option === 'force_remote') {
21968             return target.update({members: remote.members});
21969         }
21970
21971         conflicts.push(t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) }));
21972         return target;
21973     }
21974
21975
21976     function mergeTags(base, remote, target) {
21977         function ignoreKey(k) {
21978             return _.contains(iD.data.discarded, k);
21979         }
21980
21981         if (option === 'force_local' || _.isEqual(target.tags, remote.tags)) {
21982             return target;
21983         }
21984         if (option === 'force_remote') {
21985             return target.update({tags: remote.tags});
21986         }
21987
21988         var ccount = conflicts.length,
21989             o = base.tags || {},
21990             a = target.tags || {},
21991             b = remote.tags || {},
21992             keys = _.reject(_.union(_.keys(o), _.keys(a), _.keys(b)), ignoreKey),
21993             tags = _.clone(a),
21994             changed = false;
21995
21996         for (var i = 0; i < keys.length; i++) {
21997             var k = keys[i];
21998
21999             if (o[k] !== b[k] && a[k] !== b[k]) {    // changed remotely..
22000                 if (o[k] !== a[k]) {      // changed locally..
22001                     conflicts.push(t('merge_remote_changes.conflict.tags',
22002                         { tag: k, local: a[k], remote: b[k], user: user(remote.user) }));
22003
22004                 } else {                  // unchanged locally, accept remote change..
22005                     if (b.hasOwnProperty(k)) {
22006                         tags[k] = b[k];
22007                     } else {
22008                         delete tags[k];
22009                     }
22010                     changed = true;
22011                 }
22012             }
22013         }
22014
22015         return (changed && conflicts.length === ccount) ? target.update({tags: tags}) : target;
22016     }
22017
22018
22019     //  `graph.base()` is the common ancestor of the two graphs.
22020     //  `localGraph` contains user's edits up to saving
22021     //  `remoteGraph` contains remote edits to modified nodes
22022     //  `graph` must be a descendent of `localGraph` and may include
22023     //      some conflict resolution actions performed on it.
22024     //
22025     //                  --- ... --- `localGraph` -- ... -- `graph`
22026     //                 /
22027     //  `graph.base()` --- ... --- `remoteGraph`
22028     //
22029     var action = function(graph) {
22030         var updates = { replacements: [], removeIds: [] },
22031             base = graph.base().entities[id],
22032             local = localGraph.entity(id),
22033             remote = remoteGraph.entity(id),
22034             target = iD.Entity(local, { version: remote.version });
22035
22036         // delete/undelete
22037         if (!remote.visible) {
22038             if (option === 'force_remote') {
22039                 return iD.actions.DeleteMultiple([id])(graph);
22040
22041             } else if (option === 'force_local') {
22042                 if (target.type === 'way') {
22043                     target = mergeChildren(target, _.uniq(local.nodes), updates, graph);
22044                     graph = updateChildren(updates, graph);
22045                 }
22046                 return graph.replace(target);
22047
22048             } else {
22049                 conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
22050                 return graph;  // do nothing
22051             }
22052         }
22053
22054         // merge
22055         if (target.type === 'node') {
22056             target = mergeLocation(remote, target);
22057
22058         } else if (target.type === 'way') {
22059             // pull in any child nodes that may not be present locally..
22060             graph.rebase(remoteGraph.childNodes(remote), [graph], false);
22061             target = mergeNodes(base, remote, target);
22062             target = mergeChildren(target, _.union(local.nodes, remote.nodes), updates, graph);
22063
22064         } else if (target.type === 'relation') {
22065             target = mergeMembers(remote, target);
22066         }
22067
22068         target = mergeTags(base, remote, target);
22069
22070         if (!conflicts.length) {
22071             graph = updateChildren(updates, graph).replace(target);
22072         }
22073
22074         return graph;
22075     };
22076
22077     action.withOption = function(opt) {
22078         option = opt;
22079         return action;
22080     };
22081
22082     action.conflicts = function() {
22083         return conflicts;
22084     };
22085
22086     return action;
22087 };
22088 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
22089 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
22090 iD.actions.Move = function(moveIds, tryDelta, projection, cache) {
22091     var delta = tryDelta;
22092
22093     function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; }
22094     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
22095
22096     function setupCache(graph) {
22097         function canMove(nodeId) {
22098             var parents = _.pluck(graph.parentWays(graph.entity(nodeId)), 'id');
22099             if (parents.length < 3) return true;
22100
22101             // Don't move a vertex where >2 ways meet, unless all parentWays are moving too..
22102             var parentsMoving = _.all(parents, function(id) { return cache.moving[id]; });
22103             if (!parentsMoving) delete cache.moving[nodeId];
22104
22105             return parentsMoving;
22106         }
22107
22108         function cacheEntities(ids) {
22109             _.each(ids, function(id) {
22110                 if (cache.moving[id]) return;
22111                 cache.moving[id] = true;
22112
22113                 var entity = graph.hasEntity(id);
22114                 if (!entity) return;
22115
22116                 if (entity.type === 'node') {
22117                     cache.nodes.push(id);
22118                     cache.startLoc[id] = entity.loc;
22119                 } else if (entity.type === 'way') {
22120                     cache.ways.push(id);
22121                     cacheEntities(entity.nodes);
22122                 } else {
22123                     cacheEntities(_.pluck(entity.members, 'id'));
22124                 }
22125             });
22126         }
22127
22128         function cacheIntersections(ids) {
22129             function isEndpoint(way, id) { return !way.isClosed() && !!way.affix(id); }
22130
22131             _.each(ids, function(id) {
22132                 // consider only intersections with 1 moved and 1 unmoved way.
22133                 _.each(graph.childNodes(graph.entity(id)), function(node) {
22134                     var parents = graph.parentWays(node);
22135                     if (parents.length !== 2) return;
22136
22137                     var moved = graph.entity(id),
22138                         unmoved = _.find(parents, function(way) { return !cache.moving[way.id]; });
22139                     if (!unmoved) return;
22140
22141                     // exclude ways that are overly connected..
22142                     if (_.intersection(moved.nodes, unmoved.nodes).length > 2) return;
22143
22144                     if (moved.isArea() || unmoved.isArea()) return;
22145
22146                     cache.intersection[node.id] = {
22147                         nodeId: node.id,
22148                         movedId: moved.id,
22149                         unmovedId: unmoved.id,
22150                         movedIsEP: isEndpoint(moved, node.id),
22151                         unmovedIsEP: isEndpoint(unmoved, node.id)
22152                     };
22153                 });
22154             });
22155         }
22156
22157
22158         if (!cache) {
22159             cache = {};
22160         }
22161         if (!cache.ok) {
22162             cache.moving = {};
22163             cache.intersection = {};
22164             cache.replacedVertex = {};
22165             cache.startLoc = {};
22166             cache.nodes = [];
22167             cache.ways = [];
22168
22169             cacheEntities(moveIds);
22170             cacheIntersections(cache.ways);
22171             cache.nodes = _.filter(cache.nodes, canMove);
22172
22173             cache.ok = true;
22174         }
22175     }
22176
22177
22178     // Place a vertex where the moved vertex used to be, to preserve way shape..
22179     function replaceMovedVertex(nodeId, wayId, graph, delta) {
22180         var way = graph.entity(wayId),
22181             moved = graph.entity(nodeId),
22182             movedIndex = way.nodes.indexOf(nodeId),
22183             len, prevIndex, nextIndex;
22184
22185         if (way.isClosed()) {
22186             len = way.nodes.length - 1;
22187             prevIndex = (movedIndex + len - 1) % len;
22188             nextIndex = (movedIndex + len + 1) % len;
22189         } else {
22190             len = way.nodes.length;
22191             prevIndex = movedIndex - 1;
22192             nextIndex = movedIndex + 1;
22193         }
22194
22195         var prev = graph.hasEntity(way.nodes[prevIndex]),
22196             next = graph.hasEntity(way.nodes[nextIndex]);
22197
22198         // Don't add orig vertex at endpoint..
22199         if (!prev || !next) return graph;
22200
22201         var key = wayId + '_' + nodeId,
22202             orig = cache.replacedVertex[key];
22203         if (!orig) {
22204             orig = iD.Node();
22205             cache.replacedVertex[key] = orig;
22206             cache.startLoc[orig.id] = cache.startLoc[nodeId];
22207         }
22208
22209         var start, end;
22210         if (delta) {
22211             start = projection(cache.startLoc[nodeId]);
22212             end = projection.invert(vecAdd(start, delta));
22213         } else {
22214             end = cache.startLoc[nodeId];
22215         }
22216         orig = orig.move(end);
22217
22218         var angle = Math.abs(iD.geo.angle(orig, prev, projection) -
22219                 iD.geo.angle(orig, next, projection)) * 180 / Math.PI;
22220
22221         // Don't add orig vertex if it would just make a straight line..
22222         if (angle > 175 && angle < 185) return graph;
22223
22224         // Don't add orig vertex if another point is already nearby (within 10m)
22225         if (iD.geo.sphericalDistance(prev.loc, orig.loc) < 10 ||
22226             iD.geo.sphericalDistance(orig.loc, next.loc) < 10) return graph;
22227
22228         // moving forward or backward along way?
22229         var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection),
22230             p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection),
22231             d1 = iD.geo.pathLength(p1),
22232             d2 = iD.geo.pathLength(p2),
22233             insertAt = (d1 < d2) ? movedIndex : nextIndex;
22234
22235         // moving around closed loop?
22236         if (way.isClosed() && insertAt === 0) insertAt = len;
22237
22238         way = way.addNode(orig.id, insertAt);
22239         return graph.replace(orig).replace(way);
22240     }
22241
22242     // Reorder nodes around intersections that have moved..
22243     function unZorroIntersection(intersection, graph) {
22244         var vertex = graph.entity(intersection.nodeId),
22245             way1 = graph.entity(intersection.movedId),
22246             way2 = graph.entity(intersection.unmovedId),
22247             isEP1 = intersection.movedIsEP,
22248             isEP2 = intersection.unmovedIsEP;
22249
22250         // don't move the vertex if it is the endpoint of both ways.
22251         if (isEP1 && isEP2) return graph;
22252
22253         var nodes1 = _.without(graph.childNodes(way1), vertex),
22254             nodes2 = _.without(graph.childNodes(way2), vertex);
22255
22256         if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
22257         if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
22258
22259         var edge1 = !isEP1 && iD.geo.chooseEdge(nodes1, projection(vertex.loc), projection),
22260             edge2 = !isEP2 && iD.geo.chooseEdge(nodes2, projection(vertex.loc), projection),
22261             loc;
22262
22263         // snap vertex to nearest edge (or some point between them)..
22264         if (!isEP1 && !isEP2) {
22265             var epsilon = 1e-4, maxIter = 10;
22266             for (var i = 0; i < maxIter; i++) {
22267                 loc = iD.geo.interp(edge1.loc, edge2.loc, 0.5);
22268                 edge1 = iD.geo.chooseEdge(nodes1, projection(loc), projection);
22269                 edge2 = iD.geo.chooseEdge(nodes2, projection(loc), projection);
22270                 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
22271             }
22272         } else if (!isEP1) {
22273             loc = edge1.loc;
22274         } else {
22275             loc = edge2.loc;
22276         }
22277
22278         graph = graph.replace(vertex.move(loc));
22279
22280         // if zorro happened, reorder nodes..
22281         if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
22282             way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
22283             graph = graph.replace(way1);
22284         }
22285         if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
22286             way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
22287             graph = graph.replace(way2);
22288         }
22289
22290         return graph;
22291     }
22292
22293
22294     function cleanupIntersections(graph) {
22295         _.each(cache.intersection, function(obj) {
22296             graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, delta);
22297             graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
22298             graph = unZorroIntersection(obj, graph);
22299         });
22300
22301         return graph;
22302     }
22303
22304     // check if moving way endpoint can cross an unmoved way, if so limit delta..
22305     function limitDelta(graph) {
22306         _.each(cache.intersection, function(obj) {
22307             if (!obj.movedIsEP) return;
22308
22309             var node = graph.entity(obj.nodeId),
22310                 start = projection(node.loc),
22311                 end = vecAdd(start, delta),
22312                 movedNodes = graph.childNodes(graph.entity(obj.movedId)),
22313                 movedPath = _.map(_.pluck(movedNodes, 'loc'),
22314                     function(loc) { return vecAdd(projection(loc), delta); }),
22315                 unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId)),
22316                 unmovedPath = _.map(_.pluck(unmovedNodes, 'loc'), projection),
22317                 hits = iD.geo.pathIntersections(movedPath, unmovedPath);
22318
22319             for (var i = 0; i < hits.length; i++) {
22320                 if (_.isEqual(hits[i], end)) continue;
22321                 var edge = iD.geo.chooseEdge(unmovedNodes, end, projection);
22322                 delta = vecSub(projection(edge.loc), start);
22323             }
22324         });
22325     }
22326
22327
22328     var action = function(graph) {
22329         if (delta[0] === 0 && delta[1] === 0) return graph;
22330
22331         setupCache(graph);
22332
22333         if (!_.isEmpty(cache.intersection)) {
22334             limitDelta(graph);
22335         }
22336
22337         _.each(cache.nodes, function(id) {
22338             var node = graph.entity(id),
22339                 start = projection(node.loc),
22340                 end = vecAdd(start, delta);
22341             graph = graph.replace(node.move(projection.invert(end)));
22342         });
22343
22344         if (!_.isEmpty(cache.intersection)) {
22345             graph = cleanupIntersections(graph);
22346         }
22347
22348         return graph;
22349     };
22350
22351     action.disabled = function(graph) {
22352         function incompleteRelation(id) {
22353             var entity = graph.entity(id);
22354             return entity.type === 'relation' && !entity.isComplete(graph);
22355         }
22356
22357         if (_.any(moveIds, incompleteRelation))
22358             return 'incomplete_relation';
22359     };
22360
22361     action.delta = function() {
22362         return delta;
22363     };
22364
22365     return action;
22366 };
22367 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
22368 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
22369 iD.actions.MoveNode = function(nodeId, loc) {
22370     return function(graph) {
22371         return graph.replace(graph.entity(nodeId).move(loc));
22372     };
22373 };
22374 iD.actions.Noop = function() {
22375     return function(graph) {
22376         return graph;
22377     };
22378 };
22379 /*
22380  * Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
22381  */
22382
22383 iD.actions.Orthogonalize = function(wayId, projection) {
22384     var threshold = 12, // degrees within right or straight to alter
22385         lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180),
22386         upperThreshold = Math.cos(threshold * Math.PI / 180);
22387
22388     var action = function(graph) {
22389         var way = graph.entity(wayId),
22390             nodes = graph.childNodes(way),
22391             points = _.uniq(nodes).map(function(n) { return projection(n.loc); }),
22392             corner = {i: 0, dotp: 1},
22393             epsilon = 1e-4,
22394             i, j, score, motions;
22395
22396         if (nodes.length === 4) {
22397             for (i = 0; i < 1000; i++) {
22398                 motions = points.map(calcMotion);
22399                 points[corner.i] = addPoints(points[corner.i],motions[corner.i]);
22400                 score = corner.dotp;
22401                 if (score < epsilon) {
22402                     break;
22403                 }
22404             }
22405
22406             graph = graph.replace(graph.entity(nodes[corner.i].id)
22407                 .move(projection.invert(points[corner.i])));
22408         } else {
22409             var best,
22410                 originalPoints = _.clone(points);
22411             score = Infinity;
22412
22413             for (i = 0; i < 1000; i++) {
22414                 motions = points.map(calcMotion);
22415                 for (j = 0; j < motions.length; j++) {
22416                     points[j] = addPoints(points[j],motions[j]);
22417                 }
22418                 var newScore = squareness(points);
22419                 if (newScore < score) {
22420                     best = _.clone(points);
22421                     score = newScore;
22422                 }
22423                 if (score < epsilon) {
22424                     break;
22425                 }
22426             }
22427
22428             points = best;
22429
22430             for (i = 0; i < points.length; i++) {
22431                 // only move the points that actually moved
22432                 if (originalPoints[i][0] !== points[i][0] || originalPoints[i][1] !== points[i][1]) {
22433                     graph = graph.replace(graph.entity(nodes[i].id)
22434                         .move(projection.invert(points[i])));
22435                 }
22436             }
22437
22438             // remove empty nodes on straight sections
22439             for (i = 0; i < points.length; i++) {
22440                 var node = nodes[i];
22441
22442                 if (graph.parentWays(node).length > 1 ||
22443                     graph.parentRelations(node).length ||
22444                     node.hasInterestingTags()) {
22445
22446                     continue;
22447                 }
22448
22449                 var dotp = normalizedDotProduct(i, points);
22450                 if (dotp < -1 + epsilon) {
22451                     graph = iD.actions.DeleteNode(nodes[i].id)(graph);
22452                 }
22453             }
22454         }
22455
22456         return graph;
22457
22458         function calcMotion(b, i, array) {
22459             var a = array[(i - 1 + array.length) % array.length],
22460                 c = array[(i + 1) % array.length],
22461                 p = subtractPoints(a, b),
22462                 q = subtractPoints(c, b),
22463                 scale, dotp;
22464
22465             scale = 2 * Math.min(iD.geo.euclideanDistance(p, [0, 0]), iD.geo.euclideanDistance(q, [0, 0]));
22466             p = normalizePoint(p, 1.0);
22467             q = normalizePoint(q, 1.0);
22468
22469             dotp = filterDotProduct(p[0] * q[0] + p[1] * q[1]);
22470
22471             // nasty hack to deal with almost-straight segments (angle is closer to 180 than to 90/270).
22472             if (array.length > 3) {
22473                 if (dotp < -0.707106781186547) {
22474                     dotp += 1.0;
22475                 }
22476             } else if (dotp && Math.abs(dotp) < corner.dotp) {
22477                 corner.i = i;
22478                 corner.dotp = Math.abs(dotp);
22479             }
22480
22481             return normalizePoint(addPoints(p, q), 0.1 * dotp * scale);
22482         }
22483     };
22484
22485     function squareness(points) {
22486         return points.reduce(function(sum, val, i, array) {
22487             var dotp = normalizedDotProduct(i, array);
22488
22489             dotp = filterDotProduct(dotp);
22490             return sum + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
22491         }, 0);
22492     }
22493
22494     function normalizedDotProduct(i, points) {
22495         var a = points[(i - 1 + points.length) % points.length],
22496             b = points[i],
22497             c = points[(i + 1) % points.length],
22498             p = subtractPoints(a, b),
22499             q = subtractPoints(c, b);
22500
22501         p = normalizePoint(p, 1.0);
22502         q = normalizePoint(q, 1.0);
22503
22504         return p[0] * q[0] + p[1] * q[1];
22505     }
22506
22507     function subtractPoints(a, b) {
22508         return [a[0] - b[0], a[1] - b[1]];
22509     }
22510
22511     function addPoints(a, b) {
22512         return [a[0] + b[0], a[1] + b[1]];
22513     }
22514
22515     function normalizePoint(point, scale) {
22516         var vector = [0, 0];
22517         var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]);
22518         if (length !== 0) {
22519             vector[0] = point[0] / length;
22520             vector[1] = point[1] / length;
22521         }
22522
22523         vector[0] *= scale;
22524         vector[1] *= scale;
22525
22526         return vector;
22527     }
22528
22529     function filterDotProduct(dotp) {
22530         if (lowerThreshold > Math.abs(dotp) || Math.abs(dotp) > upperThreshold) {
22531             return dotp;
22532         }
22533
22534         return 0;
22535     }
22536
22537     action.disabled = function(graph) {
22538         var way = graph.entity(wayId),
22539             nodes = graph.childNodes(way),
22540             points = _.uniq(nodes).map(function(n) { return projection(n.loc); });
22541
22542         if (squareness(points)) {
22543             return false;
22544         }
22545
22546         return 'not_squarish';
22547     };
22548
22549     return action;
22550 };
22551 // Create a restriction relation for `turn`, which must have the following structure:
22552 //
22553 //     {
22554 //         from: { node: <node ID>, way: <way ID> },
22555 //         via:  { node: <node ID> },
22556 //         to:   { node: <node ID>, way: <way ID> },
22557 //         restriction: <'no_right_turn', 'no_left_turn', etc.>
22558 //     }
22559 //
22560 // This specifies a restriction of type `restriction` when traveling from
22561 // `from.node` in `from.way` toward `to.node` in `to.way` via `via.node`.
22562 // (The action does not check that these entities form a valid intersection.)
22563 //
22564 // If `restriction` is not provided, it is automatically determined by
22565 // iD.geo.inferRestriction.
22566 //
22567 // If necessary, the `from` and `to` ways are split. In these cases, `from.node`
22568 // and `to.node` are used to determine which portion of the split ways become
22569 // members of the restriction.
22570 //
22571 // For testing convenience, accepts an ID to assign to the new relation.
22572 // Normally, this will be undefined and the relation will automatically
22573 // be assigned a new ID.
22574 //
22575 iD.actions.RestrictTurn = function(turn, projection, restrictionId) {
22576     return function(graph) {
22577         var from = graph.entity(turn.from.way),
22578             via  = graph.entity(turn.via.node),
22579             to   = graph.entity(turn.to.way);
22580
22581         function isClosingNode(way, nodeId) {
22582             return nodeId === way.first() && nodeId === way.last();
22583         }
22584
22585         function split(toOrFrom) {
22586             var newID = toOrFrom.newID || iD.Way().id;
22587             graph = iD.actions.Split(via.id, [newID])
22588                 .limitWays([toOrFrom.way])(graph);
22589
22590             var a = graph.entity(newID),
22591                 b = graph.entity(toOrFrom.way);
22592
22593             if (a.nodes.indexOf(toOrFrom.node) !== -1) {
22594                 return [a, b];
22595             } else {
22596                 return [b, a];
22597             }
22598         }
22599
22600         if (!from.affix(via.id) || isClosingNode(from, via.id)) {
22601             if (turn.from.node === turn.to.node) {
22602                 // U-turn
22603                 from = to = split(turn.from)[0];
22604             } else if (turn.from.way === turn.to.way) {
22605                 // Straight-on or circular
22606                 var s = split(turn.from);
22607                 from = s[0];
22608                 to   = s[1];
22609             } else {
22610                 // Other
22611                 from = split(turn.from)[0];
22612             }
22613         }
22614
22615         if (!to.affix(via.id) || isClosingNode(to, via.id)) {
22616             to = split(turn.to)[0];
22617         }
22618
22619         return graph.replace(iD.Relation({
22620             id: restrictionId,
22621             tags: {
22622                 type: 'restriction',
22623                 restriction: turn.restriction ||
22624                     iD.geo.inferRestriction(
22625                         graph,
22626                         turn.from,
22627                         turn.via,
22628                         turn.to,
22629                         projection)
22630             },
22631             members: [
22632                 {id: from.id, type: 'way',  role: 'from'},
22633                 {id: via.id,  type: 'node', role: 'via'},
22634                 {id: to.id,   type: 'way',  role: 'to'}
22635             ]
22636         }));
22637     };
22638 };
22639 /*
22640   Order the nodes of a way in reverse order and reverse any direction dependent tags
22641   other than `oneway`. (We assume that correcting a backwards oneway is the primary
22642   reason for reversing a way.)
22643
22644   The following transforms are performed:
22645
22646     Keys:
22647           *:right=* ⟺ *:left=*
22648         *:forward=* ⟺ *:backward=*
22649        direction=up ⟺ direction=down
22650          incline=up ⟺ incline=down
22651             *=right ⟺ *=left
22652
22653     Relation members:
22654        role=forward ⟺ role=backward
22655          role=north ⟺ role=south
22656           role=east ⟺ role=west
22657
22658    In addition, numeric-valued `incline` tags are negated.
22659
22660    The JOSM implementation was used as a guide, but transformations that were of unclear benefit
22661    or adjusted tags that don't seem to be used in practice were omitted.
22662
22663    References:
22664       http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
22665       http://wiki.openstreetmap.org/wiki/Key:direction#Steps
22666       http://wiki.openstreetmap.org/wiki/Key:incline
22667       http://wiki.openstreetmap.org/wiki/Route#Members
22668       http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
22669  */
22670 iD.actions.Reverse = function(wayId, options) {
22671     var replacements = [
22672             [/:right$/, ':left'], [/:left$/, ':right'],
22673             [/:forward$/, ':backward'], [/:backward$/, ':forward']
22674         ],
22675         numeric = /^([+\-]?)(?=[\d.])/,
22676         roleReversals = {
22677             forward: 'backward',
22678             backward: 'forward',
22679             north: 'south',
22680             south: 'north',
22681             east: 'west',
22682             west: 'east'
22683         };
22684
22685     function reverseKey(key) {
22686         for (var i = 0; i < replacements.length; ++i) {
22687             var replacement = replacements[i];
22688             if (replacement[0].test(key)) {
22689                 return key.replace(replacement[0], replacement[1]);
22690             }
22691         }
22692         return key;
22693     }
22694
22695     function reverseValue(key, value) {
22696         if (key === 'incline' && numeric.test(value)) {
22697             return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
22698         } else if (key === 'incline' || key === 'direction') {
22699             return {up: 'down', down: 'up'}[value] || value;
22700         } else if (options && options.reverseOneway && key === 'oneway') {
22701             return {yes: '-1', '1': '-1', '-1': 'yes'}[value] || value;
22702         } else {
22703             return {left: 'right', right: 'left'}[value] || value;
22704         }
22705     }
22706
22707     return function(graph) {
22708         var way = graph.entity(wayId),
22709             nodes = way.nodes.slice().reverse(),
22710             tags = {}, key, role;
22711
22712         for (key in way.tags) {
22713             tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
22714         }
22715
22716         graph.parentRelations(way).forEach(function(relation) {
22717             relation.members.forEach(function(member, index) {
22718                 if (member.id === way.id && (role = roleReversals[member.role])) {
22719                     relation = relation.updateMember({role: role}, index);
22720                     graph = graph.replace(relation);
22721                 }
22722             });
22723         });
22724
22725         return graph.replace(way.update({nodes: nodes, tags: tags}));
22726     };
22727 };
22728 iD.actions.Revert = function(id) {
22729
22730     var action = function(graph) {
22731         var entity = graph.hasEntity(id),
22732             base = graph.base().entities[id];
22733
22734         if (entity && !base) {    // entity will be removed..
22735             if (entity.type === 'node') {
22736                 graph.parentWays(entity)
22737                     .forEach(function(parent) {
22738                         parent = parent.removeNode(id);
22739                         graph = graph.replace(parent);
22740
22741                         if (parent.isDegenerate()) {
22742                             graph = iD.actions.DeleteWay(parent.id)(graph);
22743                         }
22744                     });
22745             }
22746
22747             graph.parentRelations(entity)
22748                 .forEach(function(parent) {
22749                     parent = parent.removeMembersWithID(id);
22750                     graph = graph.replace(parent);
22751
22752                     if (parent.isDegenerate()) {
22753                         graph = iD.actions.DeleteRelation(parent.id)(graph);
22754                     }
22755                 });
22756         }
22757
22758         return graph.revert(id);
22759     };
22760
22761     return action;
22762 };
22763 iD.actions.RotateWay = function(wayId, pivot, angle, projection) {
22764     return function(graph) {
22765         return graph.update(function(graph) {
22766             var way = graph.entity(wayId);
22767
22768             _.unique(way.nodes).forEach(function(id) {
22769
22770                 var node = graph.entity(id),
22771                     point = projection(node.loc),
22772                     radial = [0,0];
22773
22774                 radial[0] = point[0] - pivot[0];
22775                 radial[1] = point[1] - pivot[1];
22776
22777                 point = [
22778                     radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + pivot[0],
22779                     radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + pivot[1]
22780                 ];
22781
22782                 graph = graph.replace(node.move(projection.invert(point)));
22783
22784             });
22785
22786         });
22787     };
22788 };
22789 // Split a way at the given node.
22790 //
22791 // Optionally, split only the given ways, if multiple ways share
22792 // the given node.
22793 //
22794 // This is the inverse of `iD.actions.Join`.
22795 //
22796 // For testing convenience, accepts an ID to assign to the new way.
22797 // Normally, this will be undefined and the way will automatically
22798 // be assigned a new ID.
22799 //
22800 // Reference:
22801 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
22802 //
22803 iD.actions.Split = function(nodeId, newWayIds) {
22804     var wayIds;
22805
22806     // if the way is closed, we need to search for a partner node
22807     // to split the way at.
22808     //
22809     // The following looks for a node that is both far away from
22810     // the initial node in terms of way segment length and nearby
22811     // in terms of beeline-distance. This assures that areas get
22812     // split on the most "natural" points (independent of the number
22813     // of nodes).
22814     // For example: bone-shaped areas get split across their waist
22815     // line, circles across the diameter.
22816     function splitArea(nodes, idxA, graph) {
22817         var lengths = new Array(nodes.length),
22818             length,
22819             i,
22820             best = 0,
22821             idxB;
22822
22823         function wrap(index) {
22824             return iD.util.wrap(index, nodes.length);
22825         }
22826
22827         function dist(nA, nB) {
22828             return iD.geo.sphericalDistance(graph.entity(nA).loc, graph.entity(nB).loc);
22829         }
22830
22831         // calculate lengths
22832         length = 0;
22833         for (i = wrap(idxA+1); i !== idxA; i = wrap(i+1)) {
22834             length += dist(nodes[i], nodes[wrap(i-1)]);
22835             lengths[i] = length;
22836         }
22837
22838         length = 0;
22839         for (i = wrap(idxA-1); i !== idxA; i = wrap(i-1)) {
22840             length += dist(nodes[i], nodes[wrap(i+1)]);
22841             if (length < lengths[i])
22842                 lengths[i] = length;
22843         }
22844
22845         // determine best opposite node to split
22846         for (i = 0; i < nodes.length; i++) {
22847             var cost = lengths[i] / dist(nodes[idxA], nodes[i]);
22848             if (cost > best) {
22849                 idxB = i;
22850                 best = cost;
22851             }
22852         }
22853
22854         return idxB;
22855     }
22856
22857     function split(graph, wayA, newWayId) {
22858         var wayB = iD.Way({id: newWayId, tags: wayA.tags}),
22859             nodesA,
22860             nodesB,
22861             isArea = wayA.isArea(),
22862             isOuter = iD.geo.isSimpleMultipolygonOuterMember(wayA, graph);
22863
22864         if (wayA.isClosed()) {
22865             var nodes = wayA.nodes.slice(0, -1),
22866                 idxA = _.indexOf(nodes, nodeId),
22867                 idxB = splitArea(nodes, idxA, graph);
22868
22869             if (idxB < idxA) {
22870                 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
22871                 nodesB = nodes.slice(idxB, idxA + 1);
22872             } else {
22873                 nodesA = nodes.slice(idxA, idxB + 1);
22874                 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
22875             }
22876         } else {
22877             var idx = _.indexOf(wayA.nodes, nodeId, 1);
22878             nodesA = wayA.nodes.slice(0, idx + 1);
22879             nodesB = wayA.nodes.slice(idx);
22880         }
22881
22882         wayA = wayA.update({nodes: nodesA});
22883         wayB = wayB.update({nodes: nodesB});
22884
22885         graph = graph.replace(wayA);
22886         graph = graph.replace(wayB);
22887
22888         graph.parentRelations(wayA).forEach(function(relation) {
22889             if (relation.isRestriction()) {
22890                 var via = relation.memberByRole('via');
22891                 if (via && wayB.contains(via.id)) {
22892                     relation = relation.updateMember({id: wayB.id}, relation.memberById(wayA.id).index);
22893                     graph = graph.replace(relation);
22894                 }
22895             } else {
22896                 if (relation === isOuter) {
22897                     graph = graph.replace(relation.mergeTags(wayA.tags));
22898                     graph = graph.replace(wayA.update({tags: {}}));
22899                     graph = graph.replace(wayB.update({tags: {}}));
22900                 }
22901
22902                 var member = {
22903                     id: wayB.id,
22904                     type: 'way',
22905                     role: relation.memberById(wayA.id).role
22906                 };
22907
22908                 graph = iD.actions.AddMember(relation.id, member)(graph);
22909             }
22910         });
22911
22912         if (!isOuter && isArea) {
22913             var multipolygon = iD.Relation({
22914                 tags: _.extend({}, wayA.tags, {type: 'multipolygon'}),
22915                 members: [
22916                     {id: wayA.id, role: 'outer', type: 'way'},
22917                     {id: wayB.id, role: 'outer', type: 'way'}
22918                 ]});
22919
22920             graph = graph.replace(multipolygon);
22921             graph = graph.replace(wayA.update({tags: {}}));
22922             graph = graph.replace(wayB.update({tags: {}}));
22923         }
22924
22925         return graph;
22926     }
22927
22928     var action = function(graph) {
22929         var candidates = action.ways(graph);
22930         for (var i = 0; i < candidates.length; i++) {
22931             graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
22932         }
22933         return graph;
22934     };
22935
22936     action.ways = function(graph) {
22937         var node = graph.entity(nodeId),
22938             parents = graph.parentWays(node),
22939             hasLines = _.any(parents, function(parent) { return parent.geometry(graph) === 'line'; });
22940
22941         return parents.filter(function(parent) {
22942             if (wayIds && wayIds.indexOf(parent.id) === -1)
22943                 return false;
22944
22945             if (!wayIds && hasLines && parent.geometry(graph) !== 'line')
22946                 return false;
22947
22948             if (parent.isClosed()) {
22949                 return true;
22950             }
22951
22952             for (var i = 1; i < parent.nodes.length - 1; i++) {
22953                 if (parent.nodes[i] === nodeId) {
22954                     return true;
22955                 }
22956             }
22957
22958             return false;
22959         });
22960     };
22961
22962     action.disabled = function(graph) {
22963         var candidates = action.ways(graph);
22964         if (candidates.length === 0 || (wayIds && wayIds.length !== candidates.length))
22965             return 'not_eligible';
22966     };
22967
22968     action.limitWays = function(_) {
22969         if (!arguments.length) return wayIds;
22970         wayIds = _;
22971         return action;
22972     };
22973
22974     return action;
22975 };
22976 /*
22977  * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
22978  */
22979
22980 iD.actions.Straighten = function(wayId, projection) {
22981     function positionAlongWay(n, s, e) {
22982         return ((n[0] - s[0]) * (e[0] - s[0]) + (n[1] - s[1]) * (e[1] - s[1]))/
22983                 (Math.pow(e[0] - s[0], 2) + Math.pow(e[1] - s[1], 2));
22984     }
22985
22986     var action = function(graph) {
22987         var way = graph.entity(wayId),
22988             nodes = graph.childNodes(way),
22989             points = nodes.map(function(n) { return projection(n.loc); }),
22990             startPoint = points[0],
22991             endPoint = points[points.length-1],
22992             toDelete = [],
22993             i;
22994
22995         for (i = 1; i < points.length-1; i++) {
22996             var node = nodes[i],
22997                 point = points[i];
22998
22999             if (graph.parentWays(node).length > 1 ||
23000                 graph.parentRelations(node).length ||
23001                 node.hasInterestingTags()) {
23002
23003                 var u = positionAlongWay(point, startPoint, endPoint),
23004                     p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
23005                     p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]);
23006
23007                 graph = graph.replace(graph.entity(node.id)
23008                     .move(projection.invert([p0, p1])));
23009             } else {
23010                 // safe to delete
23011                 if (toDelete.indexOf(node) === -1) {
23012                     toDelete.push(node);
23013                 }
23014             }
23015         }
23016
23017         for (i = 0; i < toDelete.length; i++) {
23018             graph = iD.actions.DeleteNode(toDelete[i].id)(graph);
23019         }
23020
23021         return graph;
23022     };
23023     
23024     action.disabled = function(graph) {
23025         // check way isn't too bendy
23026         var way = graph.entity(wayId),
23027             nodes = graph.childNodes(way),
23028             points = nodes.map(function(n) { return projection(n.loc); }),
23029             startPoint = points[0],
23030             endPoint = points[points.length-1],
23031             threshold = 0.2 * Math.sqrt(Math.pow(startPoint[0] - endPoint[0], 2) + Math.pow(startPoint[1] - endPoint[1], 2)),
23032             i;
23033
23034         for (i = 1; i < points.length-1; i++) {
23035             var point = points[i],
23036                 u = positionAlongWay(point, startPoint, endPoint),
23037                 p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
23038                 p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]),
23039                 dist = Math.sqrt(Math.pow(p0 - point[0], 2) + Math.pow(p1 - point[1], 2));
23040
23041             // to bendy if point is off by 20% of total start/end distance in projected space
23042             if (dist > threshold) {
23043                 return 'too_bendy';
23044             }
23045         }
23046     };
23047
23048     return action;
23049 };
23050 // Remove the effects of `turn.restriction` on `turn`, which must have the
23051 // following structure:
23052 //
23053 //     {
23054 //         from: { node: <node ID>, way: <way ID> },
23055 //         via:  { node: <node ID> },
23056 //         to:   { node: <node ID>, way: <way ID> },
23057 //         restriction: <relation ID>
23058 //     }
23059 //
23060 // In the simple case, `restriction` is a reference to a `no_*` restriction
23061 // on the turn itself. In this case, it is simply deleted.
23062 //
23063 // The more complex case is where `restriction` references an `only_*`
23064 // restriction on a different turn in the same intersection. In that case,
23065 // that restriction is also deleted, but at the same time restrictions on
23066 // the turns other than the first two are created.
23067 //
23068 iD.actions.UnrestrictTurn = function(turn) {
23069     return function(graph) {
23070         return iD.actions.DeleteRelation(turn.restriction)(graph);
23071     };
23072 };
23073 iD.behavior = {};
23074 iD.behavior.AddWay = function(context) {
23075     var event = d3.dispatch('start', 'startFromWay', 'startFromNode'),
23076         draw = iD.behavior.Draw(context);
23077
23078     var addWay = function(surface) {
23079         draw.on('click', event.start)
23080             .on('clickWay', event.startFromWay)
23081             .on('clickNode', event.startFromNode)
23082             .on('cancel', addWay.cancel)
23083             .on('finish', addWay.cancel);
23084
23085         context.map()
23086             .dblclickEnable(false);
23087
23088         surface.call(draw);
23089     };
23090
23091     addWay.off = function(surface) {
23092         surface.call(draw.off);
23093     };
23094
23095     addWay.cancel = function() {
23096         window.setTimeout(function() {
23097             context.map().dblclickEnable(true);
23098         }, 1000);
23099
23100         context.enter(iD.modes.Browse(context));
23101     };
23102
23103     addWay.tail = function(text) {
23104         draw.tail(text);
23105         return addWay;
23106     };
23107
23108     return d3.rebind(addWay, event, 'on');
23109 };
23110 iD.behavior.Breathe = function() {
23111     var duration = 800,
23112         selector = '.selected.shadow, .selected .shadow',
23113         selected = d3.select(null),
23114         classed = '',
23115         params = {},
23116         done;
23117
23118     function reset(selection) {
23119         selection
23120             .style('stroke-opacity', null)
23121             .style('stroke-width', null)
23122             .style('fill-opacity', null)
23123             .style('r', null);
23124     }
23125
23126     function setAnimationParams(transition, fromTo) {
23127         transition
23128             .style('stroke-opacity', function(d) { return params[d.id][fromTo].opacity; })
23129             .style('stroke-width', function(d) { return params[d.id][fromTo].width; })
23130             .style('fill-opacity', function(d) { return params[d.id][fromTo].opacity; })
23131             .style('r', function(d) { return params[d.id][fromTo].width; });
23132     }
23133
23134     function calcAnimationParams(selection) {
23135         selection
23136             .call(reset)
23137             .each(function(d) {
23138                 var s = d3.select(this),
23139                     tag = s.node().tagName,
23140                     p = {'from': {}, 'to': {}},
23141                     opacity, width;
23142
23143                 // determine base opacity and width
23144                 if (tag === 'circle') {
23145                     opacity = parseFloat(s.style('fill-opacity') || 0.5);
23146                     width = parseFloat(s.style('r') || 15.5);
23147                 } else {
23148                     opacity = parseFloat(s.style('stroke-opacity') || 0.7);
23149                     width = parseFloat(s.style('stroke-width') || 10);
23150                 }
23151
23152                 // calculate from/to interpolation params..
23153                 p.tag = tag;
23154                 p.from.opacity = opacity * 0.6;
23155                 p.to.opacity = opacity * 1.25;
23156                 p.from.width = width * 0.9;
23157                 p.to.width = width * (tag === 'circle' ? 1.5 : 1.25);
23158                 params[d.id] = p;
23159             });
23160     }
23161
23162     function run(surface, fromTo) {
23163         var toFrom = (fromTo === 'from' ? 'to': 'from'),
23164             currSelected = surface.selectAll(selector),
23165             currClassed = surface.attr('class'),
23166             n = 0;
23167
23168         if (done || currSelected.empty()) {
23169             selected.call(reset);
23170             return;
23171         }
23172
23173         if (!_.isEqual(currSelected, selected) || currClassed !== classed) {
23174             selected.call(reset);
23175             classed = currClassed;
23176             selected = currSelected.call(calcAnimationParams);
23177         }
23178
23179         selected
23180             .transition()
23181             .call(setAnimationParams, fromTo)
23182             .duration(duration)
23183             .each(function() { ++n; })
23184             .each('end', function() {
23185                 if (!--n) {  // call once
23186                     surface.call(run, toFrom);
23187                 }
23188             });
23189     }
23190
23191     var breathe = function(surface) {
23192         done = false;
23193         d3.timer(function() {
23194             if (done) return true;
23195
23196             var currSelected = surface.selectAll(selector);
23197             if (currSelected.empty()) return false;
23198
23199             surface.call(run, 'from');
23200             return true;
23201         }, 200);
23202     };
23203
23204     breathe.off = function() {
23205         done = true;
23206         d3.timer.flush();
23207         selected
23208             .transition()
23209             .call(reset)
23210             .duration(0);
23211     };
23212
23213     return breathe;
23214 };
23215 iD.behavior.Copy = function(context) {
23216     var keybinding = d3.keybinding('copy');
23217
23218     function groupEntities(ids, graph) {
23219         var entities = ids.map(function (id) { return graph.entity(id); });
23220         return _.extend({relation: [], way: [], node: []},
23221             _.groupBy(entities, function(entity) { return entity.type; }));
23222     }
23223
23224     function getDescendants(id, graph, descendants) {
23225         var entity = graph.entity(id),
23226             i, children;
23227
23228         descendants = descendants || {};
23229
23230         if (entity.type === 'relation') {
23231             children = _.pluck(entity.members, 'id');
23232         } else if (entity.type === 'way') {
23233             children = entity.nodes;
23234         } else {
23235             children = [];
23236         }
23237
23238         for (i = 0; i < children.length; i++) {
23239             if (!descendants[children[i]]) {
23240                 descendants[children[i]] = true;
23241                 descendants = getDescendants(children[i], graph, descendants);
23242             }
23243         }
23244
23245         return descendants;
23246     }
23247
23248     function doCopy() {
23249         d3.event.preventDefault();
23250         if (context.inIntro()) return;
23251
23252         var graph = context.graph(),
23253             selected = groupEntities(context.selectedIDs(), graph),
23254             canCopy = [],
23255             skip = {},
23256             i, entity;
23257
23258         for (i = 0; i < selected.relation.length; i++) {
23259             entity = selected.relation[i];
23260             if (!skip[entity.id] && entity.isComplete(graph)) {
23261                 canCopy.push(entity.id);
23262                 skip = getDescendants(entity.id, graph, skip);
23263             }
23264         }
23265         for (i = 0; i < selected.way.length; i++) {
23266             entity = selected.way[i];
23267             if (!skip[entity.id]) {
23268                 canCopy.push(entity.id);
23269                 skip = getDescendants(entity.id, graph, skip);
23270             }
23271         }
23272         for (i = 0; i < selected.node.length; i++) {
23273             entity = selected.node[i];
23274             if (!skip[entity.id]) {
23275                 canCopy.push(entity.id);
23276             }
23277         }
23278
23279         context.copyIDs(canCopy);
23280     }
23281
23282     function copy() {
23283         keybinding.on(iD.ui.cmd('⌘C'), doCopy);
23284         d3.select(document).call(keybinding);
23285         return copy;
23286     }
23287
23288     copy.off = function() {
23289         d3.select(document).call(keybinding.off);
23290     };
23291
23292     return copy;
23293 };
23294 /*
23295     `iD.behavior.drag` is like `d3.behavior.drag`, with the following differences:
23296
23297     * The `origin` function is expected to return an [x, y] tuple rather than an
23298       {x, y} object.
23299     * The events are `start`, `move`, and `end`.
23300       (https://github.com/mbostock/d3/issues/563)
23301     * The `start` event is not dispatched until the first cursor movement occurs.
23302       (https://github.com/mbostock/d3/pull/368)
23303     * The `move` event has a `point` and `delta` [x, y] tuple properties rather
23304       than `x`, `y`, `dx`, and `dy` properties.
23305     * The `end` event is not dispatched if no movement occurs.
23306     * An `off` function is available that unbinds the drag's internal event handlers.
23307     * Delegation is supported via the `delegate` function.
23308
23309  */
23310 iD.behavior.drag = function() {
23311     function d3_eventCancel() {
23312       d3.event.stopPropagation();
23313       d3.event.preventDefault();
23314     }
23315
23316     var event = d3.dispatch('start', 'move', 'end'),
23317         origin = null,
23318         selector = '',
23319         filter = null,
23320         event_, target, surface;
23321
23322     event.of = function(thiz, argumentz) {
23323       return function(e1) {
23324         var e0 = e1.sourceEvent = d3.event;
23325         e1.target = drag;
23326         d3.event = e1;
23327         try {
23328           event[e1.type].apply(thiz, argumentz);
23329         } finally {
23330           d3.event = e0;
23331         }
23332       };
23333     };
23334
23335     var d3_event_userSelectProperty = iD.util.prefixCSSProperty('UserSelect'),
23336         d3_event_userSelectSuppress = d3_event_userSelectProperty ?
23337             function () {
23338                 var selection = d3.selection(),
23339                     select = selection.style(d3_event_userSelectProperty);
23340                 selection.style(d3_event_userSelectProperty, 'none');
23341                 return function () {
23342                     selection.style(d3_event_userSelectProperty, select);
23343                 };
23344             } :
23345             function (type) {
23346                 var w = d3.select(window).on('selectstart.' + type, d3_eventCancel);
23347                 return function () {
23348                     w.on('selectstart.' + type, null);
23349                 };
23350             };
23351
23352     function mousedown() {
23353         target = this;
23354         event_ = event.of(target, arguments);
23355         var eventTarget = d3.event.target,
23356             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
23357             offset,
23358             origin_ = point(),
23359             started = false,
23360             selectEnable = d3_event_userSelectSuppress(touchId !== null ? 'drag-' + touchId : 'drag');
23361
23362         var w = d3.select(window)
23363             .on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', dragmove)
23364             .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', dragend, true);
23365
23366         if (origin) {
23367             offset = origin.apply(target, arguments);
23368             offset = [offset[0] - origin_[0], offset[1] - origin_[1]];
23369         } else {
23370             offset = [0, 0];
23371         }
23372
23373         if (touchId === null) d3.event.stopPropagation();
23374
23375         function point() {
23376             var p = target.parentNode || surface;
23377             return touchId !== null ? d3.touches(p).filter(function(p) {
23378                 return p.identifier === touchId;
23379             })[0] : d3.mouse(p);
23380         }
23381
23382         function dragmove() {
23383
23384             var p = point(),
23385                 dx = p[0] - origin_[0],
23386                 dy = p[1] - origin_[1];
23387             
23388             if (dx === 0 && dy === 0)
23389                 return;
23390
23391             if (!started) {
23392                 started = true;
23393                 event_({
23394                     type: 'start'
23395                 });
23396             }
23397
23398             origin_ = p;
23399             d3_eventCancel();
23400
23401             event_({
23402                 type: 'move',
23403                 point: [p[0] + offset[0],  p[1] + offset[1]],
23404                 delta: [dx, dy]
23405             });
23406         }
23407
23408         function dragend() {
23409             if (started) {
23410                 event_({
23411                     type: 'end'
23412                 });
23413
23414                 d3_eventCancel();
23415                 if (d3.event.target === eventTarget) w.on('click.drag', click, true);
23416             }
23417
23418             w.on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', null)
23419                 .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', null);
23420             selectEnable();
23421         }
23422
23423         function click() {
23424             d3_eventCancel();
23425             w.on('click.drag', null);
23426         }
23427     }
23428
23429     function drag(selection) {
23430         var matchesSelector = iD.util.prefixDOMProperty('matchesSelector'),
23431             delegate = mousedown;
23432
23433         if (selector) {
23434             delegate = function() {
23435                 var root = this,
23436                     target = d3.event.target;
23437                 for (; target && target !== root; target = target.parentNode) {
23438                     if (target[matchesSelector](selector) &&
23439                             (!filter || filter(target.__data__))) {
23440                         return mousedown.call(target, target.__data__);
23441                     }
23442                 }
23443             };
23444         }
23445
23446         selection.on('mousedown.drag' + selector, delegate)
23447             .on('touchstart.drag' + selector, delegate);
23448     }
23449
23450     drag.off = function(selection) {
23451         selection.on('mousedown.drag' + selector, null)
23452             .on('touchstart.drag' + selector, null);
23453     };
23454
23455     drag.delegate = function(_) {
23456         if (!arguments.length) return selector;
23457         selector = _;
23458         return drag;
23459     };
23460
23461     drag.filter = function(_) {
23462         if (!arguments.length) return origin;
23463         filter = _;
23464         return drag;
23465     };
23466
23467     drag.origin = function (_) {
23468         if (!arguments.length) return origin;
23469         origin = _;
23470         return drag;
23471     };
23472
23473     drag.cancel = function() {
23474         d3.select(window)
23475             .on('mousemove.drag', null)
23476             .on('mouseup.drag', null);
23477         return drag;
23478     };
23479
23480     drag.target = function() {
23481         if (!arguments.length) return target;
23482         target = arguments[0];
23483         event_ = event.of(target, Array.prototype.slice.call(arguments, 1));
23484         return drag;
23485     };
23486
23487     drag.surface = function() {
23488         if (!arguments.length) return surface;
23489         surface = arguments[0];
23490         return drag;
23491     };
23492
23493     return d3.rebind(drag, event, 'on');
23494 };
23495 iD.behavior.Draw = function(context) {
23496     var event = d3.dispatch('move', 'click', 'clickWay',
23497             'clickNode', 'undo', 'cancel', 'finish'),
23498         keybinding = d3.keybinding('draw'),
23499         hover = iD.behavior.Hover(context)
23500             .altDisables(true)
23501             .on('hover', context.ui().sidebar.hover),
23502         tail = iD.behavior.Tail(),
23503         edit = iD.behavior.Edit(context),
23504         closeTolerance = 4,
23505         tolerance = 12;
23506
23507     function datum() {
23508         if (d3.event.altKey) return {};
23509         else return d3.event.target.__data__ || {};
23510     }
23511
23512     function mousedown() {
23513
23514         function point() {
23515             var p = context.container().node();
23516             return touchId !== null ? d3.touches(p).filter(function(p) {
23517                 return p.identifier === touchId;
23518             })[0] : d3.mouse(p);
23519         }
23520
23521         var element = d3.select(this),
23522             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
23523             t1 = +new Date(),
23524             p1 = point();
23525
23526         element.on('mousemove.draw', null);
23527
23528         d3.select(window).on('mouseup.draw', function() {
23529             var t2 = +new Date(),
23530                 p2 = point(),
23531                 dist = iD.geo.euclideanDistance(p1, p2);
23532
23533             element.on('mousemove.draw', mousemove);
23534             d3.select(window).on('mouseup.draw', null);
23535
23536             if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) {
23537                 // Prevent a quick second click
23538                 d3.select(window).on('click.draw-block', function() {
23539                     d3.event.stopPropagation();
23540                 }, true);
23541
23542                 context.map().dblclickEnable(false);
23543
23544                 window.setTimeout(function() {
23545                     context.map().dblclickEnable(true);
23546                     d3.select(window).on('click.draw-block', null);
23547                 }, 500);
23548
23549                 click();
23550             }
23551         });
23552     }
23553
23554     function mousemove() {
23555         event.move(datum());
23556     }
23557
23558     function click() {
23559         var d = datum();
23560         if (d.type === 'way') {
23561             var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection),
23562                 edge = [d.nodes[choice.index - 1], d.nodes[choice.index]];
23563             event.clickWay(choice.loc, edge);
23564
23565         } else if (d.type === 'node') {
23566             event.clickNode(d);
23567
23568         } else {
23569             event.click(context.map().mouseCoordinates());
23570         }
23571     }
23572
23573     function backspace() {
23574         d3.event.preventDefault();
23575         event.undo();
23576     }
23577
23578     function del() {
23579         d3.event.preventDefault();
23580         event.cancel();
23581     }
23582
23583     function ret() {
23584         d3.event.preventDefault();
23585         event.finish();
23586     }
23587
23588     function draw(selection) {
23589         context.install(hover);
23590         context.install(edit);
23591
23592         if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
23593             context.install(tail);
23594         }
23595
23596         keybinding
23597             .on('⌫', backspace)
23598             .on('⌦', del)
23599             .on('⎋', ret)
23600             .on('↩', ret);
23601
23602         selection
23603             .on('mousedown.draw', mousedown)
23604             .on('mousemove.draw', mousemove);
23605
23606         d3.select(document)
23607             .call(keybinding);
23608
23609         return draw;
23610     }
23611
23612     draw.off = function(selection) {
23613         context.ui().sidebar.hover.cancel();
23614         context.uninstall(hover);
23615         context.uninstall(edit);
23616
23617         if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
23618             context.uninstall(tail);
23619             iD.behavior.Draw.usedTails[tail.text()] = true;
23620         }
23621
23622         selection
23623             .on('mousedown.draw', null)
23624             .on('mousemove.draw', null);
23625
23626         d3.select(window)
23627             .on('mouseup.draw', null);
23628
23629         d3.select(document)
23630             .call(keybinding.off);
23631     };
23632
23633     draw.tail = function(_) {
23634         tail.text(_);
23635         return draw;
23636     };
23637
23638     return d3.rebind(draw, event, 'on');
23639 };
23640
23641 iD.behavior.Draw.usedTails = {};
23642 iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
23643     var way = context.entity(wayId),
23644         isArea = context.geometry(wayId) === 'area',
23645         finished = false,
23646         annotation = t((way.isDegenerate() ?
23647             'operations.start.annotation.' :
23648             'operations.continue.annotation.') + context.geometry(wayId)),
23649         draw = iD.behavior.Draw(context);
23650
23651     var startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0,
23652         start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}),
23653         end = iD.Node({loc: context.map().mouseCoordinates()}),
23654         segment = iD.Way({
23655             nodes: typeof index === 'undefined' ? [start.id, end.id] : [end.id, start.id],
23656             tags: _.clone(way.tags)
23657         });
23658
23659     var f = context[way.isDegenerate() ? 'replace' : 'perform'];
23660     if (isArea) {
23661         f(iD.actions.AddEntity(end),
23662             iD.actions.AddVertex(wayId, end.id, index));
23663     } else {
23664         f(iD.actions.AddEntity(start),
23665             iD.actions.AddEntity(end),
23666             iD.actions.AddEntity(segment));
23667     }
23668
23669     function move(datum) {
23670         var loc;
23671
23672         if (datum.type === 'node' && datum.id !== end.id) {
23673             loc = datum.loc;
23674         } else if (datum.type === 'way' && datum.id !== segment.id) {
23675             loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc;
23676         } else {
23677             loc = context.map().mouseCoordinates();
23678         }
23679
23680         context.replace(iD.actions.MoveNode(end.id, loc));
23681     }
23682
23683     function undone() {
23684         finished = true;
23685         context.enter(iD.modes.Browse(context));
23686     }
23687
23688     function setActiveElements() {
23689         var active = isArea ? [wayId, end.id] : [segment.id, start.id, end.id];
23690         context.surface().selectAll(iD.util.entitySelector(active))
23691             .classed('active', true);
23692     }
23693
23694     var drawWay = function(surface) {
23695         draw.on('move', move)
23696             .on('click', drawWay.add)
23697             .on('clickWay', drawWay.addWay)
23698             .on('clickNode', drawWay.addNode)
23699             .on('undo', context.undo)
23700             .on('cancel', drawWay.cancel)
23701             .on('finish', drawWay.finish);
23702
23703         context.map()
23704             .dblclickEnable(false)
23705             .on('drawn.draw', setActiveElements);
23706
23707         setActiveElements();
23708
23709         surface.call(draw);
23710
23711         context.history()
23712             .on('undone.draw', undone);
23713     };
23714
23715     drawWay.off = function(surface) {
23716         if (!finished)
23717             context.pop();
23718
23719         context.map()
23720             .on('drawn.draw', null);
23721
23722         surface.call(draw.off)
23723             .selectAll('.active')
23724             .classed('active', false);
23725
23726         context.history()
23727             .on('undone.draw', null);
23728     };
23729
23730     function ReplaceTemporaryNode(newNode) {
23731         return function(graph) {
23732             if (isArea) {
23733                 return graph
23734                     .replace(way.addNode(newNode.id, index))
23735                     .remove(end);
23736
23737             } else {
23738                 return graph
23739                     .replace(graph.entity(wayId).addNode(newNode.id, index))
23740                     .remove(end)
23741                     .remove(segment)
23742                     .remove(start);
23743             }
23744         };
23745     }
23746
23747     // Accept the current position of the temporary node and continue drawing.
23748     drawWay.add = function(loc) {
23749
23750         // prevent duplicate nodes
23751         var last = context.hasEntity(way.nodes[way.nodes.length - (isArea ? 2 : 1)]);
23752         if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return;
23753
23754         var newNode = iD.Node({loc: loc});
23755
23756         context.replace(
23757             iD.actions.AddEntity(newNode),
23758             ReplaceTemporaryNode(newNode),
23759             annotation);
23760
23761         finished = true;
23762         context.enter(mode);
23763     };
23764
23765     // Connect the way to an existing way.
23766     drawWay.addWay = function(loc, edge) {
23767         var previousEdge = startIndex ?
23768             [way.nodes[startIndex], way.nodes[startIndex - 1]] :
23769             [way.nodes[0], way.nodes[1]];
23770
23771         // Avoid creating duplicate segments
23772         if (!isArea && iD.geo.edgeEqual(edge, previousEdge))
23773             return;
23774
23775         var newNode = iD.Node({ loc: loc });
23776
23777         context.perform(
23778             iD.actions.AddMidpoint({ loc: loc, edge: edge}, newNode),
23779             ReplaceTemporaryNode(newNode),
23780             annotation);
23781
23782         finished = true;
23783         context.enter(mode);
23784     };
23785
23786     // Connect the way to an existing node and continue drawing.
23787     drawWay.addNode = function(node) {
23788
23789         // Avoid creating duplicate segments
23790         if (way.areAdjacent(node.id, way.nodes[way.nodes.length - 1])) return;
23791
23792         context.perform(
23793             ReplaceTemporaryNode(node),
23794             annotation);
23795
23796         finished = true;
23797         context.enter(mode);
23798     };
23799
23800     // Finish the draw operation, removing the temporary node. If the way has enough
23801     // nodes to be valid, it's selected. Otherwise, return to browse mode.
23802     drawWay.finish = function() {
23803         context.pop();
23804         finished = true;
23805
23806         window.setTimeout(function() {
23807             context.map().dblclickEnable(true);
23808         }, 1000);
23809
23810         if (context.hasEntity(wayId)) {
23811             context.enter(
23812                 iD.modes.Select(context, [wayId])
23813                     .suppressMenu(true)
23814                     .newFeature(true));
23815         } else {
23816             context.enter(iD.modes.Browse(context));
23817         }
23818     };
23819
23820     // Cancel the draw operation and return to browse, deleting everything drawn.
23821     drawWay.cancel = function() {
23822         context.perform(
23823             d3.functor(baseGraph),
23824             t('operations.cancel_draw.annotation'));
23825
23826         window.setTimeout(function() {
23827             context.map().dblclickEnable(true);
23828         }, 1000);
23829
23830         finished = true;
23831         context.enter(iD.modes.Browse(context));
23832     };
23833
23834     drawWay.tail = function(text) {
23835         draw.tail(text);
23836         return drawWay;
23837     };
23838
23839     return drawWay;
23840 };
23841 iD.behavior.Edit = function(context) {
23842     function edit() {
23843         context.map()
23844             .minzoom(context.minEditableZoom());
23845     }
23846
23847     edit.off = function() {
23848         context.map()
23849             .minzoom(0);
23850     };
23851
23852     return edit;
23853 };
23854 iD.behavior.Hash = function(context) {
23855     var s0 = null, // cached location.hash
23856         lat = 90 - 1e-8; // allowable latitude range
23857
23858     var parser = function(map, s) {
23859         var q = iD.util.stringQs(s);
23860         var args = (q.map || '').split('/').map(Number);
23861         if (args.length < 3 || args.some(isNaN)) {
23862             return true; // replace bogus hash
23863         } else if (s !== formatter(map).slice(1)) {
23864             map.centerZoom([args[1],
23865                 Math.min(lat, Math.max(-lat, args[2]))], args[0]);
23866         }
23867     };
23868
23869     var formatter = function(map) {
23870         var mode = context.mode(),
23871             center = map.center(),
23872             zoom = map.zoom(),
23873             precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
23874             q = _.omit(iD.util.stringQs(location.hash.substring(1)), 'comment'),
23875             newParams = {};
23876
23877         if (mode && mode.id === 'browse') {
23878             delete q.id;
23879         } else {
23880             var selected = context.selectedIDs().filter(function(id) {
23881                 return !context.entity(id).isNew();
23882             });
23883             if (selected.length) {
23884                 newParams.id = selected.join(',');
23885             }
23886         }
23887
23888         newParams.map = zoom.toFixed(2) +
23889                 '/' + center[0].toFixed(precision) +
23890                 '/' + center[1].toFixed(precision);
23891
23892         return '#' + iD.util.qsString(_.assign(q, newParams), true);
23893     };
23894
23895     function update() {
23896         if (context.inIntro()) return;
23897         var s1 = formatter(context.map());
23898         if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map!
23899     }
23900
23901     var throttledUpdate = _.throttle(update, 500);
23902
23903     function hashchange() {
23904         if (location.hash === s0) return; // ignore spurious hashchange events
23905         if (parser(context.map(), (s0 = location.hash).substring(1))) {
23906             update(); // replace bogus hash
23907         }
23908     }
23909
23910     function hash() {
23911         context.map()
23912             .on('move.hash', throttledUpdate);
23913
23914         context
23915             .on('enter.hash', throttledUpdate);
23916
23917         d3.select(window)
23918             .on('hashchange.hash', hashchange);
23919
23920         if (location.hash) {
23921             var q = iD.util.stringQs(location.hash.substring(1));
23922             if (q.id) context.zoomToEntity(q.id.split(',')[0], !q.map);
23923             if (q.comment) context.storage('comment', q.comment);
23924             hashchange();
23925             if (q.map) hash.hadHash = true;
23926         }
23927     }
23928
23929     hash.off = function() {
23930         throttledUpdate.cancel();
23931
23932         context.map()
23933             .on('move.hash', null);
23934
23935         context
23936             .on('enter.hash', null);
23937
23938         d3.select(window)
23939             .on('hashchange.hash', null);
23940
23941         location.hash = '';
23942     };
23943
23944     return hash;
23945 };
23946 /*
23947    The hover behavior adds the `.hover` class on mouseover to all elements to which
23948    the identical datum is bound, and removes it on mouseout.
23949
23950    The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
23951    representation may consist of several elements scattered throughout the DOM hierarchy.
23952    Only one of these elements can have the :hover pseudo-class, but all of them will
23953    have the .hover class.
23954  */
23955 iD.behavior.Hover = function() {
23956     var dispatch = d3.dispatch('hover'),
23957         selection,
23958         altDisables,
23959         target;
23960
23961     function keydown() {
23962         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
23963             dispatch.hover(null);
23964             selection.selectAll('.hover')
23965                 .classed('hover-suppressed', true)
23966                 .classed('hover', false);
23967         }
23968     }
23969
23970     function keyup() {
23971         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
23972             dispatch.hover(target ? target.id : null);
23973             selection.selectAll('.hover-suppressed')
23974                 .classed('hover-suppressed', false)
23975                 .classed('hover', true);
23976         }
23977     }
23978
23979     var hover = function(__) {
23980         selection = __;
23981
23982         function enter(d) {
23983             if (d === target) return;
23984
23985             target = d;
23986
23987             selection.selectAll('.hover')
23988                 .classed('hover', false);
23989             selection.selectAll('.hover-suppressed')
23990                 .classed('hover-suppressed', false);
23991
23992             if (target instanceof iD.Entity) {
23993                 var selector = '.' + target.id;
23994
23995                 if (target.type === 'relation') {
23996                     target.members.forEach(function(member) {
23997                         selector += ', .' + member.id;
23998                     });
23999                 }
24000
24001                 var suppressed = altDisables && d3.event && d3.event.altKey;
24002
24003                 selection.selectAll(selector)
24004                     .classed(suppressed ? 'hover-suppressed' : 'hover', true);
24005
24006                 dispatch.hover(target.id);
24007             } else {
24008                 dispatch.hover(null);
24009             }
24010         }
24011
24012         var down;
24013
24014         function mouseover() {
24015             if (down) return;
24016             var target = d3.event.target;
24017             enter(target ? target.__data__ : null);
24018         }
24019
24020         function mouseout() {
24021             if (down) return;
24022             var target = d3.event.relatedTarget;
24023             enter(target ? target.__data__ : null);
24024         }
24025
24026         function mousedown() {
24027             down = true;
24028             d3.select(window)
24029                 .on('mouseup.hover', mouseup);
24030         }
24031
24032         function mouseup() {
24033             down = false;
24034         }
24035
24036         selection
24037             .on('mouseover.hover', mouseover)
24038             .on('mouseout.hover', mouseout)
24039             .on('mousedown.hover', mousedown)
24040             .on('mouseup.hover', mouseup);
24041
24042         d3.select(window)
24043             .on('keydown.hover', keydown)
24044             .on('keyup.hover', keyup);
24045     };
24046
24047     hover.off = function(selection) {
24048         selection.selectAll('.hover')
24049             .classed('hover', false);
24050         selection.selectAll('.hover-suppressed')
24051             .classed('hover-suppressed', false);
24052
24053         selection
24054             .on('mouseover.hover', null)
24055             .on('mouseout.hover', null)
24056             .on('mousedown.hover', null)
24057             .on('mouseup.hover', null);
24058
24059         d3.select(window)
24060             .on('keydown.hover', null)
24061             .on('keyup.hover', null)
24062             .on('mouseup.hover', null);
24063     };
24064
24065     hover.altDisables = function(_) {
24066         if (!arguments.length) return altDisables;
24067         altDisables = _;
24068         return hover;
24069     };
24070
24071     return d3.rebind(hover, dispatch, 'on');
24072 };
24073 iD.behavior.Lasso = function(context) {
24074
24075     var behavior = function(selection) {
24076         var mouse = null,
24077             lasso;
24078
24079         function mousedown() {
24080             if (d3.event.shiftKey === true) {
24081
24082                 mouse = context.mouse();
24083                 lasso = null;
24084
24085                 selection
24086                     .on('mousemove.lasso', mousemove)
24087                     .on('mouseup.lasso', mouseup);
24088
24089                 d3.event.stopPropagation();
24090             }
24091         }
24092
24093         function mousemove() {
24094             if (!lasso) {
24095                 lasso = iD.ui.Lasso(context).a(mouse);
24096                 context.surface().call(lasso);
24097             }
24098
24099             lasso.b(context.mouse());
24100         }
24101
24102         function normalize(a, b) {
24103             return [
24104                 [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
24105                 [Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
24106         }
24107
24108         function mouseup() {
24109             selection
24110                 .on('mousemove.lasso', null)
24111                 .on('mouseup.lasso', null);
24112
24113             if (!lasso) return;
24114
24115             var graph = context.graph(),
24116                 extent = iD.geo.Extent(
24117                 normalize(context.projection.invert(lasso.a()),
24118                 context.projection.invert(lasso.b())));
24119
24120             lasso.close();
24121
24122             var selected = context.intersects(extent).filter(function(entity) {
24123                 return entity.type === 'node' &&
24124                     !context.features().isHidden(entity, graph, entity.geometry(graph));
24125             });
24126
24127             if (selected.length) {
24128                 context.enter(iD.modes.Select(context, _.pluck(selected, 'id')));
24129             }
24130         }
24131
24132         selection
24133             .on('mousedown.lasso', mousedown);
24134     };
24135
24136     behavior.off = function(selection) {
24137         selection.on('mousedown.lasso', null);
24138     };
24139
24140     return behavior;
24141 };
24142 iD.behavior.Paste = function(context) {
24143     var keybinding = d3.keybinding('paste');
24144
24145     function omitTag(v, k) {
24146         return (
24147             k === 'phone' ||
24148             k === 'fax' ||
24149             k === 'email' ||
24150             k === 'website' ||
24151             k === 'url' ||
24152             k === 'note' ||
24153             k === 'description' ||
24154             k.indexOf('name') !== -1 ||
24155             k.indexOf('wiki') === 0 ||
24156             k.indexOf('addr:') === 0 ||
24157             k.indexOf('contact:') === 0
24158         );
24159     }
24160
24161     function doPaste() {
24162         d3.event.preventDefault();
24163         if (context.inIntro()) return;
24164
24165         var baseGraph = context.graph(),
24166             mouse = context.mouse(),
24167             projection = context.projection,
24168             viewport = iD.geo.Extent(projection.clipExtent()).polygon();
24169
24170         if (!iD.geo.pointInPolygon(mouse, viewport)) return;
24171
24172         var extent = iD.geo.Extent(),
24173             oldIDs = context.copyIDs(),
24174             oldGraph = context.copyGraph(),
24175             newIDs = [];
24176
24177         if (!oldIDs.length) return;
24178
24179         var action = iD.actions.CopyEntities(oldIDs, oldGraph);
24180         context.perform(action);
24181
24182         var copies = action.copies();
24183         for (var id in copies) {
24184             var oldEntity = oldGraph.entity(id),
24185                 newEntity = copies[id];
24186
24187             extent._extend(oldEntity.extent(oldGraph));
24188             newIDs.push(newEntity.id);
24189             context.perform(iD.actions.ChangeTags(newEntity.id, _.omit(newEntity.tags, omitTag)));
24190         }
24191
24192         // Put pasted objects where mouse pointer is..
24193         var center = projection(extent.center()),
24194             delta = [ mouse[0] - center[0], mouse[1] - center[1] ];
24195
24196         context.perform(iD.actions.Move(newIDs, delta, projection));
24197         context.enter(iD.modes.Move(context, newIDs, baseGraph));
24198     }
24199
24200     function paste() {
24201         keybinding.on(iD.ui.cmd('⌘V'), doPaste);
24202         d3.select(document).call(keybinding);
24203         return paste;
24204     }
24205
24206     paste.off = function() {
24207         d3.select(document).call(keybinding.off);
24208     };
24209
24210     return paste;
24211 };
24212 iD.behavior.Select = function(context) {
24213     function keydown() {
24214         if (d3.event && d3.event.shiftKey) {
24215             context.surface()
24216                 .classed('behavior-multiselect', true);
24217         }
24218     }
24219
24220     function keyup() {
24221         if (!d3.event || !d3.event.shiftKey) {
24222             context.surface()
24223                 .classed('behavior-multiselect', false);
24224         }
24225     }
24226
24227     function click() {
24228         var datum = d3.event.target.__data__,
24229             lasso = d3.select('#surface .lasso').node(),
24230             mode = context.mode();
24231
24232         if (!(datum instanceof iD.Entity)) {
24233             if (!d3.event.shiftKey && !lasso && mode.id !== 'browse')
24234                 context.enter(iD.modes.Browse(context));
24235
24236         } else if (!d3.event.shiftKey && !lasso) {
24237             // Avoid re-entering Select mode with same entity.
24238             if (context.selectedIDs().length !== 1 || context.selectedIDs()[0] !== datum.id) {
24239                 context.enter(iD.modes.Select(context, [datum.id]));
24240             } else {
24241                 mode.suppressMenu(false).reselect();
24242             }
24243         } else if (context.selectedIDs().indexOf(datum.id) >= 0) {
24244             var selectedIDs = _.without(context.selectedIDs(), datum.id);
24245             context.enter(selectedIDs.length ?
24246                 iD.modes.Select(context, selectedIDs) :
24247                 iD.modes.Browse(context));
24248
24249         } else {
24250             context.enter(iD.modes.Select(context, context.selectedIDs().concat([datum.id])));
24251         }
24252     }
24253
24254     var behavior = function(selection) {
24255         d3.select(window)
24256             .on('keydown.select', keydown)
24257             .on('keyup.select', keyup);
24258
24259         selection.on('click.select', click);
24260
24261         keydown();
24262     };
24263
24264     behavior.off = function(selection) {
24265         d3.select(window)
24266             .on('keydown.select', null)
24267             .on('keyup.select', null);
24268
24269         selection.on('click.select', null);
24270
24271         keyup();
24272     };
24273
24274     return behavior;
24275 };
24276 iD.behavior.Tail = function() {
24277     var text,
24278         container,
24279         xmargin = 25,
24280         tooltipSize = [0, 0],
24281         selectionSize = [0, 0];
24282
24283     function tail(selection) {
24284         if (!text) return;
24285
24286         d3.select(window)
24287             .on('resize.tail', function() { selectionSize = selection.dimensions(); });
24288
24289         function show() {
24290             container.style('display', 'block');
24291             tooltipSize = container.dimensions();
24292         }
24293
24294         function mousemove() {
24295             if (container.style('display') === 'none') show();
24296             var xoffset = ((d3.event.clientX + tooltipSize[0] + xmargin) > selectionSize[0]) ?
24297                 -tooltipSize[0] - xmargin : xmargin;
24298             container.classed('left', xoffset > 0);
24299             iD.util.setTransform(container, d3.event.clientX + xoffset, d3.event.clientY);
24300         }
24301
24302         function mouseleave() {
24303             if (d3.event.relatedTarget !== container.node()) {
24304                 container.style('display', 'none');
24305             }
24306         }
24307
24308         function mouseenter() {
24309             if (d3.event.relatedTarget !== container.node()) {
24310                 show();
24311             }
24312         }
24313
24314         container = d3.select(document.body)
24315             .append('div')
24316             .style('display', 'none')
24317             .attr('class', 'tail tooltip-inner');
24318
24319         container.append('div')
24320             .text(text);
24321
24322         selection
24323             .on('mousemove.tail', mousemove)
24324             .on('mouseenter.tail', mouseenter)
24325             .on('mouseleave.tail', mouseleave);
24326
24327         container
24328             .on('mousemove.tail', mousemove);
24329
24330         tooltipSize = container.dimensions();
24331         selectionSize = selection.dimensions();
24332     }
24333
24334     tail.off = function(selection) {
24335         if (!text) return;
24336
24337         container
24338             .on('mousemove.tail', null)
24339             .remove();
24340
24341         selection
24342             .on('mousemove.tail', null)
24343             .on('mouseenter.tail', null)
24344             .on('mouseleave.tail', null);
24345
24346         d3.select(window)
24347             .on('resize.tail', null);
24348     };
24349
24350     tail.text = function(_) {
24351         if (!arguments.length) return text;
24352         text = _;
24353         return tail;
24354     };
24355
24356     return tail;
24357 };
24358 iD.modes = {};
24359 iD.modes.AddArea = function(context) {
24360     var mode = {
24361         id: 'add-area',
24362         button: 'area',
24363         title: t('modes.add_area.title'),
24364         description: t('modes.add_area.description'),
24365         key: '3'
24366     };
24367
24368     var behavior = iD.behavior.AddWay(context)
24369             .tail(t('modes.add_area.tail'))
24370             .on('start', start)
24371             .on('startFromWay', startFromWay)
24372             .on('startFromNode', startFromNode),
24373         defaultTags = {area: 'yes'};
24374
24375     function start(loc) {
24376         var graph = context.graph(),
24377             node = iD.Node({loc: loc}),
24378             way = iD.Way({tags: defaultTags});
24379
24380         context.perform(
24381             iD.actions.AddEntity(node),
24382             iD.actions.AddEntity(way),
24383             iD.actions.AddVertex(way.id, node.id),
24384             iD.actions.AddVertex(way.id, node.id));
24385
24386         context.enter(iD.modes.DrawArea(context, way.id, graph));
24387     }
24388
24389     function startFromWay(loc, edge) {
24390         var graph = context.graph(),
24391             node = iD.Node({loc: loc}),
24392             way = iD.Way({tags: defaultTags});
24393
24394         context.perform(
24395             iD.actions.AddEntity(node),
24396             iD.actions.AddEntity(way),
24397             iD.actions.AddVertex(way.id, node.id),
24398             iD.actions.AddVertex(way.id, node.id),
24399             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24400
24401         context.enter(iD.modes.DrawArea(context, way.id, graph));
24402     }
24403
24404     function startFromNode(node) {
24405         var graph = context.graph(),
24406             way = iD.Way({tags: defaultTags});
24407
24408         context.perform(
24409             iD.actions.AddEntity(way),
24410             iD.actions.AddVertex(way.id, node.id),
24411             iD.actions.AddVertex(way.id, node.id));
24412
24413         context.enter(iD.modes.DrawArea(context, way.id, graph));
24414     }
24415
24416     mode.enter = function() {
24417         context.install(behavior);
24418     };
24419
24420     mode.exit = function() {
24421         context.uninstall(behavior);
24422     };
24423
24424     return mode;
24425 };
24426 iD.modes.AddLine = function(context) {
24427     var mode = {
24428         id: 'add-line',
24429         button: 'line',
24430         title: t('modes.add_line.title'),
24431         description: t('modes.add_line.description'),
24432         key: '2'
24433     };
24434
24435     var behavior = iD.behavior.AddWay(context)
24436         .tail(t('modes.add_line.tail'))
24437         .on('start', start)
24438         .on('startFromWay', startFromWay)
24439         .on('startFromNode', startFromNode);
24440
24441     function start(loc) {
24442         var baseGraph = context.graph(),
24443             node = iD.Node({loc: loc}),
24444             way = iD.Way();
24445
24446         context.perform(
24447             iD.actions.AddEntity(node),
24448             iD.actions.AddEntity(way),
24449             iD.actions.AddVertex(way.id, node.id));
24450
24451         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24452     }
24453
24454     function startFromWay(loc, edge) {
24455         var baseGraph = context.graph(),
24456             node = iD.Node({loc: loc}),
24457             way = iD.Way();
24458
24459         context.perform(
24460             iD.actions.AddEntity(node),
24461             iD.actions.AddEntity(way),
24462             iD.actions.AddVertex(way.id, node.id),
24463             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24464
24465         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24466     }
24467
24468     function startFromNode(node) {
24469         var baseGraph = context.graph(),
24470             way = iD.Way();
24471
24472         context.perform(
24473             iD.actions.AddEntity(way),
24474             iD.actions.AddVertex(way.id, node.id));
24475
24476         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24477     }
24478
24479     mode.enter = function() {
24480         context.install(behavior);
24481     };
24482
24483     mode.exit = function() {
24484         context.uninstall(behavior);
24485     };
24486
24487     return mode;
24488 };
24489 iD.modes.AddPoint = function(context) {
24490     var mode = {
24491         id: 'add-point',
24492         button: 'point',
24493         title: t('modes.add_point.title'),
24494         description: t('modes.add_point.description'),
24495         key: '1'
24496     };
24497
24498     var behavior = iD.behavior.Draw(context)
24499         .tail(t('modes.add_point.tail'))
24500         .on('click', add)
24501         .on('clickWay', addWay)
24502         .on('clickNode', addNode)
24503         .on('cancel', cancel)
24504         .on('finish', cancel);
24505
24506     function add(loc) {
24507         var node = iD.Node({loc: loc});
24508
24509         context.perform(
24510             iD.actions.AddEntity(node),
24511             t('operations.add.annotation.point'));
24512
24513         context.enter(
24514             iD.modes.Select(context, [node.id])
24515                 .suppressMenu(true)
24516                 .newFeature(true));
24517     }
24518
24519     function addWay(loc) {
24520         add(loc);
24521     }
24522
24523     function addNode(node) {
24524         add(node.loc);
24525     }
24526
24527     function cancel() {
24528         context.enter(iD.modes.Browse(context));
24529     }
24530
24531     mode.enter = function() {
24532         context.install(behavior);
24533     };
24534
24535     mode.exit = function() {
24536         context.uninstall(behavior);
24537     };
24538
24539     return mode;
24540 };
24541 iD.modes.Browse = function(context) {
24542     var mode = {
24543         button: 'browse',
24544         id: 'browse',
24545         title: t('modes.browse.title'),
24546         description: t('modes.browse.description')
24547     }, sidebar;
24548
24549     var behaviors = [
24550         iD.behavior.Paste(context),
24551         iD.behavior.Hover(context)
24552             .on('hover', context.ui().sidebar.hover),
24553         iD.behavior.Select(context),
24554         iD.behavior.Lasso(context),
24555         iD.modes.DragNode(context).behavior];
24556
24557     mode.enter = function() {
24558         behaviors.forEach(function(behavior) {
24559             context.install(behavior);
24560         });
24561
24562         // Get focus on the body.
24563         if (document.activeElement && document.activeElement.blur) {
24564             document.activeElement.blur();
24565         }
24566
24567         if (sidebar) {
24568             context.ui().sidebar.show(sidebar);
24569         } else {
24570             context.ui().sidebar.select(null);
24571         }
24572     };
24573
24574     mode.exit = function() {
24575         context.ui().sidebar.hover.cancel();
24576         behaviors.forEach(function(behavior) {
24577             context.uninstall(behavior);
24578         });
24579
24580         if (sidebar) {
24581             context.ui().sidebar.hide();
24582         }
24583     };
24584
24585     mode.sidebar = function(_) {
24586         if (!arguments.length) return sidebar;
24587         sidebar = _;
24588         return mode;
24589     };
24590
24591     return mode;
24592 };
24593 iD.modes.DragNode = function(context) {
24594     var mode = {
24595         id: 'drag-node',
24596         button: 'browse'
24597     };
24598
24599     var nudgeInterval,
24600         activeIDs,
24601         wasMidpoint,
24602         cancelled,
24603         selectedIDs = [],
24604         hover = iD.behavior.Hover(context)
24605             .altDisables(true)
24606             .on('hover', context.ui().sidebar.hover),
24607         edit = iD.behavior.Edit(context);
24608
24609     function edge(point, size) {
24610         var pad = [30, 100, 30, 100];
24611         if (point[0] > size[0] - pad[0]) return [-10, 0];
24612         else if (point[0] < pad[2]) return [10, 0];
24613         else if (point[1] > size[1] - pad[1]) return [0, -10];
24614         else if (point[1] < pad[3]) return [0, 10];
24615         return null;
24616     }
24617
24618     function startNudge(nudge) {
24619         if (nudgeInterval) window.clearInterval(nudgeInterval);
24620         nudgeInterval = window.setInterval(function() {
24621             context.pan(nudge);
24622         }, 50);
24623     }
24624
24625     function stopNudge() {
24626         if (nudgeInterval) window.clearInterval(nudgeInterval);
24627         nudgeInterval = null;
24628     }
24629
24630     function moveAnnotation(entity) {
24631         return t('operations.move.annotation.' + entity.geometry(context.graph()));
24632     }
24633
24634     function connectAnnotation(entity) {
24635         return t('operations.connect.annotation.' + entity.geometry(context.graph()));
24636     }
24637
24638     function origin(entity) {
24639         return context.projection(entity.loc);
24640     }
24641
24642     function start(entity) {
24643         cancelled = d3.event.sourceEvent.shiftKey ||
24644             context.features().hasHiddenConnections(entity, context.graph());
24645
24646         if (cancelled) return behavior.cancel();
24647
24648         wasMidpoint = entity.type === 'midpoint';
24649         if (wasMidpoint) {
24650             var midpoint = entity;
24651             entity = iD.Node();
24652             context.perform(iD.actions.AddMidpoint(midpoint, entity));
24653
24654              var vertex = context.surface()
24655                 .selectAll('.' + entity.id);
24656              behavior.target(vertex.node(), entity);
24657
24658         } else {
24659             context.perform(
24660                 iD.actions.Noop());
24661         }
24662
24663         activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
24664         activeIDs.push(entity.id);
24665
24666         context.enter(mode);
24667     }
24668
24669     function datum() {
24670         if (d3.event.sourceEvent.altKey) {
24671             return {};
24672         }
24673
24674         return d3.event.sourceEvent.target.__data__ || {};
24675     }
24676
24677     // via https://gist.github.com/shawnbot/4166283
24678     function childOf(p, c) {
24679         if (p === c) return false;
24680         while (c && c !== p) c = c.parentNode;
24681         return c === p;
24682     }
24683
24684     function move(entity) {
24685         if (cancelled) return;
24686         d3.event.sourceEvent.stopPropagation();
24687
24688         var nudge = childOf(context.container().node(),
24689             d3.event.sourceEvent.toElement) &&
24690             edge(d3.event.point, context.map().dimensions());
24691
24692         if (nudge) startNudge(nudge);
24693         else stopNudge();
24694
24695         var loc = context.map().mouseCoordinates();
24696
24697         var d = datum();
24698         if (d.type === 'node' && d.id !== entity.id) {
24699             loc = d.loc;
24700         } else if (d.type === 'way' && !d3.select(d3.event.sourceEvent.target).classed('fill')) {
24701             loc = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection).loc;
24702         }
24703
24704         context.replace(
24705             iD.actions.MoveNode(entity.id, loc),
24706             moveAnnotation(entity));
24707     }
24708
24709     function end(entity) {
24710         if (cancelled) return;
24711
24712         var d = datum();
24713
24714         if (d.type === 'way') {
24715             var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection);
24716             context.replace(
24717                 iD.actions.AddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity),
24718                 connectAnnotation(d));
24719
24720         } else if (d.type === 'node' && d.id !== entity.id) {
24721             context.replace(
24722                 iD.actions.Connect([d.id, entity.id]),
24723                 connectAnnotation(d));
24724
24725         } else if (wasMidpoint) {
24726             context.replace(
24727                 iD.actions.Noop(),
24728                 t('operations.add.annotation.vertex'));
24729
24730         } else {
24731             context.replace(
24732                 iD.actions.Noop(),
24733                 moveAnnotation(entity));
24734         }
24735
24736         var reselection = selectedIDs.filter(function(id) {
24737             return context.graph().hasEntity(id);
24738         });
24739
24740         if (reselection.length) {
24741             context.enter(
24742                 iD.modes.Select(context, reselection)
24743                     .suppressMenu(true));
24744         } else {
24745             context.enter(iD.modes.Browse(context));
24746         }
24747     }
24748
24749     function cancel() {
24750         behavior.cancel();
24751         context.enter(iD.modes.Browse(context));
24752     }
24753
24754     function setActiveElements() {
24755         context.surface().selectAll(iD.util.entitySelector(activeIDs))
24756             .classed('active', true);
24757     }
24758
24759     var behavior = iD.behavior.drag()
24760         .delegate('g.node, g.point, g.midpoint')
24761         .surface(context.surface().node())
24762         .origin(origin)
24763         .on('start', start)
24764         .on('move', move)
24765         .on('end', end);
24766
24767     mode.enter = function() {
24768         context.install(hover);
24769         context.install(edit);
24770
24771         context.history()
24772             .on('undone.drag-node', cancel);
24773
24774         context.map()
24775             .on('drawn.drag-node', setActiveElements);
24776
24777         setActiveElements();
24778     };
24779
24780     mode.exit = function() {
24781         context.ui().sidebar.hover.cancel();
24782         context.uninstall(hover);
24783         context.uninstall(edit);
24784
24785         context.history()
24786             .on('undone.drag-node', null);
24787
24788         context.map()
24789             .on('drawn.drag-node', null);
24790
24791         context.surface()
24792             .selectAll('.active')
24793             .classed('active', false);
24794
24795         stopNudge();
24796     };
24797
24798     mode.selectedIDs = function(_) {
24799         if (!arguments.length) return selectedIDs;
24800         selectedIDs = _;
24801         return mode;
24802     };
24803
24804     mode.behavior = behavior;
24805
24806     return mode;
24807 };
24808 iD.modes.DrawArea = function(context, wayId, baseGraph) {
24809     var mode = {
24810         button: 'area',
24811         id: 'draw-area'
24812     };
24813
24814     var behavior;
24815
24816     mode.enter = function() {
24817         var way = context.entity(wayId),
24818             headId = way.nodes[way.nodes.length - 2],
24819             tailId = way.first();
24820
24821         behavior = iD.behavior.DrawWay(context, wayId, -1, mode, baseGraph)
24822             .tail(t('modes.draw_area.tail'));
24823
24824         var addNode = behavior.addNode;
24825
24826         behavior.addNode = function(node) {
24827             if (node.id === headId || node.id === tailId) {
24828                 behavior.finish();
24829             } else {
24830                 addNode(node);
24831             }
24832         };
24833
24834         context.install(behavior);
24835     };
24836
24837     mode.exit = function() {
24838         context.uninstall(behavior);
24839     };
24840
24841     mode.selectedIDs = function() {
24842         return [wayId];
24843     };
24844
24845     return mode;
24846 };
24847 iD.modes.DrawLine = function(context, wayId, baseGraph, affix) {
24848     var mode = {
24849         button: 'line',
24850         id: 'draw-line'
24851     };
24852
24853     var behavior;
24854
24855     mode.enter = function() {
24856         var way = context.entity(wayId),
24857             index = (affix === 'prefix') ? 0 : undefined,
24858             headId = (affix === 'prefix') ? way.first() : way.last();
24859
24860         behavior = iD.behavior.DrawWay(context, wayId, index, mode, baseGraph)
24861             .tail(t('modes.draw_line.tail'));
24862
24863         var addNode = behavior.addNode;
24864
24865         behavior.addNode = function(node) {
24866             if (node.id === headId) {
24867                 behavior.finish();
24868             } else {
24869                 addNode(node);
24870             }
24871         };
24872
24873         context.install(behavior);
24874     };
24875
24876     mode.exit = function() {
24877         context.uninstall(behavior);
24878     };
24879
24880     mode.selectedIDs = function() {
24881         return [wayId];
24882     };
24883
24884     return mode;
24885 };
24886 iD.modes.Move = function(context, entityIDs, baseGraph) {
24887     var mode = {
24888         id: 'move',
24889         button: 'browse'
24890     };
24891
24892     var keybinding = d3.keybinding('move'),
24893         edit = iD.behavior.Edit(context),
24894         annotation = entityIDs.length === 1 ?
24895             t('operations.move.annotation.' + context.geometry(entityIDs[0])) :
24896             t('operations.move.annotation.multiple'),
24897         cache,
24898         origin,
24899         nudgeInterval;
24900
24901     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
24902
24903     function edge(point, size) {
24904         var pad = [30, 100, 30, 100];
24905         if (point[0] > size[0] - pad[0]) return [-10, 0];
24906         else if (point[0] < pad[2]) return [10, 0];
24907         else if (point[1] > size[1] - pad[1]) return [0, -10];
24908         else if (point[1] < pad[3]) return [0, 10];
24909         return null;
24910     }
24911
24912     function startNudge(nudge) {
24913         if (nudgeInterval) window.clearInterval(nudgeInterval);
24914         nudgeInterval = window.setInterval(function() {
24915             context.pan(nudge);
24916
24917             var currMouse = context.mouse(),
24918                 origMouse = context.projection(origin),
24919                 delta = vecSub(vecSub(currMouse, origMouse), nudge),
24920                 action = iD.actions.Move(entityIDs, delta, context.projection, cache);
24921
24922             context.overwrite(action, annotation);
24923
24924         }, 50);
24925     }
24926
24927     function stopNudge() {
24928         if (nudgeInterval) window.clearInterval(nudgeInterval);
24929         nudgeInterval = null;
24930     }
24931
24932     function move() {
24933         var currMouse = context.mouse(),
24934             origMouse = context.projection(origin),
24935             delta = vecSub(currMouse, origMouse),
24936             action = iD.actions.Move(entityIDs, delta, context.projection, cache);
24937
24938         context.overwrite(action, annotation);
24939
24940         var nudge = edge(currMouse, context.map().dimensions());
24941         if (nudge) startNudge(nudge);
24942         else stopNudge();
24943     }
24944
24945     function finish() {
24946         d3.event.stopPropagation();
24947         context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
24948         stopNudge();
24949     }
24950
24951     function cancel() {
24952         if (baseGraph) {
24953             while (context.graph() !== baseGraph) context.pop();
24954             context.enter(iD.modes.Browse(context));
24955         } else {
24956             context.pop();
24957             context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
24958         }
24959         stopNudge();
24960     }
24961
24962     function undone() {
24963         context.enter(iD.modes.Browse(context));
24964     }
24965
24966     mode.enter = function() {
24967         origin = context.map().mouseCoordinates();
24968         cache = {};
24969
24970         context.install(edit);
24971
24972         context.perform(
24973             iD.actions.Noop(),
24974             annotation);
24975
24976         context.surface()
24977             .on('mousemove.move', move)
24978             .on('click.move', finish);
24979
24980         context.history()
24981             .on('undone.move', undone);
24982
24983         keybinding
24984             .on('⎋', cancel)
24985             .on('↩', finish);
24986
24987         d3.select(document)
24988             .call(keybinding);
24989     };
24990
24991     mode.exit = function() {
24992         stopNudge();
24993
24994         context.uninstall(edit);
24995
24996         context.surface()
24997             .on('mousemove.move', null)
24998             .on('click.move', null);
24999
25000         context.history()
25001             .on('undone.move', null);
25002
25003         keybinding.off();
25004     };
25005
25006     return mode;
25007 };
25008 iD.modes.RotateWay = function(context, wayId) {
25009     var mode = {
25010         id: 'rotate-way',
25011         button: 'browse'
25012     };
25013
25014     var keybinding = d3.keybinding('rotate-way'),
25015         edit = iD.behavior.Edit(context);
25016
25017     mode.enter = function() {
25018         context.install(edit);
25019
25020         var annotation = t('operations.rotate.annotation.' + context.geometry(wayId)),
25021             way = context.graph().entity(wayId),
25022             nodes = _.uniq(context.graph().childNodes(way)),
25023             points = nodes.map(function(n) { return context.projection(n.loc); }),
25024             pivot = d3.geom.polygon(points).centroid(),
25025             angle;
25026
25027         context.perform(
25028             iD.actions.Noop(),
25029             annotation);
25030
25031         function rotate() {
25032
25033             var mousePoint = context.mouse(),
25034                 newAngle = Math.atan2(mousePoint[1] - pivot[1], mousePoint[0] - pivot[0]);
25035
25036             if (typeof angle === 'undefined') angle = newAngle;
25037
25038             context.replace(
25039                 iD.actions.RotateWay(wayId, pivot, newAngle - angle, context.projection),
25040                 annotation);
25041
25042             angle = newAngle;
25043         }
25044
25045         function finish() {
25046             d3.event.stopPropagation();
25047             context.enter(iD.modes.Select(context, [wayId])
25048                 .suppressMenu(true));
25049         }
25050
25051         function cancel() {
25052             context.pop();
25053             context.enter(iD.modes.Select(context, [wayId])
25054                 .suppressMenu(true));
25055         }
25056
25057         function undone() {
25058             context.enter(iD.modes.Browse(context));
25059         }
25060
25061         context.surface()
25062             .on('mousemove.rotate-way', rotate)
25063             .on('click.rotate-way', finish);
25064
25065         context.history()
25066             .on('undone.rotate-way', undone);
25067
25068         keybinding
25069             .on('⎋', cancel)
25070             .on('↩', finish);
25071
25072         d3.select(document)
25073             .call(keybinding);
25074     };
25075
25076     mode.exit = function() {
25077         context.uninstall(edit);
25078
25079         context.surface()
25080             .on('mousemove.rotate-way', null)
25081             .on('click.rotate-way', null);
25082
25083         context.history()
25084             .on('undone.rotate-way', null);
25085
25086         keybinding.off();
25087     };
25088
25089     return mode;
25090 };
25091 iD.modes.Save = function(context) {
25092     var ui = iD.ui.Commit(context)
25093             .on('cancel', cancel)
25094             .on('save', save);
25095
25096     function cancel() {
25097         context.enter(iD.modes.Browse(context));
25098     }
25099
25100     function save(e, tryAgain) {
25101         function withChildNodes(ids, graph) {
25102             return _.uniq(_.reduce(ids, function(result, id) {
25103                 var e = graph.entity(id);
25104                 if (e.type === 'way') {
25105                     try {
25106                         var cn = graph.childNodes(e);
25107                         result.push.apply(result, _.pluck(_.filter(cn, 'version'), 'id'));
25108                     } catch(err) {
25109                         /* eslint-disable no-console */
25110                         if (typeof console !== 'undefined') console.error(err);
25111                         /* eslint-enable no-console */
25112                     }
25113                 }
25114                 return result;
25115             }, _.clone(ids)));
25116         }
25117
25118         var loading = iD.ui.Loading(context).message(t('save.uploading')).blocking(true),
25119             history = context.history(),
25120             origChanges = history.changes(iD.actions.DiscardTags(history.difference())),
25121             localGraph = context.graph(),
25122             remoteGraph = iD.Graph(history.base(), true),
25123             modified = _.filter(history.difference().summary(), {changeType: 'modified'}),
25124             toCheck = _.pluck(_.pluck(modified, 'entity'), 'id'),
25125             toLoad = withChildNodes(toCheck, localGraph),
25126             conflicts = [],
25127             errors = [];
25128
25129         if (!tryAgain) history.perform(iD.actions.Noop());  // checkpoint
25130         context.container().call(loading);
25131
25132         if (toCheck.length) {
25133             context.connection().loadMultiple(toLoad, loaded);
25134         } else {
25135             finalize();
25136         }
25137
25138
25139         // Reload modified entities into an alternate graph and check for conflicts..
25140         function loaded(err, result) {
25141             if (errors.length) return;
25142
25143             if (err) {
25144                 errors.push({
25145                     msg: err.responseText,
25146                     details: [ t('save.status_code', { code: err.status }) ]
25147                 });
25148                 showErrors();
25149
25150             } else {
25151                 var loadMore = [];
25152                 _.each(result.data, function(entity) {
25153                     remoteGraph.replace(entity);
25154                     toLoad = _.without(toLoad, entity.id);
25155
25156                     // Because loadMultiple doesn't download /full like loadEntity,
25157                     // need to also load children that aren't already being checked..
25158                     if (!entity.visible) return;
25159                     if (entity.type === 'way') {
25160                         loadMore.push.apply(loadMore,
25161                             _.difference(entity.nodes, toCheck, toLoad, loadMore));
25162                     } else if (entity.type === 'relation' && entity.isMultipolygon()) {
25163                         loadMore.push.apply(loadMore,
25164                             _.difference(_.pluck(entity.members, 'id'), toCheck, toLoad, loadMore));
25165                     }
25166                 });
25167
25168                 if (loadMore.length) {
25169                     toLoad.push.apply(toLoad, loadMore);
25170                     context.connection().loadMultiple(loadMore, loaded);
25171                 }
25172
25173                 if (!toLoad.length) {
25174                     checkConflicts();
25175                 }
25176             }
25177         }
25178
25179
25180         function checkConflicts() {
25181             function choice(id, text, action) {
25182                 return { id: id, text: text, action: function() { history.replace(action); } };
25183             }
25184             function formatUser(d) {
25185                 return '<a href="' + context.connection().userURL(d) + '" target="_blank">' + d + '</a>';
25186             }
25187             function entityName(entity) {
25188                 return iD.util.displayName(entity) || (iD.util.displayType(entity.id) + ' ' + entity.id);
25189             }
25190
25191             function compareVersions(local, remote) {
25192                 if (local.version !== remote.version) return false;
25193
25194                 if (local.type === 'way') {
25195                     var children = _.union(local.nodes, remote.nodes);
25196
25197                     for (var i = 0; i < children.length; i++) {
25198                         var a = localGraph.hasEntity(children[i]),
25199                             b = remoteGraph.hasEntity(children[i]);
25200
25201                         if (a && b && a.version !== b.version) return false;
25202                     }
25203                 }
25204
25205                 return true;
25206             }
25207
25208             _.each(toCheck, function(id) {
25209                 var local = localGraph.entity(id),
25210                     remote = remoteGraph.entity(id);
25211
25212                 if (compareVersions(local, remote)) return;
25213
25214                 var action = iD.actions.MergeRemoteChanges,
25215                     merge = action(id, localGraph, remoteGraph, formatUser);
25216
25217                 history.replace(merge);
25218
25219                 var mergeConflicts = merge.conflicts();
25220                 if (!mergeConflicts.length) return;  // merged safely
25221
25222                 var forceLocal = action(id, localGraph, remoteGraph).withOption('force_local'),
25223                     forceRemote = action(id, localGraph, remoteGraph).withOption('force_remote'),
25224                     keepMine = t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore')),
25225                     keepTheirs = t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
25226
25227                 conflicts.push({
25228                     id: id,
25229                     name: entityName(local),
25230                     details: mergeConflicts,
25231                     chosen: 1,
25232                     choices: [
25233                         choice(id, keepMine, forceLocal),
25234                         choice(id, keepTheirs, forceRemote)
25235                     ]
25236                 });
25237             });
25238
25239             finalize();
25240         }
25241
25242
25243         function finalize() {
25244             if (conflicts.length) {
25245                 conflicts.sort(function(a,b) { return b.id.localeCompare(a.id); });
25246                 showConflicts();
25247             } else if (errors.length) {
25248                 showErrors();
25249             } else {
25250                 var changes = history.changes(iD.actions.DiscardTags(history.difference()));
25251                 if (changes.modified.length || changes.created.length || changes.deleted.length) {
25252                     context.connection().putChangeset(
25253                         changes,
25254                         e.comment,
25255                         history.imageryUsed(),
25256                         function(err, changeset_id) {
25257                             if (err) {
25258                                 errors.push({
25259                                     msg: err.responseText,
25260                                     details: [ t('save.status_code', { code: err.status }) ]
25261                                 });
25262                                 showErrors();
25263                             } else {
25264                                 history.clearSaved();
25265                                 success(e, changeset_id);
25266                                 // Add delay to allow for postgres replication #1646 #2678
25267                                 window.setTimeout(function() {
25268                                     loading.close();
25269                                     context.flush();
25270                                 }, 2500);
25271                             }
25272                         });
25273                 } else {        // changes were insignificant or reverted by user
25274                     loading.close();
25275                     context.flush();
25276                     cancel();
25277                 }
25278             }
25279         }
25280
25281
25282         function showConflicts() {
25283             var selection = context.container()
25284                 .select('#sidebar')
25285                 .append('div')
25286                 .attr('class','sidebar-component');
25287
25288             loading.close();
25289
25290             selection.call(iD.ui.Conflicts(context)
25291                 .list(conflicts)
25292                 .on('download', function() {
25293                     var data = JXON.stringify(context.connection().osmChangeJXON('CHANGEME', origChanges)),
25294                         win = window.open('data:text/xml,' + encodeURIComponent(data), '_blank');
25295                     win.focus();
25296                 })
25297                 .on('cancel', function() {
25298                     history.pop();
25299                     selection.remove();
25300                 })
25301                 .on('save', function() {
25302                     for (var i = 0; i < conflicts.length; i++) {
25303                         if (conflicts[i].chosen === 1) {  // user chose "keep theirs"
25304                             var entity = context.hasEntity(conflicts[i].id);
25305                             if (entity && entity.type === 'way') {
25306                                 var children = _.uniq(entity.nodes);
25307                                 for (var j = 0; j < children.length; j++) {
25308                                     history.replace(iD.actions.Revert(children[j]));
25309                                 }
25310                             }
25311                             history.replace(iD.actions.Revert(conflicts[i].id));
25312                         }
25313                     }
25314
25315                     selection.remove();
25316                     save(e, true);
25317                 })
25318             );
25319         }
25320
25321
25322         function showErrors() {
25323             var selection = iD.ui.confirm(context.container());
25324
25325             history.pop();
25326             loading.close();
25327
25328             selection
25329                 .select('.modal-section.header')
25330                 .append('h3')
25331                 .text(t('save.error'));
25332
25333             addErrors(selection, errors);
25334             selection.okButton();
25335         }
25336
25337
25338         function addErrors(selection, data) {
25339             var message = selection
25340                 .select('.modal-section.message-text');
25341
25342             var items = message
25343                 .selectAll('.error-container')
25344                 .data(data);
25345
25346             var enter = items.enter()
25347                 .append('div')
25348                 .attr('class', 'error-container');
25349
25350             enter
25351                 .append('a')
25352                 .attr('class', 'error-description')
25353                 .attr('href', '#')
25354                 .classed('hide-toggle', true)
25355                 .text(function(d) { return d.msg || t('save.unknown_error_details'); })
25356                 .on('click', function() {
25357                     var error = d3.select(this),
25358                         detail = d3.select(this.nextElementSibling),
25359                         exp = error.classed('expanded');
25360
25361                     detail.style('display', exp ? 'none' : 'block');
25362                     error.classed('expanded', !exp);
25363
25364                     d3.event.preventDefault();
25365                 });
25366
25367             var details = enter
25368                 .append('div')
25369                 .attr('class', 'error-detail-container')
25370                 .style('display', 'none');
25371
25372             details
25373                 .append('ul')
25374                 .attr('class', 'error-detail-list')
25375                 .selectAll('li')
25376                 .data(function(d) { return d.details || []; })
25377                 .enter()
25378                 .append('li')
25379                 .attr('class', 'error-detail-item')
25380                 .text(function(d) { return d; });
25381
25382             items.exit()
25383                 .remove();
25384         }
25385
25386     }
25387
25388
25389     function success(e, changeset_id) {
25390         context.enter(iD.modes.Browse(context)
25391             .sidebar(iD.ui.Success(context)
25392                 .changeset({
25393                     id: changeset_id,
25394                     comment: e.comment
25395                 })
25396                 .on('cancel', function() {
25397                     context.ui().sidebar.hide();
25398                 })));
25399     }
25400
25401     var mode = {
25402         id: 'save'
25403     };
25404
25405     mode.enter = function() {
25406         context.connection().authenticate(function(err) {
25407             if (err) {
25408                 cancel();
25409             } else {
25410                 context.ui().sidebar.show(ui);
25411             }
25412         });
25413     };
25414
25415     mode.exit = function() {
25416         context.ui().sidebar.hide();
25417     };
25418
25419     return mode;
25420 };
25421 iD.modes.Select = function(context, selectedIDs) {
25422     var mode = {
25423         id: 'select',
25424         button: 'browse'
25425     };
25426
25427     var keybinding = d3.keybinding('select'),
25428         timeout = null,
25429         behaviors = [
25430             iD.behavior.Copy(context),
25431             iD.behavior.Paste(context),
25432             iD.behavior.Breathe(context),
25433             iD.behavior.Hover(context),
25434             iD.behavior.Select(context),
25435             iD.behavior.Lasso(context),
25436             iD.modes.DragNode(context)
25437                 .selectedIDs(selectedIDs)
25438                 .behavior],
25439         inspector,
25440         radialMenu,
25441         newFeature = false,
25442         suppressMenu = false;
25443
25444     var wrap = context.container()
25445         .select('.inspector-wrap');
25446
25447
25448     function singular() {
25449         if (selectedIDs.length === 1) {
25450             return context.hasEntity(selectedIDs[0]);
25451         }
25452     }
25453
25454     function closeMenu() {
25455         if (radialMenu) {
25456             context.surface().call(radialMenu.close);
25457         }
25458     }
25459
25460     function positionMenu() {
25461         if (suppressMenu || !radialMenu) { return; }
25462
25463         var entity = singular();
25464         if (entity && context.geometry(entity.id) === 'relation') {
25465             suppressMenu = true;
25466         } else if (entity && entity.type === 'node') {
25467             radialMenu.center(context.projection(entity.loc));
25468         } else {
25469             var point = context.mouse(),
25470                 viewport = iD.geo.Extent(context.projection.clipExtent()).polygon();
25471             if (iD.geo.pointInPolygon(point, viewport)) {
25472                 radialMenu.center(point);
25473             } else {
25474                 suppressMenu = true;
25475             }
25476         }
25477     }
25478
25479     function showMenu() {
25480         closeMenu();
25481         if (!suppressMenu && radialMenu) {
25482             context.surface().call(radialMenu);
25483         }
25484     }
25485
25486     function toggleMenu() {
25487         if (d3.select('.radial-menu').empty()) {
25488             showMenu();
25489         } else {
25490             closeMenu();
25491         }
25492     }
25493
25494     mode.selectedIDs = function() {
25495         return selectedIDs;
25496     };
25497
25498     mode.reselect = function() {
25499         var surfaceNode = context.surface().node();
25500         if (surfaceNode.focus) { // FF doesn't support it
25501             surfaceNode.focus();
25502         }
25503
25504         positionMenu();
25505         showMenu();
25506     };
25507
25508     mode.newFeature = function(_) {
25509         if (!arguments.length) return newFeature;
25510         newFeature = _;
25511         return mode;
25512     };
25513
25514     mode.suppressMenu = function(_) {
25515         if (!arguments.length) return suppressMenu;
25516         suppressMenu = _;
25517         return mode;
25518     };
25519
25520     mode.enter = function() {
25521         function update() {
25522             closeMenu();
25523             if (_.any(selectedIDs, function(id) { return !context.hasEntity(id); })) {
25524                 // Exit mode if selected entity gets undone
25525                 context.enter(iD.modes.Browse(context));
25526             }
25527         }
25528
25529         function dblclick() {
25530             var target = d3.select(d3.event.target),
25531                 datum = target.datum();
25532
25533             if (datum instanceof iD.Way && !target.classed('fill')) {
25534                 var choice = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection),
25535                     node = iD.Node();
25536
25537                 var prev = datum.nodes[choice.index - 1],
25538                     next = datum.nodes[choice.index];
25539
25540                 context.perform(
25541                     iD.actions.AddMidpoint({loc: choice.loc, edge: [prev, next]}, node),
25542                     t('operations.add.annotation.vertex'));
25543
25544                 d3.event.preventDefault();
25545                 d3.event.stopPropagation();
25546             }
25547         }
25548
25549         function selectElements(drawn) {
25550             var entity = singular();
25551             if (entity && context.geometry(entity.id) === 'relation') {
25552                 suppressMenu = true;
25553                 return;
25554             }
25555
25556             var selection = context.surface()
25557                     .selectAll(iD.util.entityOrMemberSelector(selectedIDs, context.graph()));
25558
25559             if (selection.empty()) {
25560                 if (drawn) {  // Exit mode if selected DOM elements have disappeared..
25561                     context.enter(iD.modes.Browse(context));
25562                 }
25563             } else {
25564                 selection
25565                     .classed('selected', true);
25566             }
25567         }
25568
25569         function esc() {
25570             if (!context.inIntro()) {
25571                 context.enter(iD.modes.Browse(context));
25572             }
25573         }
25574
25575
25576         behaviors.forEach(function(behavior) {
25577             context.install(behavior);
25578         });
25579
25580         var operations = _.without(d3.values(iD.operations), iD.operations.Delete)
25581                 .map(function(o) { return o(selectedIDs, context); })
25582                 .filter(function(o) { return o.available(); });
25583
25584         operations.unshift(iD.operations.Delete(selectedIDs, context));
25585
25586         keybinding
25587             .on('⎋', esc, true)
25588             .on('space', toggleMenu);
25589
25590         operations.forEach(function(operation) {
25591             operation.keys.forEach(function(key) {
25592                 keybinding.on(key, function() {
25593                     if (!(context.inIntro() || operation.disabled())) {
25594                         operation();
25595                     }
25596                 });
25597             });
25598         });
25599
25600         d3.select(document)
25601             .call(keybinding);
25602
25603         radialMenu = iD.ui.RadialMenu(context, operations);
25604
25605         context.ui().sidebar
25606             .select(singular() ? singular().id : null, newFeature);
25607
25608         context.history()
25609             .on('undone.select', update)
25610             .on('redone.select', update);
25611
25612         context.map()
25613             .on('move.select', closeMenu)
25614             .on('drawn.select', selectElements);
25615
25616         selectElements();
25617
25618         var show = d3.event && !suppressMenu;
25619
25620         if (show) {
25621             positionMenu();
25622         }
25623
25624         timeout = window.setTimeout(function() {
25625             if (show) {
25626                 showMenu();
25627             }
25628
25629             context.surface()
25630                 .on('dblclick.select', dblclick);
25631         }, 200);
25632
25633         if (selectedIDs.length > 1) {
25634             var entities = iD.ui.SelectionList(context, selectedIDs);
25635             context.ui().sidebar.show(entities);
25636         }
25637     };
25638
25639     mode.exit = function() {
25640         if (timeout) window.clearTimeout(timeout);
25641
25642         if (inspector) wrap.call(inspector.close);
25643
25644         behaviors.forEach(function(behavior) {
25645             context.uninstall(behavior);
25646         });
25647
25648         keybinding.off();
25649         closeMenu();
25650         radialMenu = undefined;
25651
25652         context.history()
25653             .on('undone.select', null)
25654             .on('redone.select', null);
25655
25656         context.surface()
25657             .on('dblclick.select', null)
25658             .selectAll('.selected')
25659             .classed('selected', false);
25660
25661         context.map().on('drawn.select', null);
25662         context.ui().sidebar.hide();
25663     };
25664
25665     return mode;
25666 };
25667 iD.operations = {};
25668 iD.operations.Circularize = function(selectedIDs, context) {
25669     var entityId = selectedIDs[0],
25670         entity = context.entity(entityId),
25671         extent = entity.extent(context.graph()),
25672         geometry = context.geometry(entityId),
25673         action = iD.actions.Circularize(entityId, context.projection);
25674
25675     var operation = function() {
25676         var annotation = t('operations.circularize.annotation.' + geometry);
25677         context.perform(action, annotation);
25678     };
25679
25680     operation.available = function() {
25681         return selectedIDs.length === 1 &&
25682             entity.type === 'way' &&
25683             _.uniq(entity.nodes).length > 1;
25684     };
25685
25686     operation.disabled = function() {
25687         var reason;
25688         if (extent.percentContainedIn(context.extent()) < 0.8) {
25689             reason = 'too_large';
25690         } else if (context.hasHiddenConnections(entityId)) {
25691             reason = 'connected_to_hidden';
25692         }
25693         return action.disabled(context.graph()) || reason;
25694     };
25695
25696     operation.tooltip = function() {
25697         var disable = operation.disabled();
25698         return disable ?
25699             t('operations.circularize.' + disable) :
25700             t('operations.circularize.description.' + geometry);
25701     };
25702
25703     operation.id = 'circularize';
25704     operation.keys = [t('operations.circularize.key')];
25705     operation.title = t('operations.circularize.title');
25706
25707     return operation;
25708 };
25709 iD.operations.Continue = function(selectedIDs, context) {
25710     var graph = context.graph(),
25711         entities = selectedIDs.map(function(id) { return graph.entity(id); }),
25712         geometries = _.extend({line: [], vertex: []},
25713             _.groupBy(entities, function(entity) { return entity.geometry(graph); })),
25714         vertex = geometries.vertex[0];
25715
25716     function candidateWays() {
25717         return graph.parentWays(vertex).filter(function(parent) {
25718             return parent.geometry(graph) === 'line' &&
25719                 parent.affix(vertex.id) &&
25720                 (geometries.line.length === 0 || geometries.line[0] === parent);
25721         });
25722     }
25723
25724     var operation = function() {
25725         var candidate = candidateWays()[0];
25726         context.enter(iD.modes.DrawLine(
25727             context,
25728             candidate.id,
25729             context.graph(),
25730             candidate.affix(vertex.id)));
25731     };
25732
25733     operation.available = function() {
25734         return geometries.vertex.length === 1 && geometries.line.length <= 1 &&
25735             !context.features().hasHiddenConnections(vertex, context.graph());
25736     };
25737
25738     operation.disabled = function() {
25739         var candidates = candidateWays();
25740         if (candidates.length === 0)
25741             return 'not_eligible';
25742         if (candidates.length > 1)
25743             return 'multiple';
25744     };
25745
25746     operation.tooltip = function() {
25747         var disable = operation.disabled();
25748         return disable ?
25749             t('operations.continue.' + disable) :
25750             t('operations.continue.description');
25751     };
25752
25753     operation.id = 'continue';
25754     operation.keys = [t('operations.continue.key')];
25755     operation.title = t('operations.continue.title');
25756
25757     return operation;
25758 };
25759 iD.operations.Delete = function(selectedIDs, context) {
25760     var action = iD.actions.DeleteMultiple(selectedIDs);
25761
25762     var operation = function() {
25763         var annotation,
25764             nextSelectedID;
25765
25766         if (selectedIDs.length > 1) {
25767             annotation = t('operations.delete.annotation.multiple', {n: selectedIDs.length});
25768
25769         } else {
25770             var id = selectedIDs[0],
25771                 entity = context.entity(id),
25772                 geometry = context.geometry(id),
25773                 parents = context.graph().parentWays(entity),
25774                 parent = parents[0];
25775
25776             annotation = t('operations.delete.annotation.' + geometry);
25777
25778             // Select the next closest node in the way.
25779             if (geometry === 'vertex' && parents.length === 1 && parent.nodes.length > 2) {
25780                 var nodes = parent.nodes,
25781                     i = nodes.indexOf(id);
25782
25783                 if (i === 0) {
25784                     i++;
25785                 } else if (i === nodes.length - 1) {
25786                     i--;
25787                 } else {
25788                     var a = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc),
25789                         b = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
25790                     i = a < b ? i - 1 : i + 1;
25791                 }
25792
25793                 nextSelectedID = nodes[i];
25794             }
25795         }
25796
25797         if (nextSelectedID && context.hasEntity(nextSelectedID)) {
25798             context.enter(iD.modes.Select(context, [nextSelectedID]));
25799         } else {
25800             context.enter(iD.modes.Browse(context));
25801         }
25802
25803         context.perform(
25804             action,
25805             annotation);
25806     };
25807
25808     operation.available = function() {
25809         return true;
25810     };
25811
25812     operation.disabled = function() {
25813         var reason;
25814         if (_.any(selectedIDs, context.hasHiddenConnections)) {
25815             reason = 'connected_to_hidden';
25816         }
25817         return action.disabled(context.graph()) || reason;
25818     };
25819
25820     operation.tooltip = function() {
25821         var disable = operation.disabled();
25822         return disable ?
25823             t('operations.delete.' + disable) :
25824             t('operations.delete.description');
25825     };
25826
25827     operation.id = 'delete';
25828     operation.keys = [iD.ui.cmd('⌘⌫'), iD.ui.cmd('⌘⌦')];
25829     operation.title = t('operations.delete.title');
25830
25831     return operation;
25832 };
25833 iD.operations.Disconnect = function(selectedIDs, context) {
25834     var vertices = _.filter(selectedIDs, function vertex(entityId) {
25835         return context.geometry(entityId) === 'vertex';
25836     });
25837
25838     var entityId = vertices[0],
25839         action = iD.actions.Disconnect(entityId);
25840
25841     if (selectedIDs.length > 1) {
25842         action.limitWays(_.without(selectedIDs, entityId));
25843     }
25844
25845     var operation = function() {
25846         context.perform(action, t('operations.disconnect.annotation'));
25847     };
25848
25849     operation.available = function() {
25850         return vertices.length === 1;
25851     };
25852
25853     operation.disabled = function() {
25854         var reason;
25855         if (_.any(selectedIDs, context.hasHiddenConnections)) {
25856             reason = 'connected_to_hidden';
25857         }
25858         return action.disabled(context.graph()) || reason;
25859     };
25860
25861     operation.tooltip = function() {
25862         var disable = operation.disabled();
25863         return disable ?
25864             t('operations.disconnect.' + disable) :
25865             t('operations.disconnect.description');
25866     };
25867
25868     operation.id = 'disconnect';
25869     operation.keys = [t('operations.disconnect.key')];
25870     operation.title = t('operations.disconnect.title');
25871
25872     return operation;
25873 };
25874 iD.operations.Merge = function(selectedIDs, context) {
25875     var join = iD.actions.Join(selectedIDs),
25876         merge = iD.actions.Merge(selectedIDs),
25877         mergePolygon = iD.actions.MergePolygon(selectedIDs);
25878
25879     var operation = function() {
25880         var annotation = t('operations.merge.annotation', {n: selectedIDs.length}),
25881             action;
25882
25883         if (!join.disabled(context.graph())) {
25884             action = join;
25885         } else if (!merge.disabled(context.graph())) {
25886             action = merge;
25887         } else {
25888             action = mergePolygon;
25889         }
25890
25891         context.perform(action, annotation);
25892         context.enter(iD.modes.Select(context, selectedIDs.filter(function(id) { return context.hasEntity(id); }))
25893             .suppressMenu(true));
25894     };
25895
25896     operation.available = function() {
25897         return selectedIDs.length >= 2;
25898     };
25899
25900     operation.disabled = function() {
25901         return join.disabled(context.graph()) &&
25902             merge.disabled(context.graph()) &&
25903             mergePolygon.disabled(context.graph());
25904     };
25905
25906     operation.tooltip = function() {
25907         var j = join.disabled(context.graph()),
25908             m = merge.disabled(context.graph()),
25909             p = mergePolygon.disabled(context.graph());
25910
25911         if (j === 'restriction' && m && p)
25912             return t('operations.merge.restriction', {relation: context.presets().item('type/restriction').name()});
25913
25914         if (p === 'incomplete_relation' && j && m)
25915             return t('operations.merge.incomplete_relation');
25916
25917         if (j && m && p)
25918             return t('operations.merge.' + j);
25919
25920         return t('operations.merge.description');
25921     };
25922
25923     operation.id = 'merge';
25924     operation.keys = [t('operations.merge.key')];
25925     operation.title = t('operations.merge.title');
25926
25927     return operation;
25928 };
25929 iD.operations.Move = function(selectedIDs, context) {
25930     var extent = selectedIDs.reduce(function(extent, id) {
25931             return extent.extend(context.entity(id).extent(context.graph()));
25932         }, iD.geo.Extent());
25933
25934     var operation = function() {
25935         context.enter(iD.modes.Move(context, selectedIDs));
25936     };
25937
25938     operation.available = function() {
25939         return selectedIDs.length > 1 ||
25940             context.entity(selectedIDs[0]).type !== 'node';
25941     };
25942
25943     operation.disabled = function() {
25944         var reason;
25945         if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
25946             reason = 'too_large';
25947         } else if (_.any(selectedIDs, context.hasHiddenConnections)) {
25948             reason = 'connected_to_hidden';
25949         }
25950         return iD.actions.Move(selectedIDs).disabled(context.graph()) || reason;
25951     };
25952
25953     operation.tooltip = function() {
25954         var disable = operation.disabled();
25955         return disable ?
25956             t('operations.move.' + disable) :
25957             t('operations.move.description');
25958     };
25959
25960     operation.id = 'move';
25961     operation.keys = [t('operations.move.key')];
25962     operation.title = t('operations.move.title');
25963
25964     return operation;
25965 };
25966 iD.operations.Orthogonalize = function(selectedIDs, context) {
25967     var entityId = selectedIDs[0],
25968         entity = context.entity(entityId),
25969         extent = entity.extent(context.graph()),
25970         geometry = context.geometry(entityId),
25971         action = iD.actions.Orthogonalize(entityId, context.projection);
25972
25973     var operation = function() {
25974         var annotation = t('operations.orthogonalize.annotation.' + geometry);
25975         context.perform(action, annotation);
25976     };
25977
25978     operation.available = function() {
25979         return selectedIDs.length === 1 &&
25980             entity.type === 'way' &&
25981             entity.isClosed() &&
25982             _.uniq(entity.nodes).length > 2;
25983     };
25984
25985     operation.disabled = function() {
25986         var reason;
25987         if (extent.percentContainedIn(context.extent()) < 0.8) {
25988             reason = 'too_large';
25989         } else if (context.hasHiddenConnections(entityId)) {
25990             reason = 'connected_to_hidden';
25991         }
25992         return action.disabled(context.graph()) || reason;
25993     };
25994
25995     operation.tooltip = function() {
25996         var disable = operation.disabled();
25997         return disable ?
25998             t('operations.orthogonalize.' + disable) :
25999             t('operations.orthogonalize.description.' + geometry);
26000     };
26001
26002     operation.id = 'orthogonalize';
26003     operation.keys = [t('operations.orthogonalize.key')];
26004     operation.title = t('operations.orthogonalize.title');
26005
26006     return operation;
26007 };
26008 iD.operations.Reverse = function(selectedIDs, context) {
26009     var entityId = selectedIDs[0];
26010
26011     var operation = function() {
26012         context.perform(
26013             iD.actions.Reverse(entityId),
26014             t('operations.reverse.annotation'));
26015     };
26016
26017     operation.available = function() {
26018         return selectedIDs.length === 1 &&
26019             context.geometry(entityId) === 'line';
26020     };
26021
26022     operation.disabled = function() {
26023         return false;
26024     };
26025
26026     operation.tooltip = function() {
26027         return t('operations.reverse.description');
26028     };
26029
26030     operation.id = 'reverse';
26031     operation.keys = [t('operations.reverse.key')];
26032     operation.title = t('operations.reverse.title');
26033
26034     return operation;
26035 };
26036 iD.operations.Rotate = function(selectedIDs, context) {
26037     var entityId = selectedIDs[0],
26038         entity = context.entity(entityId),
26039         extent = entity.extent(context.graph()),
26040         geometry = context.geometry(entityId);
26041
26042     var operation = function() {
26043         context.enter(iD.modes.RotateWay(context, entityId));
26044     };
26045
26046     operation.available = function() {
26047         if (selectedIDs.length !== 1 || entity.type !== 'way')
26048             return false;
26049         if (geometry === 'area')
26050             return true;
26051         if (entity.isClosed() &&
26052             context.graph().parentRelations(entity).some(function(r) { return r.isMultipolygon(); }))
26053             return true;
26054         return false;
26055     };
26056
26057     operation.disabled = function() {
26058         if (extent.percentContainedIn(context.extent()) < 0.8) {
26059             return 'too_large';
26060         } else if (context.hasHiddenConnections(entityId)) {
26061             return 'connected_to_hidden';
26062         } else {
26063             return false;
26064         }
26065     };
26066
26067     operation.tooltip = function() {
26068         var disable = operation.disabled();
26069         return disable ?
26070             t('operations.rotate.' + disable) :
26071             t('operations.rotate.description');
26072     };
26073
26074     operation.id = 'rotate';
26075     operation.keys = [t('operations.rotate.key')];
26076     operation.title = t('operations.rotate.title');
26077
26078     return operation;
26079 };
26080 iD.operations.Split = function(selectedIDs, context) {
26081     var vertices = _.filter(selectedIDs, function vertex(entityId) {
26082         return context.geometry(entityId) === 'vertex';
26083     });
26084
26085     var entityId = vertices[0],
26086         action = iD.actions.Split(entityId);
26087
26088     if (selectedIDs.length > 1) {
26089         action.limitWays(_.without(selectedIDs, entityId));
26090     }
26091
26092     var operation = function() {
26093         var annotation;
26094
26095         var ways = action.ways(context.graph());
26096         if (ways.length === 1) {
26097             annotation = t('operations.split.annotation.' + context.geometry(ways[0].id));
26098         } else {
26099             annotation = t('operations.split.annotation.multiple', {n: ways.length});
26100         }
26101
26102         var difference = context.perform(action, annotation);
26103         context.enter(iD.modes.Select(context, difference.extantIDs()));
26104     };
26105
26106     operation.available = function() {
26107         return vertices.length === 1;
26108     };
26109
26110     operation.disabled = function() {
26111         var reason;
26112         if (_.any(selectedIDs, context.hasHiddenConnections)) {
26113             reason = 'connected_to_hidden';
26114         }
26115         return action.disabled(context.graph()) || reason;
26116     };
26117
26118     operation.tooltip = function() {
26119         var disable = operation.disabled();
26120         if (disable) {
26121             return t('operations.split.' + disable);
26122         }
26123
26124         var ways = action.ways(context.graph());
26125         if (ways.length === 1) {
26126             return t('operations.split.description.' + context.geometry(ways[0].id));
26127         } else {
26128             return t('operations.split.description.multiple');
26129         }
26130     };
26131
26132     operation.id = 'split';
26133     operation.keys = [t('operations.split.key')];
26134     operation.title = t('operations.split.title');
26135
26136     return operation;
26137 };
26138 iD.operations.Straighten = function(selectedIDs, context) {
26139     var entityId = selectedIDs[0],
26140         action = iD.actions.Straighten(entityId, context.projection);
26141
26142     function operation() {
26143         var annotation = t('operations.straighten.annotation');
26144         context.perform(action, annotation);
26145     }
26146
26147     operation.available = function() {
26148         var entity = context.entity(entityId);
26149         return selectedIDs.length === 1 &&
26150             entity.type === 'way' &&
26151             !entity.isClosed() &&
26152             _.uniq(entity.nodes).length > 2;
26153     };
26154
26155     operation.disabled = function() {
26156         var reason;
26157         if (context.hasHiddenConnections(entityId)) {
26158             reason = 'connected_to_hidden';
26159         }
26160         return action.disabled(context.graph()) || reason;
26161     };
26162
26163     operation.tooltip = function() {
26164         var disable = operation.disabled();
26165         return disable ?
26166             t('operations.straighten.' + disable) :
26167             t('operations.straighten.description');
26168     };
26169
26170     operation.id = 'straighten';
26171     operation.keys = [t('operations.straighten.key')];
26172     operation.title = t('operations.straighten.title');
26173
26174     return operation;
26175 };
26176 iD.Connection = function(useHttps) {
26177     if (typeof useHttps !== 'boolean') {
26178       useHttps = window.location.protocol === 'https:';
26179     }
26180
26181     var event = d3.dispatch('authenticating', 'authenticated', 'auth', 'loading', 'loaded'),
26182         protocol = useHttps ? 'https:' : 'http:',
26183         url = protocol + '//www.openstreetmap.org',
26184         connection = {},
26185         inflight = {},
26186         loadedTiles = {},
26187         tileZoom = 16,
26188         oauth = osmAuth({
26189             url: protocol + '//www.openstreetmap.org',
26190             oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
26191             oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
26192             loading: authenticating,
26193             done: authenticated
26194         }),
26195         ndStr = 'nd',
26196         tagStr = 'tag',
26197         memberStr = 'member',
26198         nodeStr = 'node',
26199         wayStr = 'way',
26200         relationStr = 'relation',
26201         userDetails,
26202         off;
26203
26204
26205     connection.changesetURL = function(changesetId) {
26206         return url + '/changeset/' + changesetId;
26207     };
26208
26209     connection.changesetsURL = function(center, zoom) {
26210         var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
26211         return url + '/history#map=' +
26212             Math.floor(zoom) + '/' +
26213             center[1].toFixed(precision) + '/' +
26214             center[0].toFixed(precision);
26215     };
26216
26217     connection.entityURL = function(entity) {
26218         return url + '/' + entity.type + '/' + entity.osmId();
26219     };
26220
26221     connection.userURL = function(username) {
26222         return url + '/user/' + username;
26223     };
26224
26225     connection.loadFromURL = function(url, callback) {
26226         function done(err, dom) {
26227             return callback(err, parse(dom));
26228         }
26229         return d3.xml(url).get(done);
26230     };
26231
26232     connection.loadEntity = function(id, callback) {
26233         var type = iD.Entity.id.type(id),
26234             osmID = iD.Entity.id.toOSM(id);
26235
26236         connection.loadFromURL(
26237             url + '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''),
26238             function(err, entities) {
26239                 if (callback) callback(err, {data: entities});
26240             });
26241     };
26242
26243     connection.loadEntityVersion = function(id, version, callback) {
26244         var type = iD.Entity.id.type(id),
26245             osmID = iD.Entity.id.toOSM(id);
26246
26247         connection.loadFromURL(
26248             url + '/api/0.6/' + type + '/' + osmID + '/' + version,
26249             function(err, entities) {
26250                 if (callback) callback(err, {data: entities});
26251             });
26252     };
26253
26254     connection.loadMultiple = function(ids, callback) {
26255         _.each(_.groupBy(_.uniq(ids), iD.Entity.id.type), function(v, k) {
26256             var type = k + 's',
26257                 osmIDs = _.map(v, iD.Entity.id.toOSM);
26258
26259             _.each(_.chunk(osmIDs, 150), function(arr) {
26260                 connection.loadFromURL(
26261                     url + '/api/0.6/' + type + '?' + type + '=' + arr.join(),
26262                     function(err, entities) {
26263                         if (callback) callback(err, {data: entities});
26264                     });
26265             });
26266         });
26267     };
26268
26269     function authenticating() {
26270         event.authenticating();
26271     }
26272
26273     function authenticated() {
26274         event.authenticated();
26275     }
26276
26277     function getLoc(attrs) {
26278         var lon = attrs.lon && attrs.lon.value,
26279             lat = attrs.lat && attrs.lat.value;
26280         return [parseFloat(lon), parseFloat(lat)];
26281     }
26282
26283     function getNodes(obj) {
26284         var elems = obj.getElementsByTagName(ndStr),
26285             nodes = new Array(elems.length);
26286         for (var i = 0, l = elems.length; i < l; i++) {
26287             nodes[i] = 'n' + elems[i].attributes.ref.value;
26288         }
26289         return nodes;
26290     }
26291
26292     function getTags(obj) {
26293         var elems = obj.getElementsByTagName(tagStr),
26294             tags = {};
26295         for (var i = 0, l = elems.length; i < l; i++) {
26296             var attrs = elems[i].attributes;
26297             tags[attrs.k.value] = attrs.v.value;
26298         }
26299         return tags;
26300     }
26301
26302     function getMembers(obj) {
26303         var elems = obj.getElementsByTagName(memberStr),
26304             members = new Array(elems.length);
26305         for (var i = 0, l = elems.length; i < l; i++) {
26306             var attrs = elems[i].attributes;
26307             members[i] = {
26308                 id: attrs.type.value[0] + attrs.ref.value,
26309                 type: attrs.type.value,
26310                 role: attrs.role.value
26311             };
26312         }
26313         return members;
26314     }
26315
26316     function getVisible(attrs) {
26317         return (!attrs.visible || attrs.visible.value !== 'false');
26318     }
26319
26320     var parsers = {
26321         node: function nodeData(obj) {
26322             var attrs = obj.attributes;
26323             return new iD.Node({
26324                 id: iD.Entity.id.fromOSM(nodeStr, attrs.id.value),
26325                 loc: getLoc(attrs),
26326                 version: attrs.version.value,
26327                 user: attrs.user && attrs.user.value,
26328                 tags: getTags(obj),
26329                 visible: getVisible(attrs)
26330             });
26331         },
26332
26333         way: function wayData(obj) {
26334             var attrs = obj.attributes;
26335             return new iD.Way({
26336                 id: iD.Entity.id.fromOSM(wayStr, attrs.id.value),
26337                 version: attrs.version.value,
26338                 user: attrs.user && attrs.user.value,
26339                 tags: getTags(obj),
26340                 nodes: getNodes(obj),
26341                 visible: getVisible(attrs)
26342             });
26343         },
26344
26345         relation: function relationData(obj) {
26346             var attrs = obj.attributes;
26347             return new iD.Relation({
26348                 id: iD.Entity.id.fromOSM(relationStr, attrs.id.value),
26349                 version: attrs.version.value,
26350                 user: attrs.user && attrs.user.value,
26351                 tags: getTags(obj),
26352                 members: getMembers(obj),
26353                 visible: getVisible(attrs)
26354             });
26355         }
26356     };
26357
26358     function parse(dom) {
26359         if (!dom || !dom.childNodes) return;
26360
26361         var root = dom.childNodes[0],
26362             children = root.childNodes,
26363             entities = [];
26364
26365         for (var i = 0, l = children.length; i < l; i++) {
26366             var child = children[i],
26367                 parser = parsers[child.nodeName];
26368             if (parser) {
26369                 entities.push(parser(child));
26370             }
26371         }
26372
26373         return entities;
26374     }
26375
26376     connection.authenticated = function() {
26377         return oauth.authenticated();
26378     };
26379
26380     // Generate Changeset XML. Returns a string.
26381     connection.changesetJXON = function(tags) {
26382         return {
26383             osm: {
26384                 changeset: {
26385                     tag: _.map(tags, function(value, key) {
26386                         return { '@k': key, '@v': value };
26387                     }),
26388                     '@version': 0.6,
26389                     '@generator': 'iD'
26390                 }
26391             }
26392         };
26393     };
26394
26395     // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
26396     // XML. Returns a string.
26397     connection.osmChangeJXON = function(changeset_id, changes) {
26398         function nest(x, order) {
26399             var groups = {};
26400             for (var i = 0; i < x.length; i++) {
26401                 var tagName = Object.keys(x[i])[0];
26402                 if (!groups[tagName]) groups[tagName] = [];
26403                 groups[tagName].push(x[i][tagName]);
26404             }
26405             var ordered = {};
26406             order.forEach(function(o) {
26407                 if (groups[o]) ordered[o] = groups[o];
26408             });
26409             return ordered;
26410         }
26411
26412         function rep(entity) {
26413             return entity.asJXON(changeset_id);
26414         }
26415
26416         return {
26417             osmChange: {
26418                 '@version': 0.6,
26419                 '@generator': 'iD',
26420                 'create': nest(changes.created.map(rep), ['node', 'way', 'relation']),
26421                 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
26422                 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true})
26423             }
26424         };
26425     };
26426
26427     connection.changesetTags = function(comment, imageryUsed) {
26428         var detected = iD.detect(),
26429             tags = {
26430                 created_by: 'iD ' + iD.version,
26431                 imagery_used: imageryUsed.join(';').substr(0, 255),
26432                 host: (window.location.origin + window.location.pathname).substr(0, 255),
26433                 locale: detected.locale
26434             };
26435
26436         if (comment) {
26437             tags.comment = comment.substr(0, 255);
26438         }
26439
26440         return tags;
26441     };
26442
26443     connection.putChangeset = function(changes, comment, imageryUsed, callback) {
26444         oauth.xhr({
26445                 method: 'PUT',
26446                 path: '/api/0.6/changeset/create',
26447                 options: { header: { 'Content-Type': 'text/xml' } },
26448                 content: JXON.stringify(connection.changesetJXON(connection.changesetTags(comment, imageryUsed)))
26449             }, function(err, changeset_id) {
26450                 if (err) return callback(err);
26451                 oauth.xhr({
26452                     method: 'POST',
26453                     path: '/api/0.6/changeset/' + changeset_id + '/upload',
26454                     options: { header: { 'Content-Type': 'text/xml' } },
26455                     content: JXON.stringify(connection.osmChangeJXON(changeset_id, changes))
26456                 }, function(err) {
26457                     if (err) return callback(err);
26458                     // POST was successful, safe to call the callback.
26459                     // Still attempt to close changeset, but ignore response because #2667
26460                     // Add delay to allow for postgres replication #1646 #2678
26461                     window.setTimeout(function() { callback(null, changeset_id); }, 2500);
26462                     oauth.xhr({
26463                         method: 'PUT',
26464                         path: '/api/0.6/changeset/' + changeset_id + '/close',
26465                         options: { header: { 'Content-Type': 'text/xml' } }
26466                     }, d3.functor(true));
26467                 });
26468             });
26469     };
26470
26471     connection.userDetails = function(callback) {
26472         if (userDetails) {
26473             callback(undefined, userDetails);
26474             return;
26475         }
26476
26477         function done(err, user_details) {
26478             if (err) return callback(err);
26479
26480             var u = user_details.getElementsByTagName('user')[0],
26481                 img = u.getElementsByTagName('img'),
26482                 image_url = '';
26483
26484             if (img && img[0] && img[0].getAttribute('href')) {
26485                 image_url = img[0].getAttribute('href');
26486             }
26487
26488             userDetails = {
26489                 display_name: u.attributes.display_name.value,
26490                 image_url: image_url,
26491                 id: u.attributes.id.value
26492             };
26493
26494             callback(undefined, userDetails);
26495         }
26496
26497         oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
26498     };
26499
26500     connection.userChangesets = function(callback) {
26501         connection.userDetails(function(err, user) {
26502             if (err) return callback(err);
26503
26504             function done(changesets) {
26505                 callback(undefined, Array.prototype.map.call(changesets.getElementsByTagName('changeset'),
26506                     function (changeset) {
26507                         return { tags: getTags(changeset) };
26508                     }));
26509             }
26510
26511             d3.xml(url + '/api/0.6/changesets?user=' + user.id).get()
26512                 .on('load', done)
26513                 .on('error', callback);
26514         });
26515     };
26516
26517     connection.status = function(callback) {
26518         function done(capabilities) {
26519             var apiStatus = capabilities.getElementsByTagName('status');
26520             callback(undefined, apiStatus[0].getAttribute('api'));
26521         }
26522         d3.xml(url + '/api/capabilities').get()
26523             .on('load', done)
26524             .on('error', callback);
26525     };
26526
26527     function abortRequest(i) { i.abort(); }
26528
26529     connection.tileZoom = function(_) {
26530         if (!arguments.length) return tileZoom;
26531         tileZoom = _;
26532         return connection;
26533     };
26534
26535     connection.loadTiles = function(projection, dimensions, callback) {
26536
26537         if (off) return;
26538
26539         var s = projection.scale() * 2 * Math.PI,
26540             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
26541             ts = 256 * Math.pow(2, z - tileZoom),
26542             origin = [
26543                 s / 2 - projection.translate()[0],
26544                 s / 2 - projection.translate()[1]];
26545
26546         var tiles = d3.geo.tile()
26547             .scaleExtent([tileZoom, tileZoom])
26548             .scale(s)
26549             .size(dimensions)
26550             .translate(projection.translate())()
26551             .map(function(tile) {
26552                 var x = tile[0] * ts - origin[0],
26553                     y = tile[1] * ts - origin[1];
26554
26555                 return {
26556                     id: tile.toString(),
26557                     extent: iD.geo.Extent(
26558                         projection.invert([x, y + ts]),
26559                         projection.invert([x + ts, y]))
26560                 };
26561             });
26562
26563         function bboxUrl(tile) {
26564             return url + '/api/0.6/map?bbox=' + tile.extent.toParam();
26565         }
26566
26567         _.filter(inflight, function(v, i) {
26568             var wanted = _.find(tiles, function(tile) {
26569                 return i === tile.id;
26570             });
26571             if (!wanted) delete inflight[i];
26572             return !wanted;
26573         }).map(abortRequest);
26574
26575         tiles.forEach(function(tile) {
26576             var id = tile.id;
26577
26578             if (loadedTiles[id] || inflight[id]) return;
26579
26580             if (_.isEmpty(inflight)) {
26581                 event.loading();
26582             }
26583
26584             inflight[id] = connection.loadFromURL(bboxUrl(tile), function(err, parsed) {
26585                 loadedTiles[id] = true;
26586                 delete inflight[id];
26587
26588                 if (callback) callback(err, _.extend({data: parsed}, tile));
26589
26590                 if (_.isEmpty(inflight)) {
26591                     event.loaded();
26592                 }
26593             });
26594         });
26595     };
26596
26597     connection.switch = function(options) {
26598         url = options.url;
26599         oauth.options(_.extend({
26600             loading: authenticating,
26601             done: authenticated
26602         }, options));
26603         event.auth();
26604         connection.flush();
26605         return connection;
26606     };
26607
26608     connection.toggle = function(_) {
26609         off = !_;
26610         return connection;
26611     };
26612
26613     connection.flush = function() {
26614         userDetails = undefined;
26615         _.forEach(inflight, abortRequest);
26616         loadedTiles = {};
26617         inflight = {};
26618         return connection;
26619     };
26620
26621     connection.loadedTiles = function(_) {
26622         if (!arguments.length) return loadedTiles;
26623         loadedTiles = _;
26624         return connection;
26625     };
26626
26627     connection.logout = function() {
26628         userDetails = undefined;
26629         oauth.logout();
26630         event.auth();
26631         return connection;
26632     };
26633
26634     connection.authenticate = function(callback) {
26635         userDetails = undefined;
26636         function done(err, res) {
26637             event.auth();
26638             if (callback) callback(err, res);
26639         }
26640         return oauth.authenticate(done);
26641     };
26642
26643     return d3.rebind(connection, event, 'on');
26644 };
26645 /*
26646     iD.Difference represents the difference between two graphs.
26647     It knows how to calculate the set of entities that were
26648     created, modified, or deleted, and also contains the logic
26649     for recursively extending a difference to the complete set
26650     of entities that will require a redraw, taking into account
26651     child and parent relationships.
26652  */
26653 iD.Difference = function(base, head) {
26654     var changes = {}, length = 0;
26655
26656     function changed(h, b) {
26657         return h !== b && !_.isEqual(_.omit(h, 'v'), _.omit(b, 'v'));
26658     }
26659
26660     _.each(head.entities, function(h, id) {
26661         var b = base.entities[id];
26662         if (changed(h, b)) {
26663             changes[id] = {base: b, head: h};
26664             length++;
26665         }
26666     });
26667
26668     _.each(base.entities, function(b, id) {
26669         var h = head.entities[id];
26670         if (!changes[id] && changed(h, b)) {
26671             changes[id] = {base: b, head: h};
26672             length++;
26673         }
26674     });
26675
26676     function addParents(parents, result) {
26677         for (var i = 0; i < parents.length; i++) {
26678             var parent = parents[i];
26679
26680             if (parent.id in result)
26681                 continue;
26682
26683             result[parent.id] = parent;
26684             addParents(head.parentRelations(parent), result);
26685         }
26686     }
26687
26688     var difference = {};
26689
26690     difference.length = function() {
26691         return length;
26692     };
26693
26694     difference.changes = function() {
26695         return changes;
26696     };
26697
26698     difference.extantIDs = function() {
26699         var result = [];
26700         _.each(changes, function(change, id) {
26701             if (change.head) result.push(id);
26702         });
26703         return result;
26704     };
26705
26706     difference.modified = function() {
26707         var result = [];
26708         _.each(changes, function(change) {
26709             if (change.base && change.head) result.push(change.head);
26710         });
26711         return result;
26712     };
26713
26714     difference.created = function() {
26715         var result = [];
26716         _.each(changes, function(change) {
26717             if (!change.base && change.head) result.push(change.head);
26718         });
26719         return result;
26720     };
26721
26722     difference.deleted = function() {
26723         var result = [];
26724         _.each(changes, function(change) {
26725             if (change.base && !change.head) result.push(change.base);
26726         });
26727         return result;
26728     };
26729
26730     difference.summary = function() {
26731         var relevant = {};
26732
26733         function addEntity(entity, graph, changeType) {
26734             relevant[entity.id] = {
26735                 entity: entity,
26736                 graph: graph,
26737                 changeType: changeType
26738             };
26739         }
26740
26741         function addParents(entity) {
26742             var parents = head.parentWays(entity);
26743             for (var j = parents.length - 1; j >= 0; j--) {
26744                 var parent = parents[j];
26745                 if (!(parent.id in relevant)) addEntity(parent, head, 'modified');
26746             }
26747         }
26748
26749         _.each(changes, function(change) {
26750             if (change.head && change.head.geometry(head) !== 'vertex') {
26751                 addEntity(change.head, head, change.base ? 'modified' : 'created');
26752
26753             } else if (change.base && change.base.geometry(base) !== 'vertex') {
26754                 addEntity(change.base, base, 'deleted');
26755
26756             } else if (change.base && change.head) { // modified vertex
26757                 var moved    = !_.isEqual(change.base.loc,  change.head.loc),
26758                     retagged = !_.isEqual(change.base.tags, change.head.tags);
26759
26760                 if (moved) {
26761                     addParents(change.head);
26762                 }
26763
26764                 if (retagged || (moved && change.head.hasInterestingTags())) {
26765                     addEntity(change.head, head, 'modified');
26766                 }
26767
26768             } else if (change.head && change.head.hasInterestingTags()) { // created vertex
26769                 addEntity(change.head, head, 'created');
26770
26771             } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
26772                 addEntity(change.base, base, 'deleted');
26773             }
26774         });
26775
26776         return d3.values(relevant);
26777     };
26778
26779     difference.complete = function(extent) {
26780         var result = {}, id, change;
26781
26782         for (id in changes) {
26783             change = changes[id];
26784
26785             var h = change.head,
26786                 b = change.base,
26787                 entity = h || b;
26788
26789             if (extent &&
26790                 (!h || !h.intersects(extent, head)) &&
26791                 (!b || !b.intersects(extent, base)))
26792                 continue;
26793
26794             result[id] = h;
26795
26796             if (entity.type === 'way') {
26797                 var nh = h ? h.nodes : [],
26798                     nb = b ? b.nodes : [],
26799                     diff, i;
26800
26801                 diff = _.difference(nh, nb);
26802                 for (i = 0; i < diff.length; i++) {
26803                     result[diff[i]] = head.hasEntity(diff[i]);
26804                 }
26805
26806                 diff = _.difference(nb, nh);
26807                 for (i = 0; i < diff.length; i++) {
26808                     result[diff[i]] = head.hasEntity(diff[i]);
26809                 }
26810             }
26811
26812             addParents(head.parentWays(entity), result);
26813             addParents(head.parentRelations(entity), result);
26814         }
26815
26816         return result;
26817     };
26818
26819     return difference;
26820 };
26821 iD.Entity = function(attrs) {
26822     // For prototypal inheritance.
26823     if (this instanceof iD.Entity) return;
26824
26825     // Create the appropriate subtype.
26826     if (attrs && attrs.type) {
26827         return iD.Entity[attrs.type].apply(this, arguments);
26828     } else if (attrs && attrs.id) {
26829         return iD.Entity[iD.Entity.id.type(attrs.id)].apply(this, arguments);
26830     }
26831
26832     // Initialize a generic Entity (used only in tests).
26833     return (new iD.Entity()).initialize(arguments);
26834 };
26835
26836 iD.Entity.id = function(type) {
26837     return iD.Entity.id.fromOSM(type, iD.Entity.id.next[type]--);
26838 };
26839
26840 iD.Entity.id.next = {node: -1, way: -1, relation: -1};
26841
26842 iD.Entity.id.fromOSM = function(type, id) {
26843     return type[0] + id;
26844 };
26845
26846 iD.Entity.id.toOSM = function(id) {
26847     return id.slice(1);
26848 };
26849
26850 iD.Entity.id.type = function(id) {
26851     return {'n': 'node', 'w': 'way', 'r': 'relation'}[id[0]];
26852 };
26853
26854 // A function suitable for use as the second argument to d3.selection#data().
26855 iD.Entity.key = function(entity) {
26856     return entity.id + 'v' + (entity.v || 0);
26857 };
26858
26859 iD.Entity.prototype = {
26860     tags: {},
26861
26862     initialize: function(sources) {
26863         for (var i = 0; i < sources.length; ++i) {
26864             var source = sources[i];
26865             for (var prop in source) {
26866                 if (Object.prototype.hasOwnProperty.call(source, prop)) {
26867                     if (source[prop] === undefined) {
26868                         delete this[prop];
26869                     } else {
26870                         this[prop] = source[prop];
26871                     }
26872                 }
26873             }
26874         }
26875
26876         if (!this.id && this.type) {
26877             this.id = iD.Entity.id(this.type);
26878         }
26879         if (!this.hasOwnProperty('visible')) {
26880             this.visible = true;
26881         }
26882
26883         if (iD.debug) {
26884             Object.freeze(this);
26885             Object.freeze(this.tags);
26886
26887             if (this.loc) Object.freeze(this.loc);
26888             if (this.nodes) Object.freeze(this.nodes);
26889             if (this.members) Object.freeze(this.members);
26890         }
26891
26892         return this;
26893     },
26894
26895     copy: function(resolver, copies) {
26896         if (copies[this.id])
26897             return copies[this.id];
26898
26899         var copy = iD.Entity(this, {id: undefined, user: undefined, version: undefined});
26900         copies[this.id] = copy;
26901
26902         return copy;
26903     },
26904
26905     osmId: function() {
26906         return iD.Entity.id.toOSM(this.id);
26907     },
26908
26909     isNew: function() {
26910         return this.osmId() < 0;
26911     },
26912
26913     update: function(attrs) {
26914         return iD.Entity(this, attrs, {v: 1 + (this.v || 0)});
26915     },
26916
26917     mergeTags: function(tags) {
26918         var merged = _.clone(this.tags), changed = false;
26919         for (var k in tags) {
26920             var t1 = merged[k],
26921                 t2 = tags[k];
26922             if (!t1) {
26923                 changed = true;
26924                 merged[k] = t2;
26925             } else if (t1 !== t2) {
26926                 changed = true;
26927                 merged[k] = _.union(t1.split(/;\s*/), t2.split(/;\s*/)).join(';');
26928             }
26929         }
26930         return changed ? this.update({tags: merged}) : this;
26931     },
26932
26933     intersects: function(extent, resolver) {
26934         return this.extent(resolver).intersects(extent);
26935     },
26936
26937     isUsed: function(resolver) {
26938         return _.without(Object.keys(this.tags), 'area').length > 0 ||
26939             resolver.parentRelations(this).length > 0;
26940     },
26941
26942     hasInterestingTags: function() {
26943         return _.keys(this.tags).some(iD.interestingTag);
26944     },
26945
26946     isHighwayIntersection: function() {
26947         return false;
26948     },
26949
26950     deprecatedTags: function() {
26951         var tags = _.pairs(this.tags);
26952         var deprecated = {};
26953
26954         iD.data.deprecated.forEach(function(d) {
26955             var match = _.pairs(d.old)[0];
26956             tags.forEach(function(t) {
26957                 if (t[0] === match[0] &&
26958                     (t[1] === match[1] || match[1] === '*')) {
26959                     deprecated[t[0]] = t[1];
26960                 }
26961             });
26962         });
26963
26964         return deprecated;
26965     }
26966 };
26967 iD.Graph = function(other, mutable) {
26968     if (!(this instanceof iD.Graph)) return new iD.Graph(other, mutable);
26969
26970     if (other instanceof iD.Graph) {
26971         var base = other.base();
26972         this.entities = _.assign(Object.create(base.entities), other.entities);
26973         this._parentWays = _.assign(Object.create(base.parentWays), other._parentWays);
26974         this._parentRels = _.assign(Object.create(base.parentRels), other._parentRels);
26975
26976     } else {
26977         this.entities = Object.create({});
26978         this._parentWays = Object.create({});
26979         this._parentRels = Object.create({});
26980         this.rebase(other || [], [this]);
26981     }
26982
26983     this.transients = {};
26984     this._childNodes = {};
26985     this.frozen = !mutable;
26986 };
26987
26988 iD.Graph.prototype = {
26989     hasEntity: function(id) {
26990         return this.entities[id];
26991     },
26992
26993     entity: function(id) {
26994         var entity = this.entities[id];
26995         if (!entity) {
26996             throw new Error('entity ' + id + ' not found');
26997         }
26998         return entity;
26999     },
27000
27001     transient: function(entity, key, fn) {
27002         var id = entity.id,
27003             transients = this.transients[id] ||
27004             (this.transients[id] = {});
27005
27006         if (transients[key] !== undefined) {
27007             return transients[key];
27008         }
27009
27010         transients[key] = fn.call(entity);
27011
27012         return transients[key];
27013     },
27014
27015     parentWays: function(entity) {
27016         var parents = this._parentWays[entity.id],
27017             result = [];
27018
27019         if (parents) {
27020             for (var i = 0; i < parents.length; i++) {
27021                 result.push(this.entity(parents[i]));
27022             }
27023         }
27024         return result;
27025     },
27026
27027     isPoi: function(entity) {
27028         var parentWays = this._parentWays[entity.id];
27029         return !parentWays || parentWays.length === 0;
27030     },
27031
27032     isShared: function(entity) {
27033         var parentWays = this._parentWays[entity.id];
27034         return parentWays && parentWays.length > 1;
27035     },
27036
27037     parentRelations: function(entity) {
27038         var parents = this._parentRels[entity.id],
27039             result = [];
27040
27041         if (parents) {
27042             for (var i = 0; i < parents.length; i++) {
27043                 result.push(this.entity(parents[i]));
27044             }
27045         }
27046         return result;
27047     },
27048
27049     childNodes: function(entity) {
27050         if (this._childNodes[entity.id]) return this._childNodes[entity.id];
27051         if (!entity.nodes) return [];
27052
27053         var nodes = [];
27054         for (var i = 0; i < entity.nodes.length; i++) {
27055             nodes[i] = this.entity(entity.nodes[i]);
27056         }
27057
27058         if (iD.debug) Object.freeze(nodes);
27059
27060         this._childNodes[entity.id] = nodes;
27061         return this._childNodes[entity.id];
27062     },
27063
27064     base: function() {
27065         return {
27066             'entities': iD.util.getPrototypeOf(this.entities),
27067             'parentWays': iD.util.getPrototypeOf(this._parentWays),
27068             'parentRels': iD.util.getPrototypeOf(this._parentRels)
27069         };
27070     },
27071
27072     // Unlike other graph methods, rebase mutates in place. This is because it
27073     // is used only during the history operation that merges newly downloaded
27074     // data into each state. To external consumers, it should appear as if the
27075     // graph always contained the newly downloaded data.
27076     rebase: function(entities, stack, force) {
27077         var base = this.base(),
27078             i, j, k, id;
27079
27080         for (i = 0; i < entities.length; i++) {
27081             var entity = entities[i];
27082
27083             if (!entity.visible || (!force && base.entities[entity.id]))
27084                 continue;
27085
27086             // Merging data into the base graph
27087             base.entities[entity.id] = entity;
27088             this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
27089
27090             // Restore provisionally-deleted nodes that are discovered to have an extant parent
27091             if (entity.type === 'way') {
27092                 for (j = 0; j < entity.nodes.length; j++) {
27093                     id = entity.nodes[j];
27094                     for (k = 1; k < stack.length; k++) {
27095                         var ents = stack[k].entities;
27096                         if (ents.hasOwnProperty(id) && ents[id] === undefined) {
27097                             delete ents[id];
27098                         }
27099                     }
27100                 }
27101             }
27102         }
27103
27104         for (i = 0; i < stack.length; i++) {
27105             stack[i]._updateRebased();
27106         }
27107     },
27108
27109     _updateRebased: function() {
27110         var base = this.base(),
27111             i, k, child, id, keys;
27112
27113         keys = Object.keys(this._parentWays);
27114         for (i = 0; i < keys.length; i++) {
27115             child = keys[i];
27116             if (base.parentWays[child]) {
27117                 for (k = 0; k < base.parentWays[child].length; k++) {
27118                     id = base.parentWays[child][k];
27119                     if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentWays[child], id)) {
27120                         this._parentWays[child].push(id);
27121                     }
27122                 }
27123             }
27124         }
27125
27126         keys = Object.keys(this._parentRels);
27127         for (i = 0; i < keys.length; i++) {
27128             child = keys[i];
27129             if (base.parentRels[child]) {
27130                 for (k = 0; k < base.parentRels[child].length; k++) {
27131                     id = base.parentRels[child][k];
27132                     if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentRels[child], id)) {
27133                         this._parentRels[child].push(id);
27134                     }
27135                 }
27136             }
27137         }
27138
27139         this.transients = {};
27140
27141         // this._childNodes is not updated, under the assumption that
27142         // ways are always downloaded with their child nodes.
27143     },
27144
27145     // Updates calculated properties (parentWays, parentRels) for the specified change
27146     _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
27147
27148         parentWays = parentWays || this._parentWays;
27149         parentRels = parentRels || this._parentRels;
27150
27151         var type = entity && entity.type || oldentity && oldentity.type,
27152             removed, added, ways, rels, i;
27153
27154
27155         if (type === 'way') {
27156
27157             // Update parentWays
27158             if (oldentity && entity) {
27159                 removed = _.difference(oldentity.nodes, entity.nodes);
27160                 added = _.difference(entity.nodes, oldentity.nodes);
27161             } else if (oldentity) {
27162                 removed = oldentity.nodes;
27163                 added = [];
27164             } else if (entity) {
27165                 removed = [];
27166                 added = entity.nodes;
27167             }
27168             for (i = 0; i < removed.length; i++) {
27169                 parentWays[removed[i]] = _.without(parentWays[removed[i]], oldentity.id);
27170             }
27171             for (i = 0; i < added.length; i++) {
27172                 ways = _.without(parentWays[added[i]], entity.id);
27173                 ways.push(entity.id);
27174                 parentWays[added[i]] = ways;
27175             }
27176
27177         } else if (type === 'relation') {
27178
27179             // Update parentRels
27180             if (oldentity && entity) {
27181                 removed = _.difference(oldentity.members, entity.members);
27182                 added = _.difference(entity.members, oldentity);
27183             } else if (oldentity) {
27184                 removed = oldentity.members;
27185                 added = [];
27186             } else if (entity) {
27187                 removed = [];
27188                 added = entity.members;
27189             }
27190             for (i = 0; i < removed.length; i++) {
27191                 parentRels[removed[i].id] = _.without(parentRels[removed[i].id], oldentity.id);
27192             }
27193             for (i = 0; i < added.length; i++) {
27194                 rels = _.without(parentRels[added[i].id], entity.id);
27195                 rels.push(entity.id);
27196                 parentRels[added[i].id] = rels;
27197             }
27198         }
27199     },
27200
27201     replace: function(entity) {
27202         if (this.entities[entity.id] === entity)
27203             return this;
27204
27205         return this.update(function() {
27206             this._updateCalculated(this.entities[entity.id], entity);
27207             this.entities[entity.id] = entity;
27208         });
27209     },
27210
27211     remove: function(entity) {
27212         return this.update(function() {
27213             this._updateCalculated(entity, undefined);
27214             this.entities[entity.id] = undefined;
27215         });
27216     },
27217
27218     revert: function(id) {
27219         var baseEntity = this.base().entities[id],
27220             headEntity = this.entities[id];
27221
27222         if (headEntity === baseEntity)
27223             return this;
27224
27225         return this.update(function() {
27226             this._updateCalculated(headEntity, baseEntity);
27227             delete this.entities[id];
27228         });
27229     },
27230
27231     update: function() {
27232         var graph = this.frozen ? iD.Graph(this, true) : this;
27233
27234         for (var i = 0; i < arguments.length; i++) {
27235             arguments[i].call(graph, graph);
27236         }
27237
27238         if (this.frozen) graph.frozen = true;
27239
27240         return graph;
27241     },
27242
27243     // Obliterates any existing entities
27244     load: function(entities) {
27245         var base = this.base();
27246         this.entities = Object.create(base.entities);
27247
27248         for (var i in entities) {
27249             this.entities[i] = entities[i];
27250             this._updateCalculated(base.entities[i], this.entities[i]);
27251         }
27252
27253         return this;
27254     }
27255 };
27256 iD.History = function(context) {
27257     var stack, index, tree,
27258         imageryUsed = ['Bing'],
27259         dispatch = d3.dispatch('change', 'undone', 'redone'),
27260         lock = iD.util.SessionMutex('lock');
27261
27262     function perform(actions) {
27263         actions = Array.prototype.slice.call(actions);
27264
27265         var annotation;
27266
27267         if (!_.isFunction(_.last(actions))) {
27268             annotation = actions.pop();
27269         }
27270
27271         var graph = stack[index].graph;
27272         for (var i = 0; i < actions.length; i++) {
27273             graph = actions[i](graph);
27274         }
27275
27276         return {
27277             graph: graph,
27278             annotation: annotation,
27279             imageryUsed: imageryUsed
27280         };
27281     }
27282
27283     function change(previous) {
27284         var difference = iD.Difference(previous, history.graph());
27285         dispatch.change(difference);
27286         return difference;
27287     }
27288
27289     // iD uses namespaced keys so multiple installations do not conflict
27290     function getKey(n) {
27291         return 'iD_' + window.location.origin + '_' + n;
27292     }
27293
27294     var history = {
27295         graph: function() {
27296             return stack[index].graph;
27297         },
27298
27299         base: function() {
27300             return stack[0].graph;
27301         },
27302
27303         merge: function(entities, extent) {
27304             stack[0].graph.rebase(entities, _.pluck(stack, 'graph'), false);
27305             tree.rebase(entities, false);
27306
27307             dispatch.change(undefined, extent);
27308         },
27309
27310         perform: function() {
27311             var previous = stack[index].graph;
27312
27313             stack = stack.slice(0, index + 1);
27314             stack.push(perform(arguments));
27315             index++;
27316
27317             return change(previous);
27318         },
27319
27320         replace: function() {
27321             var previous = stack[index].graph;
27322
27323             // assert(index == stack.length - 1)
27324             stack[index] = perform(arguments);
27325
27326             return change(previous);
27327         },
27328
27329         pop: function() {
27330             var previous = stack[index].graph;
27331
27332             if (index > 0) {
27333                 index--;
27334                 stack.pop();
27335                 return change(previous);
27336             }
27337         },
27338
27339         // Same as calling pop and then perform
27340         overwrite: function() {
27341             var previous = stack[index].graph;
27342
27343             if (index > 0) {
27344                 index--;
27345                 stack.pop();
27346             }
27347             stack = stack.slice(0, index + 1);
27348             stack.push(perform(arguments));
27349             index++;
27350
27351             return change(previous);
27352         },
27353
27354         undo: function() {
27355             var previous = stack[index].graph;
27356
27357             // Pop to the next annotated state.
27358             while (index > 0) {
27359                 index--;
27360                 if (stack[index].annotation) break;
27361             }
27362
27363             dispatch.undone();
27364             return change(previous);
27365         },
27366
27367         redo: function() {
27368             var previous = stack[index].graph;
27369
27370             while (index < stack.length - 1) {
27371                 index++;
27372                 if (stack[index].annotation) break;
27373             }
27374
27375             dispatch.redone();
27376             return change(previous);
27377         },
27378
27379         undoAnnotation: function() {
27380             var i = index;
27381             while (i >= 0) {
27382                 if (stack[i].annotation) return stack[i].annotation;
27383                 i--;
27384             }
27385         },
27386
27387         redoAnnotation: function() {
27388             var i = index + 1;
27389             while (i <= stack.length - 1) {
27390                 if (stack[i].annotation) return stack[i].annotation;
27391                 i++;
27392             }
27393         },
27394
27395         intersects: function(extent) {
27396             return tree.intersects(extent, stack[index].graph);
27397         },
27398
27399         difference: function() {
27400             var base = stack[0].graph,
27401                 head = stack[index].graph;
27402             return iD.Difference(base, head);
27403         },
27404
27405         changes: function(action) {
27406             var base = stack[0].graph,
27407                 head = stack[index].graph;
27408
27409             if (action) {
27410                 head = action(head);
27411             }
27412
27413             var difference = iD.Difference(base, head);
27414
27415             return {
27416                 modified: difference.modified(),
27417                 created: difference.created(),
27418                 deleted: difference.deleted()
27419             };
27420         },
27421
27422         validate: function(changes) {
27423             return _(iD.validations)
27424                 .map(function(fn) { return fn()(changes, stack[index].graph); })
27425                 .flatten()
27426                 .value();
27427         },
27428
27429         hasChanges: function() {
27430             return this.difference().length() > 0;
27431         },
27432
27433         imageryUsed: function(sources) {
27434             if (sources) {
27435                 imageryUsed = sources;
27436                 return history;
27437             } else {
27438                 return _(stack.slice(1, index + 1))
27439                     .pluck('imageryUsed')
27440                     .flatten()
27441                     .unique()
27442                     .without(undefined, 'Custom')
27443                     .value();
27444             }
27445         },
27446
27447         reset: function() {
27448             stack = [{graph: iD.Graph()}];
27449             index = 0;
27450             tree = iD.Tree(stack[0].graph);
27451             dispatch.change();
27452             return history;
27453         },
27454
27455         toJSON: function() {
27456             if (!this.hasChanges()) return;
27457
27458             var allEntities = {},
27459                 baseEntities = {},
27460                 base = stack[0];
27461
27462             var s = stack.map(function(i) {
27463                 var modified = [], deleted = [];
27464
27465                 _.forEach(i.graph.entities, function(entity, id) {
27466                     if (entity) {
27467                         var key = iD.Entity.key(entity);
27468                         allEntities[key] = entity;
27469                         modified.push(key);
27470                     } else {
27471                         deleted.push(id);
27472                     }
27473
27474                     // make sure that the originals of changed or deleted entities get merged
27475                     // into the base of the stack after restoring the data from JSON.
27476                     if (id in base.graph.entities) {
27477                         baseEntities[id] = base.graph.entities[id];
27478                     }
27479                     // get originals of parent entities too
27480                     _.forEach(base.graph._parentWays[id], function(parentId) {
27481                         if (parentId in base.graph.entities) {
27482                             baseEntities[parentId] = base.graph.entities[parentId];
27483                         }
27484                     });
27485                 });
27486
27487                 var x = {};
27488
27489                 if (modified.length) x.modified = modified;
27490                 if (deleted.length) x.deleted = deleted;
27491                 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
27492                 if (i.annotation) x.annotation = i.annotation;
27493
27494                 return x;
27495             });
27496
27497             return JSON.stringify({
27498                 version: 3,
27499                 entities: _.values(allEntities),
27500                 baseEntities: _.values(baseEntities),
27501                 stack: s,
27502                 nextIDs: iD.Entity.id.next,
27503                 index: index
27504             });
27505         },
27506
27507         fromJSON: function(json, loadChildNodes) {
27508             var h = JSON.parse(json),
27509                 loadComplete = true;
27510
27511             iD.Entity.id.next = h.nextIDs;
27512             index = h.index;
27513
27514             if (h.version === 2 || h.version === 3) {
27515                 var allEntities = {};
27516
27517                 h.entities.forEach(function(entity) {
27518                     allEntities[iD.Entity.key(entity)] = iD.Entity(entity);
27519                 });
27520
27521                 if (h.version === 3) {
27522                     // This merges originals for changed entities into the base of
27523                     // the stack even if the current stack doesn't have them (for
27524                     // example when iD has been restarted in a different region)
27525                     var baseEntities = h.baseEntities.map(function(d) { return iD.Entity(d); });
27526                     stack[0].graph.rebase(baseEntities, _.pluck(stack, 'graph'), true);
27527                     tree.rebase(baseEntities, true);
27528
27529                     // When we restore a modified way, we also need to fetch any missing
27530                     // childnodes that would normally have been downloaded with it.. #2142
27531                     if (loadChildNodes) {
27532                         var missing =  _(baseEntities)
27533                                 .filter('type', 'way')
27534                                 .pluck('nodes')
27535                                 .flatten()
27536                                 .uniq()
27537                                 .reject(function(n) { return stack[0].graph.hasEntity(n); })
27538                                 .value();
27539
27540                         if (!_.isEmpty(missing)) {
27541                             loadComplete = false;
27542                             context.redrawEnable(false);
27543
27544                             var loading = iD.ui.Loading(context).blocking(true);
27545                             context.container().call(loading);
27546
27547                             var childNodesLoaded = function(err, result) {
27548                                 if (!err) {
27549                                     var visible = _.groupBy(result.data, 'visible');
27550                                     if (!_.isEmpty(visible.true)) {
27551                                         missing = _.difference(missing, _.pluck(visible.true, 'id'));
27552                                         stack[0].graph.rebase(visible.true, _.pluck(stack, 'graph'), true);
27553                                         tree.rebase(visible.true, true);
27554                                     }
27555
27556                                     // fetch older versions of nodes that were deleted..
27557                                     _.each(visible.false, function(entity) {
27558                                         context.connection()
27559                                             .loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
27560                                     });
27561                                 }
27562
27563                                 if (err || _.isEmpty(missing)) {
27564                                     loading.close();
27565                                     context.redrawEnable(true);
27566                                     dispatch.change();
27567                                 }
27568                             };
27569
27570                             context.connection().loadMultiple(missing, childNodesLoaded);
27571                         }
27572                     }
27573                 }
27574
27575                 stack = h.stack.map(function(d) {
27576                     var entities = {}, entity;
27577
27578                     if (d.modified) {
27579                         d.modified.forEach(function(key) {
27580                             entity = allEntities[key];
27581                             entities[entity.id] = entity;
27582                         });
27583                     }
27584
27585                     if (d.deleted) {
27586                         d.deleted.forEach(function(id) {
27587                             entities[id] = undefined;
27588                         });
27589                     }
27590
27591                     return {
27592                         graph: iD.Graph(stack[0].graph).load(entities),
27593                         annotation: d.annotation,
27594                         imageryUsed: d.imageryUsed
27595                     };
27596                 });
27597
27598             } else { // original version
27599                 stack = h.stack.map(function(d) {
27600                     var entities = {};
27601
27602                     for (var i in d.entities) {
27603                         var entity = d.entities[i];
27604                         entities[i] = entity === 'undefined' ? undefined : iD.Entity(entity);
27605                     }
27606
27607                     d.graph = iD.Graph(stack[0].graph).load(entities);
27608                     return d;
27609                 });
27610             }
27611
27612             if (loadComplete) {
27613                 dispatch.change();
27614             }
27615
27616             return history;
27617         },
27618
27619         save: function() {
27620             if (lock.locked()) context.storage(getKey('saved_history'), history.toJSON() || null);
27621             return history;
27622         },
27623
27624         clearSaved: function() {
27625             context.debouncedSave.cancel();
27626             if (lock.locked()) context.storage(getKey('saved_history'), null);
27627             return history;
27628         },
27629
27630         lock: function() {
27631             return lock.lock();
27632         },
27633
27634         unlock: function() {
27635             lock.unlock();
27636         },
27637
27638         // is iD not open in another window and it detects that
27639         // there's a history stored in localStorage that's recoverable?
27640         restorableChanges: function() {
27641             return lock.locked() && !!context.storage(getKey('saved_history'));
27642         },
27643
27644         // load history from a version stored in localStorage
27645         restore: function() {
27646             if (!lock.locked()) return;
27647
27648             var json = context.storage(getKey('saved_history'));
27649             if (json) history.fromJSON(json, true);
27650         },
27651
27652         _getKey: getKey
27653
27654     };
27655
27656     history.reset();
27657
27658     return d3.rebind(history, dispatch, 'on');
27659 };
27660 iD.Node = iD.Entity.node = function iD_Node() {
27661     if (!(this instanceof iD_Node)) {
27662         return (new iD_Node()).initialize(arguments);
27663     } else if (arguments.length) {
27664         this.initialize(arguments);
27665     }
27666 };
27667
27668 iD.Node.prototype = Object.create(iD.Entity.prototype);
27669
27670 _.extend(iD.Node.prototype, {
27671     type: 'node',
27672
27673     extent: function() {
27674         return new iD.geo.Extent(this.loc);
27675     },
27676
27677     geometry: function(graph) {
27678         return graph.transient(this, 'geometry', function() {
27679             return graph.isPoi(this) ? 'point' : 'vertex';
27680         });
27681     },
27682
27683     move: function(loc) {
27684         return this.update({loc: loc});
27685     },
27686
27687     isIntersection: function(resolver) {
27688         return resolver.transient(this, 'isIntersection', function() {
27689             return resolver.parentWays(this).filter(function(parent) {
27690                 return (parent.tags.highway ||
27691                     parent.tags.waterway ||
27692                     parent.tags.railway ||
27693                     parent.tags.aeroway) &&
27694                     parent.geometry(resolver) === 'line';
27695             }).length > 1;
27696         });
27697     },
27698
27699     isHighwayIntersection: function(resolver) {
27700         return resolver.transient(this, 'isHighwayIntersection', function() {
27701             return resolver.parentWays(this).filter(function(parent) {
27702                 return parent.tags.highway && parent.geometry(resolver) === 'line';
27703             }).length > 1;
27704         });
27705     },
27706
27707     asJXON: function(changeset_id) {
27708         var r = {
27709             node: {
27710                 '@id': this.osmId(),
27711                 '@lon': this.loc[0],
27712                 '@lat': this.loc[1],
27713                 '@version': (this.version || 0),
27714                 tag: _.map(this.tags, function(v, k) {
27715                     return { keyAttributes: { k: k, v: v } };
27716                 })
27717             }
27718         };
27719         if (changeset_id) r.node['@changeset'] = changeset_id;
27720         return r;
27721     },
27722
27723     asGeoJSON: function() {
27724         return {
27725             type: 'Point',
27726             coordinates: this.loc
27727         };
27728     }
27729 });
27730 iD.Relation = iD.Entity.relation = function iD_Relation() {
27731     if (!(this instanceof iD_Relation)) {
27732         return (new iD_Relation()).initialize(arguments);
27733     } else if (arguments.length) {
27734         this.initialize(arguments);
27735     }
27736 };
27737
27738 iD.Relation.prototype = Object.create(iD.Entity.prototype);
27739
27740 iD.Relation.creationOrder = function(a, b) {
27741     var aId = parseInt(iD.Entity.id.toOSM(a.id), 10);
27742     var bId = parseInt(iD.Entity.id.toOSM(b.id), 10);
27743
27744     if (aId < 0 || bId < 0) return aId - bId;
27745     return bId - aId;
27746 };
27747
27748 _.extend(iD.Relation.prototype, {
27749     type: 'relation',
27750     members: [],
27751
27752     copy: function(resolver, copies) {
27753         if (copies[this.id])
27754             return copies[this.id];
27755
27756         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
27757
27758         var members = this.members.map(function(member) {
27759             return _.extend({}, member, {id: resolver.entity(member.id).copy(resolver, copies).id});
27760         });
27761
27762         copy = copy.update({members: members});
27763         copies[this.id] = copy;
27764
27765         return copy;
27766     },
27767
27768     extent: function(resolver, memo) {
27769         return resolver.transient(this, 'extent', function() {
27770             if (memo && memo[this.id]) return iD.geo.Extent();
27771             memo = memo || {};
27772             memo[this.id] = true;
27773
27774             var extent = iD.geo.Extent();
27775             for (var i = 0; i < this.members.length; i++) {
27776                 var member = resolver.hasEntity(this.members[i].id);
27777                 if (member) {
27778                     extent._extend(member.extent(resolver, memo));
27779                 }
27780             }
27781             return extent;
27782         });
27783     },
27784
27785     geometry: function(graph) {
27786         return graph.transient(this, 'geometry', function() {
27787             return this.isMultipolygon() ? 'area' : 'relation';
27788         });
27789     },
27790
27791     isDegenerate: function() {
27792         return this.members.length === 0;
27793     },
27794
27795     // Return an array of members, each extended with an 'index' property whose value
27796     // is the member index.
27797     indexedMembers: function() {
27798         var result = new Array(this.members.length);
27799         for (var i = 0; i < this.members.length; i++) {
27800             result[i] = _.extend({}, this.members[i], {index: i});
27801         }
27802         return result;
27803     },
27804
27805     // Return the first member with the given role. A copy of the member object
27806     // is returned, extended with an 'index' property whose value is the member index.
27807     memberByRole: function(role) {
27808         for (var i = 0; i < this.members.length; i++) {
27809             if (this.members[i].role === role) {
27810                 return _.extend({}, this.members[i], {index: i});
27811             }
27812         }
27813     },
27814
27815     // Return the first member with the given id. A copy of the member object
27816     // is returned, extended with an 'index' property whose value is the member index.
27817     memberById: function(id) {
27818         for (var i = 0; i < this.members.length; i++) {
27819             if (this.members[i].id === id) {
27820                 return _.extend({}, this.members[i], {index: i});
27821             }
27822         }
27823     },
27824
27825     // Return the first member with the given id and role. A copy of the member object
27826     // is returned, extended with an 'index' property whose value is the member index.
27827     memberByIdAndRole: function(id, role) {
27828         for (var i = 0; i < this.members.length; i++) {
27829             if (this.members[i].id === id && this.members[i].role === role) {
27830                 return _.extend({}, this.members[i], {index: i});
27831             }
27832         }
27833     },
27834
27835     addMember: function(member, index) {
27836         var members = this.members.slice();
27837         members.splice(index === undefined ? members.length : index, 0, member);
27838         return this.update({members: members});
27839     },
27840
27841     updateMember: function(member, index) {
27842         var members = this.members.slice();
27843         members.splice(index, 1, _.extend({}, members[index], member));
27844         return this.update({members: members});
27845     },
27846
27847     removeMember: function(index) {
27848         var members = this.members.slice();
27849         members.splice(index, 1);
27850         return this.update({members: members});
27851     },
27852
27853     removeMembersWithID: function(id) {
27854         var members = _.reject(this.members, function(m) { return m.id === id; });
27855         return this.update({members: members});
27856     },
27857
27858     // Wherever a member appears with id `needle.id`, replace it with a member
27859     // with id `replacement.id`, type `replacement.type`, and the original role,
27860     // unless a member already exists with that id and role. Return an updated
27861     // relation.
27862     replaceMember: function(needle, replacement) {
27863         if (!this.memberById(needle.id))
27864             return this;
27865
27866         var members = [];
27867
27868         for (var i = 0; i < this.members.length; i++) {
27869             var member = this.members[i];
27870             if (member.id !== needle.id) {
27871                 members.push(member);
27872             } else if (!this.memberByIdAndRole(replacement.id, member.role)) {
27873                 members.push({id: replacement.id, type: replacement.type, role: member.role});
27874             }
27875         }
27876
27877         return this.update({members: members});
27878     },
27879
27880     asJXON: function(changeset_id) {
27881         var r = {
27882             relation: {
27883                 '@id': this.osmId(),
27884                 '@version': this.version || 0,
27885                 member: _.map(this.members, function(member) {
27886                     return { keyAttributes: { type: member.type, role: member.role, ref: iD.Entity.id.toOSM(member.id) } };
27887                 }),
27888                 tag: _.map(this.tags, function(v, k) {
27889                     return { keyAttributes: { k: k, v: v } };
27890                 })
27891             }
27892         };
27893         if (changeset_id) r.relation['@changeset'] = changeset_id;
27894         return r;
27895     },
27896
27897     asGeoJSON: function(resolver) {
27898         return resolver.transient(this, 'GeoJSON', function () {
27899             if (this.isMultipolygon()) {
27900                 return {
27901                     type: 'MultiPolygon',
27902                     coordinates: this.multipolygon(resolver)
27903                 };
27904             } else {
27905                 return {
27906                     type: 'FeatureCollection',
27907                     properties: this.tags,
27908                     features: this.members.map(function (member) {
27909                         return _.extend({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
27910                     })
27911                 };
27912             }
27913         });
27914     },
27915
27916     area: function(resolver) {
27917         return resolver.transient(this, 'area', function() {
27918             return d3.geo.area(this.asGeoJSON(resolver));
27919         });
27920     },
27921
27922     isMultipolygon: function() {
27923         return this.tags.type === 'multipolygon';
27924     },
27925
27926     isComplete: function(resolver) {
27927         for (var i = 0; i < this.members.length; i++) {
27928             if (!resolver.hasEntity(this.members[i].id)) {
27929                 return false;
27930             }
27931         }
27932         return true;
27933     },
27934
27935     isRestriction: function() {
27936         return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
27937     },
27938
27939     // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
27940     // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
27941     //
27942     // This corresponds to the structure needed for rendering a multipolygon path using a
27943     // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
27944     //
27945     // In the case of invalid geometries, this function will still return a result which
27946     // includes the nodes of all way members, but some Nds may be unclosed and some inner
27947     // rings not matched with the intended outer ring.
27948     //
27949     multipolygon: function(resolver) {
27950         var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
27951             inners = this.members.filter(function(m) { return 'inner' === m.role; });
27952
27953         outers = iD.geo.joinWays(outers, resolver);
27954         inners = iD.geo.joinWays(inners, resolver);
27955
27956         outers = outers.map(function(outer) { return _.pluck(outer.nodes, 'loc'); });
27957         inners = inners.map(function(inner) { return _.pluck(inner.nodes, 'loc'); });
27958
27959         var result = outers.map(function(o) {
27960             // Heuristic for detecting counterclockwise winding order. Assumes
27961             // that OpenStreetMap polygons are not hemisphere-spanning.
27962             return [d3.geo.area({type: 'Polygon', coordinates: [o]}) > 2 * Math.PI ? o.reverse() : o];
27963         });
27964
27965         function findOuter(inner) {
27966             var o, outer;
27967
27968             for (o = 0; o < outers.length; o++) {
27969                 outer = outers[o];
27970                 if (iD.geo.polygonContainsPolygon(outer, inner))
27971                     return o;
27972             }
27973
27974             for (o = 0; o < outers.length; o++) {
27975                 outer = outers[o];
27976                 if (iD.geo.polygonIntersectsPolygon(outer, inner))
27977                     return o;
27978             }
27979         }
27980
27981         for (var i = 0; i < inners.length; i++) {
27982             var inner = inners[i];
27983
27984             if (d3.geo.area({type: 'Polygon', coordinates: [inner]}) < 2 * Math.PI) {
27985                 inner = inner.reverse();
27986             }
27987
27988             var o = findOuter(inners[i]);
27989             if (o !== undefined)
27990                 result[o].push(inners[i]);
27991             else
27992                 result.push([inners[i]]); // Invalid geometry
27993         }
27994
27995         return result;
27996     }
27997 });
27998 iD.oneWayTags = {
27999     'aerialway': {
28000         'chair_lift': true,
28001         'mixed_lift': true,
28002         't-bar': true,
28003         'j-bar': true,
28004         'platter': true,
28005         'rope_tow': true,
28006         'magic_carpet': true,
28007         'yes': true
28008     },
28009     'highway': {
28010         'motorway': true,
28011         'motorway_link': true
28012     },
28013     'junction': {
28014         'roundabout': true
28015     },
28016     'man_made': {
28017         'piste:halfpipe': true
28018     },
28019     'piste:type': {
28020         'downhill': true,
28021         'sled': true,
28022         'yes': true
28023     },
28024     'waterway': {
28025         'river': true,
28026         'stream': true
28027     }
28028 };
28029
28030 iD.pavedTags = {
28031     'surface': {
28032         'paved': true,
28033         'asphalt': true,
28034         'concrete': true
28035     },
28036     'tracktype': {
28037         'grade1': true
28038     }
28039 };
28040
28041 iD.interestingTag = function (key) {
28042     return key !== 'attribution' &&
28043         key !== 'created_by' &&
28044         key !== 'source' &&
28045         key !== 'odbl' &&
28046         key.indexOf('tiger:') !== 0;
28047
28048 };
28049 iD.Tree = function(head) {
28050     var rtree = rbush(),
28051         rectangles = {};
28052
28053     function entityRectangle(entity) {
28054         var rect = entity.extent(head).rectangle();
28055         rect.id = entity.id;
28056         rectangles[entity.id] = rect;
28057         return rect;
28058     }
28059
28060     function updateParents(entity, insertions, memo) {
28061         head.parentWays(entity).forEach(function(way) {
28062             if (rectangles[way.id]) {
28063                 rtree.remove(rectangles[way.id]);
28064                 insertions[way.id] = way;
28065             }
28066             updateParents(way, insertions, memo);
28067         });
28068
28069         head.parentRelations(entity).forEach(function(relation) {
28070             if (memo[entity.id]) return;
28071             memo[entity.id] = true;
28072             if (rectangles[relation.id]) {
28073                 rtree.remove(rectangles[relation.id]);
28074                 insertions[relation.id] = relation;
28075             }
28076             updateParents(relation, insertions, memo);
28077         });
28078     }
28079
28080     var tree = {};
28081
28082     tree.rebase = function(entities, force) {
28083         var insertions = {};
28084
28085         for (var i = 0; i < entities.length; i++) {
28086             var entity = entities[i];
28087
28088             if (!entity.visible)
28089                 continue;
28090
28091             if (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id]) {
28092                 if (!force) {
28093                     continue;
28094                 } else if (rectangles[entity.id]) {
28095                     rtree.remove(rectangles[entity.id]);
28096                 }
28097             }
28098
28099             insertions[entity.id] = entity;
28100             updateParents(entity, insertions, {});
28101         }
28102
28103         rtree.load(_.map(insertions, entityRectangle));
28104
28105         return tree;
28106     };
28107
28108     tree.intersects = function(extent, graph) {
28109         if (graph !== head) {
28110             var diff = iD.Difference(head, graph),
28111                 insertions = {};
28112
28113             head = graph;
28114
28115             diff.deleted().forEach(function(entity) {
28116                 rtree.remove(rectangles[entity.id]);
28117                 delete rectangles[entity.id];
28118             });
28119
28120             diff.modified().forEach(function(entity) {
28121                 rtree.remove(rectangles[entity.id]);
28122                 insertions[entity.id] = entity;
28123                 updateParents(entity, insertions, {});
28124             });
28125
28126             diff.created().forEach(function(entity) {
28127                 insertions[entity.id] = entity;
28128             });
28129
28130             rtree.load(_.map(insertions, entityRectangle));
28131         }
28132
28133         return rtree.search(extent.rectangle()).map(function(rect) {
28134             return head.entity(rect.id);
28135         });
28136     };
28137
28138     return tree;
28139 };
28140 iD.Way = iD.Entity.way = function iD_Way() {
28141     if (!(this instanceof iD_Way)) {
28142         return (new iD_Way()).initialize(arguments);
28143     } else if (arguments.length) {
28144         this.initialize(arguments);
28145     }
28146 };
28147
28148 iD.Way.prototype = Object.create(iD.Entity.prototype);
28149
28150 _.extend(iD.Way.prototype, {
28151     type: 'way',
28152     nodes: [],
28153
28154     copy: function(resolver, copies) {
28155         if (copies[this.id])
28156             return copies[this.id];
28157
28158         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
28159
28160         var nodes = this.nodes.map(function(id) {
28161             return resolver.entity(id).copy(resolver, copies).id;
28162         });
28163
28164         copy = copy.update({nodes: nodes});
28165         copies[this.id] = copy;
28166
28167         return copy;
28168     },
28169
28170     extent: function(resolver) {
28171         return resolver.transient(this, 'extent', function() {
28172             var extent = iD.geo.Extent();
28173             for (var i = 0; i < this.nodes.length; i++) {
28174                 var node = resolver.hasEntity(this.nodes[i]);
28175                 if (node) {
28176                     extent._extend(node.extent());
28177                 }
28178             }
28179             return extent;
28180         });
28181     },
28182
28183     first: function() {
28184         return this.nodes[0];
28185     },
28186
28187     last: function() {
28188         return this.nodes[this.nodes.length - 1];
28189     },
28190
28191     contains: function(node) {
28192         return this.nodes.indexOf(node) >= 0;
28193     },
28194
28195     affix: function(node) {
28196         if (this.nodes[0] === node) return 'prefix';
28197         if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
28198     },
28199
28200     layer: function() {
28201         // explicit layer tag, clamp between -10, 10..
28202         if (this.tags.layer !== undefined) {
28203             return Math.max(-10, Math.min(+(this.tags.layer), 10));
28204         }
28205
28206         // implied layer tag..
28207         if (this.tags.location === 'overground') return 1;
28208         if (this.tags.location === 'underground') return -1;
28209         if (this.tags.location === 'underwater') return -10;
28210
28211         if (this.tags.power === 'line') return 10;
28212         if (this.tags.power === 'minor_line') return 10;
28213         if (this.tags.aerialway) return 10;
28214         if (this.tags.bridge) return 1;
28215         if (this.tags.cutting) return -1;
28216         if (this.tags.tunnel) return -1;
28217         if (this.tags.waterway) return -1;
28218         if (this.tags.man_made === 'pipeline') return -10;
28219         if (this.tags.boundary) return -10;
28220         return 0;
28221     },
28222
28223     isOneWay: function() {
28224         // explicit oneway tag..
28225         if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; }
28226         if (['no', '0'].indexOf(this.tags.oneway) !== -1) { return false; }
28227
28228         // implied oneway tag..
28229         for (var key in this.tags) {
28230             if (key in iD.oneWayTags && (this.tags[key] in iD.oneWayTags[key]))
28231                 return true;
28232         }
28233         return false;
28234     },
28235
28236     isClosed: function() {
28237         return this.nodes.length > 0 && this.first() === this.last();
28238     },
28239
28240     isConvex: function(resolver) {
28241         if (!this.isClosed() || this.isDegenerate()) return null;
28242
28243         var nodes = _.uniq(resolver.childNodes(this)),
28244             coords = _.pluck(nodes, 'loc'),
28245             curr = 0, prev = 0;
28246
28247         for (var i = 0; i < coords.length; i++) {
28248             var o = coords[(i+1) % coords.length],
28249                 a = coords[i],
28250                 b = coords[(i+2) % coords.length],
28251                 res = iD.geo.cross(o, a, b);
28252
28253             curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
28254             if (curr === 0) {
28255                 continue;
28256             } else if (prev && curr !== prev) {
28257                 return false;
28258             }
28259             prev = curr;
28260         }
28261         return true;
28262     },
28263
28264     isArea: function() {
28265         if (this.tags.area === 'yes')
28266             return true;
28267         if (!this.isClosed() || this.tags.area === 'no')
28268             return false;
28269         for (var key in this.tags)
28270             if (key in iD.areaKeys && !(this.tags[key] in iD.areaKeys[key]))
28271                 return true;
28272         return false;
28273     },
28274
28275     isDegenerate: function() {
28276         return _.uniq(this.nodes).length < (this.isArea() ? 3 : 2);
28277     },
28278
28279     areAdjacent: function(n1, n2) {
28280         for (var i = 0; i < this.nodes.length; i++) {
28281             if (this.nodes[i] === n1) {
28282                 if (this.nodes[i - 1] === n2) return true;
28283                 if (this.nodes[i + 1] === n2) return true;
28284             }
28285         }
28286         return false;
28287     },
28288
28289     geometry: function(graph) {
28290         return graph.transient(this, 'geometry', function() {
28291             return this.isArea() ? 'area' : 'line';
28292         });
28293     },
28294
28295     addNode: function(id, index) {
28296         var nodes = this.nodes.slice();
28297         nodes.splice(index === undefined ? nodes.length : index, 0, id);
28298         return this.update({nodes: nodes});
28299     },
28300
28301     updateNode: function(id, index) {
28302         var nodes = this.nodes.slice();
28303         nodes.splice(index, 1, id);
28304         return this.update({nodes: nodes});
28305     },
28306
28307     replaceNode: function(needle, replacement) {
28308         if (this.nodes.indexOf(needle) < 0)
28309             return this;
28310
28311         var nodes = this.nodes.slice();
28312         for (var i = 0; i < nodes.length; i++) {
28313             if (nodes[i] === needle) {
28314                 nodes[i] = replacement;
28315             }
28316         }
28317         return this.update({nodes: nodes});
28318     },
28319
28320     removeNode: function(id) {
28321         var nodes = [];
28322
28323         for (var i = 0; i < this.nodes.length; i++) {
28324             var node = this.nodes[i];
28325             if (node !== id && nodes[nodes.length - 1] !== node) {
28326                 nodes.push(node);
28327             }
28328         }
28329
28330         // Preserve circularity
28331         if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] !== nodes[0]) {
28332             nodes.push(nodes[0]);
28333         }
28334
28335         return this.update({nodes: nodes});
28336     },
28337
28338     asJXON: function(changeset_id) {
28339         var r = {
28340             way: {
28341                 '@id': this.osmId(),
28342                 '@version': this.version || 0,
28343                 nd: _.map(this.nodes, function(id) {
28344                     return { keyAttributes: { ref: iD.Entity.id.toOSM(id) } };
28345                 }),
28346                 tag: _.map(this.tags, function(v, k) {
28347                     return { keyAttributes: { k: k, v: v } };
28348                 })
28349             }
28350         };
28351         if (changeset_id) r.way['@changeset'] = changeset_id;
28352         return r;
28353     },
28354
28355     asGeoJSON: function(resolver) {
28356         return resolver.transient(this, 'GeoJSON', function() {
28357             var coordinates = _.pluck(resolver.childNodes(this), 'loc');
28358             if (this.isArea() && this.isClosed()) {
28359                 return {
28360                     type: 'Polygon',
28361                     coordinates: [coordinates]
28362                 };
28363             } else {
28364                 return {
28365                     type: 'LineString',
28366                     coordinates: coordinates
28367                 };
28368             }
28369         });
28370     },
28371
28372     area: function(resolver) {
28373         return resolver.transient(this, 'area', function() {
28374             var nodes = resolver.childNodes(this);
28375
28376             var json = {
28377                 type: 'Polygon',
28378                 coordinates: [_.pluck(nodes, 'loc')]
28379             };
28380
28381             if (!this.isClosed() && nodes.length) {
28382                 json.coordinates[0].push(nodes[0].loc);
28383             }
28384
28385             var area = d3.geo.area(json);
28386
28387             // Heuristic for detecting counterclockwise winding order. Assumes
28388             // that OpenStreetMap polygons are not hemisphere-spanning.
28389             if (area > 2 * Math.PI) {
28390                 json.coordinates[0] = json.coordinates[0].reverse();
28391                 area = d3.geo.area(json);
28392             }
28393
28394             return isNaN(area) ? 0 : area;
28395         });
28396     }
28397 });
28398 iD.Background = function(context) {
28399     var dispatch = d3.dispatch('change'),
28400         baseLayer = iD.TileLayer().projection(context.projection),
28401         overlayLayers = [];
28402
28403     var backgroundSources;
28404
28405     function findSource(id) {
28406         return _.find(backgroundSources, function(d) {
28407             return d.id && d.id === id;
28408         });
28409     }
28410
28411     function updateImagery() {
28412         var b = background.baseLayerSource(),
28413             o = overlayLayers.map(function (d) { return d.source().id; }).join(','),
28414             q = iD.util.stringQs(location.hash.substring(1));
28415
28416         var id = b.id;
28417         if (id === 'custom') {
28418             id = 'custom:' + b.template;
28419         }
28420
28421         if (id) {
28422             q.background = id;
28423         } else {
28424             delete q.background;
28425         }
28426
28427         if (o) {
28428             q.overlays = o;
28429         } else {
28430             delete q.overlays;
28431         }
28432
28433         location.replace('#' + iD.util.qsString(q, true));
28434
28435         var imageryUsed = [b.imageryUsed()];
28436
28437         overlayLayers.forEach(function (d) {
28438             var source = d.source();
28439             if (!source.isLocatorOverlay()) {
28440                 imageryUsed.push(source.imageryUsed());
28441             }
28442         });
28443
28444         var gpx = context.layers().layer('gpx');
28445         if (gpx && gpx.enabled() && gpx.hasGpx()) {
28446             imageryUsed.push('Local GPX');
28447         }
28448
28449         context.history().imageryUsed(imageryUsed);
28450     }
28451
28452     function background(selection) {
28453         var base = selection.selectAll('.layer-background')
28454             .data([0]);
28455
28456         base.enter()
28457             .insert('div', '.layer-data')
28458             .attr('class', 'layer layer-background');
28459
28460         base.call(baseLayer);
28461
28462         var overlays = selection.selectAll('.layer-overlay')
28463             .data(overlayLayers, function(d) { return d.source().name(); });
28464
28465         overlays.enter()
28466             .insert('div', '.layer-data')
28467             .attr('class', 'layer layer-overlay');
28468
28469         overlays.each(function(layer) {
28470             d3.select(this).call(layer);
28471         });
28472
28473         overlays.exit()
28474             .remove();
28475     }
28476
28477     background.sources = function(extent) {
28478         return backgroundSources.filter(function(source) {
28479             return source.intersects(extent);
28480         });
28481     };
28482
28483     background.dimensions = function(_) {
28484         baseLayer.dimensions(_);
28485
28486         overlayLayers.forEach(function(layer) {
28487             layer.dimensions(_);
28488         });
28489     };
28490
28491     background.baseLayerSource = function(d) {
28492         if (!arguments.length) return baseLayer.source();
28493
28494         baseLayer.source(d);
28495         dispatch.change();
28496         updateImagery();
28497
28498         return background;
28499     };
28500
28501     background.bing = function() {
28502         background.baseLayerSource(findSource('Bing'));
28503     };
28504
28505     background.showsLayer = function(d) {
28506         return d === baseLayer.source() ||
28507             (d.id === 'custom' && baseLayer.source().id === 'custom') ||
28508             overlayLayers.some(function(l) { return l.source() === d; });
28509     };
28510
28511     background.overlayLayerSources = function() {
28512         return overlayLayers.map(function (l) { return l.source(); });
28513     };
28514
28515     background.toggleOverlayLayer = function(d) {
28516         var layer;
28517
28518         for (var i = 0; i < overlayLayers.length; i++) {
28519             layer = overlayLayers[i];
28520             if (layer.source() === d) {
28521                 overlayLayers.splice(i, 1);
28522                 dispatch.change();
28523                 updateImagery();
28524                 return;
28525             }
28526         }
28527
28528         layer = iD.TileLayer()
28529             .source(d)
28530             .projection(context.projection)
28531             .dimensions(baseLayer.dimensions());
28532
28533         overlayLayers.push(layer);
28534         dispatch.change();
28535         updateImagery();
28536     };
28537
28538     background.nudge = function(d, zoom) {
28539         baseLayer.source().nudge(d, zoom);
28540         dispatch.change();
28541         return background;
28542     };
28543
28544     background.offset = function(d) {
28545         if (!arguments.length) return baseLayer.source().offset();
28546         baseLayer.source().offset(d);
28547         dispatch.change();
28548         return background;
28549     };
28550
28551     background.load = function(imagery) {
28552         function parseMap(qmap) {
28553             if (!qmap) return false;
28554             var args = qmap.split('/').map(Number);
28555             if (args.length < 3 || args.some(isNaN)) return false;
28556             return iD.geo.Extent([args[1], args[2]]);
28557         }
28558
28559         var q = iD.util.stringQs(location.hash.substring(1)),
28560             chosen = q.background || q.layer,
28561             extent = parseMap(q.map),
28562             best;
28563
28564         backgroundSources = imagery.map(function(source) {
28565             if (source.type === 'bing') {
28566                 return iD.BackgroundSource.Bing(source, dispatch);
28567             } else {
28568                 return iD.BackgroundSource(source);
28569             }
28570         });
28571
28572         backgroundSources.unshift(iD.BackgroundSource.None());
28573
28574         if (!chosen && extent) {
28575             best = _.find(this.sources(extent), function(s) { return s.best(); });
28576         }
28577
28578         if (chosen && chosen.indexOf('custom:') === 0) {
28579             background.baseLayerSource(iD.BackgroundSource.Custom(chosen.replace(/^custom:/, '')));
28580         } else {
28581             background.baseLayerSource(findSource(chosen) || best || findSource('Bing') || backgroundSources[1] || backgroundSources[0]);
28582         }
28583
28584         var locator = _.find(backgroundSources, function(d) {
28585             return d.overlay && d.default;
28586         });
28587
28588         if (locator) {
28589             background.toggleOverlayLayer(locator);
28590         }
28591
28592         var overlays = (q.overlays || '').split(',');
28593         overlays.forEach(function(overlay) {
28594             overlay = findSource(overlay);
28595             if (overlay) background.toggleOverlayLayer(overlay);
28596         });
28597
28598         if (q.gpx) {
28599             var gpx = context.layers().layer('gpx');
28600             if (gpx) { gpx.url(q.gpx); }
28601         }
28602     };
28603
28604     return d3.rebind(background, dispatch, 'on');
28605 };
28606 iD.BackgroundSource = function(data) {
28607     var source = _.clone(data),
28608         offset = [0, 0],
28609         name = source.name,
28610         best = !!source.best;
28611
28612     source.scaleExtent = data.scaleExtent || [0, 20];
28613     source.overzoom = data.overzoom !== false;
28614
28615     source.offset = function(_) {
28616         if (!arguments.length) return offset;
28617         offset = _;
28618         return source;
28619     };
28620
28621     source.nudge = function(_, zoomlevel) {
28622         offset[0] += _[0] / Math.pow(2, zoomlevel);
28623         offset[1] += _[1] / Math.pow(2, zoomlevel);
28624         return source;
28625     };
28626
28627     source.name = function() {
28628         return name;
28629     };
28630
28631     source.best = function() {
28632         return best;
28633     };
28634
28635     source.imageryUsed = function() {
28636         return source.id || name;
28637     };
28638
28639     source.url = function(coord) {
28640         return data.template
28641             .replace('{x}', coord[0])
28642             .replace('{y}', coord[1])
28643             // TMS-flipped y coordinate
28644             .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
28645             .replace(/\{z(oom)?\}/, coord[2])
28646             .replace(/\{switch:([^}]+)\}/, function(s, r) {
28647                 var subdomains = r.split(',');
28648                 return subdomains[(coord[0] + coord[1]) % subdomains.length];
28649             })
28650             .replace('{u}', function() {
28651                 var u = '';
28652                 for (var zoom = coord[2]; zoom > 0; zoom--) {
28653                     var b = 0;
28654                     var mask = 1 << (zoom - 1);
28655                     if ((coord[0] & mask) !== 0) b++;
28656                     if ((coord[1] & mask) !== 0) b += 2;
28657                     u += b.toString();
28658                 }
28659                 return u;
28660             });
28661     };
28662
28663     source.intersects = function(extent) {
28664         extent = extent.polygon();
28665         return !data.polygon || data.polygon.some(function(polygon) {
28666             return iD.geo.polygonIntersectsPolygon(polygon, extent, true);
28667         });
28668     };
28669
28670     source.validZoom = function(z) {
28671         return source.scaleExtent[0] <= z &&
28672             (source.overzoom || source.scaleExtent[1] > z);
28673     };
28674
28675     source.isLocatorOverlay = function() {
28676         return name === 'Locator Overlay';
28677     };
28678
28679     source.copyrightNotices = function() {};
28680
28681     return source;
28682 };
28683
28684 iD.BackgroundSource.Bing = function(data, dispatch) {
28685     // http://msdn.microsoft.com/en-us/library/ff701716.aspx
28686     // http://msdn.microsoft.com/en-us/library/ff701701.aspx
28687
28688     data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
28689
28690     var bing = iD.BackgroundSource(data),
28691         key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU', // Same as P2 and JOSM
28692         url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' +
28693             key + '&jsonp={callback}',
28694         providers = [];
28695
28696     d3.jsonp(url, function(json) {
28697         providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
28698             return {
28699                 attribution: provider.attribution,
28700                 areas: provider.coverageAreas.map(function(area) {
28701                     return {
28702                         zoom: [area.zoomMin, area.zoomMax],
28703                         extent: iD.geo.Extent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
28704                     };
28705                 })
28706             };
28707         });
28708         dispatch.change();
28709     });
28710
28711     bing.copyrightNotices = function(zoom, extent) {
28712         zoom = Math.min(zoom, 21);
28713         return providers.filter(function(provider) {
28714             return _.any(provider.areas, function(area) {
28715                 return extent.intersects(area.extent) &&
28716                     area.zoom[0] <= zoom &&
28717                     area.zoom[1] >= zoom;
28718             });
28719         }).map(function(provider) {
28720             return provider.attribution;
28721         }).join(', ');
28722     };
28723
28724     bing.logo = 'bing_maps.png';
28725     bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
28726
28727     return bing;
28728 };
28729
28730 iD.BackgroundSource.None = function() {
28731     var source = iD.BackgroundSource({id: 'none', template: ''});
28732
28733     source.name = function() {
28734         return t('background.none');
28735     };
28736
28737     source.imageryUsed = function() {
28738         return 'None';
28739     };
28740
28741     return source;
28742 };
28743
28744 iD.BackgroundSource.Custom = function(template) {
28745     var source = iD.BackgroundSource({id: 'custom', template: template});
28746
28747     source.name = function() {
28748         return t('background.custom');
28749     };
28750
28751     source.imageryUsed = function() {
28752         return 'Custom (' + template + ')';
28753     };
28754
28755     return source;
28756 };
28757 iD.Features = function(context) {
28758     var major_roads = {
28759         'motorway': true,
28760         'motorway_link': true,
28761         'trunk': true,
28762         'trunk_link': true,
28763         'primary': true,
28764         'primary_link': true,
28765         'secondary': true,
28766         'secondary_link': true,
28767         'tertiary': true,
28768         'tertiary_link': true,
28769         'residential': true
28770     };
28771
28772     var minor_roads = {
28773         'service': true,
28774         'living_street': true,
28775         'road': true,
28776         'unclassified': true,
28777         'track': true
28778     };
28779
28780     var paths = {
28781         'path': true,
28782         'footway': true,
28783         'cycleway': true,
28784         'bridleway': true,
28785         'steps': true,
28786         'pedestrian': true,
28787         'corridor': true
28788     };
28789
28790     var past_futures = {
28791         'proposed': true,
28792         'construction': true,
28793         'abandoned': true,
28794         'dismantled': true,
28795         'disused': true,
28796         'razed': true,
28797         'demolished': true,
28798         'obliterated': true
28799     };
28800
28801     var dispatch = d3.dispatch('change', 'redraw'),
28802         _cullFactor = 1,
28803         _cache = {},
28804         _features = {},
28805         _stats = {},
28806         _keys = [],
28807         _hidden = [];
28808
28809     function update() {
28810         _hidden = features.hidden();
28811         dispatch.change();
28812         dispatch.redraw();
28813     }
28814
28815     function defineFeature(k, filter, max) {
28816         _keys.push(k);
28817         _features[k] = {
28818             filter: filter,
28819             enabled: true,   // whether the user wants it enabled..
28820             count: 0,
28821             currentMax: (max || Infinity),
28822             defaultMax: (max || Infinity),
28823             enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
28824             disable: function() { this.enabled = false; this.currentMax = 0; },
28825             hidden: function() { return !context.editable() || this.count > this.currentMax * _cullFactor; },
28826             autoHidden: function() { return this.hidden() && this.currentMax > 0; }
28827         };
28828     }
28829
28830
28831     defineFeature('points', function isPoint(entity, resolver, geometry) {
28832         return geometry === 'point';
28833     }, 200);
28834
28835     defineFeature('major_roads', function isMajorRoad(entity) {
28836         return major_roads[entity.tags.highway];
28837     });
28838
28839     defineFeature('minor_roads', function isMinorRoad(entity) {
28840         return minor_roads[entity.tags.highway];
28841     });
28842
28843     defineFeature('paths', function isPath(entity) {
28844         return paths[entity.tags.highway];
28845     });
28846
28847     defineFeature('buildings', function isBuilding(entity) {
28848         return (
28849             !!entity.tags['building:part'] ||
28850             (!!entity.tags.building && entity.tags.building !== 'no') ||
28851             entity.tags.amenity === 'shelter' ||
28852             entity.tags.parking === 'multi-storey' ||
28853             entity.tags.parking === 'sheds' ||
28854             entity.tags.parking === 'carports' ||
28855             entity.tags.parking === 'garage_boxes'
28856         );
28857     }, 250);
28858
28859     defineFeature('landuse', function isLanduse(entity, resolver, geometry) {
28860         return geometry === 'area' &&
28861             !_features.buildings.filter(entity) &&
28862             !_features.water.filter(entity);
28863     });
28864
28865     defineFeature('boundaries', function isBoundary(entity) {
28866         return !!entity.tags.boundary;
28867     });
28868
28869     defineFeature('water', function isWater(entity) {
28870         return (
28871             !!entity.tags.waterway ||
28872             entity.tags.natural === 'water' ||
28873             entity.tags.natural === 'coastline' ||
28874             entity.tags.natural === 'bay' ||
28875             entity.tags.landuse === 'pond' ||
28876             entity.tags.landuse === 'basin' ||
28877             entity.tags.landuse === 'reservoir' ||
28878             entity.tags.landuse === 'salt_pond'
28879         );
28880     });
28881
28882     defineFeature('rail', function isRail(entity) {
28883         return (
28884             !!entity.tags.railway ||
28885             entity.tags.landuse === 'railway'
28886         ) && !(
28887             major_roads[entity.tags.highway] ||
28888             minor_roads[entity.tags.highway] ||
28889             paths[entity.tags.highway]
28890         );
28891     });
28892
28893     defineFeature('power', function isPower(entity) {
28894         return !!entity.tags.power;
28895     });
28896
28897     // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
28898     defineFeature('past_future', function isPastFuture(entity) {
28899         if (
28900             major_roads[entity.tags.highway] ||
28901             minor_roads[entity.tags.highway] ||
28902             paths[entity.tags.highway]
28903         ) { return false; }
28904
28905         var strings = Object.keys(entity.tags);
28906
28907         for (var i = 0; i < strings.length; i++) {
28908             var s = strings[i];
28909             if (past_futures[s] || past_futures[entity.tags[s]]) { return true; }
28910         }
28911         return false;
28912     });
28913
28914     // Lines or areas that don't match another feature filter.
28915     // IMPORTANT: The 'others' feature must be the last one defined,
28916     //   so that code in getMatches can skip this test if `hasMatch = true`
28917     defineFeature('others', function isOther(entity, resolver, geometry) {
28918         return (geometry === 'line' || geometry === 'area');
28919     });
28920
28921
28922     function features() {}
28923
28924     features.features = function() {
28925         return _features;
28926     };
28927
28928     features.keys = function() {
28929         return _keys;
28930     };
28931
28932     features.enabled = function(k) {
28933         if (!arguments.length) {
28934             return _.filter(_keys, function(k) { return _features[k].enabled; });
28935         }
28936         return _features[k] && _features[k].enabled;
28937     };
28938
28939     features.disabled = function(k) {
28940         if (!arguments.length) {
28941             return _.reject(_keys, function(k) { return _features[k].enabled; });
28942         }
28943         return _features[k] && !_features[k].enabled;
28944     };
28945
28946     features.hidden = function(k) {
28947         if (!arguments.length) {
28948             return _.filter(_keys, function(k) { return _features[k].hidden(); });
28949         }
28950         return _features[k] && _features[k].hidden();
28951     };
28952
28953     features.autoHidden = function(k) {
28954         if (!arguments.length) {
28955             return _.filter(_keys, function(k) { return _features[k].autoHidden(); });
28956         }
28957         return _features[k] && _features[k].autoHidden();
28958     };
28959
28960     features.enable = function(k) {
28961         if (_features[k] && !_features[k].enabled) {
28962             _features[k].enable();
28963             update();
28964         }
28965     };
28966
28967     features.disable = function(k) {
28968         if (_features[k] && _features[k].enabled) {
28969             _features[k].disable();
28970             update();
28971         }
28972     };
28973
28974     features.toggle = function(k) {
28975         if (_features[k]) {
28976             (function(f) { return f.enabled ? f.disable() : f.enable(); }(_features[k]));
28977             update();
28978         }
28979     };
28980
28981     features.resetStats = function() {
28982         _.each(_features, function(f) { f.count = 0; });
28983         dispatch.change();
28984     };
28985
28986     features.gatherStats = function(d, resolver, dimensions) {
28987         var needsRedraw = false,
28988             type = _.groupBy(d, function(ent) { return ent.type; }),
28989             entities = [].concat(type.relation || [], type.way || [], type.node || []),
28990             currHidden, geometry, matches;
28991
28992         _.each(_features, function(f) { f.count = 0; });
28993
28994         // adjust the threshold for point/building culling based on viewport size..
28995         // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
28996         _cullFactor = dimensions[0] * dimensions[1] / 1000000;
28997
28998         for (var i = 0; i < entities.length; i++) {
28999             geometry = entities[i].geometry(resolver);
29000             if (!(geometry === 'vertex' || geometry === 'relation')) {
29001                 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
29002                 for (var j = 0; j < matches.length; j++) {
29003                     _features[matches[j]].count++;
29004                 }
29005             }
29006         }
29007
29008         currHidden = features.hidden();
29009         if (currHidden !== _hidden) {
29010             _hidden = currHidden;
29011             needsRedraw = true;
29012             dispatch.change();
29013         }
29014
29015         return needsRedraw;
29016     };
29017
29018     features.stats = function() {
29019         _.each(_keys, function(k) { _stats[k] = _features[k].count; });
29020         return _stats;
29021     };
29022
29023     features.clear = function(d) {
29024         for (var i = 0; i < d.length; i++) {
29025             features.clearEntity(d[i]);
29026         }
29027     };
29028
29029     features.clearEntity = function(entity) {
29030         delete _cache[iD.Entity.key(entity)];
29031     };
29032
29033     features.reset = function() {
29034         _cache = {};
29035     };
29036
29037     features.getMatches = function(entity, resolver, geometry) {
29038         if (geometry === 'vertex' || geometry === 'relation') return {};
29039
29040         var ent = iD.Entity.key(entity);
29041         if (!_cache[ent]) {
29042             _cache[ent] = {};
29043         }
29044
29045         if (!_cache[ent].matches) {
29046             var matches = {},
29047                 hasMatch = false;
29048
29049             for (var i = 0; i < _keys.length; i++) {
29050                 if (_keys[i] === 'others') {
29051                     if (hasMatch) continue;
29052
29053                     // Multipolygon members:
29054                     // If an entity...
29055                     //   1. is a way that hasn't matched other "interesting" feature rules,
29056                     //   2. and it belongs to a single parent multipolygon relation
29057                     // ...then match whatever feature rules the parent multipolygon has matched.
29058                     // see #2548, #2887
29059                     //
29060                     // IMPORTANT:
29061                     // For this to work, getMatches must be called on relations before ways.
29062                     //
29063                     if (entity.type === 'way') {
29064                         var parents = features.getParents(entity, resolver, geometry);
29065                         if (parents.length === 1 && parents[0].isMultipolygon()) {
29066                             var pkey = iD.Entity.key(parents[0]);
29067                             if (_cache[pkey] && _cache[pkey].matches) {
29068                                 matches = _.clone(_cache[pkey].matches);
29069                                 continue;
29070                             }
29071                         }
29072                     }
29073                 }
29074
29075                 if (_features[_keys[i]].filter(entity, resolver, geometry)) {
29076                     matches[_keys[i]] = hasMatch = true;
29077                 }
29078             }
29079             _cache[ent].matches = matches;
29080         }
29081
29082         return _cache[ent].matches;
29083     };
29084
29085     features.getParents = function(entity, resolver, geometry) {
29086         if (geometry === 'point') return [];
29087
29088         var ent = iD.Entity.key(entity);
29089         if (!_cache[ent]) {
29090             _cache[ent] = {};
29091         }
29092
29093         if (!_cache[ent].parents) {
29094             var parents = [];
29095             if (geometry === 'vertex') {
29096                 parents = resolver.parentWays(entity);
29097             } else {   // 'line', 'area', 'relation'
29098                 parents = resolver.parentRelations(entity);
29099             }
29100             _cache[ent].parents = parents;
29101         }
29102         return _cache[ent].parents;
29103     };
29104
29105     features.isHiddenFeature = function(entity, resolver, geometry) {
29106         if (!_hidden.length) return false;
29107         if (!entity.version) return false;
29108
29109         var matches = features.getMatches(entity, resolver, geometry);
29110
29111         for (var i = 0; i < _hidden.length; i++) {
29112             if (matches[_hidden[i]]) return true;
29113         }
29114         return false;
29115     };
29116
29117     features.isHiddenChild = function(entity, resolver, geometry) {
29118         if (!_hidden.length) return false;
29119         if (!entity.version || geometry === 'point') return false;
29120
29121         var parents = features.getParents(entity, resolver, geometry);
29122         if (!parents.length) return false;
29123
29124         for (var i = 0; i < parents.length; i++) {
29125             if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
29126                 return false;
29127             }
29128         }
29129         return true;
29130     };
29131
29132     features.hasHiddenConnections = function(entity, resolver) {
29133         if (!_hidden.length) return false;
29134         var childNodes, connections;
29135
29136         if (entity.type === 'midpoint') {
29137             childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
29138             connections = [];
29139         } else {
29140             childNodes = entity.nodes ? resolver.childNodes(entity) : [];
29141             connections = features.getParents(entity, resolver, entity.geometry(resolver));
29142         }
29143
29144         // gather ways connected to child nodes..
29145         connections = _.reduce(childNodes, function(result, e) {
29146             return resolver.isShared(e) ? _.union(result, resolver.parentWays(e)) : result;
29147         }, connections);
29148
29149         return connections.length ? _.any(connections, function(e) {
29150             return features.isHidden(e, resolver, e.geometry(resolver));
29151         }) : false;
29152     };
29153
29154     features.isHidden = function(entity, resolver, geometry) {
29155         if (!_hidden.length) return false;
29156         if (!entity.version) return false;
29157
29158         var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
29159         return fn(entity, resolver, geometry);
29160     };
29161
29162     features.filter = function(d, resolver) {
29163         if (!_hidden.length) return d;
29164
29165         var result = [];
29166         for (var i = 0; i < d.length; i++) {
29167             var entity = d[i];
29168             if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
29169                 result.push(entity);
29170             }
29171         }
29172         return result;
29173     };
29174
29175     return d3.rebind(features, dispatch, 'on');
29176 };
29177 iD.Map = function(context) {
29178     var dimensions = [1, 1],
29179         dispatch = d3.dispatch('move', 'drawn'),
29180         projection = context.projection,
29181         zoom = d3.behavior.zoom()
29182             .translate(projection.translate())
29183             .scale(projection.scale() * 2 * Math.PI)
29184             .scaleExtent([1024, 256 * Math.pow(2, 24)])
29185             .on('zoom', zoomPan),
29186         dblclickEnabled = true,
29187         redrawEnabled = true,
29188         transformStart,
29189         transformed = false,
29190         minzoom = 0,
29191         drawLayers = iD.svg.Layers(projection, context),
29192         drawPoints = iD.svg.Points(projection, context),
29193         drawVertices = iD.svg.Vertices(projection, context),
29194         drawLines = iD.svg.Lines(projection),
29195         drawAreas = iD.svg.Areas(projection),
29196         drawMidpoints = iD.svg.Midpoints(projection, context),
29197         drawLabels = iD.svg.Labels(projection, context),
29198         supersurface,
29199         wrapper,
29200         surface,
29201         mouse,
29202         mousemove;
29203
29204     function map(selection) {
29205         context.history()
29206             .on('change.map', redraw);
29207         context.background()
29208             .on('change.map', redraw);
29209         context.features()
29210             .on('redraw.map', redraw);
29211
29212         selection
29213             .on('dblclick.map', dblClick)
29214             .call(zoom);
29215
29216         supersurface = selection.append('div')
29217             .attr('id', 'supersurface')
29218             .call(iD.util.setTransform, 0, 0);
29219
29220         // Need a wrapper div because Opera can't cope with an absolutely positioned
29221         // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
29222         wrapper = supersurface
29223             .append('div')
29224             .attr('class', 'layer layer-data');
29225
29226         map.surface = surface = wrapper
29227             .call(drawLayers)
29228             .selectAll('.surface')
29229             .attr('id', 'surface');
29230
29231         surface
29232             .on('mousedown.zoom', function() {
29233                 if (d3.event.button === 2) {
29234                     d3.event.stopPropagation();
29235                 }
29236             }, true)
29237             .on('mouseup.zoom', function() {
29238                 if (resetTransform()) redraw();
29239             })
29240             .on('mousemove.map', function() {
29241                 mousemove = d3.event;
29242             })
29243             .on('mouseover.vertices', function() {
29244                 if (map.editable() && !transformed) {
29245                     var hover = d3.event.target.__data__;
29246                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29247                     dispatch.drawn({full: false});
29248                 }
29249             })
29250             .on('mouseout.vertices', function() {
29251                 if (map.editable() && !transformed) {
29252                     var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__;
29253                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29254                     dispatch.drawn({full: false});
29255                 }
29256             });
29257
29258
29259         supersurface
29260             .call(context.background());
29261
29262
29263         context.on('enter.map', function() {
29264             if (map.editable() && !transformed) {
29265                 var all = context.intersects(map.extent()),
29266                     filter = d3.functor(true),
29267                     graph = context.graph();
29268
29269                 all = context.features().filter(all, graph);
29270                 surface
29271                     .call(drawVertices, graph, all, filter, map.extent(), map.zoom())
29272                     .call(drawMidpoints, graph, all, filter, map.trimmedExtent());
29273                 dispatch.drawn({full: false});
29274             }
29275         });
29276
29277         map.dimensions(selection.dimensions());
29278
29279         drawLabels.supersurface(supersurface);
29280     }
29281
29282     function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }
29283
29284     function drawVector(difference, extent) {
29285         var graph = context.graph(),
29286             features = context.features(),
29287             all = context.intersects(map.extent()),
29288             data, filter;
29289
29290         if (difference) {
29291             var complete = difference.complete(map.extent());
29292             data = _.compact(_.values(complete));
29293             filter = function(d) { return d.id in complete; };
29294             features.clear(data);
29295
29296         } else {
29297             // force a full redraw if gatherStats detects that a feature
29298             // should be auto-hidden (e.g. points or buildings)..
29299             if (features.gatherStats(all, graph, dimensions)) {
29300                 extent = undefined;
29301             }
29302
29303             if (extent) {
29304                 data = context.intersects(map.extent().intersection(extent));
29305                 var set = d3.set(_.pluck(data, 'id'));
29306                 filter = function(d) { return set.has(d.id); };
29307
29308             } else {
29309                 data = all;
29310                 filter = d3.functor(true);
29311             }
29312         }
29313
29314         data = features.filter(data, graph);
29315
29316         surface
29317             .call(drawVertices, graph, data, filter, map.extent(), map.zoom())
29318             .call(drawLines, graph, data, filter)
29319             .call(drawAreas, graph, data, filter)
29320             .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
29321             .call(drawLabels, graph, data, filter, dimensions, !difference && !extent)
29322             .call(drawPoints, graph, data, filter);
29323
29324         dispatch.drawn({full: true});
29325     }
29326
29327     function editOff() {
29328         context.features().resetStats();
29329         surface.selectAll('.layer-osm *').remove();
29330         dispatch.drawn({full: true});
29331     }
29332
29333     function dblClick() {
29334         if (!dblclickEnabled) {
29335             d3.event.preventDefault();
29336             d3.event.stopImmediatePropagation();
29337         }
29338     }
29339
29340     function zoomPan() {
29341         if (Math.log(d3.event.scale) / Math.LN2 - 8 < minzoom) {
29342             surface.interrupt();
29343             iD.ui.flash(context.container())
29344                 .select('.content')
29345                 .text(t('cannot_zoom'));
29346             setZoom(context.minEditableZoom(), true);
29347             queueRedraw();
29348             dispatch.move(map);
29349             return;
29350         }
29351
29352         projection
29353             .translate(d3.event.translate)
29354             .scale(d3.event.scale / (2 * Math.PI));
29355
29356         var scale = d3.event.scale / transformStart[0],
29357             tX = (d3.event.translate[0] / scale - transformStart[1][0]) * scale,
29358             tY = (d3.event.translate[1] / scale - transformStart[1][1]) * scale;
29359
29360         transformed = true;
29361         iD.util.setTransform(supersurface, tX, tY, scale);
29362         queueRedraw();
29363
29364         dispatch.move(map);
29365     }
29366
29367     function resetTransform() {
29368         if (!transformed) return false;
29369
29370         surface.selectAll('.radial-menu').interrupt().remove();
29371         iD.util.setTransform(supersurface, 0, 0);
29372         transformed = false;
29373         return true;
29374     }
29375
29376     function redraw(difference, extent) {
29377         if (!surface || !redrawEnabled) return;
29378
29379         clearTimeout(timeoutId);
29380
29381         // If we are in the middle of a zoom/pan, we can't do differenced redraws.
29382         // It would result in artifacts where differenced entities are redrawn with
29383         // one transform and unchanged entities with another.
29384         if (resetTransform()) {
29385             difference = extent = undefined;
29386         }
29387
29388         var zoom = String(~~map.zoom());
29389         if (surface.attr('data-zoom') !== zoom) {
29390             surface.attr('data-zoom', zoom)
29391                 .classed('low-zoom', zoom <= 16);
29392         }
29393
29394         if (!difference) {
29395             supersurface.call(context.background());
29396         }
29397
29398         // OSM
29399         if (map.editable()) {
29400             context.loadTiles(projection, dimensions);
29401             drawVector(difference, extent);
29402         } else {
29403             editOff();
29404         }
29405
29406         wrapper
29407             .call(drawLayers);
29408
29409         transformStart = [
29410             projection.scale() * 2 * Math.PI,
29411             projection.translate().slice()];
29412
29413         return map;
29414     }
29415
29416     var timeoutId;
29417     function queueRedraw() {
29418         clearTimeout(timeoutId);
29419         timeoutId = setTimeout(function() { redraw(); }, 300);
29420     }
29421
29422     function pointLocation(p) {
29423         var translate = projection.translate(),
29424             scale = projection.scale() * 2 * Math.PI;
29425         return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
29426     }
29427
29428     function locationPoint(l) {
29429         var translate = projection.translate(),
29430             scale = projection.scale() * 2 * Math.PI;
29431         return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
29432     }
29433
29434     map.mouse = function() {
29435         var e = mousemove || d3.event, s;
29436         while ((s = e.sourceEvent)) e = s;
29437         return mouse(e);
29438     };
29439
29440     map.mouseCoordinates = function() {
29441         return projection.invert(map.mouse());
29442     };
29443
29444     map.dblclickEnable = function(_) {
29445         if (!arguments.length) return dblclickEnabled;
29446         dblclickEnabled = _;
29447         return map;
29448     };
29449
29450     map.redrawEnable = function(_) {
29451         if (!arguments.length) return redrawEnabled;
29452         redrawEnabled = _;
29453         return map;
29454     };
29455
29456     function interpolateZoom(_) {
29457         var k = projection.scale(),
29458             t = projection.translate();
29459
29460         surface.node().__chart__ = {
29461             x: t[0],
29462             y: t[1],
29463             k: k * 2 * Math.PI
29464         };
29465
29466         setZoom(_);
29467         projection.scale(k).translate(t);  // undo setZoom projection changes
29468
29469         zoom.event(surface.transition());
29470     }
29471
29472     function setZoom(_, force) {
29473         if (_ === map.zoom() && !force)
29474             return false;
29475         var scale = 256 * Math.pow(2, _),
29476             center = pxCenter(),
29477             l = pointLocation(center);
29478         scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
29479         projection.scale(scale / (2 * Math.PI));
29480         zoom.scale(scale);
29481         var t = projection.translate();
29482         l = locationPoint(l);
29483         t[0] += center[0] - l[0];
29484         t[1] += center[1] - l[1];
29485         projection.translate(t);
29486         zoom.translate(projection.translate());
29487         return true;
29488     }
29489
29490     function setCenter(_) {
29491         var c = map.center();
29492         if (_[0] === c[0] && _[1] === c[1])
29493             return false;
29494         var t = projection.translate(),
29495             pxC = pxCenter(),
29496             ll = projection(_);
29497         projection.translate([
29498             t[0] - ll[0] + pxC[0],
29499             t[1] - ll[1] + pxC[1]]);
29500         zoom.translate(projection.translate());
29501         return true;
29502     }
29503
29504     map.pan = function(d) {
29505         var t = projection.translate();
29506         t[0] += d[0];
29507         t[1] += d[1];
29508         projection.translate(t);
29509         zoom.translate(projection.translate());
29510         dispatch.move(map);
29511         return redraw();
29512     };
29513
29514     map.dimensions = function(_) {
29515         if (!arguments.length) return dimensions;
29516         var center = map.center();
29517         dimensions = _;
29518         drawLayers.dimensions(dimensions);
29519         context.background().dimensions(dimensions);
29520         projection.clipExtent([[0, 0], dimensions]);
29521         mouse = iD.util.fastMouse(supersurface.node());
29522         setCenter(center);
29523         return redraw();
29524     };
29525
29526     function zoomIn(integer) {
29527       interpolateZoom(~~map.zoom() + integer);
29528     }
29529
29530     function zoomOut(integer) {
29531       interpolateZoom(~~map.zoom() - integer);
29532     }
29533
29534     map.zoomIn = function() { zoomIn(1); };
29535     map.zoomInFurther = function() { zoomIn(4); };
29536
29537     map.zoomOut = function() { zoomOut(1); };
29538     map.zoomOutFurther = function() { zoomOut(4); };
29539
29540     map.center = function(loc) {
29541         if (!arguments.length) {
29542             return projection.invert(pxCenter());
29543         }
29544
29545         if (setCenter(loc)) {
29546             dispatch.move(map);
29547         }
29548
29549         return redraw();
29550     };
29551
29552     map.zoom = function(z) {
29553         if (!arguments.length) {
29554             return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
29555         }
29556
29557         if (z < minzoom) {
29558             surface.interrupt();
29559             iD.ui.flash(context.container())
29560                 .select('.content')
29561                 .text(t('cannot_zoom'));
29562             z = context.minEditableZoom();
29563         }
29564
29565         if (setZoom(z)) {
29566             dispatch.move(map);
29567         }
29568
29569         return redraw();
29570     };
29571
29572     map.zoomTo = function(entity, zoomLimits) {
29573         var extent = entity.extent(context.graph());
29574         if (!isFinite(extent.area())) return;
29575
29576         var zoom = map.trimmedExtentZoom(extent);
29577         zoomLimits = zoomLimits || [context.minEditableZoom(), 20];
29578         map.centerZoom(extent.center(), Math.min(Math.max(zoom, zoomLimits[0]), zoomLimits[1]));
29579     };
29580
29581     map.centerZoom = function(loc, z) {
29582         var centered = setCenter(loc),
29583             zoomed   = setZoom(z);
29584
29585         if (centered || zoomed) {
29586             dispatch.move(map);
29587         }
29588
29589         return redraw();
29590     };
29591
29592     map.centerEase = function(loc) {
29593         var from = map.center().slice(),
29594             t = 0,
29595             stop;
29596
29597         surface.one('mousedown.ease', function() {
29598             stop = true;
29599         });
29600
29601         d3.timer(function() {
29602             if (stop) return true;
29603             map.center(iD.geo.interp(from, loc, (t += 1) / 10));
29604             return t === 10;
29605         }, 20);
29606         return map;
29607     };
29608
29609     map.extent = function(_) {
29610         if (!arguments.length) {
29611             return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
29612                                  projection.invert([dimensions[0], 0]));
29613         } else {
29614             var extent = iD.geo.Extent(_);
29615             map.centerZoom(extent.center(), map.extentZoom(extent));
29616         }
29617     };
29618
29619     map.trimmedExtent = function(_) {
29620         if (!arguments.length) {
29621             var headerY = 60, footerY = 30, pad = 10;
29622             return new iD.geo.Extent(projection.invert([pad, dimensions[1] - footerY - pad]),
29623                     projection.invert([dimensions[0] - pad, headerY + pad]));
29624         } else {
29625             var extent = iD.geo.Extent(_);
29626             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
29627         }
29628     };
29629
29630     function calcZoom(extent, dim) {
29631         var tl = projection([extent[0][0], extent[1][1]]),
29632             br = projection([extent[1][0], extent[0][1]]);
29633
29634         // Calculate maximum zoom that fits extent
29635         var hFactor = (br[0] - tl[0]) / dim[0],
29636             vFactor = (br[1] - tl[1]) / dim[1],
29637             hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
29638             vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
29639             newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
29640
29641         return newZoom;
29642     }
29643
29644     map.extentZoom = function(_) {
29645         return calcZoom(iD.geo.Extent(_), dimensions);
29646     };
29647
29648     map.trimmedExtentZoom = function(_) {
29649         var trimY = 120, trimX = 40,
29650             trimmed = [dimensions[0] - trimX, dimensions[1] - trimY];
29651         return calcZoom(iD.geo.Extent(_), trimmed);
29652     };
29653
29654     map.editable = function() {
29655         return map.zoom() >= context.minEditableZoom();
29656     };
29657
29658     map.minzoom = function(_) {
29659         if (!arguments.length) return minzoom;
29660         minzoom = _;
29661         return map;
29662     };
29663
29664     map.layers = drawLayers;
29665
29666     return d3.rebind(map, dispatch, 'on');
29667 };
29668 iD.TileLayer = function() {
29669     var tileSize = 256,
29670         tile = d3.geo.tile(),
29671         projection,
29672         cache = {},
29673         tileOrigin,
29674         z,
29675         transformProp = iD.util.prefixCSSProperty('Transform'),
29676         source = d3.functor('');
29677
29678     function tileSizeAtZoom(d, z) {
29679         return (tileSize * Math.pow(2, z - d[2])) / tileSize;
29680     }
29681
29682     function atZoom(t, distance) {
29683         var power = Math.pow(2, distance);
29684         return [
29685             Math.floor(t[0] * power),
29686             Math.floor(t[1] * power),
29687             t[2] + distance];
29688     }
29689
29690     function lookUp(d) {
29691         for (var up = -1; up > -d[2]; up--) {
29692             var tile = atZoom(d, up);
29693             if (cache[source.url(tile)] !== false) {
29694                 return tile;
29695             }
29696         }
29697     }
29698
29699     function uniqueBy(a, n) {
29700         var o = [], seen = {};
29701         for (var i = 0; i < a.length; i++) {
29702             if (seen[a[i][n]] === undefined) {
29703                 o.push(a[i]);
29704                 seen[a[i][n]] = true;
29705             }
29706         }
29707         return o;
29708     }
29709
29710     function addSource(d) {
29711         d.push(source.url(d));
29712         return d;
29713     }
29714
29715     // Update tiles based on current state of `projection`.
29716     function background(selection) {
29717         tile.scale(projection.scale() * 2 * Math.PI)
29718             .translate(projection.translate());
29719
29720         tileOrigin = [
29721             projection.scale() * Math.PI - projection.translate()[0],
29722             projection.scale() * Math.PI - projection.translate()[1]];
29723
29724         z = Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.log(2) - 8, 0);
29725
29726         render(selection);
29727     }
29728
29729     // Derive the tiles onscreen, remove those offscreen and position them.
29730     // Important that this part not depend on `projection` because it's
29731     // rentered when tiles load/error (see #644).
29732     function render(selection) {
29733         var requests = [];
29734
29735         if (source.validZoom(z)) {
29736             tile().forEach(function(d) {
29737                 addSource(d);
29738                 if (d[3] === '') return;
29739                 if (typeof d[3] !== 'string') return; // Workaround for chrome crash https://github.com/openstreetmap/iD/issues/2295
29740                 requests.push(d);
29741                 if (cache[d[3]] === false && lookUp(d)) {
29742                     requests.push(addSource(lookUp(d)));
29743                 }
29744             });
29745
29746             requests = uniqueBy(requests, 3).filter(function(r) {
29747                 // don't re-request tiles which have failed in the past
29748                 return cache[r[3]] !== false;
29749             });
29750         }
29751
29752         var pixelOffset = [
29753             source.offset()[0] * Math.pow(2, z),
29754             source.offset()[1] * Math.pow(2, z)
29755         ];
29756
29757         function load(d) {
29758             cache[d[3]] = true;
29759             d3.select(this)
29760                 .on('error', null)
29761                 .on('load', null)
29762                 .classed('tile-loaded', true);
29763             render(selection);
29764         }
29765
29766         function error(d) {
29767             cache[d[3]] = false;
29768             d3.select(this)
29769                 .on('error', null)
29770                 .on('load', null)
29771                 .remove();
29772             render(selection);
29773         }
29774
29775         function imageTransform(d) {
29776             var _ts = tileSize * Math.pow(2, z - d[2]);
29777             var scale = tileSizeAtZoom(d, z);
29778             return 'translate(' +
29779                 ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0]) + 'px,' +
29780                 ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1]) + 'px)' +
29781                 'scale(' + scale + ',' + scale + ')';
29782         }
29783
29784         var image = selection
29785             .selectAll('img')
29786             .data(requests, function(d) { return d[3]; });
29787
29788         image.exit()
29789             .style(transformProp, imageTransform)
29790             .classed('tile-removing', true)
29791             .each(function() {
29792                 var tile = d3.select(this);
29793                 window.setTimeout(function() {
29794                     if (tile.classed('tile-removing')) {
29795                         tile.remove();
29796                     }
29797                 }, 300);
29798             });
29799
29800         image.enter().append('img')
29801             .attr('class', 'tile')
29802             .attr('src', function(d) { return d[3]; })
29803             .on('error', error)
29804             .on('load', load);
29805
29806         image
29807             .style(transformProp, imageTransform)
29808             .classed('tile-removing', false);
29809     }
29810
29811     background.projection = function(_) {
29812         if (!arguments.length) return projection;
29813         projection = _;
29814         return background;
29815     };
29816
29817     background.dimensions = function(_) {
29818         if (!arguments.length) return tile.size();
29819         tile.size(_);
29820         return background;
29821     };
29822
29823     background.source = function(_) {
29824         if (!arguments.length) return source;
29825         source = _;
29826         cache = {};
29827         tile.scaleExtent(source.scaleExtent);
29828         return background;
29829     };
29830
29831     return background;
29832 };
29833 iD.svg = {
29834     PointTransform: function(projection) {
29835         return function(entity) {
29836             // http://jsperf.com/short-array-join
29837             var pt = projection(entity.loc);
29838             return 'translate(' + pt[0] + ',' + pt[1] + ')';
29839         };
29840     },
29841
29842     Path: function(projection, graph, polygon) {
29843         var cache = {},
29844             clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
29845             project = projection.stream,
29846             path = d3.geo.path()
29847                 .projection({stream: function(output) { return polygon ? project(output) : project(clip(output)); }});
29848
29849         return function(entity) {
29850             if (entity.id in cache) {
29851                 return cache[entity.id];
29852             } else {
29853                 return cache[entity.id] = path(entity.asGeoJSON(graph));
29854             }
29855         };
29856     },
29857
29858     OneWaySegments: function(projection, graph, dt) {
29859         return function(entity) {
29860             var a,
29861                 b,
29862                 i = 0,
29863                 offset = dt,
29864                 segments = [],
29865                 clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
29866                 coordinates = graph.childNodes(entity).map(function(n) {
29867                     return n.loc;
29868                 });
29869
29870             if (entity.tags.oneway === '-1') coordinates.reverse();
29871
29872             d3.geo.stream({
29873                 type: 'LineString',
29874                 coordinates: coordinates
29875             }, projection.stream(clip({
29876                 lineStart: function() {},
29877                 lineEnd: function() {
29878                     a = null;
29879                 },
29880                 point: function(x, y) {
29881                     b = [x, y];
29882
29883                     if (a) {
29884                         var span = iD.geo.euclideanDistance(a, b) - offset;
29885
29886                         if (span >= 0) {
29887                             var angle = Math.atan2(b[1] - a[1], b[0] - a[0]),
29888                                 dx = dt * Math.cos(angle),
29889                                 dy = dt * Math.sin(angle),
29890                                 p = [a[0] + offset * Math.cos(angle),
29891                                      a[1] + offset * Math.sin(angle)];
29892
29893                             var segment = 'M' + a[0] + ',' + a[1] +
29894                                           'L' + p[0] + ',' + p[1];
29895
29896                             for (span -= dt; span >= 0; span -= dt) {
29897                                 p[0] += dx;
29898                                 p[1] += dy;
29899                                 segment += 'L' + p[0] + ',' + p[1];
29900                             }
29901
29902                             segment += 'L' + b[0] + ',' + b[1];
29903                             segments.push({id: entity.id, index: i, d: segment});
29904                         }
29905
29906                         offset = -span;
29907                         i++;
29908                     }
29909
29910                     a = b;
29911                 }
29912             })));
29913
29914             return segments;
29915         };
29916     },
29917
29918     RelationMemberTags: function(graph) {
29919         return function(entity) {
29920             var tags = entity.tags;
29921             graph.parentRelations(entity).forEach(function(relation) {
29922                 var type = relation.tags.type;
29923                 if (type === 'multipolygon' || type === 'boundary') {
29924                     tags = _.extend({}, relation.tags, tags);
29925                 }
29926             });
29927             return tags;
29928         };
29929     }
29930 };
29931 iD.svg.Areas = function(projection) {
29932     // Patterns only work in Firefox when set directly on element.
29933     // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
29934     var patterns = {
29935         wetland: 'wetland',
29936         beach: 'beach',
29937         scrub: 'scrub',
29938         construction: 'construction',
29939         military: 'construction',
29940         cemetery: 'cemetery',
29941         grave_yard: 'cemetery',
29942         meadow: 'meadow',
29943         farm: 'farmland',
29944         farmland: 'farmland',
29945         orchard: 'orchard'
29946     };
29947
29948     var patternKeys = ['landuse', 'natural', 'amenity'];
29949
29950     function setPattern(d) {
29951         for (var i = 0; i < patternKeys.length; i++) {
29952             if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) {
29953                 this.style.fill = this.style.stroke = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")';
29954                 return;
29955             }
29956         }
29957         this.style.fill = this.style.stroke = '';
29958     }
29959
29960     return function drawAreas(surface, graph, entities, filter) {
29961         var path = iD.svg.Path(projection, graph, true),
29962             areas = {},
29963             multipolygon;
29964
29965         for (var i = 0; i < entities.length; i++) {
29966             var entity = entities[i];
29967             if (entity.geometry(graph) !== 'area') continue;
29968
29969             multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph);
29970             if (multipolygon) {
29971                 areas[multipolygon.id] = {
29972                     entity: multipolygon.mergeTags(entity.tags),
29973                     area: Math.abs(entity.area(graph))
29974                 };
29975             } else if (!areas[entity.id]) {
29976                 areas[entity.id] = {
29977                     entity: entity,
29978                     area: Math.abs(entity.area(graph))
29979                 };
29980             }
29981         }
29982
29983         areas = d3.values(areas).filter(function hasPath(a) { return path(a.entity); });
29984         areas.sort(function areaSort(a, b) { return b.area - a.area; });
29985         areas = _.pluck(areas, 'entity');
29986
29987         var strokes = areas.filter(function(area) {
29988             return area.type === 'way';
29989         });
29990
29991         var data = {
29992             clip: areas,
29993             shadow: strokes,
29994             stroke: strokes,
29995             fill: areas
29996         };
29997
29998         var clipPaths = surface.selectAll('defs').selectAll('.clipPath')
29999            .filter(filter)
30000            .data(data.clip, iD.Entity.key);
30001
30002         clipPaths.enter()
30003            .append('clipPath')
30004            .attr('class', 'clipPath')
30005            .attr('id', function(entity) { return entity.id + '-clippath'; })
30006            .append('path');
30007
30008         clipPaths.selectAll('path')
30009            .attr('d', path);
30010
30011         clipPaths.exit()
30012            .remove();
30013
30014         var areagroup = surface
30015             .selectAll('.layer-areas')
30016             .selectAll('g.areagroup')
30017             .data(['fill', 'shadow', 'stroke']);
30018
30019         areagroup.enter()
30020             .append('g')
30021             .attr('class', function(d) { return 'layer areagroup area-' + d; });
30022
30023         var paths = areagroup
30024             .selectAll('path')
30025             .filter(filter)
30026             .data(function(layer) { return data[layer]; }, iD.Entity.key);
30027
30028         // Remove exiting areas first, so they aren't included in the `fills`
30029         // array used for sorting below (https://github.com/openstreetmap/iD/issues/1903).
30030         paths.exit()
30031             .remove();
30032
30033         var fills = surface.selectAll('.area-fill path.area')[0];
30034
30035         var bisect = d3.bisector(function(node) {
30036             return -node.__data__.area(graph);
30037         }).left;
30038
30039         function sortedByArea(entity) {
30040             if (this.__data__ === 'fill') {
30041                 return fills[bisect(fills, -entity.area(graph))];
30042             }
30043         }
30044
30045         paths.enter()
30046             .insert('path', sortedByArea)
30047             .each(function(entity) {
30048                 var layer = this.parentNode.__data__;
30049
30050                 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
30051
30052                 if (layer === 'fill') {
30053                     this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)');
30054                     setPattern.apply(this, arguments);
30055                 }
30056             })
30057             .call(iD.svg.TagClasses());
30058
30059         paths
30060             .attr('d', path);
30061     };
30062 };
30063 /*
30064     A standalone SVG element that contains only a `defs` sub-element. To be
30065     used once globally, since defs IDs must be unique within a document.
30066 */
30067 iD.svg.Defs = function(context) {
30068
30069     function SVGSpriteDefinition(id, href) {
30070         return function(defs) {
30071             d3.xml(href, 'image/svg+xml', function(err, svg) {
30072                 if (err) return;
30073                 defs.node().appendChild(
30074                     d3.select(svg.documentElement).attr('id', id).node()
30075                 );
30076             });
30077         };
30078     }
30079
30080     return function drawDefs(selection) {
30081         var defs = selection.append('defs');
30082
30083         // marker
30084         defs.append('marker')
30085             .attr({
30086                 id: 'oneway-marker',
30087                 viewBox: '0 0 10 10',
30088                 refY: 2.5,
30089                 refX: 5,
30090                 markerWidth: 2,
30091                 markerHeight: 2,
30092                 markerUnits: 'strokeWidth',
30093                 orient: 'auto'
30094             })
30095             .append('path')
30096             .attr('class', 'oneway')
30097             .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')
30098             .attr('stroke', 'none')
30099             .attr('fill', '#000')
30100             .attr('opacity', '0.5');
30101
30102         // patterns
30103         var patterns = defs.selectAll('pattern')
30104             .data([
30105                 // pattern name, pattern image name
30106                 ['wetland', 'wetland'],
30107                 ['construction', 'construction'],
30108                 ['cemetery', 'cemetery'],
30109                 ['orchard', 'orchard'],
30110                 ['farmland', 'farmland'],
30111                 ['beach', 'dots'],
30112                 ['scrub', 'dots'],
30113                 ['meadow', 'dots']
30114             ])
30115             .enter()
30116             .append('pattern')
30117             .attr({
30118                 id: function (d) {
30119                     return 'pattern-' + d[0];
30120                 },
30121                 width: 32,
30122                 height: 32,
30123                 patternUnits: 'userSpaceOnUse'
30124             });
30125
30126         patterns.append('rect')
30127             .attr({
30128                 x: 0,
30129                 y: 0,
30130                 width: 32,
30131                 height: 32,
30132                 'class': function (d) {
30133                     return 'pattern-color-' + d[0];
30134                 }
30135             });
30136
30137         patterns.append('image')
30138             .attr({
30139                 x: 0,
30140                 y: 0,
30141                 width: 32,
30142                 height: 32
30143             })
30144             .attr('xlink:href', function (d) {
30145                 return context.imagePath('pattern/' + d[1] + '.png');
30146             });
30147
30148         // clip paths
30149         defs.selectAll()
30150             .data([12, 18, 20, 32, 45])
30151             .enter().append('clipPath')
30152             .attr('id', function (d) {
30153                 return 'clip-square-' + d;
30154             })
30155             .append('rect')
30156             .attr('x', 0)
30157             .attr('y', 0)
30158             .attr('width', function (d) {
30159                 return d;
30160             })
30161             .attr('height', function (d) {
30162                 return d;
30163             });
30164
30165         defs.call(SVGSpriteDefinition(
30166             'iD-sprite',
30167             context.imagePath('iD-sprite.svg')));
30168
30169         defs.call(SVGSpriteDefinition(
30170             'maki-sprite',
30171             context.imagePath('maki-sprite.svg')));
30172     };
30173 };
30174 iD.svg.Gpx = function(projection, context) {
30175     var showLabels = true,
30176         layer;
30177
30178     function init() {
30179         if (iD.svg.Gpx.initialized) return;  // run once
30180
30181         iD.svg.Gpx.geojson = {};
30182         iD.svg.Gpx.enabled = true;
30183
30184         function over() {
30185             d3.event.stopPropagation();
30186             d3.event.preventDefault();
30187             d3.event.dataTransfer.dropEffect = 'copy';
30188         }
30189
30190         d3.select('body')
30191             .attr('dropzone', 'copy')
30192             .on('drop.localgpx', function() {
30193                 d3.event.stopPropagation();
30194                 d3.event.preventDefault();
30195                 if (!iD.detect().filedrop) return;
30196                 drawGpx.files(d3.event.dataTransfer.files);
30197             })
30198             .on('dragenter.localgpx', over)
30199             .on('dragexit.localgpx', over)
30200             .on('dragover.localgpx', over);
30201
30202         iD.svg.Gpx.initialized = true;
30203     }
30204
30205
30206     function drawGpx(surface) {
30207         var geojson = iD.svg.Gpx.geojson,
30208             enabled = iD.svg.Gpx.enabled;
30209
30210         layer = surface.selectAll('.layer-gpx')
30211             .data(enabled ? [0] : []);
30212
30213         layer.enter()
30214             .append('g')
30215             .attr('class', 'layer-gpx');
30216
30217         layer.exit()
30218             .remove();
30219
30220
30221         var paths = layer
30222             .selectAll('path')
30223             .data([geojson]);
30224
30225         paths.enter()
30226             .append('path')
30227             .attr('class', 'gpx');
30228
30229         paths.exit()
30230             .remove();
30231
30232         var path = d3.geo.path()
30233             .projection(projection);
30234
30235         paths
30236             .attr('d', path);
30237
30238
30239         var labels = layer.selectAll('text')
30240             .data(showLabels && geojson.features ? geojson.features : []);
30241
30242         labels.enter()
30243             .append('text')
30244             .attr('class', 'gpx')
30245             .text(function(d) {
30246                 return d.properties.desc || d.properties.name;
30247             });
30248
30249         labels.exit()
30250             .remove();
30251
30252         labels
30253             .attr('x', function(d) {
30254                 var centroid = path.centroid(d);
30255                 return centroid[0] + 7;
30256             })
30257             .attr('y', function(d) {
30258                 var centroid = path.centroid(d);
30259                 return centroid[1];
30260             });
30261
30262     }
30263
30264     function toDom(x) {
30265         return (new DOMParser()).parseFromString(x, 'text/xml');
30266     }
30267
30268     function redraw() {
30269         context.pan([0,0]);
30270     }
30271
30272     drawGpx.showLabels = function(_) {
30273         if (!arguments.length) return showLabels;
30274         showLabels = _;
30275         return this;
30276     };
30277
30278     drawGpx.enabled = function(_) {
30279         if (!arguments.length) return iD.svg.Gpx.enabled;
30280         iD.svg.Gpx.enabled = _;
30281         return this;
30282     };
30283
30284     drawGpx.hasGpx = function() {
30285         var geojson = iD.svg.Gpx.geojson;
30286         return (!(_.isEmpty(geojson) || _.isEmpty(geojson.features)));
30287     };
30288
30289     drawGpx.geojson = function(gj) {
30290         if (!arguments.length) return iD.svg.Gpx.geojson;
30291         if (_.isEmpty(gj) || _.isEmpty(gj.features)) return this;
30292         iD.svg.Gpx.geojson = gj;
30293         return this;
30294     };
30295
30296     drawGpx.url = function(url) {
30297         d3.text(url, function(err, data) {
30298             if (!err) {
30299                 drawGpx.geojson(toGeoJSON.gpx(toDom(data)));
30300                 redraw();
30301             }
30302         });
30303         return this;
30304     };
30305
30306     drawGpx.files = function(fileList) {
30307         var f = fileList[0],
30308             reader = new FileReader();
30309
30310         reader.onload = function(e) {
30311             drawGpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom();
30312             redraw();
30313         };
30314
30315         reader.readAsText(f);
30316         return this;
30317     };
30318
30319     drawGpx.fitZoom = function() {
30320         if (!this.hasGpx()) return this;
30321         var geojson = iD.svg.Gpx.geojson;
30322
30323         var map = context.map(),
30324             viewport = map.trimmedExtent().polygon(),
30325             coords = _.reduce(geojson.features, function(coords, feature) {
30326                 var c = feature.geometry.coordinates;
30327                 return _.union(coords, feature.geometry.type === 'Point' ? [c] : c);
30328             }, []);
30329
30330         if (!iD.geo.polygonIntersectsPolygon(viewport, coords, true)) {
30331             var extent = iD.geo.Extent(d3.geo.bounds(geojson));
30332             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
30333         }
30334
30335         return this;
30336     };
30337
30338     init();
30339     return drawGpx;
30340 };
30341 iD.svg.Icon = function(name, svgklass, useklass) {
30342     return function drawIcon(selection) {
30343         selection.selectAll('svg')
30344             .data([0])
30345             .enter()
30346             .append('svg')
30347             .attr('class', 'icon ' + (svgklass || ''))
30348             .append('use')
30349             .attr('xlink:href', name)
30350             .attr('class', useklass);
30351     };
30352 };
30353 iD.svg.Labels = function(projection, context) {
30354     var path = d3.geo.path().projection(projection);
30355
30356     // Replace with dict and iterate over entities tags instead?
30357     var label_stack = [
30358         ['line', 'aeroway'],
30359         ['line', 'highway'],
30360         ['line', 'railway'],
30361         ['line', 'waterway'],
30362         ['area', 'aeroway'],
30363         ['area', 'amenity'],
30364         ['area', 'building'],
30365         ['area', 'historic'],
30366         ['area', 'leisure'],
30367         ['area', 'man_made'],
30368         ['area', 'natural'],
30369         ['area', 'shop'],
30370         ['area', 'tourism'],
30371         ['point', 'aeroway'],
30372         ['point', 'amenity'],
30373         ['point', 'building'],
30374         ['point', 'historic'],
30375         ['point', 'leisure'],
30376         ['point', 'man_made'],
30377         ['point', 'natural'],
30378         ['point', 'shop'],
30379         ['point', 'tourism'],
30380         ['line', 'name'],
30381         ['area', 'name'],
30382         ['point', 'name']
30383     ];
30384
30385     var default_size = 12;
30386
30387     var font_sizes = label_stack.map(function(d) {
30388         var style = iD.util.getStyle('text.' + d[0] + '.tag-' + d[1]),
30389             m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30390         if (m) return parseInt(m[1], 10);
30391
30392         style = iD.util.getStyle('text.' + d[0]);
30393         m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30394         if (m) return parseInt(m[1], 10);
30395
30396         return default_size;
30397     });
30398
30399     var iconSize = 18;
30400
30401     var pointOffsets = [
30402         [15, -11, 'start'], // right
30403         [10, -11, 'start'], // unused right now
30404         [-15, -11, 'end']
30405     ];
30406
30407     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25,
30408         75, 20, 80, 15, 95, 10, 90, 5, 95];
30409
30410
30411     var noIcons = ['building', 'landuse', 'natural'];
30412     function blacklisted(preset) {
30413         return _.any(noIcons, function(s) {
30414             return preset.id.indexOf(s) >= 0;
30415         });
30416     }
30417
30418     function get(array, prop) {
30419         return function(d, i) { return array[i][prop]; };
30420     }
30421
30422     var textWidthCache = {};
30423
30424     function textWidth(text, size, elem) {
30425         var c = textWidthCache[size];
30426         if (!c) c = textWidthCache[size] = {};
30427
30428         if (c[text]) {
30429             return c[text];
30430
30431         } else if (elem) {
30432             c[text] = elem.getComputedTextLength();
30433             return c[text];
30434
30435         } else {
30436             var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
30437             if (str === null) {
30438                 return size / 3 * 2 * text.length;
30439             } else {
30440                 return size / 3 * (2 * text.length + str.length);
30441             }
30442         }
30443     }
30444
30445     function drawLineLabels(group, entities, filter, classes, labels) {
30446         var texts = group.selectAll('text.' + classes)
30447             .filter(filter)
30448             .data(entities, iD.Entity.key);
30449
30450         texts.enter()
30451             .append('text')
30452             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
30453             .append('textPath')
30454             .attr('class', 'textpath');
30455
30456
30457         texts.selectAll('.textpath')
30458             .filter(filter)
30459             .data(entities, iD.Entity.key)
30460             .attr({
30461                 'startOffset': '50%',
30462                 'xlink:href': function(d) { return '#labelpath-' + d.id; }
30463             })
30464             .text(iD.util.displayName);
30465
30466         texts.exit().remove();
30467     }
30468
30469     function drawLinePaths(group, entities, filter, classes, labels) {
30470         var halos = group.selectAll('path')
30471             .filter(filter)
30472             .data(entities, iD.Entity.key);
30473
30474         halos.enter()
30475             .append('path')
30476             .style('stroke-width', get(labels, 'font-size'))
30477             .attr('id', function(d) { return 'labelpath-' + d.id; })
30478             .attr('class', classes);
30479
30480         halos.attr('d', get(labels, 'lineString'));
30481
30482         halos.exit().remove();
30483     }
30484
30485     function drawPointLabels(group, entities, filter, classes, labels) {
30486         var texts = group.selectAll('text.' + classes)
30487             .filter(filter)
30488             .data(entities, iD.Entity.key);
30489
30490         texts.enter()
30491             .append('text')
30492             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; });
30493
30494         texts.attr('x', get(labels, 'x'))
30495             .attr('y', get(labels, 'y'))
30496             .style('text-anchor', get(labels, 'textAnchor'))
30497             .text(iD.util.displayName)
30498             .each(function(d, i) { textWidth(iD.util.displayName(d), labels[i].height, this); });
30499
30500         texts.exit().remove();
30501         return texts;
30502     }
30503
30504     function drawAreaLabels(group, entities, filter, classes, labels) {
30505         entities = entities.filter(hasText);
30506         labels = labels.filter(hasText);
30507         return drawPointLabels(group, entities, filter, classes, labels);
30508
30509         function hasText(d, i) {
30510             return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
30511         }
30512     }
30513
30514     function drawAreaIcons(group, entities, filter, classes, labels) {
30515         var icons = group.selectAll('use')
30516             .filter(filter)
30517             .data(entities, iD.Entity.key);
30518
30519         icons.enter()
30520             .append('use')
30521             .attr('class', 'icon areaicon')
30522             .attr('width', '18px')
30523             .attr('height', '18px');
30524
30525         icons.attr('transform', get(labels, 'transform'))
30526             .attr('xlink:href', function(d) {
30527                 var icon = context.presets().match(d, context.graph()).icon;
30528                 return '#' + icon + (icon === 'hairdresser' ? '-24': '-18');    // workaround: maki hairdresser-18 broken?
30529             });
30530
30531
30532         icons.exit().remove();
30533     }
30534
30535     function reverse(p) {
30536         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
30537         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
30538     }
30539
30540     function lineString(nodes) {
30541         return 'M' + nodes.join('L');
30542     }
30543
30544     function subpath(nodes, from, to) {
30545         function segmentLength(i) {
30546             var dx = nodes[i][0] - nodes[i + 1][0];
30547             var dy = nodes[i][1] - nodes[i + 1][1];
30548             return Math.sqrt(dx * dx + dy * dy);
30549         }
30550
30551         var sofar = 0,
30552             start, end, i0, i1;
30553         for (var i = 0; i < nodes.length - 1; i++) {
30554             var current = segmentLength(i);
30555             var portion;
30556             if (!start && sofar + current >= from) {
30557                 portion = (from - sofar) / current;
30558                 start = [
30559                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30560                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30561                 ];
30562                 i0 = i + 1;
30563             }
30564             if (!end && sofar + current >= to) {
30565                 portion = (to - sofar) / current;
30566                 end = [
30567                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30568                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30569                 ];
30570                 i1 = i + 1;
30571             }
30572             sofar += current;
30573
30574         }
30575         var ret = nodes.slice(i0, i1);
30576         ret.unshift(start);
30577         ret.push(end);
30578         return ret;
30579
30580     }
30581
30582     function hideOnMouseover() {
30583         var layers = d3.select(this)
30584             .selectAll('.layer-label, .layer-halo');
30585
30586         layers.selectAll('.proximate')
30587             .classed('proximate', false);
30588
30589         var mouse = context.mouse(),
30590             pad = 50,
30591             rect = [mouse[0] - pad, mouse[1] - pad, mouse[0] + pad, mouse[1] + pad],
30592             ids = _.pluck(rtree.search(rect), 'id');
30593
30594         if (!ids.length) return;
30595         layers.selectAll('.' + ids.join(', .'))
30596             .classed('proximate', true);
30597     }
30598
30599     var rtree = rbush(),
30600         rectangles = {};
30601
30602     function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
30603         var hidePoints = !surface.selectAll('.node.point').node();
30604
30605         var labelable = [], i, k, entity;
30606         for (i = 0; i < label_stack.length; i++) labelable.push([]);
30607
30608         if (fullRedraw) {
30609             rtree.clear();
30610             rectangles = {};
30611         } else {
30612             for (i = 0; i < entities.length; i++) {
30613                 rtree.remove(rectangles[entities[i].id]);
30614             }
30615         }
30616
30617         // Split entities into groups specified by label_stack
30618         for (i = 0; i < entities.length; i++) {
30619             entity = entities[i];
30620             var geometry = entity.geometry(graph);
30621
30622             if (geometry === 'vertex')
30623                 continue;
30624             if (hidePoints && geometry === 'point')
30625                 continue;
30626
30627             var preset = geometry === 'area' && context.presets().match(entity, graph),
30628                 icon = preset && !blacklisted(preset) && preset.icon;
30629
30630             if (!icon && !iD.util.displayName(entity))
30631                 continue;
30632
30633             for (k = 0; k < label_stack.length; k++) {
30634                 if (geometry === label_stack[k][0] && entity.tags[label_stack[k][1]]) {
30635                     labelable[k].push(entity);
30636                     break;
30637                 }
30638             }
30639         }
30640
30641         var positions = {
30642             point: [],
30643             line: [],
30644             area: []
30645         };
30646
30647         var labelled = {
30648             point: [],
30649             line: [],
30650             area: []
30651         };
30652
30653         // Try and find a valid label for labellable entities
30654         for (k = 0; k < labelable.length; k++) {
30655             var font_size = font_sizes[k];
30656             for (i = 0; i < labelable[k].length; i++) {
30657                 entity = labelable[k][i];
30658                 var name = iD.util.displayName(entity),
30659                     width = name && textWidth(name, font_size),
30660                     p;
30661                 if (entity.geometry(graph) === 'point') {
30662                     p = getPointLabel(entity, width, font_size);
30663                 } else if (entity.geometry(graph) === 'line') {
30664                     p = getLineLabel(entity, width, font_size);
30665                 } else if (entity.geometry(graph) === 'area') {
30666                     p = getAreaLabel(entity, width, font_size);
30667                 }
30668                 if (p) {
30669                     p.classes = entity.geometry(graph) + ' tag-' + label_stack[k][1];
30670                     positions[entity.geometry(graph)].push(p);
30671                     labelled[entity.geometry(graph)].push(entity);
30672                 }
30673             }
30674         }
30675
30676         function getPointLabel(entity, width, height) {
30677             var coord = projection(entity.loc),
30678                 m = 5,  // margin
30679                 offset = pointOffsets[0],
30680                 p = {
30681                     height: height,
30682                     width: width,
30683                     x: coord[0] + offset[0],
30684                     y: coord[1] + offset[1],
30685                     textAnchor: offset[2]
30686                 };
30687             var rect = [p.x - m, p.y - m, p.x + width + m, p.y + height + m];
30688             if (tryInsert(rect, entity.id)) return p;
30689         }
30690
30691
30692         function getLineLabel(entity, width, height) {
30693             var nodes = _.pluck(graph.childNodes(entity), 'loc').map(projection),
30694                 length = iD.geo.pathLength(nodes);
30695             if (length < width + 20) return;
30696
30697             for (var i = 0; i < lineOffsets.length; i++) {
30698                 var offset = lineOffsets[i],
30699                     middle = offset / 100 * length,
30700                     start = middle - width/2;
30701                 if (start < 0 || start + width > length) continue;
30702                 var sub = subpath(nodes, start, start + width),
30703                     rev = reverse(sub),
30704                     rect = [
30705                         Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
30706                         Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
30707                         Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
30708                         Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
30709                     ];
30710                 if (rev) sub = sub.reverse();
30711                 if (tryInsert(rect, entity.id)) return {
30712                     'font-size': height + 2,
30713                     lineString: lineString(sub),
30714                     startOffset: offset + '%'
30715                 };
30716             }
30717         }
30718
30719         function getAreaLabel(entity, width, height) {
30720             var centroid = path.centroid(entity.asGeoJSON(graph, true)),
30721                 extent = entity.extent(graph),
30722                 entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
30723                 rect;
30724
30725             if (isNaN(centroid[0]) || entitywidth < 20) return;
30726
30727             var iconX = centroid[0] - (iconSize/2),
30728                 iconY = centroid[1] - (iconSize/2),
30729                 textOffset = iconSize + 5;
30730
30731             var p = {
30732                 transform: 'translate(' + iconX + ',' + iconY + ')'
30733             };
30734
30735             if (width && entitywidth >= width + 20) {
30736                 p.x = centroid[0];
30737                 p.y = centroid[1] + textOffset;
30738                 p.textAnchor = 'middle';
30739                 p.height = height;
30740                 rect = [p.x - width/2, p.y, p.x + width/2, p.y + height + textOffset];
30741             } else {
30742                 rect = [iconX, iconY, iconX + iconSize, iconY + iconSize];
30743             }
30744
30745             if (tryInsert(rect, entity.id)) return p;
30746
30747         }
30748
30749         function tryInsert(rect, id) {
30750             // Check that label is visible
30751             if (rect[0] < 0 || rect[1] < 0 || rect[2] > dimensions[0] ||
30752                 rect[3] > dimensions[1]) return false;
30753             var v = rtree.search(rect).length === 0;
30754             if (v) {
30755                 rect.id = id;
30756                 rtree.insert(rect);
30757                 rectangles[id] = rect;
30758             }
30759             return v;
30760         }
30761
30762         var label = surface.selectAll('.layer-label'),
30763             halo = surface.selectAll('.layer-halo');
30764
30765         // points
30766         drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
30767         drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
30768
30769         // lines
30770         drawLinePaths(halo, labelled.line, filter, '', positions.line);
30771         drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
30772         drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
30773
30774         // areas
30775         drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
30776         drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
30777         drawAreaIcons(label, labelled.area, filter, 'arealabel-icon', positions.area);
30778     }
30779
30780     drawLabels.supersurface = function(supersurface) {
30781         supersurface
30782             .on('mousemove.hidelabels', hideOnMouseover)
30783             .on('mousedown.hidelabels', function () {
30784                 supersurface.on('mousemove.hidelabels', null);
30785             })
30786             .on('mouseup.hidelabels', function () {
30787                 supersurface.on('mousemove.hidelabels', hideOnMouseover);
30788             });
30789     };
30790
30791     return drawLabels;
30792 };
30793 iD.svg.Layers = function(projection, context) {
30794     var svg = d3.select(null),
30795         layers = [
30796             { id: 'osm', layer: iD.svg.Osm(projection, context) },
30797             { id: 'gpx', layer: iD.svg.Gpx(projection, context) },
30798             { id: 'mapillary-images', layer: iD.svg.MapillaryImages(projection, context) },
30799             { id: 'mapillary-signs',  layer: iD.svg.MapillarySigns(projection, context) }
30800         ];
30801
30802
30803     function drawLayers(selection) {
30804         svg = selection.selectAll('.surface')
30805             .data([0]);
30806
30807         svg.enter()
30808             .append('svg')
30809             .attr('class', 'surface')
30810             .append('defs');
30811
30812         var groups = svg.selectAll('.data-layer')
30813             .data(layers);
30814
30815         groups.enter()
30816             .append('g')
30817             .attr('class', function(d) { return 'data-layer data-layer-' + d.id; });
30818
30819         groups
30820             .each(function(d) { d3.select(this).call(d.layer); });
30821
30822         groups.exit()
30823             .remove();
30824     }
30825
30826     drawLayers.all = function() {
30827         return layers;
30828     };
30829
30830     drawLayers.layer = function(id) {
30831         var obj = _.find(layers, 'id', id);
30832         return obj && obj.layer;
30833     };
30834
30835     drawLayers.only = function(what) {
30836         var arr = [].concat(what);
30837         drawLayers.remove(_.difference(_.pluck(layers, 'id'), arr));
30838         return this;
30839     };
30840
30841     drawLayers.remove = function(what) {
30842         var arr = [].concat(what);
30843         arr.forEach(function(id) {
30844             layers = _.reject(layers, 'id', id);
30845         });
30846         return this;
30847     };
30848
30849     drawLayers.add = function(what) {
30850         var arr = [].concat(what);
30851         arr.forEach(function(obj) {
30852             if ('id' in obj && 'layer' in obj) {
30853                 layers.push(obj);
30854             }
30855         });
30856         return this;
30857     };
30858
30859     drawLayers.dimensions = function(_) {
30860         if (!arguments.length) return svg.dimensions();
30861         svg.dimensions(_);
30862         layers.forEach(function(obj) {
30863             if (obj.layer.dimensions) {
30864                 obj.layer.dimensions(_);
30865             }
30866         });
30867         return this;
30868     };
30869
30870
30871     return drawLayers;
30872 };
30873 iD.svg.Lines = function(projection) {
30874
30875     var highway_stack = {
30876         motorway: 0,
30877         motorway_link: 1,
30878         trunk: 2,
30879         trunk_link: 3,
30880         primary: 4,
30881         primary_link: 5,
30882         secondary: 6,
30883         tertiary: 7,
30884         unclassified: 8,
30885         residential: 9,
30886         service: 10,
30887         footway: 11
30888     };
30889
30890     function waystack(a, b) {
30891         var as = 0, bs = 0;
30892
30893         if (a.tags.highway) { as -= highway_stack[a.tags.highway]; }
30894         if (b.tags.highway) { bs -= highway_stack[b.tags.highway]; }
30895         return as - bs;
30896     }
30897
30898     return function drawLines(surface, graph, entities, filter) {
30899         var ways = [], pathdata = {}, onewaydata = {},
30900             getPath = iD.svg.Path(projection, graph);
30901
30902         for (var i = 0; i < entities.length; i++) {
30903             var entity = entities[i],
30904                 outer = iD.geo.simpleMultipolygonOuterMember(entity, graph);
30905             if (outer) {
30906                 ways.push(entity.mergeTags(outer.tags));
30907             } else if (entity.geometry(graph) === 'line') {
30908                 ways.push(entity);
30909             }
30910         }
30911
30912         ways = ways.filter(getPath);
30913
30914         pathdata = _.groupBy(ways, function(way) { return way.layer(); });
30915
30916         _.forOwn(pathdata, function(v, k) {
30917             onewaydata[k] = _(v)
30918                 .filter(function(d) { return d.isOneWay(); })
30919                 .map(iD.svg.OneWaySegments(projection, graph, 35))
30920                 .flatten()
30921                 .valueOf();
30922         });
30923
30924         var layergroup = surface
30925             .selectAll('.layer-lines')
30926             .selectAll('g.layergroup')
30927             .data(d3.range(-10, 11));
30928
30929         layergroup.enter()
30930             .append('g')
30931             .attr('class', function(d) { return 'layer layergroup layer' + String(d); });
30932
30933
30934         var linegroup = layergroup
30935             .selectAll('g.linegroup')
30936             .data(['shadow', 'casing', 'stroke']);
30937
30938         linegroup.enter()
30939             .append('g')
30940             .attr('class', function(d) { return 'layer linegroup line-' + d; });
30941
30942
30943         var lines = linegroup
30944             .selectAll('path')
30945             .filter(filter)
30946             .data(
30947                 function() { return pathdata[this.parentNode.parentNode.__data__] || []; },
30948                 iD.Entity.key
30949             );
30950
30951         // Optimization: call simple TagClasses only on enter selection. This
30952         // works because iD.Entity.key is defined to include the entity v attribute.
30953         lines.enter()
30954             .append('path')
30955             .attr('class', function(d) { return 'way line ' + this.parentNode.__data__ + ' ' + d.id; })
30956             .call(iD.svg.TagClasses());
30957
30958         lines
30959             .sort(waystack)
30960             .attr('d', getPath)
30961             .call(iD.svg.TagClasses().tags(iD.svg.RelationMemberTags(graph)));
30962
30963         lines.exit()
30964             .remove();
30965
30966
30967         var onewaygroup = layergroup
30968             .selectAll('g.onewaygroup')
30969             .data(['oneway']);
30970
30971         onewaygroup.enter()
30972             .append('g')
30973             .attr('class', 'layer onewaygroup');
30974
30975
30976         var oneways = onewaygroup
30977             .selectAll('path')
30978             .filter(filter)
30979             .data(
30980                 function() { return onewaydata[this.parentNode.parentNode.__data__] || []; },
30981                 function(d) { return [d.id, d.index]; }
30982             );
30983
30984         oneways.enter()
30985             .append('path')
30986             .attr('class', 'oneway')
30987             .attr('marker-mid', 'url(#oneway-marker)');
30988
30989         oneways
30990             .attr('d', function(d) { return d.d; });
30991
30992         if (iD.detect().ie) {
30993             oneways.each(function() { this.parentNode.insertBefore(this, this); });
30994         }
30995
30996         oneways.exit()
30997             .remove();
30998
30999     };
31000 };
31001 iD.svg.MapillaryImages = function(projection, context) {
31002     var debouncedRedraw = _.debounce(function () { context.pan([0,0]); }, 1000),
31003         minZoom = 12,
31004         layer = d3.select(null),
31005         _mapillary;
31006
31007
31008     function init() {
31009         if (iD.svg.MapillaryImages.initialized) return;  // run once
31010         iD.svg.MapillaryImages.enabled = false;
31011         iD.svg.MapillaryImages.initialized = true;
31012     }
31013
31014     function getMapillary() {
31015         if (iD.services.mapillary && !_mapillary) {
31016             _mapillary = iD.services.mapillary().on('loadedImages', debouncedRedraw);
31017         } else if (!iD.services.mapillary && _mapillary) {
31018             _mapillary = null;
31019         }
31020         return _mapillary;
31021     }
31022
31023     function showThumbnail(image) {
31024         var mapillary = getMapillary();
31025         if (!mapillary) return;
31026
31027         var thumb = mapillary.selectedThumbnail(),
31028             posX = projection(image.loc)[0],
31029             width = layer.dimensions()[0],
31030             position = (posX < width / 2) ? 'right' : 'left';
31031
31032         if (thumb) {
31033             d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31034                 .classed('selected', function(d) { return d.key === thumb.key; });
31035         }
31036
31037         mapillary.showThumbnail(image.key, position);
31038     }
31039
31040     function hideThumbnail() {
31041         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31042             .classed('selected', false);
31043
31044         var mapillary = getMapillary();
31045         if (mapillary) {
31046             mapillary.hideThumbnail();
31047         }
31048     }
31049
31050     function showLayer() {
31051         editOn();
31052         layer
31053             .style('opacity', 0)
31054             .transition()
31055             .duration(500)
31056             .style('opacity', 1)
31057             .each('end', debouncedRedraw);
31058     }
31059
31060     function hideLayer() {
31061         debouncedRedraw.cancel();
31062         hideThumbnail();
31063         layer
31064             .transition()
31065             .duration(500)
31066             .style('opacity', 0)
31067             .each('end', editOff);
31068     }
31069
31070     function editOn() {
31071         layer.style('display', 'block');
31072     }
31073
31074     function editOff() {
31075         layer.selectAll('.viewfield-group').remove();
31076         layer.style('display', 'none');
31077     }
31078
31079     function transform(d) {
31080         var t = iD.svg.PointTransform(projection)(d);
31081         if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
31082         return t;
31083     }
31084
31085     function update() {
31086         var mapillary = getMapillary(),
31087             data = (mapillary ? mapillary.images(projection, layer.dimensions()) : []);
31088
31089         var markers = layer.selectAll('.viewfield-group')
31090             .data(data, function(d) { return d.key; });
31091
31092         // Enter
31093         var enter = markers.enter()
31094             .append('g')
31095             .attr('class', 'viewfield-group');
31096
31097         enter.append('path')
31098             .attr('class', 'viewfield')
31099             .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
31100             .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');
31101
31102         enter.append('circle')
31103             .attr('dx', '0')
31104             .attr('dy', '0')
31105             .attr('r', '6');
31106
31107         // Exit
31108         markers.exit()
31109             .remove();
31110
31111         // Update
31112         markers
31113             .attr('transform', transform);
31114     }
31115
31116     function drawImages(selection) {
31117         var enabled = iD.svg.MapillaryImages.enabled,
31118             mapillary = getMapillary();
31119
31120         layer = selection.selectAll('.layer-mapillary-images')
31121             .data(mapillary ? [0] : []);
31122
31123         layer.enter()
31124             .append('g')
31125             .attr('class', 'layer-mapillary-images')
31126             .style('display', enabled ? 'block' : 'none')
31127             .on('click', function() {   // deselect/select
31128                 var mapillary = getMapillary();
31129                 if (!mapillary) return;
31130                 var d = d3.event.target.__data__,
31131                     thumb = mapillary.selectedThumbnail();
31132                 if (thumb && thumb.key === d.key) {
31133                     hideThumbnail();
31134                 } else {
31135                     mapillary.selectedThumbnail(d);
31136                     context.map().centerEase(d.loc);
31137                     showThumbnail(d);
31138                 }
31139             })
31140             .on('mouseover', function() {
31141                 var mapillary = getMapillary();
31142                 if (!mapillary) return;
31143                 showThumbnail(d3.event.target.__data__);
31144             })
31145             .on('mouseout', function() {
31146                 var mapillary = getMapillary();
31147                 if (!mapillary) return;
31148                 var thumb = mapillary.selectedThumbnail();
31149                 if (thumb) {
31150                     showThumbnail(thumb);
31151                 } else {
31152                     hideThumbnail();
31153                 }
31154             });
31155
31156         layer.exit()
31157             .remove();
31158
31159         if (enabled) {
31160             if (mapillary && ~~context.map().zoom() >= minZoom) {
31161                 editOn();
31162                 update();
31163                 mapillary.loadImages(projection, layer.dimensions());
31164             } else {
31165                 editOff();
31166             }
31167         }
31168     }
31169
31170     drawImages.enabled = function(_) {
31171         if (!arguments.length) return iD.svg.MapillaryImages.enabled;
31172         iD.svg.MapillaryImages.enabled = _;
31173         if (iD.svg.MapillaryImages.enabled) {
31174             showLayer();
31175         } else {
31176             hideLayer();
31177         }
31178         return this;
31179     };
31180
31181     drawImages.supported = function() {
31182         return !!getMapillary();
31183     };
31184
31185     drawImages.dimensions = function(_) {
31186         if (!arguments.length) return layer.dimensions();
31187         layer.dimensions(_);
31188         return this;
31189     };
31190
31191     init();
31192     return drawImages;
31193 };
31194 iD.svg.MapillarySigns = function(projection, context) {
31195     var debouncedRedraw = _.debounce(function () { context.pan([0,0]); }, 1000),
31196         minZoom = 12,
31197         layer = d3.select(null),
31198         _mapillary;
31199
31200
31201     function init() {
31202         if (iD.svg.MapillarySigns.initialized) return;  // run once
31203         iD.svg.MapillarySigns.enabled = false;
31204         iD.svg.MapillarySigns.initialized = true;
31205     }
31206
31207     function getMapillary() {
31208         if (iD.services.mapillary && !_mapillary) {
31209             _mapillary = iD.services.mapillary().on('loadedSigns', debouncedRedraw);
31210         } else if (!iD.services.mapillary && _mapillary) {
31211             _mapillary = null;
31212         }
31213         return _mapillary;
31214     }
31215
31216     function showThumbnail(image) {
31217         var mapillary = getMapillary();
31218         if (!mapillary) return;
31219
31220         var thumb = mapillary.selectedThumbnail(),
31221             posX = projection(image.loc)[0],
31222             width = layer.dimensions()[0],
31223             position = (posX < width / 2) ? 'right' : 'left';
31224
31225         if (thumb) {
31226             d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31227                 .classed('selected', function(d) { return d.key === thumb.key; });
31228         }
31229
31230         mapillary.showThumbnail(image.key, position);
31231     }
31232
31233     function hideThumbnail() {
31234         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31235             .classed('selected', false);
31236
31237         var mapillary = getMapillary();
31238         if (mapillary) {
31239             mapillary.hideThumbnail();
31240         }
31241     }
31242
31243     function showLayer() {
31244         editOn();
31245         debouncedRedraw();
31246     }
31247
31248     function hideLayer() {
31249         debouncedRedraw.cancel();
31250         hideThumbnail();
31251         editOff();
31252     }
31253
31254     function editOn() {
31255         layer.style('display', 'block');
31256     }
31257
31258     function editOff() {
31259         layer.selectAll('.icon-sign').remove();
31260         layer.style('display', 'none');
31261     }
31262
31263     function update() {
31264         var mapillary = getMapillary(),
31265             data = (mapillary ? mapillary.signs(projection, layer.dimensions()) : []);
31266
31267         var signs = layer.selectAll('.icon-sign')
31268             .data(data, function(d) { return d.key; });
31269
31270         // Enter
31271         var enter = signs.enter()
31272             .append('foreignObject')
31273             .attr('class', 'icon-sign')
31274             .attr('width', '32px')      // for Firefox
31275             .attr('height', '32px');    // for Firefox
31276
31277         enter
31278             .append('xhtml:body')
31279             .html(mapillary.signHTML);
31280
31281         enter
31282             .on('click', function(d) {   // deselect/select
31283                 var mapillary = getMapillary();
31284                 if (!mapillary) return;
31285                 var thumb = mapillary.selectedThumbnail();
31286                 if (thumb && thumb.key === d.key) {
31287                     hideThumbnail();
31288                 } else {
31289                     mapillary.selectedThumbnail(d);
31290                     context.map().centerEase(d.loc);
31291                     showThumbnail(d);
31292                 }
31293             })
31294             .on('mouseover', showThumbnail)
31295             .on('mouseout', function() {
31296                 var mapillary = getMapillary();
31297                 if (!mapillary) return;
31298                 var thumb = mapillary.selectedThumbnail();
31299                 if (thumb) {
31300                     showThumbnail(thumb);
31301                 } else {
31302                     hideThumbnail();
31303                 }
31304             });
31305
31306         // Exit
31307         signs.exit()
31308             .remove();
31309
31310         // Update
31311         signs
31312             .attr('transform', iD.svg.PointTransform(projection));
31313     }
31314
31315     function drawSigns(selection) {
31316         var enabled = iD.svg.MapillarySigns.enabled,
31317             mapillary = getMapillary();
31318
31319         layer = selection.selectAll('.layer-mapillary-signs')
31320             .data(mapillary ? [0] : []);
31321
31322         layer.enter()
31323             .append('g')
31324             .attr('class', 'layer-mapillary-signs')
31325             .style('display', enabled ? 'block' : 'none')
31326             .attr('transform', 'translate(-16, -16)');  // center signs on loc
31327
31328         layer.exit()
31329             .remove();
31330
31331         if (enabled) {
31332             if (mapillary && ~~context.map().zoom() >= minZoom) {
31333                 editOn();
31334                 update();
31335                 mapillary.loadSigns(context, projection, layer.dimensions());
31336             } else {
31337                 editOff();
31338             }
31339         }
31340     }
31341
31342     drawSigns.enabled = function(_) {
31343         if (!arguments.length) return iD.svg.MapillarySigns.enabled;
31344         iD.svg.MapillarySigns.enabled = _;
31345         if (iD.svg.MapillarySigns.enabled) {
31346             showLayer();
31347         } else {
31348             hideLayer();
31349         }
31350         return this;
31351     };
31352
31353     drawSigns.supported = function() {
31354         var mapillary = getMapillary();
31355         return (mapillary && mapillary.signsSupported());
31356     };
31357
31358     drawSigns.dimensions = function(_) {
31359         if (!arguments.length) return layer.dimensions();
31360         layer.dimensions(_);
31361         return this;
31362     };
31363
31364     init();
31365     return drawSigns;
31366 };
31367 iD.svg.Midpoints = function(projection, context) {
31368     return function drawMidpoints(surface, graph, entities, filter, extent) {
31369         var poly = extent.polygon(),
31370             midpoints = {};
31371
31372         for (var i = 0; i < entities.length; i++) {
31373             var entity = entities[i];
31374
31375             if (entity.type !== 'way')
31376                 continue;
31377             if (!filter(entity))
31378                 continue;
31379             if (context.selectedIDs().indexOf(entity.id) < 0)
31380                 continue;
31381
31382             var nodes = graph.childNodes(entity);
31383             for (var j = 0; j < nodes.length - 1; j++) {
31384
31385                 var a = nodes[j],
31386                     b = nodes[j + 1],
31387                     id = [a.id, b.id].sort().join('-');
31388
31389                 if (midpoints[id]) {
31390                     midpoints[id].parents.push(entity);
31391                 } else {
31392                     if (iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
31393                         var point = iD.geo.interp(a.loc, b.loc, 0.5),
31394                             loc = null;
31395
31396                         if (extent.intersects(point)) {
31397                             loc = point;
31398                         } else {
31399                             for (var k = 0; k < 4; k++) {
31400                                 point = iD.geo.lineIntersection([a.loc, b.loc], [poly[k], poly[k+1]]);
31401                                 if (point &&
31402                                     iD.geo.euclideanDistance(projection(a.loc), projection(point)) > 20 &&
31403                                     iD.geo.euclideanDistance(projection(b.loc), projection(point)) > 20)
31404                                 {
31405                                     loc = point;
31406                                     break;
31407                                 }
31408                             }
31409                         }
31410
31411                         if (loc) {
31412                             midpoints[id] = {
31413                                 type: 'midpoint',
31414                                 id: id,
31415                                 loc: loc,
31416                                 edge: [a.id, b.id],
31417                                 parents: [entity]
31418                             };
31419                         }
31420                     }
31421                 }
31422             }
31423         }
31424
31425         function midpointFilter(d) {
31426             if (midpoints[d.id])
31427                 return true;
31428
31429             for (var i = 0; i < d.parents.length; i++)
31430                 if (filter(d.parents[i]))
31431                     return true;
31432
31433             return false;
31434         }
31435
31436         var groups = surface.selectAll('.layer-hit').selectAll('g.midpoint')
31437             .filter(midpointFilter)
31438             .data(_.values(midpoints), function(d) { return d.id; });
31439
31440         var enter = groups.enter()
31441             .insert('g', ':first-child')
31442             .attr('class', 'midpoint');
31443
31444         enter.append('polygon')
31445             .attr('points', '-6,8 10,0 -6,-8')
31446             .attr('class', 'shadow');
31447
31448         enter.append('polygon')
31449             .attr('points', '-3,4 5,0 -3,-4')
31450             .attr('class', 'fill');
31451
31452         groups
31453             .attr('transform', function(d) {
31454                 var translate = iD.svg.PointTransform(projection),
31455                     a = context.entity(d.edge[0]),
31456                     b = context.entity(d.edge[1]),
31457                     angle = Math.round(iD.geo.angle(a, b, projection) * (180 / Math.PI));
31458                 return translate(d) + ' rotate(' + angle + ')';
31459             })
31460             .call(iD.svg.TagClasses().tags(
31461                 function(d) { return d.parents[0].tags; }
31462             ));
31463
31464         // Propagate data bindings.
31465         groups.select('polygon.shadow');
31466         groups.select('polygon.fill');
31467
31468         groups.exit()
31469             .remove();
31470     };
31471 };
31472 iD.svg.Osm = function() {
31473     return function drawOsm(selection) {
31474         var layers = selection.selectAll('.layer-osm')
31475             .data(['areas', 'lines', 'hit', 'halo', 'label']);
31476
31477         layers.enter().append('g')
31478             .attr('class', function(d) { return 'layer-osm layer-' + d; });
31479     };
31480 };
31481 iD.svg.Points = function(projection, context) {
31482     function markerPath(selection, klass) {
31483         selection
31484             .attr('class', klass)
31485             .attr('transform', 'translate(-8, -23)')
31486             .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');
31487     }
31488
31489     function sortY(a, b) {
31490         return b.loc[1] - a.loc[1];
31491     }
31492
31493     return function drawPoints(surface, graph, entities, filter) {
31494         var wireframe = surface.classed('fill-wireframe'),
31495             points = wireframe ? [] : _.filter(entities, function(e) {
31496                 return e.geometry(graph) === 'point';
31497             });
31498
31499         points.sort(sortY);
31500
31501         var groups = surface.selectAll('.layer-hit').selectAll('g.point')
31502             .filter(filter)
31503             .data(points, iD.Entity.key);
31504
31505         var group = groups.enter()
31506             .append('g')
31507             .attr('class', function(d) { return 'node point ' + d.id; })
31508             .order();
31509
31510         group.append('path')
31511             .call(markerPath, 'shadow');
31512
31513         group.append('path')
31514             .call(markerPath, 'stroke');
31515
31516         group.append('use')
31517             .attr('transform', 'translate(-6, -20)')
31518             .attr('class', 'icon')
31519             .attr('width', '12px')
31520             .attr('height', '12px');
31521
31522         groups.attr('transform', iD.svg.PointTransform(projection))
31523             .call(iD.svg.TagClasses());
31524
31525         // Selecting the following implicitly
31526         // sets the data (point entity) on the element
31527         groups.select('.shadow');
31528         groups.select('.stroke');
31529         groups.select('.icon')
31530             .attr('xlink:href', function(entity) {
31531                 var preset = context.presets().match(entity, graph);
31532                 return preset.icon ? '#' + preset.icon + '-12' : '';
31533             });
31534
31535         groups.exit()
31536             .remove();
31537     };
31538 };
31539 iD.svg.TagClasses = function() {
31540     var primaries = [
31541             'building', 'highway', 'railway', 'waterway', 'aeroway',
31542             'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse',
31543             'leisure', 'place'
31544         ],
31545         statuses = [
31546             'proposed', 'construction', 'disused', 'abandoned', 'dismantled',
31547             'razed', 'demolished', 'obliterated'
31548         ],
31549         secondaries = [
31550             'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
31551             'surface', 'tracktype', 'crossing'
31552         ],
31553         tagClassRe = /^tag-/,
31554         tags = function(entity) { return entity.tags; };
31555
31556
31557     var tagClasses = function(selection) {
31558         selection.each(function tagClassesEach(entity) {
31559             var value = this.className,
31560                 classes, primary, status;
31561
31562             if (value.baseVal !== undefined) value = value.baseVal;
31563
31564             classes = value.trim().split(/\s+/).filter(function(name) {
31565                 return name.length && !tagClassRe.test(name);
31566             }).join(' ');
31567
31568             var t = tags(entity), i, k, v;
31569
31570             // pick at most one primary classification tag..
31571             for (i = 0; i < primaries.length; i++) {
31572                 k = primaries[i];
31573                 v = t[k];
31574                 if (!v || v === 'no') continue;
31575
31576                 primary = k;
31577                 if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
31578                     status = v;
31579                     classes += ' tag-' + k;
31580                 } else {
31581                     classes += ' tag-' + k + ' tag-' + k + '-' + v;
31582                 }
31583
31584                 break;
31585             }
31586
31587             // add at most one status tag, only if relates to primary tag..
31588             if (!status) {
31589                 for (i = 0; i < statuses.length; i++) {
31590                     k = statuses[i];
31591                     v = t[k];
31592                     if (!v || v === 'no') continue;
31593
31594                     if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
31595                         status = k;
31596                     }
31597                     else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
31598                         status = k;
31599                     } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
31600                         status = k;
31601                         primary = v;
31602                         classes += ' tag-' + v;
31603                     }  // else ignore e.g.  `highway=path + abandoned=railway`
31604
31605                     if (status) break;
31606                 }
31607             }
31608
31609             if (status) {
31610                 classes += ' tag-status tag-status-' + status;
31611             }
31612
31613             // add any secondary (structure) tags
31614             for (i = 0; i < secondaries.length; i++) {
31615                 k = secondaries[i];
31616                 v = t[k];
31617                 if (!v || v === 'no') continue;
31618                 classes += ' tag-' + k + ' tag-' + k + '-' + v;
31619             }
31620
31621             // For highways, look for surface tagging..
31622             if (primary === 'highway') {
31623                 var paved = (t.highway !== 'track');
31624                 for (k in t) {
31625                     v = t[k];
31626                     if (k in iD.pavedTags) {
31627                         paved = !!iD.pavedTags[k][v];
31628                         break;
31629                     }
31630                 }
31631                 if (!paved) {
31632                     classes += ' tag-unpaved';
31633                 }
31634             }
31635
31636             classes = classes.trim();
31637
31638             if (classes !== value) {
31639                 d3.select(this).attr('class', classes);
31640             }
31641         });
31642     };
31643
31644     tagClasses.tags = function(_) {
31645         if (!arguments.length) return tags;
31646         tags = _;
31647         return tagClasses;
31648     };
31649
31650     return tagClasses;
31651 };
31652 iD.svg.Turns = function(projection) {
31653     return function drawTurns(surface, graph, turns) {
31654         function key(turn) {
31655             return [turn.from.node + turn.via.node + turn.to.node].join('-');
31656         }
31657
31658         function icon(turn) {
31659             var u = turn.u ? '-u' : '';
31660             if (!turn.restriction)
31661                 return '#turn-yes' + u;
31662             var restriction = graph.entity(turn.restriction).tags.restriction;
31663             return '#turn-' +
31664                 (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u;
31665         }
31666
31667         var groups = surface.selectAll('.layer-hit').selectAll('g.turn')
31668             .data(turns, key);
31669
31670         // Enter
31671         var enter = groups.enter().append('g')
31672             .attr('class', 'turn');
31673
31674         var nEnter = enter.filter(function (turn) { return !turn.u; });
31675
31676         nEnter.append('rect')
31677             .attr('transform', 'translate(-22, -12)')
31678             .attr('width', '44')
31679             .attr('height', '24');
31680
31681         nEnter.append('use')
31682             .attr('transform', 'translate(-22, -12)')
31683             .attr('width', '44')
31684             .attr('height', '24');
31685
31686
31687         var uEnter = enter.filter(function (turn) { return turn.u; });
31688
31689         uEnter.append('circle')
31690             .attr('r', '16');
31691
31692         uEnter.append('use')
31693             .attr('transform', 'translate(-16, -16)')
31694             .attr('width', '32')
31695             .attr('height', '32');
31696
31697
31698         // Update
31699         groups
31700             .attr('transform', function (turn) {
31701                 var v = graph.entity(turn.via.node),
31702                     t = graph.entity(turn.to.node),
31703                     a = iD.geo.angle(v, t, projection),
31704                     p = projection(v.loc),
31705                     r = turn.u ? 0 : 60;
31706
31707                 return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' +
31708                     'rotate(' + a * 180 / Math.PI + ')';
31709             });
31710
31711         groups.select('use')
31712             .attr('xlink:href', icon);
31713
31714         groups.select('rect');
31715         groups.select('circle');
31716
31717
31718         // Exit
31719         groups.exit()
31720             .remove();
31721
31722         return this;
31723     };
31724 };
31725 iD.svg.Vertices = function(projection, context) {
31726     var radiuses = {
31727         //       z16-, z17, z18+, tagged
31728         shadow: [6,    7.5,   7.5,  11.5],
31729         stroke: [2.5,  3.5,   3.5,  7],
31730         fill:   [1,    1.5,   1.5,  1.5]
31731     };
31732
31733     var hover;
31734
31735     function siblingAndChildVertices(ids, graph, extent) {
31736         var vertices = {};
31737
31738         function addChildVertices(entity) {
31739             if (!context.features().isHiddenFeature(entity, graph, entity.geometry(graph))) {
31740                 var i;
31741                 if (entity.type === 'way') {
31742                     for (i = 0; i < entity.nodes.length; i++) {
31743                         addChildVertices(graph.entity(entity.nodes[i]));
31744                     }
31745                 } else if (entity.type === 'relation') {
31746                     for (i = 0; i < entity.members.length; i++) {
31747                         var member = context.hasEntity(entity.members[i].id);
31748                         if (member) {
31749                             addChildVertices(member);
31750                         }
31751                     }
31752                 } else if (entity.intersects(extent, graph)) {
31753                     vertices[entity.id] = entity;
31754                 }
31755             }
31756         }
31757
31758         ids.forEach(function(id) {
31759             var entity = context.hasEntity(id);
31760             if (entity && entity.type === 'node') {
31761                 vertices[entity.id] = entity;
31762                 context.graph().parentWays(entity).forEach(function(entity) {
31763                     addChildVertices(entity);
31764                 });
31765             } else if (entity) {
31766                 addChildVertices(entity);
31767             }
31768         });
31769
31770         return vertices;
31771     }
31772
31773     function draw(selection, vertices, klass, graph, zoom) {
31774         var icons = {},
31775             z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
31776
31777         var groups = selection
31778             .data(vertices, iD.Entity.key);
31779
31780         function icon(entity) {
31781             if (entity.id in icons) return icons[entity.id];
31782             icons[entity.id] =
31783                 entity.hasInterestingTags() &&
31784                 context.presets().match(entity, graph).icon;
31785             return icons[entity.id];
31786         }
31787
31788         function setClass(klass) {
31789             return function(entity) {
31790                 this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id);
31791             };
31792         }
31793
31794         function setAttributes(selection) {
31795             ['shadow','stroke','fill'].forEach(function(klass) {
31796                 var rads = radiuses[klass];
31797                 selection.selectAll('.' + klass)
31798                     .each(function(entity) {
31799                         var i = z && icon(entity),
31800                             c = i ? 0.5 : 0,
31801                             r = rads[i ? 3 : z];
31802                         this.setAttribute('cx', c);
31803                         this.setAttribute('cy', -c);
31804                         this.setAttribute('r', r);
31805                         if (i && klass === 'fill') {
31806                             this.setAttribute('visibility', 'hidden');
31807                         } else {
31808                             this.removeAttribute('visibility');
31809                         }
31810                     });
31811             });
31812
31813             selection.selectAll('use')
31814                 .each(function() {
31815                     if (z) {
31816                         this.removeAttribute('visibility');
31817                     } else {
31818                         this.setAttribute('visibility', 'hidden');
31819                     }
31820                 });
31821         }
31822
31823         var enter = groups.enter()
31824             .append('g')
31825             .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; });
31826
31827         enter.append('circle')
31828             .each(setClass('shadow'));
31829
31830         enter.append('circle')
31831             .each(setClass('stroke'));
31832
31833         // Vertices with icons get a `use`.
31834         enter.filter(function(d) { return icon(d); })
31835             .append('use')
31836             .attr('transform', 'translate(-6, -6)')
31837             .attr('xlink:href', function(d) { return '#' + icon(d) + '-12'; })
31838             .attr('width', '12px')
31839             .attr('height', '12px')
31840             .each(setClass('icon'));
31841
31842         // Vertices with tags get a fill.
31843         enter.filter(function(d) { return d.hasInterestingTags(); })
31844             .append('circle')
31845             .each(setClass('fill'));
31846
31847         groups
31848             .attr('transform', iD.svg.PointTransform(projection))
31849             .classed('shared', function(entity) { return graph.isShared(entity); })
31850             .call(setAttributes);
31851
31852         groups.exit()
31853             .remove();
31854     }
31855
31856     function drawVertices(surface, graph, entities, filter, extent, zoom) {
31857         var selected = siblingAndChildVertices(context.selectedIDs(), graph, extent),
31858             wireframe = surface.classed('fill-wireframe'),
31859             vertices = [];
31860
31861         for (var i = 0; i < entities.length; i++) {
31862             var entity = entities[i],
31863                 geometry = entity.geometry(graph);
31864
31865             if (wireframe && geometry === 'point') {
31866                 vertices.push(entity);
31867                 continue;
31868             }
31869
31870             if (geometry !== 'vertex')
31871                 continue;
31872
31873             if (entity.id in selected ||
31874                 entity.hasInterestingTags() ||
31875                 entity.isIntersection(graph)) {
31876                 vertices.push(entity);
31877             }
31878         }
31879
31880         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-persistent')
31881             .filter(filter)
31882             .call(draw, vertices, 'vertex-persistent', graph, zoom);
31883
31884         drawHover(surface, graph, extent, zoom);
31885     }
31886
31887     function drawHover(surface, graph, extent, zoom) {
31888         var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent) : {};
31889
31890         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-hover')
31891             .call(draw, d3.values(hovered), 'vertex-hover', graph, zoom);
31892     }
31893
31894     drawVertices.drawHover = function(surface, graph, target, extent, zoom) {
31895         if (target === hover) return;
31896         hover = target;
31897         drawHover(surface, graph, extent, zoom);
31898     };
31899
31900     return drawVertices;
31901 };
31902 iD.ui = function(context) {
31903     function render(container) {
31904         var map = context.map();
31905
31906         if (iD.detect().opera) container.classed('opera', true);
31907
31908         var hash = iD.behavior.Hash(context);
31909
31910         hash();
31911
31912         if (!hash.hadHash) {
31913             map.centerZoom([0, 0], 2);
31914         }
31915
31916         container.append('svg')
31917             .attr('id', 'defs')
31918             .call(iD.svg.Defs(context));
31919
31920         container.append('div')
31921             .attr('id', 'sidebar')
31922             .attr('class', 'col4')
31923             .call(ui.sidebar);
31924
31925         var content = container.append('div')
31926             .attr('id', 'content');
31927
31928         var bar = content.append('div')
31929             .attr('id', 'bar')
31930             .attr('class', 'fillD');
31931
31932         content.append('div')
31933             .attr('id', 'map')
31934             .call(map);
31935
31936         content
31937             .call(iD.ui.MapInMap(context));
31938
31939         content.append('div')
31940             .call(iD.ui.Info(context));
31941
31942         bar.append('div')
31943             .attr('class', 'spacer col4');
31944
31945         var limiter = bar.append('div')
31946             .attr('class', 'limiter');
31947
31948         limiter.append('div')
31949             .attr('class', 'button-wrap joined col3')
31950             .call(iD.ui.Modes(context), limiter);
31951
31952         limiter.append('div')
31953             .attr('class', 'button-wrap joined col1')
31954             .call(iD.ui.UndoRedo(context));
31955
31956         limiter.append('div')
31957             .attr('class', 'button-wrap col1')
31958             .call(iD.ui.Save(context));
31959
31960         bar.append('div')
31961             .attr('class', 'full-screen')
31962             .call(iD.ui.FullScreen(context));
31963
31964         bar.append('div')
31965             .attr('class', 'spinner')
31966             .call(iD.ui.Spinner(context));
31967
31968         var controls = bar.append('div')
31969             .attr('class', 'map-controls');
31970
31971         controls.append('div')
31972             .attr('class', 'map-control zoombuttons')
31973             .call(iD.ui.Zoom(context));
31974
31975         controls.append('div')
31976             .attr('class', 'map-control geolocate-control')
31977             .call(iD.ui.Geolocate(map));
31978
31979         controls.append('div')
31980             .attr('class', 'map-control background-control')
31981             .call(iD.ui.Background(context));
31982
31983         controls.append('div')
31984             .attr('class', 'map-control map-data-control')
31985             .call(iD.ui.MapData(context));
31986
31987         controls.append('div')
31988             .attr('class', 'map-control help-control')
31989             .call(iD.ui.Help(context));
31990
31991         var about = content.append('div')
31992             .attr('id', 'about');
31993
31994         about.append('div')
31995             .attr('id', 'attrib')
31996             .call(iD.ui.Attribution(context));
31997
31998         var footer = about.append('div')
31999             .attr('id', 'footer')
32000             .attr('class', 'fillD');
32001
32002         footer.append('div')
32003             .attr('class', 'api-status')
32004             .call(iD.ui.Status(context));
32005
32006         footer.append('div')
32007             .attr('id', 'scale-block')
32008             .call(iD.ui.Scale(context));
32009
32010         var aboutList = footer.append('div')
32011             .attr('id', 'info-block')
32012             .append('ul')
32013             .attr('id', 'about-list');
32014
32015         if (!context.embed()) {
32016             aboutList.call(iD.ui.Account(context));
32017         }
32018
32019         aboutList.append('li')
32020             .append('a')
32021             .attr('target', '_blank')
32022             .attr('tabindex', -1)
32023             .attr('href', 'https://github.com/openstreetmap/iD')
32024             .text(iD.version);
32025
32026         var issueLinks = aboutList.append('li');
32027
32028         issueLinks.append('a')
32029             .attr('target', '_blank')
32030             .attr('tabindex', -1)
32031             .attr('href', 'https://github.com/openstreetmap/iD/issues')
32032             .call(iD.svg.Icon('#icon-bug', 'light'))
32033             .call(bootstrap.tooltip()
32034                 .title(t('report_a_bug'))
32035                 .placement('top')
32036             );
32037
32038         issueLinks.append('a')
32039             .attr('target', '_blank')
32040             .attr('tabindex', -1)
32041             .attr('href', 'https://github.com/openstreetmap/iD/blob/master/CONTRIBUTING.md#translating')
32042             .call(iD.svg.Icon('#icon-translate', 'light'))
32043             .call(bootstrap.tooltip()
32044                 .title(t('help_translate'))
32045                 .placement('top')
32046             );
32047
32048         aboutList.append('li')
32049             .attr('class', 'feature-warning')
32050             .attr('tabindex', -1)
32051             .call(iD.ui.FeatureInfo(context));
32052
32053         aboutList.append('li')
32054             .attr('class', 'user-list')
32055             .attr('tabindex', -1)
32056             .call(iD.ui.Contributors(context));
32057
32058         window.onbeforeunload = function() {
32059             return context.save();
32060         };
32061
32062         window.onunload = function() {
32063             context.history().unlock();
32064         };
32065
32066         var mapDimensions = map.dimensions();
32067
32068         d3.select(window).on('resize.editor', function() {
32069             mapDimensions = content.dimensions(null);
32070             map.dimensions(mapDimensions);
32071         });
32072
32073         function pan(d) {
32074             return function() {
32075                 d3.event.preventDefault();
32076                 if (!context.inIntro()) context.pan(d);
32077             };
32078         }
32079
32080         // pan amount
32081         var pa = 10;
32082
32083         var keybinding = d3.keybinding('main')
32084             .on('⌫', function() { d3.event.preventDefault(); })
32085             .on('←', pan([pa, 0]))
32086             .on('↑', pan([0, pa]))
32087             .on('→', pan([-pa, 0]))
32088             .on('↓', pan([0, -pa]))
32089             .on('⇧←', pan([mapDimensions[0], 0]))
32090             .on('⇧↑', pan([0, mapDimensions[1]]))
32091             .on('⇧→', pan([-mapDimensions[0], 0]))
32092             .on('⇧↓', pan([0, -mapDimensions[1]]))
32093             .on(iD.ui.cmd('⌘←'), pan([mapDimensions[0], 0]))
32094             .on(iD.ui.cmd('⌘↑'), pan([0, mapDimensions[1]]))
32095             .on(iD.ui.cmd('⌘→'), pan([-mapDimensions[0], 0]))
32096             .on(iD.ui.cmd('⌘↓'), pan([0, -mapDimensions[1]]));
32097
32098         d3.select(document)
32099             .call(keybinding);
32100
32101         context.enter(iD.modes.Browse(context));
32102
32103         context.container()
32104             .call(iD.ui.Splash(context))
32105             .call(iD.ui.Restore(context));
32106
32107         var authenticating = iD.ui.Loading(context)
32108             .message(t('loading_auth'));
32109
32110         context.connection()
32111             .on('authenticating.ui', function() {
32112                 context.container()
32113                     .call(authenticating);
32114             })
32115             .on('authenticated.ui', function() {
32116                 authenticating.close();
32117             });
32118     }
32119
32120     function ui(container) {
32121         context.container(container);
32122         context.loadLocale(function() {
32123             render(container);
32124         });
32125     }
32126
32127     ui.sidebar = iD.ui.Sidebar(context);
32128
32129     return ui;
32130 };
32131
32132 iD.ui.tooltipHtml = function(text, key) {
32133     var s = '<span>' + text + '</span>';
32134     if (key) {
32135         s += '<div class="keyhint-wrap">' +
32136             '<span> ' + (t('tooltip_keyhint')) + ' </span>' +
32137             '<span class="keyhint"> ' + key + '</span></div>';
32138     }
32139     return s;
32140 };
32141 iD.ui.Account = function(context) {
32142     var connection = context.connection();
32143
32144     function update(selection) {
32145         if (!connection.authenticated()) {
32146             selection.selectAll('#userLink, #logoutLink')
32147                 .classed('hide', true);
32148             return;
32149         }
32150
32151         connection.userDetails(function(err, details) {
32152             var userLink = selection.select('#userLink'),
32153                 logoutLink = selection.select('#logoutLink');
32154
32155             userLink.html('');
32156             logoutLink.html('');
32157
32158             if (err) return;
32159
32160             selection.selectAll('#userLink, #logoutLink')
32161                 .classed('hide', false);
32162
32163             // Link
32164             userLink.append('a')
32165                 .attr('href', connection.userURL(details.display_name))
32166                 .attr('target', '_blank');
32167
32168             // Add thumbnail or dont
32169             if (details.image_url) {
32170                 userLink.append('img')
32171                     .attr('class', 'icon pre-text user-icon')
32172                     .attr('src', details.image_url);
32173             } else {
32174                 userLink
32175                     .call(iD.svg.Icon('#icon-avatar', 'pre-text light'));
32176             }
32177
32178             // Add user name
32179             userLink.append('span')
32180                 .attr('class', 'label')
32181                 .text(details.display_name);
32182
32183             logoutLink.append('a')
32184                 .attr('class', 'logout')
32185                 .attr('href', '#')
32186                 .text(t('logout'))
32187                 .on('click.logout', function() {
32188                     d3.event.preventDefault();
32189                     connection.logout();
32190                 });
32191         });
32192     }
32193
32194     return function(selection) {
32195         selection.append('li')
32196             .attr('id', 'logoutLink')
32197             .classed('hide', true);
32198
32199         selection.append('li')
32200             .attr('id', 'userLink')
32201             .classed('hide', true);
32202
32203         connection.on('auth.account', function() { update(selection); });
32204         update(selection);
32205     };
32206 };
32207 iD.ui.Attribution = function(context) {
32208     var selection;
32209
32210     function attribution(data, klass) {
32211         var div = selection.selectAll('.' + klass)
32212             .data([0]);
32213
32214         div.enter()
32215             .append('div')
32216             .attr('class', klass);
32217
32218         var background = div.selectAll('.attribution')
32219             .data(data, function(d) { return d.name(); });
32220
32221         background.enter()
32222             .append('span')
32223             .attr('class', 'attribution')
32224             .each(function(d) {
32225                 if (d.terms_html) {
32226                     d3.select(this)
32227                         .html(d.terms_html);
32228                     return;
32229                 }
32230
32231                 var source = d.terms_text || d.id || d.name();
32232
32233                 if (d.logo) {
32234                     source = '<img class="source-image" src="' + context.imagePath(d.logo) + '">';
32235                 }
32236
32237                 if (d.terms_url) {
32238                     d3.select(this)
32239                         .append('a')
32240                         .attr('href', d.terms_url)
32241                         .attr('target', '_blank')
32242                         .html(source);
32243                 } else {
32244                     d3.select(this)
32245                         .text(source);
32246                 }
32247             });
32248
32249         background.exit()
32250             .remove();
32251
32252         var copyright = background.selectAll('.copyright-notice')
32253             .data(function(d) {
32254                 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
32255                 return notice ? [notice] : [];
32256             });
32257
32258         copyright.enter()
32259             .append('span')
32260             .attr('class', 'copyright-notice');
32261
32262         copyright.text(String);
32263
32264         copyright.exit()
32265             .remove();
32266     }
32267
32268     function update() {
32269         attribution([context.background().baseLayerSource()], 'base-layer-attribution');
32270         attribution(context.background().overlayLayerSources().filter(function (s) {
32271             return s.validZoom(context.map().zoom());
32272         }), 'overlay-layer-attribution');
32273     }
32274
32275     return function(select) {
32276         selection = select;
32277
32278         context.background()
32279             .on('change.attribution', update);
32280
32281         context.map()
32282             .on('move.attribution', _.throttle(update, 400, {leading: false}));
32283
32284         update();
32285     };
32286 };
32287 iD.ui.Background = function(context) {
32288     var key = 'B',
32289         opacities = [1, 0.75, 0.5, 0.25],
32290         directions = [
32291             ['left', [1, 0]],
32292             ['top', [0, -1]],
32293             ['right', [-1, 0]],
32294             ['bottom', [0, 1]]],
32295         opacityDefault = (context.storage('background-opacity') !== null) ?
32296             (+context.storage('background-opacity')) : 1.0,
32297         customTemplate = context.storage('background-custom-template') || '';
32298
32299     // Can be 0 from <1.3.0 use or due to issue #1923.
32300     if (opacityDefault === 0) opacityDefault = 1.0;
32301
32302     function background(selection) {
32303
32304         function sortSources(a, b) {
32305             return a.best() ? -1
32306                 : b.best() ? 1
32307                 : a.id === 'none' ? 1
32308                 : b.id === 'none' ? -1
32309                 : d3.ascending(a, b);
32310         }
32311
32312         function setOpacity(d) {
32313             var bg = context.container().selectAll('.layer-background')
32314                 .transition()
32315                 .style('opacity', d)
32316                 .attr('data-opacity', d);
32317
32318             if (!iD.detect().opera) {
32319                 iD.util.setTransform(bg, 0, 0);
32320             }
32321
32322             opacityList.selectAll('li')
32323                 .classed('active', function(_) { return _ === d; });
32324
32325             context.storage('background-opacity', d);
32326         }
32327
32328         function selectLayer() {
32329             function active(d) {
32330                 return context.background().showsLayer(d);
32331             }
32332
32333             content.selectAll('.layer, .custom_layer')
32334                 .classed('active', active)
32335                 .selectAll('input')
32336                 .property('checked', active);
32337         }
32338
32339         function clickSetSource(d) {
32340             d3.event.preventDefault();
32341             context.background().baseLayerSource(d);
32342             selectLayer();
32343         }
32344
32345         function editCustom() {
32346             d3.event.preventDefault();
32347             var template = window.prompt(t('background.custom_prompt'), customTemplate);
32348             if (!template ||
32349                 template.indexOf('google.com') !== -1 ||
32350                 template.indexOf('googleapis.com') !== -1 ||
32351                 template.indexOf('google.ru') !== -1) {
32352                 selectLayer();
32353                 return;
32354             }
32355             setCustom(template);
32356         }
32357
32358         function setCustom(template) {
32359             context.background().baseLayerSource(iD.BackgroundSource.Custom(template));
32360             selectLayer();
32361             context.storage('background-custom-template', template);
32362         }
32363
32364         function clickSetOverlay(d) {
32365             d3.event.preventDefault();
32366             context.background().toggleOverlayLayer(d);
32367             selectLayer();
32368         }
32369
32370         function drawList(layerList, type, change, filter) {
32371             var sources = context.background()
32372                 .sources(context.map().extent())
32373                 .filter(filter);
32374
32375             var layerLinks = layerList.selectAll('li.layer')
32376                 .data(sources, function(d) { return d.name(); })
32377                 .sort(sortSources);
32378
32379             var enter = layerLinks.enter()
32380                 .insert('li', '.custom_layer')
32381                 .attr('class', 'layer')
32382                 .classed('best', function(d) { return d.best(); });
32383
32384             // only set tooltips for layers with tooltips
32385             enter.filter(function(d) { return d.description; })
32386                 .call(bootstrap.tooltip()
32387                     .title(function(d) { return d.description; })
32388                     .placement('top'));
32389
32390             enter.filter(function(d) { return d.best(); })
32391                 .append('div')
32392                 .attr('class', 'best')
32393                 .call(bootstrap.tooltip()
32394                     .title(t('background.best_imagery'))
32395                     .placement('left'))
32396                 .append('span')
32397                 .html('&#9733;');
32398
32399             var label = enter.append('label');
32400
32401             label.append('input')
32402                 .attr('type', type)
32403                 .attr('name', 'layers')
32404                 .on('change', change);
32405
32406             label.append('span')
32407                 .text(function(d) { return d.name(); });
32408
32409             layerLinks.exit()
32410                 .remove();
32411
32412             layerList.style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
32413         }
32414
32415         function update() {
32416             backgroundList.call(drawList, 'radio', clickSetSource, function(d) { return !d.overlay; });
32417             overlayList.call(drawList, 'checkbox', clickSetOverlay, function(d) { return d.overlay; });
32418
32419             selectLayer();
32420
32421             var source = context.background().baseLayerSource();
32422             if (source.id === 'custom') {
32423                 customTemplate = source.template;
32424             }
32425         }
32426
32427         function clickNudge(d) {
32428             var timeout = window.setTimeout(function() {
32429                     interval = window.setInterval(nudge, 100);
32430                 }, 500),
32431                 interval;
32432
32433             d3.select(this).on('mouseup', function() {
32434                 window.clearInterval(interval);
32435                 window.clearTimeout(timeout);
32436                 nudge();
32437             });
32438
32439             function nudge() {
32440                 var offset = context.background()
32441                     .nudge(d[1], context.map().zoom())
32442                     .offset();
32443                 resetButton.classed('disabled', offset[0] === 0 && offset[1] === 0);
32444             }
32445         }
32446
32447         function hide() { setVisible(false); }
32448
32449         function toggle() {
32450             if (d3.event) d3.event.preventDefault();
32451             tooltip.hide(button);
32452             setVisible(!button.classed('active'));
32453         }
32454
32455         function setVisible(show) {
32456             if (show !== shown) {
32457                 button.classed('active', show);
32458                 shown = show;
32459
32460                 if (show) {
32461                     selection.on('mousedown.background-inside', function() {
32462                         return d3.event.stopPropagation();
32463                     });
32464                     content.style('display', 'block')
32465                         .style('right', '-300px')
32466                         .transition()
32467                         .duration(200)
32468                         .style('right', '0px');
32469                 } else {
32470                     content.style('display', 'block')
32471                         .style('right', '0px')
32472                         .transition()
32473                         .duration(200)
32474                         .style('right', '-300px')
32475                         .each('end', function() {
32476                             d3.select(this).style('display', 'none');
32477                         });
32478                     selection.on('mousedown.background-inside', null);
32479                 }
32480             }
32481         }
32482
32483
32484         var content = selection.append('div')
32485                 .attr('class', 'fillL map-overlay col3 content hide'),
32486             tooltip = bootstrap.tooltip()
32487                 .placement('left')
32488                 .html(true)
32489                 .title(iD.ui.tooltipHtml(t('background.description'), key)),
32490             button = selection.append('button')
32491                 .attr('tabindex', -1)
32492                 .on('click', toggle)
32493                 .call(iD.svg.Icon('#icon-layers', 'light'))
32494                 .call(tooltip),
32495             shown = false;
32496
32497         var opa = content.append('div')
32498                 .attr('class', 'opacity-options-wrapper');
32499
32500         opa.append('h4')
32501             .text(t('background.title'));
32502
32503         var opacityList = opa.append('ul')
32504             .attr('class', 'opacity-options');
32505
32506         opacityList.selectAll('div.opacity')
32507             .data(opacities)
32508             .enter()
32509             .append('li')
32510             .attr('data-original-title', function(d) {
32511                 return t('background.percent_brightness', { opacity: (d * 100) });
32512             })
32513             .on('click.set-opacity', setOpacity)
32514             .html('<div class="select-box"></div>')
32515             .call(bootstrap.tooltip()
32516                 .placement('left'))
32517             .append('div')
32518             .attr('class', 'opacity')
32519             .style('opacity', function(d) { return 1.25 - d; });
32520
32521         var backgroundList = content.append('ul')
32522             .attr('class', 'layer-list');
32523
32524         var custom = backgroundList.append('li')
32525             .attr('class', 'custom_layer')
32526             .datum(iD.BackgroundSource.Custom());
32527
32528         custom.append('button')
32529             .attr('class', 'layer-browse')
32530             .call(bootstrap.tooltip()
32531                 .title(t('background.custom_button'))
32532                 .placement('left'))
32533             .on('click', editCustom)
32534             .call(iD.svg.Icon('#icon-search'));
32535
32536         var label = custom.append('label');
32537
32538         label.append('input')
32539             .attr('type', 'radio')
32540             .attr('name', 'layers')
32541             .on('change', function () {
32542                 if (customTemplate) {
32543                     setCustom(customTemplate);
32544                 } else {
32545                     editCustom();
32546                 }
32547             });
32548
32549         label.append('span')
32550             .text(t('background.custom'));
32551
32552         content.append('div')
32553           .attr('class', 'imagery-faq')
32554           .append('a')
32555           .attr('target', '_blank')
32556           .attr('tabindex', -1)
32557           .call(iD.svg.Icon('#icon-out-link', 'inline'))
32558           .attr('href', 'https://github.com/openstreetmap/iD/blob/master/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
32559           .append('span')
32560           .text(t('background.imagery_source_faq'));
32561
32562         var overlayList = content.append('ul')
32563             .attr('class', 'layer-list');
32564
32565         var controls = content.append('div')
32566             .attr('class', 'controls-list');
32567
32568         var minimapLabel = controls
32569             .append('label')
32570             .call(bootstrap.tooltip()
32571                 .html(true)
32572                 .title(iD.ui.tooltipHtml(t('background.minimap.tooltip'), '/'))
32573                 .placement('top')
32574             );
32575
32576         minimapLabel.classed('minimap-toggle', true)
32577             .append('input')
32578             .attr('type', 'checkbox')
32579             .on('change', function() {
32580                 iD.ui.MapInMap.toggle();
32581                 d3.event.preventDefault();
32582             });
32583
32584         minimapLabel.append('span')
32585             .text(t('background.minimap.description'));
32586
32587         var adjustments = content.append('div')
32588             .attr('class', 'adjustments');
32589
32590         adjustments.append('a')
32591             .text(t('background.fix_misalignment'))
32592             .attr('href', '#')
32593             .classed('hide-toggle', true)
32594             .classed('expanded', false)
32595             .on('click', function() {
32596                 var exp = d3.select(this).classed('expanded');
32597                 nudgeContainer.style('display', exp ? 'none' : 'block');
32598                 d3.select(this).classed('expanded', !exp);
32599                 d3.event.preventDefault();
32600             });
32601
32602         var nudgeContainer = adjustments.append('div')
32603             .attr('class', 'nudge-container cf')
32604             .style('display', 'none');
32605
32606         nudgeContainer.selectAll('button')
32607             .data(directions).enter()
32608             .append('button')
32609             .attr('class', function(d) { return d[0] + ' nudge'; })
32610             .on('mousedown', clickNudge);
32611
32612         var resetButton = nudgeContainer
32613             .append('button')
32614             .attr('class', 'reset disabled')
32615             .on('click', function () {
32616                 context.background().offset([0, 0]);
32617                 resetButton.classed('disabled', true);
32618             })
32619             .call(iD.svg.Icon('#icon-undo'));
32620
32621         context.map()
32622             .on('move.background-update', _.debounce(update, 1000));
32623
32624         context.background()
32625             .on('change.background-update', update);
32626
32627         update();
32628         setOpacity(opacityDefault);
32629
32630         var keybinding = d3.keybinding('background')
32631             .on(key, toggle)
32632             .on('F', hide)
32633             .on('H', hide);
32634
32635         d3.select(document)
32636             .call(keybinding);
32637
32638         context.surface().on('mousedown.background-outside', hide);
32639         context.container().on('mousedown.background-outside', hide);
32640     }
32641
32642     return background;
32643 };
32644 // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
32645 // For example, ⌘Z -> Ctrl+Z
32646 iD.ui.cmd = function(code) {
32647     if (iD.detect().os === 'mac') {
32648         return code;
32649     }
32650
32651     if (iD.detect().os === 'win') {
32652         if (code === '⌘⇧Z') return 'Ctrl+Y';
32653     }
32654
32655     var result = '',
32656         replacements = {
32657             '⌘': 'Ctrl',
32658             '⇧': 'Shift',
32659             '⌥': 'Alt',
32660             '⌫': 'Backspace',
32661             '⌦': 'Delete'
32662         };
32663
32664     for (var i = 0; i < code.length; i++) {
32665         if (code[i] in replacements) {
32666             result += replacements[code[i]] + '+';
32667         } else {
32668             result += code[i];
32669         }
32670     }
32671
32672     return result;
32673 };
32674 iD.ui.Commit = function(context) {
32675     var dispatch = d3.dispatch('cancel', 'save');
32676
32677     function commit(selection) {
32678         var changes = context.history().changes(),
32679             summary = context.history().difference().summary();
32680
32681         function zoomToEntity(change) {
32682             var entity = change.entity;
32683             if (change.changeType !== 'deleted' &&
32684                 context.graph().entity(entity.id).geometry(context.graph()) !== 'vertex') {
32685                 context.map().zoomTo(entity);
32686                 context.surface().selectAll(
32687                     iD.util.entityOrMemberSelector([entity.id], context.graph()))
32688                     .classed('hover', true);
32689             }
32690         }
32691
32692         var header = selection.append('div')
32693             .attr('class', 'header fillL');
32694
32695         header.append('h3')
32696             .text(t('commit.title'));
32697
32698         var body = selection.append('div')
32699             .attr('class', 'body');
32700
32701
32702         // Comment Section
32703         var commentSection = body.append('div')
32704             .attr('class', 'modal-section form-field commit-form');
32705
32706         commentSection.append('label')
32707             .attr('class', 'form-label')
32708             .text(t('commit.message_label'));
32709
32710         var commentField = commentSection.append('textarea')
32711             .attr('placeholder', t('commit.description_placeholder'))
32712             .attr('maxlength', 255)
32713             .property('value', context.storage('comment') || '')
32714             .on('input.save', enableDisableSaveButton)
32715             .on('change.save', enableDisableSaveButton)
32716             .on('blur.save', function() {
32717                 context.storage('comment', this.value);
32718             });
32719
32720         function enableDisableSaveButton() {
32721             d3.selectAll('.save-section .save-button')
32722                 .attr('disabled', (this.value.length ? null : true));
32723         }
32724
32725         commentField.node().select();
32726
32727         context.connection().userChangesets(function (err, changesets) {
32728             if (err) return;
32729
32730             var comments = [];
32731
32732             for (var i = 0; i < changesets.length; i++) {
32733                 if (changesets[i].tags.comment) {
32734                     comments.push({
32735                         title: changesets[i].tags.comment,
32736                         value: changesets[i].tags.comment
32737                     });
32738                 }
32739             }
32740
32741             commentField.call(d3.combobox().data(comments));
32742         });
32743
32744         var changeSetInfo = commentSection.append('div')
32745             .attr('class', 'changeset-info');
32746
32747         changeSetInfo.append('a')
32748           .attr('target', '_blank')
32749           .attr('tabindex', -1)
32750           .call(iD.svg.Icon('#icon-out-link', 'inline'))
32751           .attr('href', t('commit.about_changeset_comments_link'))
32752           .append('span')
32753           .text(t('commit.about_changeset_comments'));
32754
32755         // Warnings
32756         var warnings = body.selectAll('div.warning-section')
32757             .data([context.history().validate(changes)])
32758             .enter()
32759             .append('div')
32760             .attr('class', 'modal-section warning-section fillL2')
32761             .style('display', function(d) { return _.isEmpty(d) ? 'none' : null; })
32762             .style('background', '#ffb');
32763
32764         warnings.append('h3')
32765             .text(t('commit.warnings'));
32766
32767         var warningLi = warnings.append('ul')
32768             .attr('class', 'changeset-list')
32769             .selectAll('li')
32770             .data(function(d) { return d; })
32771             .enter()
32772             .append('li')
32773             .style()
32774             .on('mouseover', mouseover)
32775             .on('mouseout', mouseout)
32776             .on('click', warningClick);
32777
32778         warningLi
32779             .call(iD.svg.Icon('#icon-alert', 'pre-text'));
32780
32781         warningLi
32782             .append('strong').text(function(d) {
32783                 return d.message;
32784             });
32785
32786         warningLi.filter(function(d) { return d.tooltip; })
32787             .call(bootstrap.tooltip()
32788                 .title(function(d) { return d.tooltip; })
32789                 .placement('top')
32790             );
32791
32792
32793         // Upload Explanation
32794         var saveSection = body.append('div')
32795             .attr('class','modal-section save-section fillL cf');
32796
32797         var prose = saveSection.append('p')
32798             .attr('class', 'commit-info')
32799             .html(t('commit.upload_explanation'));
32800
32801         context.connection().userDetails(function(err, user) {
32802             if (err) return;
32803
32804             var userLink = d3.select(document.createElement('div'));
32805
32806             if (user.image_url) {
32807                 userLink.append('img')
32808                     .attr('src', user.image_url)
32809                     .attr('class', 'icon pre-text user-icon');
32810             }
32811
32812             userLink.append('a')
32813                 .attr('class','user-info')
32814                 .text(user.display_name)
32815                 .attr('href', context.connection().userURL(user.display_name))
32816                 .attr('tabindex', -1)
32817                 .attr('target', '_blank');
32818
32819             prose.html(t('commit.upload_explanation_with_user', {user: userLink.html()}));
32820         });
32821
32822
32823         // Buttons
32824         var buttonSection = saveSection.append('div')
32825             .attr('class','buttons fillL cf');
32826
32827         var saveButton = buttonSection.append('button')
32828             .attr('class', 'action col5 button save-button')
32829             .attr('disabled', function() {
32830                 var n = d3.select('.commit-form textarea').node();
32831                 return (n && n.value.length) ? null : true;
32832             })
32833             .on('click.save', function() {
32834                 dispatch.save({
32835                     comment: commentField.node().value
32836                 });
32837             });
32838
32839         saveButton.append('span')
32840             .attr('class', 'label')
32841             .text(t('commit.save'));
32842
32843         var cancelButton = buttonSection.append('button')
32844             .attr('class', 'action col5 button cancel-button')
32845             .on('click.cancel', function() { dispatch.cancel(); });
32846
32847         cancelButton.append('span')
32848             .attr('class', 'label')
32849             .text(t('commit.cancel'));
32850
32851
32852         // Changes
32853         var changeSection = body.selectAll('div.commit-section')
32854             .data([0])
32855             .enter()
32856             .append('div')
32857             .attr('class', 'commit-section modal-section fillL2');
32858
32859         changeSection.append('h3')
32860             .text(t('commit.changes', {count: summary.length}));
32861
32862         var li = changeSection.append('ul')
32863             .attr('class', 'changeset-list')
32864             .selectAll('li')
32865             .data(summary)
32866             .enter()
32867             .append('li')
32868             .on('mouseover', mouseover)
32869             .on('mouseout', mouseout)
32870             .on('click', zoomToEntity);
32871
32872         li.each(function(d) {
32873             d3.select(this)
32874                 .call(iD.svg.Icon('#icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
32875         });
32876
32877         li.append('span')
32878             .attr('class', 'change-type')
32879             .text(function(d) {
32880                 return t('commit.' + d.changeType) + ' ';
32881             });
32882
32883         li.append('strong')
32884             .attr('class', 'entity-type')
32885             .text(function(d) {
32886                 return context.presets().match(d.entity, d.graph).name();
32887             });
32888
32889         li.append('span')
32890             .attr('class', 'entity-name')
32891             .text(function(d) {
32892                 var name = iD.util.displayName(d.entity) || '',
32893                     string = '';
32894                 if (name !== '') string += ':';
32895                 return string += ' ' + name;
32896             });
32897
32898         li.style('opacity', 0)
32899             .transition()
32900             .style('opacity', 1);
32901
32902         li.style('opacity', 0)
32903             .transition()
32904             .style('opacity', 1);
32905
32906         function mouseover(d) {
32907             if (d.entity) {
32908                 context.surface().selectAll(
32909                     iD.util.entityOrMemberSelector([d.entity.id], context.graph())
32910                 ).classed('hover', true);
32911             }
32912         }
32913
32914         function mouseout() {
32915             context.surface().selectAll('.hover')
32916                 .classed('hover', false);
32917         }
32918
32919         function warningClick(d) {
32920             if (d.entity) {
32921                 context.map().zoomTo(d.entity);
32922                 context.enter(
32923                     iD.modes.Select(context, [d.entity.id])
32924                         .suppressMenu(true));
32925             }
32926         }
32927     }
32928
32929     return d3.rebind(commit, dispatch, 'on');
32930 };
32931 iD.ui.confirm = function(selection) {
32932     var modal = iD.ui.modal(selection);
32933
32934     modal.select('.modal')
32935         .classed('modal-alert', true);
32936
32937     var section = modal.select('.content');
32938
32939     section.append('div')
32940         .attr('class', 'modal-section header');
32941
32942     section.append('div')
32943         .attr('class', 'modal-section message-text');
32944
32945     var buttons = section.append('div')
32946         .attr('class', 'modal-section buttons cf');
32947
32948     modal.okButton = function() {
32949         buttons
32950             .append('button')
32951             .attr('class', 'action col4')
32952             .on('click.confirm', function() {
32953                 modal.remove();
32954             })
32955             .text(t('confirm.okay'));
32956
32957         return modal;
32958     };
32959
32960     return modal;
32961 };
32962 iD.ui.Conflicts = function(context) {
32963     var dispatch = d3.dispatch('download', 'cancel', 'save'),
32964         list;
32965
32966     function conflicts(selection) {
32967         var header = selection
32968             .append('div')
32969             .attr('class', 'header fillL');
32970
32971         header
32972             .append('button')
32973             .attr('class', 'fr')
32974             .on('click', function() { dispatch.cancel(); })
32975             .call(iD.svg.Icon('#icon-close'));
32976
32977         header
32978             .append('h3')
32979             .text(t('save.conflict.header'));
32980
32981         var body = selection
32982             .append('div')
32983             .attr('class', 'body fillL');
32984
32985         body
32986             .append('div')
32987             .attr('class', 'conflicts-help')
32988             .text(t('save.conflict.help'))
32989             .append('a')
32990             .attr('class', 'conflicts-download')
32991             .text(t('save.conflict.download_changes'))
32992             .on('click.download', function() { dispatch.download(); });
32993
32994         body
32995             .append('div')
32996             .attr('class', 'conflict-container fillL3')
32997             .call(showConflict, 0);
32998
32999         body
33000             .append('div')
33001             .attr('class', 'conflicts-done')
33002             .attr('opacity', 0)
33003             .style('display', 'none')
33004             .text(t('save.conflict.done'));
33005
33006         var buttons = body
33007             .append('div')
33008             .attr('class','buttons col12 joined conflicts-buttons');
33009
33010         buttons
33011             .append('button')
33012             .attr('disabled', list.length > 1)
33013             .attr('class', 'action conflicts-button col6')
33014             .text(t('save.title'))
33015             .on('click.try_again', function() { dispatch.save(); });
33016
33017         buttons
33018             .append('button')
33019             .attr('class', 'secondary-action conflicts-button col6')
33020             .text(t('confirm.cancel'))
33021             .on('click.cancel', function() { dispatch.cancel(); });
33022     }
33023
33024
33025     function showConflict(selection, index) {
33026         if (index < 0 || index >= list.length) return;
33027
33028         var parent = d3.select(selection.node().parentNode);
33029
33030         // enable save button if this is the last conflict being reviewed..
33031         if (index === list.length - 1) {
33032             window.setTimeout(function() {
33033                 parent.select('.conflicts-button')
33034                     .attr('disabled', null);
33035
33036                 parent.select('.conflicts-done')
33037                     .transition()
33038                     .attr('opacity', 1)
33039                     .style('display', 'block');
33040             }, 250);
33041         }
33042
33043         var item = selection
33044             .selectAll('.conflict')
33045             .data([list[index]]);
33046
33047         var enter = item.enter()
33048             .append('div')
33049             .attr('class', 'conflict');
33050
33051         enter
33052             .append('h4')
33053             .attr('class', 'conflict-count')
33054             .text(t('save.conflict.count', { num: index + 1, total: list.length }));
33055
33056         enter
33057             .append('a')
33058             .attr('class', 'conflict-description')
33059             .attr('href', '#')
33060             .text(function(d) { return d.name; })
33061             .on('click', function(d) {
33062                 zoomToEntity(d.id);
33063                 d3.event.preventDefault();
33064             });
33065
33066         var details = enter
33067             .append('div')
33068             .attr('class', 'conflict-detail-container');
33069
33070         details
33071             .append('ul')
33072             .attr('class', 'conflict-detail-list')
33073             .selectAll('li')
33074             .data(function(d) { return d.details || []; })
33075             .enter()
33076             .append('li')
33077             .attr('class', 'conflict-detail-item')
33078             .html(function(d) { return d; });
33079
33080         details
33081             .append('div')
33082             .attr('class', 'conflict-choices')
33083             .call(addChoices);
33084
33085         details
33086             .append('div')
33087             .attr('class', 'conflict-nav-buttons joined cf')
33088             .selectAll('button')
33089             .data(['previous', 'next'])
33090             .enter()
33091             .append('button')
33092             .text(function(d) { return t('save.conflict.' + d); })
33093             .attr('class', 'conflict-nav-button action col6')
33094             .attr('disabled', function(d, i) {
33095                 return (i === 0 && index === 0) ||
33096                     (i === 1 && index === list.length - 1) || null;
33097             })
33098             .on('click', function(d, i) {
33099                 var container = parent.select('.conflict-container'),
33100                 sign = (i === 0 ? -1 : 1);
33101
33102                 container
33103                     .selectAll('.conflict')
33104                     .remove();
33105
33106                 container
33107                     .call(showConflict, index + sign);
33108
33109                 d3.event.preventDefault();
33110             });
33111
33112         item.exit()
33113             .remove();
33114
33115     }
33116
33117     function addChoices(selection) {
33118         var choices = selection
33119             .append('ul')
33120             .attr('class', 'layer-list')
33121             .selectAll('li')
33122             .data(function(d) { return d.choices || []; });
33123
33124         var enter = choices.enter()
33125             .append('li')
33126             .attr('class', 'layer');
33127
33128         var label = enter
33129             .append('label');
33130
33131         label
33132             .append('input')
33133             .attr('type', 'radio')
33134             .attr('name', function(d) { return d.id; })
33135             .on('change', function(d, i) {
33136                 var ul = this.parentNode.parentNode.parentNode;
33137                 ul.__data__.chosen = i;
33138                 choose(ul, d);
33139             });
33140
33141         label
33142             .append('span')
33143             .text(function(d) { return d.text; });
33144
33145         choices
33146             .each(function(d, i) {
33147                 var ul = this.parentNode;
33148                 if (ul.__data__.chosen === i) choose(ul, d);
33149             });
33150     }
33151
33152     function choose(ul, datum) {
33153         if (d3.event) d3.event.preventDefault();
33154
33155         d3.select(ul)
33156             .selectAll('li')
33157             .classed('active', function(d) { return d === datum; })
33158             .selectAll('input')
33159             .property('checked', function(d) { return d === datum; });
33160
33161         var extent = iD.geo.Extent(),
33162             entity;
33163
33164         entity = context.graph().hasEntity(datum.id);
33165         if (entity) extent._extend(entity.extent(context.graph()));
33166
33167         datum.action();
33168
33169         entity = context.graph().hasEntity(datum.id);
33170         if (entity) extent._extend(entity.extent(context.graph()));
33171
33172         zoomToEntity(datum.id, extent);
33173     }
33174
33175     function zoomToEntity(id, extent) {
33176         context.surface().selectAll('.hover')
33177             .classed('hover', false);
33178
33179         var entity = context.graph().hasEntity(id);
33180         if (entity) {
33181             if (extent) {
33182                 context.map().trimmedExtent(extent);
33183             } else {
33184                 context.map().zoomTo(entity);
33185             }
33186             context.surface().selectAll(
33187                 iD.util.entityOrMemberSelector([entity.id], context.graph()))
33188                 .classed('hover', true);
33189         }
33190     }
33191
33192
33193     // The conflict list should be an array of objects like:
33194     // {
33195     //     id: id,
33196     //     name: entityName(local),
33197     //     details: merge.conflicts(),
33198     //     chosen: 1,
33199     //     choices: [
33200     //         choice(id, keepMine, forceLocal),
33201     //         choice(id, keepTheirs, forceRemote)
33202     //     ]
33203     // }
33204     conflicts.list = function(_) {
33205         if (!arguments.length) return list;
33206         list = _;
33207         return conflicts;
33208     };
33209
33210     return d3.rebind(conflicts, dispatch, 'on');
33211 };
33212 iD.ui.Contributors = function(context) {
33213     var debouncedUpdate = _.debounce(function() { update(); }, 1000),
33214         limit = 4,
33215         hidden = false,
33216         wrap = d3.select(null);
33217
33218     function update() {
33219         var users = {},
33220             entities = context.intersects(context.map().extent());
33221
33222         entities.forEach(function(entity) {
33223             if (entity && entity.user) users[entity.user] = true;
33224         });
33225
33226         var u = Object.keys(users),
33227             subset = u.slice(0, u.length > limit ? limit - 1 : limit);
33228
33229         wrap.html('')
33230             .call(iD.svg.Icon('#icon-nearby', 'pre-text light'));
33231
33232         var userList = d3.select(document.createElement('span'));
33233
33234         userList.selectAll()
33235             .data(subset)
33236             .enter()
33237             .append('a')
33238             .attr('class', 'user-link')
33239             .attr('href', function(d) { return context.connection().userURL(d); })
33240             .attr('target', '_blank')
33241             .attr('tabindex', -1)
33242             .text(String);
33243
33244         if (u.length > limit) {
33245             var count = d3.select(document.createElement('span'));
33246
33247             count.append('a')
33248                 .attr('target', '_blank')
33249                 .attr('tabindex', -1)
33250                 .attr('href', function() {
33251                     return context.connection().changesetsURL(context.map().center(), context.map().zoom());
33252                 })
33253                 .text(u.length - limit + 1);
33254
33255             wrap.append('span')
33256                 .html(t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
33257
33258         } else {
33259             wrap.append('span')
33260                 .html(t('contributors.list', { users: userList.html() }));
33261         }
33262
33263         if (!u.length) {
33264             hidden = true;
33265             wrap
33266                 .transition()
33267                 .style('opacity', 0);
33268
33269         } else if (hidden) {
33270             wrap
33271                 .transition()
33272                 .style('opacity', 1);
33273         }
33274     }
33275
33276     return function(selection) {
33277         wrap = selection;
33278         update();
33279
33280         context.connection().on('loaded.contributors', debouncedUpdate);
33281         context.map().on('move.contributors', debouncedUpdate);
33282     };
33283 };
33284 iD.ui.Disclosure = function() {
33285     var dispatch = d3.dispatch('toggled'),
33286         title,
33287         expanded = false,
33288         content = function () {};
33289
33290     var disclosure = function(selection) {
33291         var $link = selection.selectAll('.hide-toggle')
33292             .data([0]);
33293
33294         $link.enter().append('a')
33295             .attr('href', '#')
33296             .attr('class', 'hide-toggle');
33297
33298         $link.text(title)
33299             .on('click', toggle)
33300             .classed('expanded', expanded);
33301
33302         var $body = selection.selectAll('div')
33303             .data([0]);
33304
33305         $body.enter().append('div');
33306
33307         $body.classed('hide', !expanded)
33308             .call(content);
33309
33310         function toggle() {
33311             expanded = !expanded;
33312             $link.classed('expanded', expanded);
33313             $body.call(iD.ui.Toggle(expanded));
33314             dispatch.toggled(expanded);
33315         }
33316     };
33317
33318     disclosure.title = function(_) {
33319         if (!arguments.length) return title;
33320         title = _;
33321         return disclosure;
33322     };
33323
33324     disclosure.expanded = function(_) {
33325         if (!arguments.length) return expanded;
33326         expanded = _;
33327         return disclosure;
33328     };
33329
33330     disclosure.content = function(_) {
33331         if (!arguments.length) return content;
33332         content = _;
33333         return disclosure;
33334     };
33335
33336     return d3.rebind(disclosure, dispatch, 'on');
33337 };
33338 iD.ui.EntityEditor = function(context) {
33339     var dispatch = d3.dispatch('choose'),
33340         state = 'select',
33341         coalesceChanges = false,
33342         modified = false,
33343         base,
33344         id,
33345         preset,
33346         reference;
33347
33348     var presetEditor = iD.ui.preset(context)
33349         .on('change', changeTags);
33350     var rawTagEditor = iD.ui.RawTagEditor(context)
33351         .on('change', changeTags);
33352
33353     function entityEditor(selection) {
33354         var entity = context.entity(id),
33355             tags = _.clone(entity.tags);
33356
33357         var $header = selection.selectAll('.header')
33358             .data([0]);
33359
33360         // Enter
33361         var $enter = $header.enter().append('div')
33362             .attr('class', 'header fillL cf');
33363
33364         $enter.append('button')
33365             .attr('class', 'fl preset-reset preset-choose')
33366             .append('span')
33367             .html('&#9668;');
33368
33369         $enter.append('button')
33370             .attr('class', 'fr preset-close')
33371             .call(iD.svg.Icon(modified ? '#icon-apply' : '#icon-close'));
33372
33373         $enter.append('h3');
33374
33375         // Update
33376         $header.select('h3')
33377             .text(t('inspector.edit'));
33378
33379         $header.select('.preset-close')
33380             .on('click', function() {
33381                 context.enter(iD.modes.Browse(context));
33382             });
33383
33384         var $body = selection.selectAll('.inspector-body')
33385             .data([0]);
33386
33387         // Enter
33388         $enter = $body.enter().append('div')
33389             .attr('class', 'inspector-body');
33390
33391         $enter.append('div')
33392             .attr('class', 'preset-list-item inspector-inner')
33393             .append('div')
33394             .attr('class', 'preset-list-button-wrap')
33395             .append('button')
33396             .attr('class', 'preset-list-button preset-reset')
33397             .call(bootstrap.tooltip()
33398                 .title(t('inspector.back_tooltip'))
33399                 .placement('bottom'))
33400             .append('div')
33401             .attr('class', 'label');
33402
33403         $body.select('.preset-list-button-wrap')
33404             .call(reference.button);
33405
33406         $body.select('.preset-list-item')
33407             .call(reference.body);
33408
33409         $enter.append('div')
33410             .attr('class', 'inspector-border inspector-preset');
33411
33412         $enter.append('div')
33413             .attr('class', 'inspector-border raw-tag-editor inspector-inner');
33414
33415         $enter.append('div')
33416             .attr('class', 'inspector-border raw-member-editor inspector-inner');
33417
33418         $enter.append('div')
33419             .attr('class', 'raw-membership-editor inspector-inner');
33420
33421         selection.selectAll('.preset-reset')
33422             .on('click', function() {
33423                 dispatch.choose(preset);
33424             });
33425
33426         // Update
33427         $body.select('.preset-list-item button')
33428             .call(iD.ui.PresetIcon()
33429                 .geometry(context.geometry(id))
33430                 .preset(preset));
33431
33432         $body.select('.preset-list-item .label')
33433             .text(preset.name());
33434
33435         $body.select('.inspector-preset')
33436             .call(presetEditor
33437                 .preset(preset)
33438                 .entityID(id)
33439                 .tags(tags)
33440                 .state(state));
33441
33442         $body.select('.raw-tag-editor')
33443             .call(rawTagEditor
33444                 .preset(preset)
33445                 .entityID(id)
33446                 .tags(tags)
33447                 .state(state));
33448
33449         if (entity.type === 'relation') {
33450             $body.select('.raw-member-editor')
33451                 .style('display', 'block')
33452                 .call(iD.ui.RawMemberEditor(context)
33453                     .entityID(id));
33454         } else {
33455             $body.select('.raw-member-editor')
33456                 .style('display', 'none');
33457         }
33458
33459         $body.select('.raw-membership-editor')
33460             .call(iD.ui.RawMembershipEditor(context)
33461                 .entityID(id));
33462
33463         function historyChanged() {
33464             if (state === 'hide') return;
33465
33466             var entity = context.hasEntity(id),
33467                 graph = context.graph();
33468             if (!entity) return;
33469
33470             entityEditor.preset(context.presets().match(entity, graph));
33471             entityEditor.modified(base !== graph);
33472             entityEditor(selection);
33473         }
33474
33475         context.history()
33476             .on('change.entity-editor', historyChanged);
33477     }
33478
33479     function clean(o) {
33480
33481         function cleanVal(k, v) {
33482             function keepSpaces(k) {
33483                 var whitelist = ['opening_hours', 'service_times', 'collection_times',
33484                     'operating_times', 'smoking_hours', 'happy_hours'];
33485                 return _.any(whitelist, function(s) { return k.indexOf(s) !== -1; });
33486             }
33487
33488             var blacklist = ['description', 'note', 'fixme'];
33489             if (_.any(blacklist, function(s) { return k.indexOf(s) !== -1; })) return v;
33490
33491             var cleaned = v.split(';')
33492                 .map(function(s) { return s.trim(); })
33493                 .join(keepSpaces(k) ? '; ' : ';');
33494
33495             // The code below is not intended to validate websites and emails.
33496             // It is only intended to prevent obvious copy-paste errors. (#2323)
33497
33498             // clean website- and email-like tags
33499             if (k.indexOf('website') !== -1 ||
33500                 k.indexOf('email') !== -1 ||
33501                 cleaned.indexOf('http') === 0) {
33502                 cleaned = cleaned
33503                     .replace(/[\u200B-\u200F\uFEFF]/g, '');  // strip LRM and other zero width chars
33504
33505             }
33506
33507             return cleaned;
33508         }
33509
33510         var out = {}, k, v;
33511         for (k in o) {
33512             if (k && (v = o[k]) !== undefined) {
33513                 out[k] = cleanVal(k, v);
33514             }
33515         }
33516         return out;
33517     }
33518
33519     // Tag changes that fire on input can all get coalesced into a single
33520     // history operation when the user leaves the field.  #2342
33521     function changeTags(changed, onInput) {
33522         var entity = context.entity(id),
33523             annotation = t('operations.change_tags.annotation'),
33524             tags = clean(_.extend({}, entity.tags, changed));
33525
33526         if (!_.isEqual(entity.tags, tags)) {
33527             if (coalesceChanges) {
33528                 context.overwrite(iD.actions.ChangeTags(id, tags), annotation);
33529             } else {
33530                 context.perform(iD.actions.ChangeTags(id, tags), annotation);
33531             }
33532         }
33533
33534         coalesceChanges = !!onInput;
33535     }
33536
33537     entityEditor.modified = function(_) {
33538         if (!arguments.length) return modified;
33539         modified = _;
33540         d3.selectAll('button.preset-close use')
33541             .attr('xlink:href', (modified ? '#icon-apply' : '#icon-close'));
33542     };
33543
33544     entityEditor.state = function(_) {
33545         if (!arguments.length) return state;
33546         state = _;
33547         return entityEditor;
33548     };
33549
33550     entityEditor.entityID = function(_) {
33551         if (!arguments.length) return id;
33552         id = _;
33553         base = context.graph();
33554         entityEditor.preset(context.presets().match(context.entity(id), base));
33555         entityEditor.modified(false);
33556         coalesceChanges = false;
33557         return entityEditor;
33558     };
33559
33560     entityEditor.preset = function(_) {
33561         if (!arguments.length) return preset;
33562         if (_ !== preset) {
33563             preset = _;
33564             reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context)
33565                 .showing(false);
33566         }
33567         return entityEditor;
33568     };
33569
33570     return d3.rebind(entityEditor, dispatch, 'on');
33571 };
33572 iD.ui.FeatureInfo = function(context) {
33573     function update(selection) {
33574         var features = context.features(),
33575             stats = features.stats(),
33576             count = 0,
33577             hiddenList = _.compact(_.map(features.hidden(), function(k) {
33578                 if (stats[k]) {
33579                     count += stats[k];
33580                     return String(stats[k]) + ' ' + t('feature.' + k + '.description');
33581                 }
33582             }));
33583
33584         selection.html('');
33585
33586         if (hiddenList.length) {
33587             var tooltip = bootstrap.tooltip()
33588                     .placement('top')
33589                     .html(true)
33590                     .title(function() {
33591                         return iD.ui.tooltipHtml(hiddenList.join('<br/>'));
33592                     });
33593
33594             var warning = selection.append('a')
33595                 .attr('href', '#')
33596                 .attr('tabindex', -1)
33597                 .html(t('feature_info.hidden_warning', { count: count }))
33598                 .call(tooltip)
33599                 .on('click', function() {
33600                     tooltip.hide(warning);
33601                     // open map data panel?
33602                     d3.event.preventDefault();
33603                 });
33604         }
33605
33606         selection
33607             .classed('hide', !hiddenList.length);
33608     }
33609
33610     return function(selection) {
33611         update(selection);
33612
33613         context.features().on('change.feature_info', function() {
33614             update(selection);
33615         });
33616     };
33617 };
33618 iD.ui.FeatureList = function(context) {
33619     var geocodeResults;
33620
33621     function featureList(selection) {
33622         var header = selection.append('div')
33623             .attr('class', 'header fillL cf');
33624
33625         header.append('h3')
33626             .text(t('inspector.feature_list'));
33627
33628         function keypress() {
33629             var q = search.property('value'),
33630                 items = list.selectAll('.feature-list-item');
33631             if (d3.event.keyCode === 13 && q.length && items.size()) {
33632                 click(items.datum());
33633             }
33634         }
33635
33636         function inputevent() {
33637             geocodeResults = undefined;
33638             drawList();
33639         }
33640
33641         var searchWrap = selection.append('div')
33642             .attr('class', 'search-header');
33643
33644         var search = searchWrap.append('input')
33645             .attr('placeholder', t('inspector.search'))
33646             .attr('type', 'search')
33647             .on('keypress', keypress)
33648             .on('input', inputevent);
33649
33650         searchWrap
33651             .call(iD.svg.Icon('#icon-search', 'pre-text'));
33652
33653         var listWrap = selection.append('div')
33654             .attr('class', 'inspector-body');
33655
33656         var list = listWrap.append('div')
33657             .attr('class', 'feature-list cf');
33658
33659         context
33660             .on('exit.feature-list', clearSearch);
33661         context.map()
33662             .on('drawn.feature-list', mapDrawn);
33663
33664         function clearSearch() {
33665             search.property('value', '');
33666             drawList();
33667         }
33668
33669         function mapDrawn(e) {
33670             if (e.full) {
33671                 drawList();
33672             }
33673         }
33674
33675         function features() {
33676             var entities = {},
33677                 result = [],
33678                 graph = context.graph(),
33679                 q = search.property('value').toLowerCase();
33680
33681             if (!q) return result;
33682
33683             var idMatch = q.match(/^([nwr])([0-9]+)$/);
33684
33685             if (idMatch) {
33686                 result.push({
33687                     id: idMatch[0],
33688                     geometry: idMatch[1] === 'n' ? 'point' : idMatch[1] === 'w' ? 'line' : 'relation',
33689                     type: idMatch[1] === 'n' ? t('inspector.node') : idMatch[1] === 'w' ? t('inspector.way') : t('inspector.relation'),
33690                     name: idMatch[2]
33691                 });
33692             }
33693
33694             var locationMatch = sexagesimal.pair(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
33695
33696             if (locationMatch) {
33697                 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
33698                 result.push({
33699                     id: -1,
33700                     geometry: 'point',
33701                     type: t('inspector.location'),
33702                     name: loc[0].toFixed(6) + ', ' + loc[1].toFixed(6),
33703                     location: loc
33704                 });
33705             }
33706
33707             function addEntity(entity) {
33708                 if (entity.id in entities || result.length > 200)
33709                     return;
33710
33711                 entities[entity.id] = true;
33712
33713                 var name = iD.util.displayName(entity) || '';
33714                 if (name.toLowerCase().indexOf(q) >= 0) {
33715                     result.push({
33716                         id: entity.id,
33717                         entity: entity,
33718                         geometry: context.geometry(entity.id),
33719                         type: context.presets().match(entity, graph).name(),
33720                         name: name
33721                     });
33722                 }
33723
33724                 graph.parentRelations(entity).forEach(function(parent) {
33725                     addEntity(parent);
33726                 });
33727             }
33728
33729             var visible = context.surface().selectAll('.point, .line, .area')[0];
33730             for (var i = 0; i < visible.length && result.length <= 200; i++) {
33731                 addEntity(visible[i].__data__);
33732             }
33733
33734             (geocodeResults || []).forEach(function(d) {
33735                 // https://github.com/openstreetmap/iD/issues/1890
33736                 if (d.osm_type && d.osm_id) {
33737                     result.push({
33738                         id: iD.Entity.id.fromOSM(d.osm_type, d.osm_id),
33739                         geometry: d.osm_type === 'relation' ? 'relation' : d.osm_type === 'way' ? 'line' : 'point',
33740                         type: d.type !== 'yes' ? (d.type.charAt(0).toUpperCase() + d.type.slice(1)).replace('_', ' ')
33741                                                : (d.class.charAt(0).toUpperCase() + d.class.slice(1)).replace('_', ' '),
33742                         name: d.display_name,
33743                         extent: new iD.geo.Extent(
33744                             [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
33745                             [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
33746                     });
33747                 }
33748             });
33749
33750             return result;
33751         }
33752
33753         function drawList() {
33754             var value = search.property('value'),
33755                 results = features();
33756
33757             list.classed('filtered', value.length);
33758
33759             var noResultsWorldwide = geocodeResults && geocodeResults.length === 0;
33760
33761             var resultsIndicator = list.selectAll('.no-results-item')
33762                 .data([0])
33763                 .enter().append('button')
33764                 .property('disabled', true)
33765                 .attr('class', 'no-results-item')
33766                 .call(iD.svg.Icon('#icon-alert', 'pre-text'));
33767
33768             resultsIndicator.append('span')
33769                 .attr('class', 'entity-name');
33770
33771             list.selectAll('.no-results-item .entity-name')
33772                 .text(noResultsWorldwide ? t('geocoder.no_results_worldwide') : t('geocoder.no_results_visible'));
33773
33774             list.selectAll('.geocode-item')
33775                 .data([0])
33776                 .enter().append('button')
33777                 .attr('class', 'geocode-item')
33778                 .on('click', geocode)
33779                 .append('div')
33780                 .attr('class', 'label')
33781                 .append('span')
33782                 .attr('class', 'entity-name')
33783                 .text(t('geocoder.search'));
33784
33785             list.selectAll('.no-results-item')
33786                 .style('display', (value.length && !results.length) ? 'block' : 'none');
33787
33788             list.selectAll('.geocode-item')
33789                 .style('display', (value && geocodeResults === undefined) ? 'block' : 'none');
33790
33791             list.selectAll('.feature-list-item')
33792                 .data([-1])
33793                 .remove();
33794
33795             var items = list.selectAll('.feature-list-item')
33796                 .data(results, function(d) { return d.id; });
33797
33798             var enter = items.enter()
33799                 .insert('button', '.geocode-item')
33800                 .attr('class', 'feature-list-item')
33801                 .on('mouseover', mouseover)
33802                 .on('mouseout', mouseout)
33803                 .on('click', click);
33804
33805             var label = enter
33806                 .append('div')
33807                 .attr('class', 'label');
33808
33809             label.each(function(d) {
33810                 d3.select(this)
33811                     .call(iD.svg.Icon('#icon-' + d.geometry, 'pre-text'));
33812             });
33813
33814             label.append('span')
33815                 .attr('class', 'entity-type')
33816                 .text(function(d) { return d.type; });
33817
33818             label.append('span')
33819                 .attr('class', 'entity-name')
33820                 .text(function(d) { return d.name; });
33821
33822             enter.style('opacity', 0)
33823                 .transition()
33824                 .style('opacity', 1);
33825
33826             items.order();
33827
33828             items.exit()
33829                 .remove();
33830         }
33831
33832         function mouseover(d) {
33833             if (d.id === -1) return;
33834
33835             context.surface().selectAll(iD.util.entityOrMemberSelector([d.id], context.graph()))
33836                 .classed('hover', true);
33837         }
33838
33839         function mouseout() {
33840             context.surface().selectAll('.hover')
33841                 .classed('hover', false);
33842         }
33843
33844         function click(d) {
33845             d3.event.preventDefault();
33846             if (d.location) {
33847                 context.map().centerZoom([d.location[1], d.location[0]], 20);
33848             }
33849             else if (d.entity) {
33850                 if (d.entity.type === 'node') {
33851                     context.map().center(d.entity.loc);
33852                 } else if (d.entity.type === 'way') {
33853                     var center = context.projection(context.map().center()),
33854                         edge = iD.geo.chooseEdge(context.childNodes(d.entity), center, context.projection);
33855                     context.map().center(edge.loc);
33856                 }
33857                 context.enter(iD.modes.Select(context, [d.entity.id]).suppressMenu(true));
33858             } else {
33859                 context.zoomToEntity(d.id);
33860             }
33861         }
33862
33863         function geocode() {
33864             var searchVal = encodeURIComponent(search.property('value'));
33865             d3.json('https://nominatim.openstreetmap.org/search/' + searchVal + '?limit=10&format=json', function(err, resp) {
33866                 geocodeResults = resp || [];
33867                 drawList();
33868             });
33869         }
33870     }
33871
33872     return featureList;
33873 };
33874 iD.ui.flash = function(selection) {
33875     var modal = iD.ui.modal(selection);
33876
33877     modal.select('.modal').classed('modal-flash', true);
33878
33879     modal.select('.content')
33880         .classed('modal-section', true)
33881         .append('div')
33882         .attr('class', 'description');
33883
33884     modal.on('click.flash', function() { modal.remove(); });
33885
33886     setTimeout(function() {
33887         modal.remove();
33888         return true;
33889     }, 1500);
33890
33891     return modal;
33892 };
33893 iD.ui.FullScreen = function(context) {
33894     var element = context.container().node(),
33895         keybinding = d3.keybinding('full-screen');
33896         // button;
33897
33898     function getFullScreenFn() {
33899         if (element.requestFullscreen) {
33900             return element.requestFullscreen;
33901         } else if (element.msRequestFullscreen) {
33902             return  element.msRequestFullscreen;
33903         } else if (element.mozRequestFullScreen) {
33904             return  element.mozRequestFullScreen;
33905         } else if (element.webkitRequestFullscreen) {
33906             return element.webkitRequestFullscreen;
33907         }
33908     }
33909
33910     function getExitFullScreenFn() {
33911         if (document.exitFullscreen) {
33912             return document.exitFullscreen;
33913         } else if (document.msExitFullscreen) {
33914             return  document.msExitFullscreen;
33915         } else if (document.mozCancelFullScreen) {
33916             return  document.mozCancelFullScreen;
33917         } else if (document.webkitExitFullscreen) {
33918             return document.webkitExitFullscreen;
33919         }
33920     }
33921
33922     function isFullScreen() {
33923         return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement ||
33924             document.msFullscreenElement;
33925     }
33926
33927     function isSupported() {
33928         return !!getFullScreenFn();
33929     }
33930
33931     function fullScreen() {
33932         d3.event.preventDefault();
33933         if (!isFullScreen()) {
33934             // button.classed('active', true);
33935             getFullScreenFn().apply(element);
33936         } else {
33937             // button.classed('active', false);
33938             getExitFullScreenFn().apply(document);
33939         }
33940     }
33941
33942     return function() { // selection) {
33943         if (!isSupported())
33944             return;
33945
33946         // var tooltip = bootstrap.tooltip()
33947         //     .placement('left');
33948
33949         // button = selection.append('button')
33950         //     .attr('title', t('full_screen'))
33951         //     .attr('tabindex', -1)
33952         //     .on('click', fullScreen)
33953         //     .call(tooltip);
33954
33955         // button.append('span')
33956         //     .attr('class', 'icon full-screen');
33957
33958         keybinding
33959             .on('f11', fullScreen)
33960             .on(iD.ui.cmd('⌘⇧F'), fullScreen);
33961
33962         d3.select(document)
33963             .call(keybinding);
33964     };
33965 };
33966 iD.ui.Geolocate = function(map) {
33967     function click() {
33968         navigator.geolocation.getCurrentPosition(
33969             success, error);
33970     }
33971
33972     function success(position) {
33973         var extent = iD.geo.Extent([position.coords.longitude, position.coords.latitude])
33974             .padByMeters(position.coords.accuracy);
33975
33976         map.centerZoom(extent.center(), Math.min(20, map.extentZoom(extent)));
33977     }
33978
33979     function error() { }
33980
33981     return function(selection) {
33982         if (!navigator.geolocation) return;
33983
33984         selection.append('button')
33985             .attr('tabindex', -1)
33986             .attr('title', t('geolocate.title'))
33987             .on('click', click)
33988             .call(iD.svg.Icon('#icon-geolocate', 'light'))
33989             .call(bootstrap.tooltip()
33990                 .placement('left'));
33991     };
33992 };
33993 iD.ui.Help = function(context) {
33994     var key = 'H';
33995
33996     var docKeys = [
33997         'help.help',
33998         'help.editing_saving',
33999         'help.roads',
34000         'help.gps',
34001         'help.imagery',
34002         'help.addresses',
34003         'help.inspector',
34004         'help.buildings',
34005         'help.relations'];
34006
34007     var docs = docKeys.map(function(key) {
34008         var text = t(key);
34009         return {
34010             title: text.split('\n')[0].replace('#', '').trim(),
34011             html: marked(text.split('\n').slice(1).join('\n'))
34012         };
34013     });
34014
34015     function help(selection) {
34016
34017         function hide() {
34018             setVisible(false);
34019         }
34020
34021         function toggle() {
34022             if (d3.event) d3.event.preventDefault();
34023             tooltip.hide(button);
34024             setVisible(!button.classed('active'));
34025         }
34026
34027         function setVisible(show) {
34028             if (show !== shown) {
34029                 button.classed('active', show);
34030                 shown = show;
34031
34032                 if (show) {
34033                     selection.on('mousedown.help-inside', function() {
34034                         return d3.event.stopPropagation();
34035                     });
34036                     pane.style('display', 'block')
34037                         .style('right', '-500px')
34038                         .transition()
34039                         .duration(200)
34040                         .style('right', '0px');
34041                 } else {
34042                     pane.style('right', '0px')
34043                         .transition()
34044                         .duration(200)
34045                         .style('right', '-500px')
34046                         .each('end', function() {
34047                             d3.select(this).style('display', 'none');
34048                         });
34049                     selection.on('mousedown.help-inside', null);
34050                 }
34051             }
34052         }
34053
34054         function clickHelp(d, i) {
34055             pane.property('scrollTop', 0);
34056             doctitle.html(d.title);
34057             body.html(d.html);
34058             body.selectAll('a')
34059                 .attr('target', '_blank');
34060             menuItems.classed('selected', function(m) {
34061                 return m.title === d.title;
34062             });
34063
34064             nav.html('');
34065
34066             if (i > 0) {
34067                 var prevLink = nav.append('a')
34068                     .attr('class', 'previous')
34069                     .on('click', function() {
34070                         clickHelp(docs[i - 1], i - 1);
34071                     });
34072                 prevLink.append('span').html('&#9668; ' + docs[i - 1].title);
34073             }
34074             if (i < docs.length - 1) {
34075                 var nextLink = nav.append('a')
34076                     .attr('class', 'next')
34077                     .on('click', function() {
34078                         clickHelp(docs[i + 1], i + 1);
34079                     });
34080                 nextLink.append('span').html(docs[i + 1].title + ' &#9658;');
34081             }
34082         }
34083
34084         function clickWalkthrough() {
34085             d3.select(document.body).call(iD.ui.intro(context));
34086             setVisible(false);
34087         }
34088
34089
34090         var pane = selection.append('div')
34091                 .attr('class', 'help-wrap map-overlay fillL col5 content hide'),
34092             tooltip = bootstrap.tooltip()
34093                 .placement('left')
34094                 .html(true)
34095                 .title(iD.ui.tooltipHtml(t('help.title'), key)),
34096             button = selection.append('button')
34097                 .attr('tabindex', -1)
34098                 .on('click', toggle)
34099                 .call(iD.svg.Icon('#icon-help', 'light'))
34100                 .call(tooltip),
34101             shown = false;
34102
34103
34104         var toc = pane.append('ul')
34105             .attr('class', 'toc');
34106
34107         var menuItems = toc.selectAll('li')
34108             .data(docs)
34109             .enter()
34110             .append('li')
34111             .append('a')
34112             .html(function(d) { return d.title; })
34113             .on('click', clickHelp);
34114
34115         toc.append('li')
34116             .attr('class','walkthrough')
34117             .append('a')
34118             .text(t('splash.walkthrough'))
34119             .on('click', clickWalkthrough);
34120
34121         var content = pane.append('div')
34122             .attr('class', 'left-content');
34123
34124         var doctitle = content.append('h2')
34125             .text(t('help.title'));
34126
34127         var body = content.append('div')
34128             .attr('class', 'body');
34129
34130         var nav = content.append('div')
34131             .attr('class', 'nav');
34132
34133         clickHelp(docs[0], 0);
34134
34135         var keybinding = d3.keybinding('help')
34136             .on(key, toggle)
34137             .on('B', hide)
34138             .on('F', hide);
34139
34140         d3.select(document)
34141             .call(keybinding);
34142
34143         context.surface().on('mousedown.help-outside', hide);
34144         context.container().on('mousedown.help-outside', hide);
34145     }
34146
34147     return help;
34148 };
34149 iD.ui.Info = function(context) {
34150     var key = iD.ui.cmd('⌘I'),
34151         imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
34152         hidden = true;
34153
34154     function info(selection) {
34155         function radiansToMeters(r) {
34156             // using WGS84 authalic radius (6371007.1809 m)
34157             return r * 6371007.1809;
34158         }
34159
34160         function steradiansToSqmeters(r) {
34161             // http://gis.stackexchange.com/a/124857/40446
34162             return r / 12.56637 * 510065621724000;
34163         }
34164
34165         function toLineString(feature) {
34166             if (feature.type === 'LineString') return feature;
34167
34168             var result = { type: 'LineString', coordinates: [] };
34169             if (feature.type === 'Polygon') {
34170                 result.coordinates = feature.coordinates[0];
34171             } else if (feature.type === 'MultiPolygon') {
34172                 result.coordinates = feature.coordinates[0][0];
34173             }
34174
34175             return result;
34176         }
34177
34178         function displayLength(m) {
34179             var d = m * (imperial ? 3.28084 : 1),
34180                 p, unit;
34181
34182             if (imperial) {
34183                 if (d >= 5280) {
34184                     d /= 5280;
34185                     unit = 'mi';
34186                 } else {
34187                     unit = 'ft';
34188                 }
34189             } else {
34190                 if (d >= 1000) {
34191                     d /= 1000;
34192                     unit = 'km';
34193                 } else {
34194                     unit = 'm';
34195                 }
34196             }
34197
34198             // drop unnecessary precision
34199             p = d > 1000 ? 0 : d > 100 ? 1 : 2;
34200
34201             return String(d.toFixed(p)) + ' ' + unit;
34202         }
34203
34204         function displayArea(m2) {
34205             var d = m2 * (imperial ? 10.7639111056 : 1),
34206                 d1, d2, p1, p2, unit1, unit2;
34207
34208             if (imperial) {
34209                 if (d >= 6969600) {     // > 0.25mi² show mi²
34210                     d1 = d / 27878400;
34211                     unit1 = 'mi²';
34212                 } else {
34213                     d1 = d;
34214                     unit1 = 'ft²';
34215                 }
34216
34217                 if (d > 4356 && d < 43560000) {   // 0.1 - 1000 acres
34218                     d2 = d / 43560;
34219                     unit2 = 'ac';
34220                 }
34221
34222             } else {
34223                 if (d >= 250000) {    // > 0.25km² show km²
34224                     d1 = d / 1000000;
34225                     unit1 = 'km²';
34226                 } else {
34227                     d1 = d;
34228                     unit1 = 'm²';
34229                 }
34230
34231                 if (d > 1000 && d < 10000000) {   // 0.1 - 1000 hectares
34232                     d2 = d / 10000;
34233                     unit2 = 'ha';
34234                 }
34235             }
34236
34237             // drop unnecessary precision
34238             p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2;
34239             p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2;
34240
34241             return String(d1.toFixed(p1)) + ' ' + unit1 +
34242                 (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : '');
34243         }
34244
34245
34246         function redraw() {
34247             if (hidden) return;
34248
34249             var resolver = context.graph(),
34250                 selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }),
34251                 singular = selected.length === 1 ? selected[0] : null,
34252                 extent = iD.geo.Extent(),
34253                 entity;
34254
34255             wrap.html('');
34256             wrap.append('h4')
34257                 .attr('class', 'infobox-heading fillD')
34258                 .text(singular || t('infobox.selected', { n: selected.length }));
34259
34260             if (!selected.length) return;
34261
34262             var center;
34263             for (var i = 0; i < selected.length; i++) {
34264                 entity = context.entity(selected[i]);
34265                 extent._extend(entity.extent(resolver));
34266             }
34267             center = extent.center();
34268
34269
34270             var list = wrap.append('ul');
34271
34272             // multiple wrap, just display extent center..
34273             if (!singular) {
34274                 list.append('li')
34275                     .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
34276                 return;
34277             }
34278
34279             // single wrap, display details..
34280             if (!entity) return;
34281             var geometry = entity.geometry(resolver);
34282
34283             if (geometry === 'line' || geometry === 'area') {
34284                 var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()),
34285                     feature = entity.asGeoJSON(resolver),
34286                     length = radiansToMeters(d3.geo.length(toLineString(feature))),
34287                     lengthLabel = t('infobox.' + (closed ? 'perimeter' : 'length')),
34288                     centroid = d3.geo.centroid(feature);
34289
34290                 list.append('li')
34291                     .text(t('infobox.geometry') + ': ' +
34292                         (closed ? t('infobox.closed') + ' ' : '') + t('geometry.' + geometry) );
34293
34294                 if (closed) {
34295                     var area = steradiansToSqmeters(entity.area(resolver));
34296                     list.append('li')
34297                         .text(t('infobox.area') + ': ' + displayArea(area));
34298                 }
34299
34300                 list.append('li')
34301                     .text(lengthLabel + ': ' + displayLength(length));
34302
34303                 list.append('li')
34304                     .text(t('infobox.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5));
34305
34306
34307                 var toggle  = imperial ? 'imperial' : 'metric';
34308                 wrap.append('a')
34309                     .text(t('infobox.' + toggle))
34310                     .attr('href', '#')
34311                     .attr('class', 'button')
34312                     .on('click', function() {
34313                         d3.event.preventDefault();
34314                         imperial = !imperial;
34315                         redraw();
34316                     });
34317
34318             } else {
34319                 var centerLabel = t('infobox.' + (entity.type === 'node' ? 'location' : 'center'));
34320
34321                 list.append('li')
34322                     .text(t('infobox.geometry') + ': ' + t('geometry.' + geometry));
34323
34324                 list.append('li')
34325                     .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
34326             }
34327         }
34328
34329
34330         function toggle() {
34331             if (d3.event) d3.event.preventDefault();
34332
34333             hidden = !hidden;
34334
34335             if (hidden) {
34336                 wrap
34337                     .style('display', 'block')
34338                     .style('opacity', 1)
34339                     .transition()
34340                     .duration(200)
34341                     .style('opacity', 0)
34342                     .each('end', function() {
34343                         d3.select(this).style('display', 'none');
34344                     });
34345             } else {
34346                 wrap
34347                     .style('display', 'block')
34348                     .style('opacity', 0)
34349                     .transition()
34350                     .duration(200)
34351                     .style('opacity', 1);
34352
34353                 redraw();
34354             }
34355         }
34356
34357
34358         var wrap = selection.selectAll('.infobox')
34359             .data([0]);
34360
34361         wrap.enter()
34362             .append('div')
34363             .attr('class', 'infobox fillD2')
34364             .style('display', (hidden ? 'none' : 'block'));
34365
34366         context.map()
34367             .on('drawn.info', redraw);
34368
34369         redraw();
34370
34371         var keybinding = d3.keybinding('info')
34372             .on(key, toggle);
34373
34374         d3.select(document)
34375             .call(keybinding);
34376     }
34377
34378     return info;
34379 };
34380 iD.ui.Inspector = function(context) {
34381     var presetList = iD.ui.PresetList(context),
34382         entityEditor = iD.ui.EntityEditor(context),
34383         state = 'select',
34384         entityID,
34385         newFeature = false;
34386
34387     function inspector(selection) {
34388         presetList
34389             .entityID(entityID)
34390             .autofocus(newFeature)
34391             .on('choose', setPreset);
34392
34393         entityEditor
34394             .state(state)
34395             .entityID(entityID)
34396             .on('choose', showList);
34397
34398         var $wrap = selection.selectAll('.panewrap')
34399             .data([0]);
34400
34401         var $enter = $wrap.enter().append('div')
34402             .attr('class', 'panewrap');
34403
34404         $enter.append('div')
34405             .attr('class', 'preset-list-pane pane');
34406
34407         $enter.append('div')
34408             .attr('class', 'entity-editor-pane pane');
34409
34410         var $presetPane = $wrap.select('.preset-list-pane');
34411         var $editorPane = $wrap.select('.entity-editor-pane');
34412
34413         var graph = context.graph(),
34414             entity = context.entity(entityID),
34415             showEditor = state === 'hover' ||
34416                 entity.isUsed(graph) ||
34417                 entity.isHighwayIntersection(graph);
34418
34419         if (showEditor) {
34420             $wrap.style('right', '0%');
34421             $editorPane.call(entityEditor);
34422         } else {
34423             $wrap.style('right', '-100%');
34424             $presetPane.call(presetList);
34425         }
34426
34427         var $footer = selection.selectAll('.footer')
34428             .data([0]);
34429
34430         $footer.enter().append('div')
34431             .attr('class', 'footer');
34432
34433         selection.select('.footer')
34434             .call(iD.ui.ViewOnOSM(context)
34435                 .entityID(entityID));
34436
34437         function showList(preset) {
34438             $wrap.transition()
34439                 .styleTween('right', function() { return d3.interpolate('0%', '-100%'); });
34440
34441             $presetPane.call(presetList
34442                 .preset(preset)
34443                 .autofocus(true));
34444         }
34445
34446         function setPreset(preset) {
34447             $wrap.transition()
34448                 .styleTween('right', function() { return d3.interpolate('-100%', '0%'); });
34449
34450             $editorPane.call(entityEditor
34451                 .preset(preset));
34452         }
34453     }
34454
34455     inspector.state = function(_) {
34456         if (!arguments.length) return state;
34457         state = _;
34458         entityEditor.state(state);
34459         return inspector;
34460     };
34461
34462     inspector.entityID = function(_) {
34463         if (!arguments.length) return entityID;
34464         entityID = _;
34465         return inspector;
34466     };
34467
34468     inspector.newFeature = function(_) {
34469         if (!arguments.length) return newFeature;
34470         newFeature = _;
34471         return inspector;
34472     };
34473
34474     return inspector;
34475 };
34476 iD.ui.intro = function(context) {
34477     var step;
34478
34479     function intro(selection) {
34480
34481         function localizedName(id) {
34482             var features = {
34483                 n2140018997: 'city_hall',
34484                 n367813436: 'fire_department',
34485                 w203988286: 'memory_isle_park',
34486                 w203972937: 'riverwalk_trail',
34487                 w203972938: 'riverwalk_trail',
34488                 w203972940: 'riverwalk_trail',
34489                 w41785752: 'w_michigan_ave',
34490                 w134150789: 'w_michigan_ave',
34491                 w134150795: 'w_michigan_ave',
34492                 w134150800: 'w_michigan_ave',
34493                 w134150811: 'w_michigan_ave',
34494                 w134150802: 'e_michigan_ave',
34495                 w134150836: 'e_michigan_ave',
34496                 w41074896: 'e_michigan_ave',
34497                 w17965834: 'spring_st',
34498                 w203986457: 'scidmore_park',
34499                 w203049587: 'petting_zoo',
34500                 w17967397: 'n_andrews_st',
34501                 w17967315: 's_andrews_st',
34502                 w17967326: 'n_constantine_st',
34503                 w17966400: 's_constantine_st',
34504                 w170848823: 'rocky_river',
34505                 w170848824: 'rocky_river',
34506                 w170848331: 'rocky_river',
34507                 w17967752: 'railroad_dr',
34508                 w17965998: 'conrail_rr',
34509                 w134150845: 'conrail_rr',
34510                 w170989131: 'st_joseph_river',
34511                 w143497377: 'n_main_st',
34512                 w134150801: 's_main_st',
34513                 w134150830: 's_main_st',
34514                 w17966462: 's_main_st',
34515                 w17967734: 'water_st',
34516                 w17964996: 'foster_st',
34517                 w170848330: 'portage_river',
34518                 w17965351: 'flower_st',
34519                 w17965502: 'elm_st',
34520                 w17965402: 'walnut_st',
34521                 w17964793: 'morris_ave',
34522                 w17967444: 'east_st',
34523                 w17966984: 'portage_ave'
34524             };
34525             return features[id] && t('intro.graph.' + features[id]);
34526         }
34527
34528         context.enter(iD.modes.Browse(context));
34529
34530         // Save current map state
34531         var history = context.history().toJSON(),
34532             hash = window.location.hash,
34533             center = context.map().center(),
34534             zoom = context.map().zoom(),
34535             background = context.background().baseLayerSource(),
34536             opacity = d3.selectAll('#map .layer-background').style('opacity'),
34537             loadedTiles = context.connection().loadedTiles(),
34538             baseEntities = context.history().graph().base().entities,
34539             introGraph, name;
34540
34541         // Block saving
34542         context.inIntro(true);
34543
34544         // Load semi-real data used in intro
34545         context.connection().toggle(false).flush();
34546         context.history().reset();
34547
34548         introGraph = JSON.parse(iD.introGraph);
34549         for (var key in introGraph) {
34550             introGraph[key] = iD.Entity(introGraph[key]);
34551             name = localizedName(key);
34552             if (name) {
34553                 introGraph[key].tags.name = name;
34554             }
34555         }
34556         context.history().merge(d3.values(iD.Graph().load(introGraph).entities));
34557         context.background().bing();
34558
34559         d3.selectAll('#map .layer-background').style('opacity', 1);
34560
34561         var curtain = d3.curtain();
34562         selection.call(curtain);
34563
34564         function reveal(box, text, options) {
34565             options = options || {};
34566             if (text) curtain.reveal(box, text, options.tooltipClass, options.duration);
34567             else curtain.reveal(box, '', '', options.duration);
34568         }
34569
34570         var steps = ['navigation', 'point', 'area', 'line', 'startEditing'].map(function(step, i) {
34571             var s = iD.ui.intro[step](context, reveal)
34572                 .on('done', function() {
34573                     entered.filter(function(d) {
34574                         return d.title === s.title;
34575                     }).classed('finished', true);
34576                     enter(steps[i + 1]);
34577                 });
34578             return s;
34579         });
34580
34581         steps[steps.length - 1].on('startEditing', function() {
34582             curtain.remove();
34583             navwrap.remove();
34584             d3.selectAll('#map .layer-background').style('opacity', opacity);
34585             context.connection().toggle(true).flush().loadedTiles(loadedTiles);
34586             context.history().reset().merge(d3.values(baseEntities));
34587             context.background().baseLayerSource(background);
34588             if (history) context.history().fromJSON(history, false);
34589             context.map().centerZoom(center, zoom);
34590             window.location.replace(hash);
34591             context.inIntro(false);
34592         });
34593
34594         var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
34595
34596         var buttonwrap = navwrap.append('div')
34597             .attr('class', 'joined')
34598             .selectAll('button.step');
34599
34600         var entered = buttonwrap
34601             .data(steps)
34602             .enter()
34603             .append('button')
34604             .attr('class', 'step')
34605             .on('click', enter);
34606
34607         entered
34608             .call(iD.svg.Icon('#icon-apply', 'pre-text'));
34609
34610         entered
34611             .append('label')
34612             .text(function(d) { return t(d.title); });
34613
34614         enter(steps[0]);
34615
34616         function enter (newStep) {
34617             if (step) { step.exit(); }
34618
34619             context.enter(iD.modes.Browse(context));
34620
34621             step = newStep;
34622             step.enter();
34623
34624             entered.classed('active', function(d) {
34625                 return d.title === step.title;
34626             });
34627         }
34628
34629     }
34630     return intro;
34631 };
34632
34633 iD.ui.intro.pointBox = function(point, context) {
34634     var rect = context.surfaceRect();
34635     point = context.projection(point);
34636     return {
34637         left: point[0] + rect.left - 30,
34638         top: point[1] + rect.top - 50,
34639         width: 60,
34640         height: 70
34641     };
34642 };
34643
34644 iD.ui.intro.pad = function(box, padding, context) {
34645     if (box instanceof Array) {
34646         var rect = context.surfaceRect();
34647         box = context.projection(box);
34648         box = {
34649             left: box[0] + rect.left,
34650             top: box[1] + rect.top
34651         };
34652     }
34653     return {
34654         left: box.left - padding,
34655         top: box.top - padding,
34656         width: (box.width || 0) + 2 * padding,
34657         height: (box.width || 0) + 2 * padding
34658     };
34659 };
34660
34661 iD.ui.intro.icon = function(name, svgklass) {
34662     return '<svg class="icon ' + (svgklass || '') + '">' +
34663         '<use xlink:href="' + name + '"></use></svg>';
34664 };
34665 iD.ui.Lasso = function(context) {
34666
34667     var box, group,
34668         a = [0, 0],
34669         b = [0, 0];
34670
34671     function lasso(selection) {
34672
34673         context.container().classed('lasso', true);
34674
34675         group = selection.append('g')
34676             .attr('class', 'lasso hide');
34677
34678         box = group.append('rect')
34679             .attr('class', 'lasso-box');
34680
34681         group.call(iD.ui.Toggle(true));
34682
34683     }
34684
34685     // top-left
34686     function topLeft(d) {
34687         return 'translate(' + Math.min(d[0][0], d[1][0]) + ',' + Math.min(d[0][1], d[1][1]) + ')';
34688     }
34689
34690     function width(d) { return Math.abs(d[0][0] - d[1][0]); }
34691     function height(d) { return Math.abs(d[0][1] - d[1][1]); }
34692
34693     function draw() {
34694         if (box) {
34695             box.data([[a, b]])
34696                 .attr('transform', topLeft)
34697                 .attr('width', width)
34698                 .attr('height', height);
34699         }
34700     }
34701
34702     lasso.a = function(_) {
34703         if (!arguments.length) return a;
34704         a = _;
34705         draw();
34706         return lasso;
34707     };
34708
34709     lasso.b = function(_) {
34710         if (!arguments.length) return b;
34711         b = _;
34712         draw();
34713         return lasso;
34714     };
34715
34716     lasso.close = function() {
34717         if (group) {
34718             group.call(iD.ui.Toggle(false, function() {
34719                 d3.select(this).remove();
34720             }));
34721         }
34722         context.container().classed('lasso', false);
34723     };
34724
34725     return lasso;
34726 };
34727 iD.ui.Loading = function(context) {
34728     var message = '',
34729         blocking = false,
34730         modal;
34731
34732     var loading = function(selection) {
34733         modal = iD.ui.modal(selection, blocking);
34734
34735         var loadertext = modal.select('.content')
34736             .classed('loading-modal', true)
34737             .append('div')
34738             .attr('class', 'modal-section fillL');
34739
34740         loadertext.append('img')
34741             .attr('class', 'loader')
34742             .attr('src', context.imagePath('loader-white.gif'));
34743
34744         loadertext.append('h3')
34745             .text(message);
34746
34747         modal.select('button.close')
34748             .attr('class', 'hide');
34749
34750         return loading;
34751     };
34752
34753     loading.message = function(_) {
34754         if (!arguments.length) return message;
34755         message = _;
34756         return loading;
34757     };
34758
34759     loading.blocking = function(_) {
34760         if (!arguments.length) return blocking;
34761         blocking = _;
34762         return loading;
34763     };
34764
34765     loading.close = function() {
34766         modal.remove();
34767     };
34768
34769     return loading;
34770 };
34771 iD.ui.MapData = function(context) {
34772     var key = 'F',
34773         features = context.features().keys(),
34774         layers = context.layers(),
34775         fills = ['wireframe', 'partial', 'full'],
34776         fillDefault = context.storage('area-fill') || 'partial',
34777         fillSelected = fillDefault;
34778
34779
34780     function map_data(selection) {
34781
34782         function showsFeature(d) {
34783             return context.features().enabled(d);
34784         }
34785
34786         function autoHiddenFeature(d) {
34787             return context.features().autoHidden(d);
34788         }
34789
34790         function clickFeature(d) {
34791             context.features().toggle(d);
34792             update();
34793         }
34794
34795         function showsFill(d) {
34796             return fillSelected === d;
34797         }
34798
34799         function setFill(d) {
34800             _.each(fills, function(opt) {
34801                 context.surface().classed('fill-' + opt, Boolean(opt === d));
34802             });
34803
34804             fillSelected = d;
34805             if (d !== 'wireframe') {
34806                 fillDefault = d;
34807                 context.storage('area-fill', d);
34808             }
34809             update();
34810         }
34811
34812         function toggleLayer(which) {
34813             var layer = layers.layer(which);
34814             if (layer) {
34815                 layer.enabled(!layer.enabled());
34816                 update();
34817             }
34818         }
34819
34820         function clickGpx() {
34821             toggleLayer('gpx');
34822         }
34823
34824         function clickMapillaryImages() {
34825             toggleLayer('mapillary-images');
34826         }
34827
34828         function clickMapillarySigns() {
34829             toggleLayer('mapillary-signs');
34830         }
34831
34832
34833         function drawMapillaryItems(selection) {
34834             var mapillaryImages = layers.layer('mapillary-images'),
34835                 mapillarySigns = layers.layer('mapillary-signs'),
34836                 supportsMapillaryImages = mapillaryImages && mapillaryImages.supported(),
34837                 supportsMapillarySigns = mapillarySigns && mapillarySigns.supported(),
34838                 showsMapillaryImages = supportsMapillaryImages && mapillaryImages.enabled(),
34839                 showsMapillarySigns = supportsMapillarySigns && mapillarySigns.enabled();
34840
34841             var mapillaryList = selection
34842                 .selectAll('.layer-list-mapillary')
34843                 .data([0]);
34844
34845             // Enter
34846             mapillaryList
34847                 .enter()
34848                 .append('ul')
34849                 .attr('class', 'layer-list layer-list-mapillary');
34850
34851             var mapillaryImageLayerItem = mapillaryList
34852                 .selectAll('.list-item-mapillary-images')
34853                 .data(supportsMapillaryImages ? [0] : []);
34854
34855             var enterImages = mapillaryImageLayerItem.enter()
34856                 .append('li')
34857                 .attr('class', 'list-item-mapillary-images');
34858
34859             var labelImages = enterImages.append('label')
34860                 .call(bootstrap.tooltip()
34861                     .title(t('mapillary_images.tooltip'))
34862                     .placement('top'));
34863
34864             labelImages.append('input')
34865                 .attr('type', 'checkbox')
34866                 .on('change', clickMapillaryImages);
34867
34868             labelImages.append('span')
34869                 .text(t('mapillary_images.title'));
34870
34871
34872             var mapillarySignLayerItem = mapillaryList
34873                 .selectAll('.list-item-mapillary-signs')
34874                 .data(supportsMapillarySigns ? [0] : []);
34875
34876             var enterSigns = mapillarySignLayerItem.enter()
34877                 .append('li')
34878                 .attr('class', 'list-item-mapillary-signs');
34879
34880             var labelSigns = enterSigns.append('label')
34881                 .call(bootstrap.tooltip()
34882                     .title(t('mapillary_signs.tooltip'))
34883                     .placement('top'));
34884
34885             labelSigns.append('input')
34886                 .attr('type', 'checkbox')
34887                 .on('change', clickMapillarySigns);
34888
34889             labelSigns.append('span')
34890                 .text(t('mapillary_signs.title'));
34891
34892             // Update
34893             mapillaryImageLayerItem
34894                 .classed('active', showsMapillaryImages)
34895                 .selectAll('input')
34896                 .property('checked', showsMapillaryImages);
34897
34898             mapillarySignLayerItem
34899                 .classed('active', showsMapillarySigns)
34900                 .selectAll('input')
34901                 .property('checked', showsMapillarySigns);
34902
34903             // Exit
34904             mapillaryImageLayerItem.exit()
34905                 .remove();
34906             mapillarySignLayerItem.exit()
34907                 .remove();
34908         }
34909
34910
34911         function drawGpxItem(selection) {
34912             var gpx = layers.layer('gpx'),
34913                 hasGpx = gpx && gpx.hasGpx(),
34914                 showsGpx = hasGpx && gpx.enabled();
34915
34916             var gpxLayerItem = selection
34917                 .selectAll('.layer-list-gpx')
34918                 .data(gpx ? [0] : []);
34919
34920             // Enter
34921             var enter = gpxLayerItem.enter()
34922                 .append('ul')
34923                 .attr('class', 'layer-list layer-list-gpx')
34924                 .append('li')
34925                 .classed('list-item-gpx', true);
34926
34927             enter.append('button')
34928                 .attr('class', 'list-item-gpx-extent')
34929                 .call(bootstrap.tooltip()
34930                     .title(t('gpx.zoom'))
34931                     .placement('left'))
34932                 .on('click', function() {
34933                     d3.event.preventDefault();
34934                     d3.event.stopPropagation();
34935                     gpx.fitZoom();
34936                 })
34937                 .call(iD.svg.Icon('#icon-search'));
34938
34939             enter.append('button')
34940                 .attr('class', 'list-item-gpx-browse')
34941                 .call(bootstrap.tooltip()
34942                     .title(t('gpx.browse'))
34943                     .placement('left'))
34944                 .on('click', function() {
34945                     d3.select(document.createElement('input'))
34946                         .attr('type', 'file')
34947                         .on('change', function() {
34948                             gpx.files(d3.event.target.files);
34949                         })
34950                         .node().click();
34951                 })
34952                 .call(iD.svg.Icon('#icon-geolocate'));
34953
34954             var labelGpx = enter.append('label')
34955                 .call(bootstrap.tooltip()
34956                     .title(t('gpx.drag_drop'))
34957                     .placement('top'));
34958
34959             labelGpx.append('input')
34960                 .attr('type', 'checkbox')
34961                 .on('change', clickGpx);
34962
34963             labelGpx.append('span')
34964                 .text(t('gpx.local_layer'));
34965
34966             // Update
34967             gpxLayerItem
34968                 .classed('active', showsGpx)
34969                 .selectAll('input')
34970                 .property('disabled', !hasGpx)
34971                 .property('checked', showsGpx);
34972
34973             // Exit
34974             gpxLayerItem.exit()
34975                 .remove();
34976         }
34977
34978
34979         function drawList(selection, data, type, name, change, active) {
34980             var items = selection.selectAll('li')
34981                 .data(data);
34982
34983             // Enter
34984             var enter = items.enter()
34985                 .append('li')
34986                 .attr('class', 'layer')
34987                 .call(bootstrap.tooltip()
34988                     .html(true)
34989                     .title(function(d) {
34990                         var tip = t(name + '.' + d + '.tooltip'),
34991                             key = (d === 'wireframe' ? 'W' : null);
34992
34993                         if (name === 'feature' && autoHiddenFeature(d)) {
34994                             tip += '<div>' + t('map_data.autohidden') + '</div>';
34995                         }
34996                         return iD.ui.tooltipHtml(tip, key);
34997                     })
34998                     .placement('top')
34999                 );
35000
35001             var label = enter.append('label');
35002
35003             label.append('input')
35004                 .attr('type', type)
35005                 .attr('name', name)
35006                 .on('change', change);
35007
35008             label.append('span')
35009                 .text(function(d) { return t(name + '.' + d + '.description'); });
35010
35011             // Update
35012             items
35013                 .classed('active', active)
35014                 .selectAll('input')
35015                 .property('checked', active)
35016                 .property('indeterminate', function(d) {
35017                     return (name === 'feature' && autoHiddenFeature(d));
35018                 });
35019
35020             // Exit
35021             items.exit()
35022                 .remove();
35023         }
35024
35025
35026         function update() {
35027             dataLayerContainer.call(drawMapillaryItems);
35028             dataLayerContainer.call(drawGpxItem);
35029
35030             fillList.call(drawList, fills, 'radio', 'area_fill', setFill, showsFill);
35031
35032             featureList.call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature);
35033         }
35034
35035         function hidePanel() {
35036             setVisible(false);
35037         }
35038
35039         function togglePanel() {
35040             if (d3.event) d3.event.preventDefault();
35041             tooltip.hide(button);
35042             setVisible(!button.classed('active'));
35043         }
35044
35045         function toggleWireframe() {
35046             if (d3.event) {
35047                 d3.event.preventDefault();
35048                 d3.event.stopPropagation();
35049             }
35050             setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe'));
35051             context.map().pan([0,0]);  // trigger a redraw
35052         }
35053
35054         function setVisible(show) {
35055             if (show !== shown) {
35056                 button.classed('active', show);
35057                 shown = show;
35058
35059                 if (show) {
35060                     update();
35061                     selection.on('mousedown.map_data-inside', function() {
35062                         return d3.event.stopPropagation();
35063                     });
35064                     content.style('display', 'block')
35065                         .style('right', '-300px')
35066                         .transition()
35067                         .duration(200)
35068                         .style('right', '0px');
35069                 } else {
35070                     content.style('display', 'block')
35071                         .style('right', '0px')
35072                         .transition()
35073                         .duration(200)
35074                         .style('right', '-300px')
35075                         .each('end', function() {
35076                             d3.select(this).style('display', 'none');
35077                         });
35078                     selection.on('mousedown.map_data-inside', null);
35079                 }
35080             }
35081         }
35082
35083
35084         var content = selection.append('div')
35085                 .attr('class', 'fillL map-overlay col3 content hide'),
35086             tooltip = bootstrap.tooltip()
35087                 .placement('left')
35088                 .html(true)
35089                 .title(iD.ui.tooltipHtml(t('map_data.description'), key)),
35090             button = selection.append('button')
35091                 .attr('tabindex', -1)
35092                 .on('click', togglePanel)
35093                 .call(iD.svg.Icon('#icon-data', 'light'))
35094                 .call(tooltip),
35095             shown = false;
35096
35097         content.append('h4')
35098             .text(t('map_data.title'));
35099
35100
35101         // data layers
35102         content.append('a')
35103             .text(t('map_data.data_layers'))
35104             .attr('href', '#')
35105             .classed('hide-toggle', true)
35106             .classed('expanded', true)
35107             .on('click', function() {
35108                 var exp = d3.select(this).classed('expanded');
35109                 dataLayerContainer.style('display', exp ? 'none' : 'block');
35110                 d3.select(this).classed('expanded', !exp);
35111                 d3.event.preventDefault();
35112             });
35113
35114         var dataLayerContainer = content.append('div')
35115             .attr('class', 'data-data-layers')
35116             .style('display', 'block');
35117
35118
35119         // area fills
35120         content.append('a')
35121             .text(t('map_data.fill_area'))
35122             .attr('href', '#')
35123             .classed('hide-toggle', true)
35124             .classed('expanded', false)
35125             .on('click', function() {
35126                 var exp = d3.select(this).classed('expanded');
35127                 fillContainer.style('display', exp ? 'none' : 'block');
35128                 d3.select(this).classed('expanded', !exp);
35129                 d3.event.preventDefault();
35130             });
35131
35132         var fillContainer = content.append('div')
35133             .attr('class', 'data-area-fills')
35134             .style('display', 'none');
35135
35136         var fillList = fillContainer.append('ul')
35137             .attr('class', 'layer-list layer-fill-list');
35138
35139
35140         // feature filters
35141         content.append('a')
35142             .text(t('map_data.map_features'))
35143             .attr('href', '#')
35144             .classed('hide-toggle', true)
35145             .classed('expanded', false)
35146             .on('click', function() {
35147                 var exp = d3.select(this).classed('expanded');
35148                 featureContainer.style('display', exp ? 'none' : 'block');
35149                 d3.select(this).classed('expanded', !exp);
35150                 d3.event.preventDefault();
35151             });
35152
35153         var featureContainer = content.append('div')
35154             .attr('class', 'data-feature-filters')
35155             .style('display', 'none');
35156
35157         var featureList = featureContainer.append('ul')
35158             .attr('class', 'layer-list layer-feature-list');
35159
35160
35161         context.features()
35162             .on('change.map_data-update', update);
35163
35164         setFill(fillDefault);
35165
35166         var keybinding = d3.keybinding('features')
35167             .on(key, togglePanel)
35168             .on('W', toggleWireframe)
35169             .on('B', hidePanel)
35170             .on('H', hidePanel);
35171
35172         d3.select(document)
35173             .call(keybinding);
35174
35175         context.surface().on('mousedown.map_data-outside', hidePanel);
35176         context.container().on('mousedown.map_data-outside', hidePanel);
35177     }
35178
35179     return map_data;
35180 };
35181 iD.ui.MapInMap = function(context) {
35182     var key = '/';
35183
35184     function map_in_map(selection) {
35185         var backgroundLayer = iD.TileLayer(),
35186             overlayLayers = {},
35187             projection = iD.geo.RawMercator(),
35188             gpxLayer = iD.svg.Gpx(projection, context).showLabels(false),
35189             zoom = d3.behavior.zoom()
35190                 .scaleExtent([ztok(0.5), ztok(24)])
35191                 .on('zoom', zoomPan),
35192             transformed = false,
35193             panning = false,
35194             hidden = true,
35195             zDiff = 6,    // by default, minimap renders at (main zoom - 6)
35196             tStart, tLast, tCurr, kLast, kCurr, tiles, svg, timeoutId;
35197
35198         function ztok(z) { return 256 * Math.pow(2, z); }
35199         function ktoz(k) { return Math.log(k) / Math.LN2 - 8; }
35200
35201
35202         function startMouse() {
35203             context.surface().on('mouseup.map-in-map-outside', endMouse);
35204             context.container().on('mouseup.map-in-map-outside', endMouse);
35205
35206             tStart = tLast = tCurr = projection.translate();
35207             panning = true;
35208         }
35209
35210
35211         function zoomPan() {
35212             var e = d3.event.sourceEvent,
35213                 t = d3.event.translate,
35214                 k = d3.event.scale,
35215                 zMain = ktoz(context.projection.scale() * 2 * Math.PI),
35216                 zMini = ktoz(k);
35217
35218             // restrict minimap zoom to < (main zoom - 3)
35219             if (zMini > zMain - 3) {
35220                 zMini = zMain - 3;
35221                 zoom.scale(kCurr).translate(tCurr);  // restore last good values
35222                 return;
35223             }
35224
35225             tCurr = t;
35226             kCurr = k;
35227             zDiff = zMain - zMini;
35228
35229             var scale = kCurr / kLast,
35230                 tX = (tCurr[0] / scale - tLast[0]) * scale,
35231                 tY = (tCurr[1] / scale - tLast[1]) * scale;
35232
35233             iD.util.setTransform(tiles, tX, tY, scale);
35234             iD.util.setTransform(svg, 0, 0, scale);
35235             transformed = true;
35236
35237             queueRedraw();
35238
35239             e.preventDefault();
35240             e.stopPropagation();
35241         }
35242
35243
35244         function endMouse() {
35245             context.surface().on('mouseup.map-in-map-outside', null);
35246             context.container().on('mouseup.map-in-map-outside', null);
35247
35248             updateProjection();
35249             panning = false;
35250
35251             if (tCurr[0] !== tStart[0] && tCurr[1] !== tStart[1]) {
35252                 var dMini = wrap.dimensions(),
35253                     cMini = [ dMini[0] / 2, dMini[1] / 2 ];
35254
35255                 context.map().center(projection.invert(cMini));
35256             }
35257         }
35258
35259
35260         function updateProjection() {
35261             var loc = context.map().center(),
35262                 dMini = wrap.dimensions(),
35263                 cMini = [ dMini[0] / 2, dMini[1] / 2 ],
35264                 tMain = context.projection.translate(),
35265                 kMain = context.projection.scale(),
35266                 zMain = ktoz(kMain * 2 * Math.PI),
35267                 zMini = Math.max(zMain - zDiff, 0.5),
35268                 kMini = ztok(zMini);
35269
35270             projection
35271                 .translate(tMain)
35272                 .scale(kMini / (2 * Math.PI));
35273
35274             var s = projection(loc),
35275                 mouse = panning ? [ tCurr[0] - tStart[0], tCurr[1] - tStart[1] ] : [0, 0],
35276                 tMini = [
35277                     cMini[0] - s[0] + tMain[0] + mouse[0],
35278                     cMini[1] - s[1] + tMain[1] + mouse[1]
35279                 ];
35280
35281             projection
35282                 .translate(tMini)
35283                 .clipExtent([[0, 0], dMini]);
35284
35285             zoom
35286                 .center(cMini)
35287                 .translate(tMini)
35288                 .scale(kMini);
35289
35290             tLast = tCurr = tMini;
35291             kLast = kCurr = kMini;
35292
35293             if (transformed) {
35294                 iD.util.setTransform(tiles, 0, 0);
35295                 iD.util.setTransform(svg, 0, 0);
35296                 transformed = false;
35297             }
35298         }
35299
35300
35301         function redraw() {
35302             if (hidden) return;
35303
35304             updateProjection();
35305
35306             var dMini = wrap.dimensions(),
35307                 zMini = ktoz(projection.scale() * 2 * Math.PI);
35308
35309             // setup tile container
35310             tiles = wrap
35311                 .selectAll('.map-in-map-tiles')
35312                 .data([0]);
35313
35314             tiles
35315                 .enter()
35316                 .append('div')
35317                 .attr('class', 'map-in-map-tiles');
35318
35319             // redraw background
35320             backgroundLayer
35321                 .source(context.background().baseLayerSource())
35322                 .projection(projection)
35323                 .dimensions(dMini);
35324
35325             var background = tiles
35326                 .selectAll('.map-in-map-background')
35327                 .data([0]);
35328
35329             background.enter()
35330                 .append('div')
35331                 .attr('class', 'map-in-map-background');
35332
35333             background
35334                 .call(backgroundLayer);
35335
35336
35337             // redraw overlay
35338             var overlaySources = context.background().overlayLayerSources();
35339             var activeOverlayLayers = [];
35340             for (var i = 0; i < overlaySources.length; i++) {
35341                 if (overlaySources[i].validZoom(zMini)) {
35342                     if (!overlayLayers[i]) overlayLayers[i] = iD.TileLayer();
35343                     activeOverlayLayers.push(overlayLayers[i]
35344                         .source(overlaySources[i])
35345                         .projection(projection)
35346                         .dimensions(dMini));
35347                 }
35348             }
35349
35350             var overlay = tiles
35351                 .selectAll('.map-in-map-overlay')
35352                 .data([0]);
35353
35354             overlay.enter()
35355                 .append('div')
35356                 .attr('class', 'map-in-map-overlay');
35357
35358             var overlays = overlay
35359                 .selectAll('div')
35360                 .data(activeOverlayLayers, function(d) { return d.source().name(); });
35361
35362             overlays.enter().append('div');
35363             overlays.each(function(layer) {
35364                 d3.select(this).call(layer);
35365             });
35366
35367             overlays.exit()
35368                 .remove();
35369
35370
35371             var gpx = tiles
35372                 .selectAll('.map-in-map-gpx')
35373                 .data(gpxLayer.enabled() ? [0] : []);
35374
35375             gpx.enter()
35376                 .append('svg')
35377                 .attr('class', 'map-in-map-gpx');
35378
35379             gpx.exit()
35380                 .remove();
35381
35382             gpx.call(gpxLayer);
35383
35384
35385             // redraw bounding box
35386             if (!panning) {
35387                 var getPath = d3.geo.path().projection(projection),
35388                     bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
35389
35390                 svg = wrap.selectAll('.map-in-map-svg')
35391                     .data([0]);
35392
35393                 svg.enter()
35394                     .append('svg')
35395                     .attr('class', 'map-in-map-svg');
35396
35397                 var path = svg.selectAll('.map-in-map-bbox')
35398                     .data([bbox]);
35399
35400                 path.enter()
35401                     .append('path')
35402                     .attr('class', 'map-in-map-bbox');
35403
35404                 path
35405                     .attr('d', getPath)
35406                     .classed('thick', function(d) { return getPath.area(d) < 30; });
35407             }
35408         }
35409
35410
35411         function queueRedraw() {
35412             clearTimeout(timeoutId);
35413             timeoutId = setTimeout(function() { redraw(); }, 300);
35414         }
35415
35416
35417         function toggle() {
35418             if (d3.event) d3.event.preventDefault();
35419
35420             hidden = !hidden;
35421
35422             var label = d3.select('.minimap-toggle');
35423             label.classed('active', !hidden)
35424                 .select('input').property('checked', !hidden);
35425
35426             if (hidden) {
35427                 wrap
35428                     .style('display', 'block')
35429                     .style('opacity', 1)
35430                     .transition()
35431                     .duration(200)
35432                     .style('opacity', 0)
35433                     .each('end', function() {
35434                         d3.select(this).style('display', 'none');
35435                     });
35436             } else {
35437                 wrap
35438                     .style('display', 'block')
35439                     .style('opacity', 0)
35440                     .transition()
35441                     .duration(200)
35442                     .style('opacity', 1);
35443
35444                 redraw();
35445             }
35446         }
35447
35448         iD.ui.MapInMap.toggle = toggle;
35449
35450         var wrap = selection.selectAll('.map-in-map')
35451             .data([0]);
35452
35453         wrap.enter()
35454             .append('div')
35455             .attr('class', 'map-in-map')
35456             .style('display', (hidden ? 'none' : 'block'))
35457             .on('mousedown.map-in-map', startMouse)
35458             .on('mouseup.map-in-map', endMouse)
35459             .call(zoom)
35460             .on('dblclick.zoom', null);
35461
35462         context.map()
35463             .on('drawn.map-in-map', function(drawn) {
35464                 if (drawn.full === true) redraw();
35465             });
35466
35467         redraw();
35468
35469         var keybinding = d3.keybinding('map-in-map')
35470             .on(key, toggle);
35471
35472         d3.select(document)
35473             .call(keybinding);
35474     }
35475
35476     return map_in_map;
35477 };
35478 iD.ui.modal = function(selection, blocking) {
35479
35480     var previous = selection.select('div.modal');
35481     var animate = previous.empty();
35482
35483     previous.transition()
35484         .duration(200)
35485         .style('opacity', 0)
35486         .remove();
35487
35488     var shaded = selection
35489         .append('div')
35490         .attr('class', 'shaded')
35491         .style('opacity', 0);
35492
35493     shaded.close = function() {
35494         shaded
35495             .transition()
35496             .duration(200)
35497             .style('opacity',0)
35498             .remove();
35499         modal
35500             .transition()
35501             .duration(200)
35502             .style('top','0px');
35503         keybinding.off();
35504     };
35505
35506     var keybinding = d3.keybinding('modal')
35507         .on('⌫', shaded.close)
35508         .on('⎋', shaded.close);
35509
35510     d3.select(document).call(keybinding);
35511
35512     var modal = shaded.append('div')
35513         .attr('class', 'modal fillL col6');
35514
35515         shaded.on('click.remove-modal', function() {
35516             if (d3.event.target === this && !blocking) shaded.close();
35517         });
35518
35519     modal.append('button')
35520         .attr('class', 'close')
35521         .on('click', function() {
35522             if (!blocking) shaded.close();
35523         })
35524         .append('div')
35525             .attr('class','icon close');
35526
35527     modal.append('div')
35528         .attr('class', 'content');
35529
35530     if (animate) {
35531         shaded.transition().style('opacity', 1);
35532     } else {
35533         shaded.style('opacity', 1);
35534     }
35535
35536     return shaded;
35537 };
35538 iD.ui.Modes = function(context) {
35539     var modes = [
35540         iD.modes.AddPoint(context),
35541         iD.modes.AddLine(context),
35542         iD.modes.AddArea(context)];
35543
35544     function editable() {
35545         return context.editable() && context.mode().id !== 'save';
35546     }
35547
35548     return function(selection) {
35549         var buttons = selection.selectAll('button.add-button')
35550             .data(modes);
35551
35552        buttons.enter().append('button')
35553            .attr('tabindex', -1)
35554            .attr('class', function(mode) { return mode.id + ' add-button col4'; })
35555            .on('click.mode-buttons', function(mode) {
35556                if (mode.id === context.mode().id) {
35557                    context.enter(iD.modes.Browse(context));
35558                } else {
35559                    context.enter(mode);
35560                }
35561            })
35562            .call(bootstrap.tooltip()
35563                .placement('bottom')
35564                .html(true)
35565                .title(function(mode) {
35566                    return iD.ui.tooltipHtml(mode.description, mode.key);
35567                }));
35568
35569         context.map()
35570             .on('move.modes', _.debounce(update, 500));
35571
35572         context
35573             .on('enter.modes', update);
35574
35575         buttons.each(function(d) {
35576             d3.select(this)
35577                 .call(iD.svg.Icon('#icon-' + d.button, 'pre-text'));
35578         });
35579
35580         buttons.append('span')
35581             .attr('class', 'label')
35582             .text(function(mode) { return mode.title; });
35583
35584         context.on('enter.editor', function(entered) {
35585             buttons.classed('active', function(mode) { return entered.button === mode.button; });
35586             context.container()
35587                 .classed('mode-' + entered.id, true);
35588         });
35589
35590         context.on('exit.editor', function(exited) {
35591             context.container()
35592                 .classed('mode-' + exited.id, false);
35593         });
35594
35595         var keybinding = d3.keybinding('mode-buttons');
35596
35597         modes.forEach(function(m) {
35598             keybinding.on(m.key, function() { if (editable()) context.enter(m); });
35599         });
35600
35601         d3.select(document)
35602             .call(keybinding);
35603
35604         function update() {
35605             buttons.property('disabled', !editable());
35606         }
35607     };
35608 };
35609 iD.ui.Notice = function(context) {
35610     return function(selection) {
35611         var div = selection.append('div')
35612             .attr('class', 'notice');
35613
35614         var button = div.append('button')
35615             .attr('class', 'zoom-to notice')
35616             .on('click', function() { context.map().zoom(context.minEditableZoom()); });
35617
35618         button
35619             .call(iD.svg.Icon('#icon-plus', 'pre-text'))
35620             .append('span')
35621             .attr('class', 'label')
35622             .text(t('zoom_in_edit'));
35623
35624         function disableTooHigh() {
35625             div.style('display', context.editable() ? 'none' : 'block');
35626         }
35627
35628         context.map()
35629             .on('move.notice', _.debounce(disableTooHigh, 500));
35630
35631         disableTooHigh();
35632     };
35633 };
35634 iD.ui.PresetIcon = function() {
35635     var preset, geometry;
35636
35637     function presetIcon(selection) {
35638         selection.each(render);
35639     }
35640
35641     function render() {
35642         var selection = d3.select(this),
35643             p = preset.apply(this, arguments),
35644             geom = geometry.apply(this, arguments),
35645             icon = p.icon || (geom === 'line' ? 'other-line' : 'marker-stroked'),
35646             maki = iD.data.featureIcons.hasOwnProperty(icon + '-24');
35647
35648         if (icon === 'dentist') maki = true;  // workaround for dentist icon missing in `maki-sprite.json`
35649
35650         function tag_classes(p) {
35651             var s = '';
35652             for (var i in p.tags) {
35653                 s += ' tag-' + i;
35654                 if (p.tags[i] !== '*') {
35655                     s += ' tag-' + i + '-' + p.tags[i];
35656                 }
35657             }
35658             return s;
35659         }
35660
35661         var $fill = selection.selectAll('.preset-icon-fill')
35662             .data([0]);
35663
35664         $fill.enter().append('div');
35665
35666         $fill.attr('class', function() {
35667             return 'preset-icon-fill preset-icon-fill-' + geom + tag_classes(p);
35668         });
35669
35670         var $frame = selection.selectAll('.preset-icon-frame')
35671             .data([0]);
35672
35673         $frame.enter()
35674             .append('div')
35675             .call(iD.svg.Icon('#preset-icon-frame'));
35676
35677         $frame.attr('class', function() {
35678             return 'preset-icon-frame ' + (geom === 'area' ? '' : 'hide');
35679         });
35680
35681
35682         var $icon = selection.selectAll('.preset-icon')
35683             .data([0]);
35684
35685         $icon.enter()
35686             .append('div')
35687             .attr('class', 'preset-icon')
35688             .call(iD.svg.Icon(''));
35689
35690         $icon
35691             .attr('class', 'preset-icon preset-icon-' + (maki ? '32' : (geom === 'area' ? '44' : '60')));
35692
35693         $icon.selectAll('svg')
35694             .attr('class', function() {
35695                 return 'icon ' + icon + tag_classes(p);
35696             });
35697
35698         $icon.selectAll('use')       // workaround: maki parking-24 broken?
35699             .attr('href', '#' + icon + (maki ? ( icon === 'parking' ? '-18' : '-24') : ''));
35700     }
35701
35702     presetIcon.preset = function(_) {
35703         if (!arguments.length) return preset;
35704         preset = d3.functor(_);
35705         return presetIcon;
35706     };
35707
35708     presetIcon.geometry = function(_) {
35709         if (!arguments.length) return geometry;
35710         geometry = d3.functor(_);
35711         return presetIcon;
35712     };
35713
35714     return presetIcon;
35715 };
35716 iD.ui.preset = function(context) {
35717     var event = d3.dispatch('change'),
35718         state,
35719         fields,
35720         preset,
35721         tags,
35722         id;
35723
35724     function UIField(field, entity, show) {
35725         field = _.clone(field);
35726
35727         field.input = iD.ui.preset[field.type](field, context)
35728             .on('change', event.change);
35729
35730         if (field.input.entity) field.input.entity(entity);
35731
35732         field.keys = field.keys || [field.key];
35733
35734         field.show = show;
35735
35736         field.shown = function() {
35737             return field.id === 'name' || field.show || _.any(field.keys, function(key) { return !!tags[key]; });
35738         };
35739
35740         field.modified = function() {
35741             var original = context.graph().base().entities[entity.id];
35742             return _.any(field.keys, function(key) {
35743                 return original ? tags[key] !== original.tags[key] : tags[key];
35744             });
35745         };
35746
35747         field.revert = function() {
35748             var original = context.graph().base().entities[entity.id],
35749                 t = {};
35750             field.keys.forEach(function(key) {
35751                 t[key] = original ? original.tags[key] : undefined;
35752             });
35753             return t;
35754         };
35755
35756         field.present = function() {
35757             return _.any(field.keys, function(key) {
35758                 return tags[key];
35759             });
35760         };
35761
35762         field.remove = function() {
35763             var t = {};
35764             field.keys.forEach(function(key) {
35765                 t[key] = undefined;
35766             });
35767             return t;
35768         };
35769
35770         return field;
35771     }
35772
35773     function fieldKey(field) {
35774         return field.id;
35775     }
35776
35777     function presets(selection) {
35778         selection.call(iD.ui.Disclosure()
35779             .title(t('inspector.all_fields'))
35780             .expanded(context.storage('preset_fields.expanded') !== 'false')
35781             .on('toggled', toggled)
35782             .content(content));
35783
35784         function toggled(expanded) {
35785             context.storage('preset_fields.expanded', expanded);
35786         }
35787     }
35788
35789     function content(selection) {
35790         if (!fields) {
35791             var entity = context.entity(id),
35792                 geometry = context.geometry(id);
35793
35794             fields = [UIField(context.presets().field('name'), entity)];
35795
35796             preset.fields.forEach(function(field) {
35797                 if (field.matchGeometry(geometry)) {
35798                     fields.push(UIField(field, entity, true));
35799                 }
35800             });
35801
35802             if (entity.isHighwayIntersection(context.graph())) {
35803                 fields.push(UIField(context.presets().field('restrictions'), entity, true));
35804             }
35805
35806             context.presets().universal().forEach(function(field) {
35807                 if (preset.fields.indexOf(field) < 0) {
35808                     fields.push(UIField(field, entity));
35809                 }
35810             });
35811         }
35812
35813         var shown = fields.filter(function(field) { return field.shown(); }),
35814             notShown = fields.filter(function(field) { return !field.shown(); });
35815
35816         var $form = selection.selectAll('.preset-form')
35817             .data([0]);
35818
35819         $form.enter().append('div')
35820             .attr('class', 'preset-form inspector-inner fillL3');
35821
35822         var $fields = $form.selectAll('.form-field')
35823             .data(shown, fieldKey);
35824
35825         // Enter
35826
35827         var $enter = $fields.enter()
35828             .append('div')
35829             .attr('class', function(field) {
35830                 return 'form-field form-field-' + field.id;
35831             });
35832
35833         var $label = $enter.append('label')
35834             .attr('class', 'form-label')
35835             .attr('for', function(field) { return 'preset-input-' + field.id; })
35836             .text(function(field) { return field.label(); });
35837
35838         var wrap = $label.append('div')
35839             .attr('class', 'form-label-button-wrap');
35840
35841         wrap.append('button')
35842             .attr('class', 'remove-icon')
35843             .call(iD.svg.Icon('#operation-delete'));
35844
35845         wrap.append('button')
35846             .attr('class', 'modified-icon')
35847             .attr('tabindex', -1)
35848             .call(iD.svg.Icon('#icon-undo'));
35849
35850         // Update
35851
35852         $fields.select('.form-label-button-wrap .remove-icon')
35853             .on('click', remove);
35854
35855         $fields.select('.modified-icon')
35856             .on('click', revert);
35857
35858         $fields
35859             .order()
35860             .classed('modified', function(field) {
35861                 return field.modified();
35862             })
35863             .classed('present', function(field) {
35864                 return field.present();
35865             })
35866             .each(function(field) {
35867                 var reference = iD.ui.TagReference(field.reference || {key: field.key}, context);
35868
35869                 if (state === 'hover') {
35870                     reference.showing(false);
35871                 }
35872
35873                 d3.select(this)
35874                     .call(field.input)
35875                     .selectAll('input')
35876                     .on('keydown', function() {
35877                         if (d3.event.keyCode === 13) {  // enter
35878                             context.enter(iD.modes.Browse(context));
35879                         }
35880                     })
35881                     .call(reference.body)
35882                     .select('.form-label-button-wrap')
35883                     .call(reference.button);
35884
35885                 field.input.tags(tags);
35886             });
35887
35888         $fields.exit()
35889             .remove();
35890
35891         notShown = notShown.map(function(field) {
35892             return {
35893                 title: field.label(),
35894                 value: field.label(),
35895                 field: field
35896             };
35897         });
35898
35899         var $more = selection.selectAll('.more-fields')
35900             .data((notShown.length > 0) ? [0] : []);
35901
35902         $more.enter().append('div')
35903             .attr('class', 'more-fields')
35904             .append('label')
35905                 .text(t('inspector.add_fields'));
35906
35907         var $input = $more.selectAll('.value')
35908             .data([0]);
35909
35910         $input.enter().append('input')
35911             .attr('class', 'value')
35912             .attr('type', 'text');
35913
35914         $input.value('')
35915             .attr('placeholder', function() {
35916                 var placeholder = [];
35917                 for (var field in notShown) {
35918                     placeholder.push(notShown[field].title);
35919                 }
35920                 return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : '');
35921             })
35922             .call(d3.combobox().data(notShown)
35923                 .minItems(1)
35924                 .on('accept', show));
35925
35926         $more.exit()
35927             .remove();
35928
35929         $input.exit()
35930             .remove();
35931
35932         function show(field) {
35933             field = field.field;
35934             field.show = true;
35935             content(selection);
35936             field.input.focus();
35937         }
35938
35939         function revert(field) {
35940             d3.event.stopPropagation();
35941             d3.event.preventDefault();
35942             event.change(field.revert());
35943         }
35944
35945         function remove(field) {
35946             d3.event.stopPropagation();
35947             d3.event.preventDefault();
35948             event.change(field.remove());
35949         }
35950     }
35951
35952     presets.preset = function(_) {
35953         if (!arguments.length) return preset;
35954         if (preset && preset.id === _.id) return presets;
35955         preset = _;
35956         fields = null;
35957         return presets;
35958     };
35959
35960     presets.state = function(_) {
35961         if (!arguments.length) return state;
35962         state = _;
35963         return presets;
35964     };
35965
35966     presets.tags = function(_) {
35967         if (!arguments.length) return tags;
35968         tags = _;
35969         // Don't reset fields here.
35970         return presets;
35971     };
35972
35973     presets.entityID = function(_) {
35974         if (!arguments.length) return id;
35975         if (id === _) return presets;
35976         id = _;
35977         fields = null;
35978         return presets;
35979     };
35980
35981     return d3.rebind(presets, event, 'on');
35982 };
35983 iD.ui.PresetList = function(context) {
35984     var event = d3.dispatch('choose'),
35985         id,
35986         currentPreset,
35987         autofocus = false;
35988
35989     function presetList(selection) {
35990         var geometry = context.geometry(id),
35991             presets = context.presets().matchGeometry(geometry);
35992
35993         selection.html('');
35994
35995         var messagewrap = selection.append('div')
35996             .attr('class', 'header fillL cf');
35997
35998         var message = messagewrap.append('h3')
35999             .text(t('inspector.choose'));
36000
36001         if (context.entity(id).isUsed(context.graph())) {
36002             messagewrap.append('button')
36003                 .attr('class', 'preset-choose')
36004                 .on('click', function() { event.choose(currentPreset); })
36005                 .append('span')
36006                 .html('&#9658;');
36007         } else {
36008             messagewrap.append('button')
36009                 .attr('class', 'close')
36010                 .on('click', function() {
36011                     context.enter(iD.modes.Browse(context));
36012                 })
36013                 .call(iD.svg.Icon('#icon-close'));
36014         }
36015
36016         function keydown() {
36017             // hack to let delete shortcut work when search is autofocused
36018             if (search.property('value').length === 0 &&
36019                 (d3.event.keyCode === d3.keybinding.keyCodes['⌫'] ||
36020                  d3.event.keyCode === d3.keybinding.keyCodes['⌦'])) {
36021                 d3.event.preventDefault();
36022                 d3.event.stopPropagation();
36023                 iD.operations.Delete([id], context)();
36024             } else if (search.property('value').length === 0 &&
36025                 (d3.event.ctrlKey || d3.event.metaKey) &&
36026                 d3.event.keyCode === d3.keybinding.keyCodes.z) {
36027                 d3.event.preventDefault();
36028                 d3.event.stopPropagation();
36029                 context.undo();
36030             } else if (!d3.event.ctrlKey && !d3.event.metaKey) {
36031                 d3.select(this).on('keydown', null);
36032             }
36033         }
36034
36035         function keypress() {
36036             // enter
36037             var value = search.property('value');
36038             if (d3.event.keyCode === 13 && value.length) {
36039                 list.selectAll('.preset-list-item:first-child').datum().choose();
36040             }
36041         }
36042
36043         function inputevent() {
36044             var value = search.property('value');
36045             list.classed('filtered', value.length);
36046             if (value.length) {
36047                 var results = presets.search(value, geometry);
36048                 message.text(t('inspector.results', {
36049                     n: results.collection.length,
36050                     search: value
36051                 }));
36052                 list.call(drawList, results);
36053             } else {
36054                 list.call(drawList, context.presets().defaults(geometry, 36));
36055                 message.text(t('inspector.choose'));
36056             }
36057         }
36058
36059         var searchWrap = selection.append('div')
36060             .attr('class', 'search-header');
36061
36062         var search = searchWrap.append('input')
36063             .attr('class', 'preset-search-input')
36064             .attr('placeholder', t('inspector.search'))
36065             .attr('type', 'search')
36066             .on('keydown', keydown)
36067             .on('keypress', keypress)
36068             .on('input', inputevent);
36069
36070         searchWrap
36071             .call(iD.svg.Icon('#icon-search', 'pre-text'));
36072
36073         if (autofocus) {
36074             search.node().focus();
36075         }
36076
36077         var listWrap = selection.append('div')
36078             .attr('class', 'inspector-body');
36079
36080         var list = listWrap.append('div')
36081             .attr('class', 'preset-list fillL cf')
36082             .call(drawList, context.presets().defaults(geometry, 36));
36083     }
36084
36085     function drawList(list, presets) {
36086         var collection = presets.collection.map(function(preset) {
36087             return preset.members ? CategoryItem(preset) : PresetItem(preset);
36088         });
36089
36090         var items = list.selectAll('.preset-list-item')
36091             .data(collection, function(d) { return d.preset.id; });
36092
36093         items.enter().append('div')
36094             .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
36095             .classed('current', function(item) { return item.preset === currentPreset; })
36096             .each(function(item) {
36097                 d3.select(this).call(item);
36098             })
36099             .style('opacity', 0)
36100             .transition()
36101             .style('opacity', 1);
36102
36103         items.order();
36104
36105         items.exit()
36106             .remove();
36107     }
36108
36109     function CategoryItem(preset) {
36110         var box, sublist, shown = false;
36111
36112         function item(selection) {
36113             var wrap = selection.append('div')
36114                 .attr('class', 'preset-list-button-wrap category col12');
36115
36116             wrap.append('button')
36117                 .attr('class', 'preset-list-button')
36118                 .classed('expanded', false)
36119                 .call(iD.ui.PresetIcon()
36120                     .geometry(context.geometry(id))
36121                     .preset(preset))
36122                 .on('click', function() {
36123                     var isExpanded = d3.select(this).classed('expanded');
36124                     var triangle = isExpanded ? '▶ ' :  '▼ ';
36125                     d3.select(this).classed('expanded', !isExpanded);
36126                     d3.select(this).selectAll('.label').text(triangle + preset.name());
36127                     item.choose();
36128                 })
36129                 .append('div')
36130                 .attr('class', 'label')
36131                 .text(function() {
36132                   return '▶ ' + preset.name();
36133                 });
36134
36135             box = selection.append('div')
36136                 .attr('class', 'subgrid col12')
36137                 .style('max-height', '0px')
36138                 .style('opacity', 0);
36139
36140             box.append('div')
36141                 .attr('class', 'arrow');
36142
36143             sublist = box.append('div')
36144                 .attr('class', 'preset-list fillL3 cf fl');
36145         }
36146
36147         item.choose = function() {
36148             if (!box || !sublist) return;
36149
36150             if (shown) {
36151                 shown = false;
36152                 box.transition()
36153                     .duration(200)
36154                     .style('opacity', '0')
36155                     .style('max-height', '0px')
36156                     .style('padding-bottom', '0px');
36157             } else {
36158                 shown = true;
36159                 sublist.call(drawList, preset.members);
36160                 box.transition()
36161                     .duration(200)
36162                     .style('opacity', '1')
36163                     .style('max-height', 200 + preset.members.collection.length * 80 + 'px')
36164                     .style('padding-bottom', '20px');
36165             }
36166         };
36167
36168         item.preset = preset;
36169
36170         return item;
36171     }
36172
36173     function PresetItem(preset) {
36174         function item(selection) {
36175             var wrap = selection.append('div')
36176                 .attr('class', 'preset-list-button-wrap col12');
36177
36178             wrap.append('button')
36179                 .attr('class', 'preset-list-button')
36180                 .call(iD.ui.PresetIcon()
36181                     .geometry(context.geometry(id))
36182                     .preset(preset))
36183                 .on('click', item.choose)
36184                 .append('div')
36185                 .attr('class', 'label')
36186                 .text(preset.name());
36187
36188             wrap.call(item.reference.button);
36189             selection.call(item.reference.body);
36190         }
36191
36192         item.choose = function() {
36193             context.presets().choose(preset);
36194
36195             context.perform(
36196                 iD.actions.ChangePreset(id, currentPreset, preset),
36197                 t('operations.change_tags.annotation'));
36198
36199             event.choose(preset);
36200         };
36201
36202         item.help = function() {
36203             d3.event.stopPropagation();
36204             item.reference.toggle();
36205         };
36206
36207         item.preset = preset;
36208         item.reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context);
36209
36210         return item;
36211     }
36212
36213     presetList.autofocus = function(_) {
36214         if (!arguments.length) return autofocus;
36215         autofocus = _;
36216         return presetList;
36217     };
36218
36219     presetList.entityID = function(_) {
36220         if (!arguments.length) return id;
36221         id = _;
36222         presetList.preset(context.presets().match(context.entity(id), context.graph()));
36223         return presetList;
36224     };
36225
36226     presetList.preset = function(_) {
36227         if (!arguments.length) return currentPreset;
36228         currentPreset = _;
36229         return presetList;
36230     };
36231
36232     return d3.rebind(presetList, event, 'on');
36233 };
36234 iD.ui.RadialMenu = function(context, operations) {
36235     var menu,
36236         center = [0, 0],
36237         tooltip;
36238
36239     var radialMenu = function(selection) {
36240         if (!operations.length)
36241             return;
36242
36243         selection.node().parentNode.focus();
36244
36245         function click(operation) {
36246             d3.event.stopPropagation();
36247             if (operation.disabled())
36248                 return;
36249             operation();
36250             radialMenu.close();
36251         }
36252
36253         menu = selection.append('g')
36254             .attr('class', 'radial-menu')
36255             .attr('transform', 'translate(' + center + ')')
36256             .attr('opacity', 0);
36257
36258         menu.transition()
36259             .attr('opacity', 1);
36260
36261         var r = 50,
36262             a = Math.PI / 4,
36263             a0 = -Math.PI / 4,
36264             a1 = a0 + (operations.length - 1) * a;
36265
36266         menu.append('path')
36267             .attr('class', 'radial-menu-background')
36268             .attr('d', 'M' + r * Math.sin(a0) + ',' +
36269                              r * Math.cos(a0) +
36270                       ' A' + r + ',' + r + ' 0 ' + (operations.length > 5 ? '1' : '0') + ',0 ' +
36271                              (r * Math.sin(a1) + 1e-3) + ',' +
36272                              (r * Math.cos(a1) + 1e-3)) // Force positive-length path (#1305)
36273             .attr('stroke-width', 50)
36274             .attr('stroke-linecap', 'round');
36275
36276         var button = menu.selectAll()
36277             .data(operations)
36278             .enter()
36279             .append('g')
36280             .attr('class', function(d) { return 'radial-menu-item radial-menu-item-' + d.id; })
36281             .classed('disabled', function(d) { return d.disabled(); })
36282             .attr('transform', function(d, i) {
36283                 return 'translate(' + iD.geo.roundCoords([
36284                         r * Math.sin(a0 + i * a),
36285                         r * Math.cos(a0 + i * a)]).join(',') + ')';
36286             });
36287
36288         button.append('circle')
36289             .attr('r', 15)
36290             .on('click', click)
36291             .on('mousedown', mousedown)
36292             .on('mouseover', mouseover)
36293             .on('mouseout', mouseout);
36294
36295         button.append('use')
36296             .attr('transform', 'translate(-10,-10)')
36297             .attr('width', '20')
36298             .attr('height', '20')
36299             .attr('xlink:href', function(d) { return '#operation-' + d.id; });
36300
36301         tooltip = d3.select(document.body)
36302             .append('div')
36303             .attr('class', 'tooltip-inner radial-menu-tooltip');
36304
36305         function mousedown() {
36306             d3.event.stopPropagation(); // https://github.com/openstreetmap/iD/issues/1869
36307         }
36308
36309         function mouseover(d, i) {
36310             var rect = context.surfaceRect(),
36311                 angle = a0 + i * a,
36312                 top = rect.top + (r + 25) * Math.cos(angle) + center[1] + 'px',
36313                 left = rect.left + (r + 25) * Math.sin(angle) + center[0] + 'px',
36314                 bottom = rect.height - (r + 25) * Math.cos(angle) - center[1] + 'px',
36315                 right = rect.width - (r + 25) * Math.sin(angle) - center[0] + 'px';
36316
36317             tooltip
36318                 .style('top', null)
36319                 .style('left', null)
36320                 .style('bottom', null)
36321                 .style('right', null)
36322                 .style('display', 'block')
36323                 .html(iD.ui.tooltipHtml(d.tooltip(), d.keys[0]));
36324
36325             if (i === 0) {
36326                 tooltip
36327                     .style('right', right)
36328                     .style('top', top);
36329             } else if (i >= 4) {
36330                 tooltip
36331                     .style('left', left)
36332                     .style('bottom', bottom);
36333             } else {
36334                 tooltip
36335                     .style('left', left)
36336                     .style('top', top);
36337             }
36338         }
36339
36340         function mouseout() {
36341             tooltip.style('display', 'none');
36342         }
36343     };
36344
36345     radialMenu.close = function() {
36346         if (menu) {
36347             menu
36348                 .style('pointer-events', 'none')
36349                 .transition()
36350                 .attr('opacity', 0)
36351                 .remove();
36352         }
36353
36354         if (tooltip) {
36355             tooltip.remove();
36356         }
36357     };
36358
36359     radialMenu.center = function(_) {
36360         if (!arguments.length) return center;
36361         center = _;
36362         return radialMenu;
36363     };
36364
36365     return radialMenu;
36366 };
36367 iD.ui.RawMemberEditor = function(context) {
36368     var id;
36369
36370     function selectMember(d) {
36371         d3.event.preventDefault();
36372         context.enter(iD.modes.Select(context, [d.id]));
36373     }
36374
36375     function changeRole(d) {
36376         var role = d3.select(this).property('value');
36377         context.perform(
36378             iD.actions.ChangeMember(d.relation.id, _.extend({}, d.id, {role: role}), d.index),
36379             t('operations.change_role.annotation'));
36380     }
36381
36382     function deleteMember(d) {
36383         context.perform(
36384             iD.actions.DeleteMember(d.relation.id, d.index),
36385             t('operations.delete_member.annotation'));
36386
36387         if (!context.hasEntity(d.relation.id)) {
36388             context.enter(iD.modes.Browse(context));
36389         }
36390     }
36391
36392     function rawMemberEditor(selection) {
36393         var entity = context.entity(id),
36394             memberships = [];
36395
36396         entity.members.forEach(function(member, index) {
36397             memberships.push({
36398                 index: index,
36399                 id: member.id,
36400                 role: member.role,
36401                 relation: entity,
36402                 member: context.hasEntity(member.id)
36403             });
36404         });
36405
36406         selection.call(iD.ui.Disclosure()
36407             .title(t('inspector.all_members') + ' (' + memberships.length + ')')
36408             .expanded(true)
36409             .on('toggled', toggled)
36410             .content(content));
36411
36412         function toggled(expanded) {
36413             if (expanded) {
36414                 selection.node().parentNode.scrollTop += 200;
36415             }
36416         }
36417
36418         function content($wrap) {
36419             var $list = $wrap.selectAll('.member-list')
36420                 .data([0]);
36421
36422             $list.enter().append('ul')
36423                 .attr('class', 'member-list');
36424
36425             var $items = $list.selectAll('li')
36426                 .data(memberships, function(d) {
36427                     return iD.Entity.key(d.relation) + ',' + d.index + ',' +
36428                         (d.member ? iD.Entity.key(d.member) : 'incomplete');
36429                 });
36430
36431             var $enter = $items.enter().append('li')
36432                 .attr('class', 'member-row form-field')
36433                 .classed('member-incomplete', function(d) { return !d.member; });
36434
36435             $enter.each(function(d) {
36436                 if (d.member) {
36437                     var $label = d3.select(this).append('label')
36438                         .attr('class', 'form-label')
36439                         .append('a')
36440                         .attr('href', '#')
36441                         .on('click', selectMember);
36442
36443                     $label.append('span')
36444                         .attr('class', 'member-entity-type')
36445                         .text(function(d) { return context.presets().match(d.member, context.graph()).name(); });
36446
36447                     $label.append('span')
36448                         .attr('class', 'member-entity-name')
36449                         .text(function(d) { return iD.util.displayName(d.member); });
36450
36451                 } else {
36452                     d3.select(this).append('label')
36453                         .attr('class', 'form-label')
36454                         .text(t('inspector.incomplete'));
36455                 }
36456             });
36457
36458             $enter.append('input')
36459                 .attr('class', 'member-role')
36460                 .property('type', 'text')
36461                 .attr('maxlength', 255)
36462                 .attr('placeholder', t('inspector.role'))
36463                 .property('value', function(d) { return d.role; })
36464                 .on('change', changeRole);
36465
36466             $enter.append('button')
36467                 .attr('tabindex', -1)
36468                 .attr('class', 'remove button-input-action member-delete minor')
36469                 .on('click', deleteMember)
36470                 .call(iD.svg.Icon('#operation-delete'));
36471
36472             $items.exit()
36473                 .remove();
36474         }
36475     }
36476
36477     rawMemberEditor.entityID = function(_) {
36478         if (!arguments.length) return id;
36479         id = _;
36480         return rawMemberEditor;
36481     };
36482
36483     return rawMemberEditor;
36484 };
36485 iD.ui.RawMembershipEditor = function(context) {
36486     var id, showBlank;
36487
36488     function selectRelation(d) {
36489         d3.event.preventDefault();
36490         context.enter(iD.modes.Select(context, [d.relation.id]));
36491     }
36492
36493     function changeRole(d) {
36494         var role = d3.select(this).property('value');
36495         context.perform(
36496             iD.actions.ChangeMember(d.relation.id, _.extend({}, d.member, {role: role}), d.index),
36497             t('operations.change_role.annotation'));
36498     }
36499
36500     function addMembership(d, role) {
36501         showBlank = false;
36502
36503         if (d.relation) {
36504             context.perform(
36505                 iD.actions.AddMember(d.relation.id, {id: id, type: context.entity(id).type, role: role}),
36506                 t('operations.add_member.annotation'));
36507
36508         } else {
36509             var relation = iD.Relation();
36510
36511             context.perform(
36512                 iD.actions.AddEntity(relation),
36513                 iD.actions.AddMember(relation.id, {id: id, type: context.entity(id).type, role: role}),
36514                 t('operations.add.annotation.relation'));
36515
36516             context.enter(iD.modes.Select(context, [relation.id]));
36517         }
36518     }
36519
36520     function deleteMembership(d) {
36521         context.perform(
36522             iD.actions.DeleteMember(d.relation.id, d.index),
36523             t('operations.delete_member.annotation'));
36524     }
36525
36526     function relations(q) {
36527         var newRelation = {
36528                 relation: null,
36529                 value: t('inspector.new_relation')
36530             },
36531             result = [],
36532             graph = context.graph();
36533
36534         context.intersects(context.extent()).forEach(function(entity) {
36535             if (entity.type !== 'relation' || entity.id === id)
36536                 return;
36537
36538             var presetName = context.presets().match(entity, graph).name(),
36539                 entityName = iD.util.displayName(entity) || '';
36540
36541             var value = presetName + ' ' + entityName;
36542             if (q && value.toLowerCase().indexOf(q.toLowerCase()) === -1)
36543                 return;
36544
36545             result.push({
36546                 relation: entity,
36547                 value: value
36548             });
36549         });
36550
36551         result.sort(function(a, b) {
36552             return iD.Relation.creationOrder(a.relation, b.relation);
36553         });
36554         result.unshift(newRelation);
36555
36556         return result;
36557     }
36558
36559     function rawMembershipEditor(selection) {
36560         var entity = context.entity(id),
36561             memberships = [];
36562
36563         context.graph().parentRelations(entity).forEach(function(relation) {
36564             relation.members.forEach(function(member, index) {
36565                 if (member.id === entity.id) {
36566                     memberships.push({relation: relation, member: member, index: index});
36567                 }
36568             });
36569         });
36570
36571         selection.call(iD.ui.Disclosure()
36572             .title(t('inspector.all_relations') + ' (' + memberships.length + ')')
36573             .expanded(true)
36574             .on('toggled', toggled)
36575             .content(content));
36576
36577         function toggled(expanded) {
36578             if (expanded) {
36579                 selection.node().parentNode.scrollTop += 200;
36580             }
36581         }
36582
36583         function content($wrap) {
36584             var $list = $wrap.selectAll('.member-list')
36585                 .data([0]);
36586
36587             $list.enter().append('ul')
36588                 .attr('class', 'member-list');
36589
36590             var $items = $list.selectAll('li.member-row-normal')
36591                 .data(memberships, function(d) { return iD.Entity.key(d.relation) + ',' + d.index; });
36592
36593             var $enter = $items.enter().append('li')
36594                 .attr('class', 'member-row member-row-normal form-field');
36595
36596             var $label = $enter.append('label')
36597                 .attr('class', 'form-label')
36598                 .append('a')
36599                 .attr('href', '#')
36600                 .on('click', selectRelation);
36601
36602             $label.append('span')
36603                 .attr('class', 'member-entity-type')
36604                 .text(function(d) { return context.presets().match(d.relation, context.graph()).name(); });
36605
36606             $label.append('span')
36607                 .attr('class', 'member-entity-name')
36608                 .text(function(d) { return iD.util.displayName(d.relation); });
36609
36610             $enter.append('input')
36611                 .attr('class', 'member-role')
36612                 .property('type', 'text')
36613                 .attr('maxlength', 255)
36614                 .attr('placeholder', t('inspector.role'))
36615                 .property('value', function(d) { return d.member.role; })
36616                 .on('change', changeRole);
36617
36618             $enter.append('button')
36619                 .attr('tabindex', -1)
36620                 .attr('class', 'remove button-input-action member-delete minor')
36621                 .on('click', deleteMembership)
36622                 .call(iD.svg.Icon('#operation-delete'));
36623
36624             $items.exit()
36625                 .remove();
36626
36627             if (showBlank) {
36628                 var $new = $list.selectAll('.member-row-new')
36629                     .data([0]);
36630
36631                 $enter = $new.enter().append('li')
36632                     .attr('class', 'member-row member-row-new form-field');
36633
36634                 $enter.append('input')
36635                     .attr('type', 'text')
36636                     .attr('class', 'member-entity-input')
36637                     .call(d3.combobox()
36638                         .minItems(1)
36639                         .fetcher(function(value, callback) {
36640                             callback(relations(value));
36641                         })
36642                         .on('accept', function(d) {
36643                             addMembership(d, $new.select('.member-role').property('value'));
36644                         }));
36645
36646                 $enter.append('input')
36647                     .attr('class', 'member-role')
36648                     .property('type', 'text')
36649                     .attr('maxlength', 255)
36650                     .attr('placeholder', t('inspector.role'))
36651                     .on('change', changeRole);
36652
36653                 $enter.append('button')
36654                     .attr('tabindex', -1)
36655                     .attr('class', 'remove button-input-action member-delete minor')
36656                     .on('click', deleteMembership)
36657                     .call(iD.svg.Icon('#operation-delete'));
36658
36659             } else {
36660                 $list.selectAll('.member-row-new')
36661                     .remove();
36662             }
36663
36664             var $add = $wrap.selectAll('.add-relation')
36665                 .data([0]);
36666
36667             $add.enter()
36668                 .append('button')
36669                 .attr('class', 'add-relation')
36670                 .call(iD.svg.Icon('#icon-plus', 'light'));
36671
36672             $wrap.selectAll('.add-relation')
36673                 .on('click', function() {
36674                     showBlank = true;
36675                     content($wrap);
36676                     $list.selectAll('.member-entity-input').node().focus();
36677                 });
36678         }
36679     }
36680
36681     rawMembershipEditor.entityID = function(_) {
36682         if (!arguments.length) return id;
36683         id = _;
36684         return rawMembershipEditor;
36685     };
36686
36687     return rawMembershipEditor;
36688 };
36689 iD.ui.RawTagEditor = function(context) {
36690     var event = d3.dispatch('change'),
36691         showBlank = false,
36692         state,
36693         preset,
36694         tags,
36695         id;
36696
36697     function rawTagEditor(selection) {
36698         var count = Object.keys(tags).filter(function(d) { return d; }).length;
36699
36700         selection.call(iD.ui.Disclosure()
36701             .title(t('inspector.all_tags') + ' (' + count + ')')
36702             .expanded(context.storage('raw_tag_editor.expanded') === 'true' || preset.isFallback())
36703             .on('toggled', toggled)
36704             .content(content));
36705
36706         function toggled(expanded) {
36707             context.storage('raw_tag_editor.expanded', expanded);
36708             if (expanded) {
36709                 selection.node().parentNode.scrollTop += 200;
36710             }
36711         }
36712     }
36713
36714     function content($wrap) {
36715         var entries = d3.entries(tags);
36716
36717         if (!entries.length || showBlank) {
36718             showBlank = false;
36719             entries.push({key: '', value: ''});
36720         }
36721
36722         var $list = $wrap.selectAll('.tag-list')
36723             .data([0]);
36724
36725         $list.enter().append('ul')
36726             .attr('class', 'tag-list');
36727
36728         var $newTag = $wrap.selectAll('.add-tag')
36729             .data([0]);
36730
36731         $newTag.enter()
36732             .append('button')
36733             .attr('class', 'add-tag')
36734             .call(iD.svg.Icon('#icon-plus', 'light'));
36735
36736         $newTag.on('click', addTag);
36737
36738         var $items = $list.selectAll('li')
36739             .data(entries, function(d) { return d.key; });
36740
36741         // Enter
36742
36743         var $enter = $items.enter().append('li')
36744             .attr('class', 'tag-row cf');
36745
36746         $enter.append('div')
36747             .attr('class', 'key-wrap')
36748             .append('input')
36749             .property('type', 'text')
36750             .attr('class', 'key')
36751             .attr('maxlength', 255);
36752
36753         $enter.append('div')
36754             .attr('class', 'input-wrap-position')
36755             .append('input')
36756             .property('type', 'text')
36757             .attr('class', 'value')
36758             .attr('maxlength', 255);
36759
36760         $enter.append('button')
36761             .attr('tabindex', -1)
36762             .attr('class', 'remove minor')
36763             .call(iD.svg.Icon('#operation-delete'));
36764
36765         if (context.taginfo()) {
36766             $enter.each(bindTypeahead);
36767         }
36768
36769         // Update
36770
36771         $items.order();
36772
36773         $items.each(function(tag) {
36774             var isRelation = (context.entity(id).type === 'relation'),
36775                 reference;
36776             if (isRelation && tag.key === 'type')
36777                 reference = iD.ui.TagReference({rtype: tag.value}, context);
36778             else
36779                 reference = iD.ui.TagReference({key: tag.key, value: tag.value}, context);
36780
36781             if (state === 'hover') {
36782                 reference.showing(false);
36783             }
36784
36785             d3.select(this)
36786                 .call(reference.button)
36787                 .call(reference.body);
36788         });
36789
36790         $items.select('input.key')
36791             .value(function(d) { return d.key; })
36792             .on('blur', keyChange)
36793             .on('change', keyChange);
36794
36795         $items.select('input.value')
36796             .value(function(d) { return d.value; })
36797             .on('blur', valueChange)
36798             .on('change', valueChange)
36799             .on('keydown.push-more', pushMore);
36800
36801         $items.select('button.remove')
36802             .on('click', removeTag);
36803
36804         $items.exit()
36805             .each(unbind)
36806             .remove();
36807
36808         function pushMore() {
36809             if (d3.event.keyCode === 9 && !d3.event.shiftKey &&
36810                 $list.selectAll('li:last-child input.value').node() === this) {
36811                 addTag();
36812             }
36813         }
36814
36815         function bindTypeahead() {
36816             var row = d3.select(this),
36817                 key = row.selectAll('input.key'),
36818                 value = row.selectAll('input.value');
36819
36820             function sort(value, data) {
36821                 var sameletter = [],
36822                     other = [];
36823                 for (var i = 0; i < data.length; i++) {
36824                     if (data[i].value.substring(0, value.length) === value) {
36825                         sameletter.push(data[i]);
36826                     } else {
36827                         other.push(data[i]);
36828                     }
36829                 }
36830                 return sameletter.concat(other);
36831             }
36832
36833             key.call(d3.combobox()
36834                 .fetcher(function(value, callback) {
36835                     context.taginfo().keys({
36836                         debounce: true,
36837                         geometry: context.geometry(id),
36838                         query: value
36839                     }, function(err, data) {
36840                         if (!err) callback(sort(value, data));
36841                     });
36842                 }));
36843
36844             value.call(d3.combobox()
36845                 .fetcher(function(value, callback) {
36846                     context.taginfo().values({
36847                         debounce: true,
36848                         key: key.value(),
36849                         geometry: context.geometry(id),
36850                         query: value
36851                     }, function(err, data) {
36852                         if (!err) callback(sort(value, data));
36853                     });
36854                 }));
36855         }
36856
36857         function unbind() {
36858             var row = d3.select(this);
36859
36860             row.selectAll('input.key')
36861                 .call(d3.combobox.off);
36862
36863             row.selectAll('input.value')
36864                 .call(d3.combobox.off);
36865         }
36866
36867         function keyChange(d) {
36868             var kOld = d.key,
36869                 kNew = this.value.trim(),
36870                 tag = {};
36871
36872             if (kNew && kNew !== kOld) {
36873                 var match = kNew.match(/^(.*?)(?:_(\d+))?$/),
36874                     base = match[1],
36875                     suffix = +(match[2] || 1);
36876                 while (tags[kNew]) {  // rename key if already in use
36877                     kNew = base + '_' + suffix++;
36878                 }
36879             }
36880             tag[kOld] = undefined;
36881             tag[kNew] = d.value;
36882             d.key = kNew; // Maintain DOM identity through the subsequent update.
36883             this.value = kNew;
36884             event.change(tag);
36885         }
36886
36887         function valueChange(d) {
36888             var tag = {};
36889             tag[d.key] = this.value;
36890             event.change(tag);
36891         }
36892
36893         function removeTag(d) {
36894             var tag = {};
36895             tag[d.key] = undefined;
36896             event.change(tag);
36897             d3.select(this.parentNode).remove();
36898         }
36899
36900         function addTag() {
36901             // Wrapped in a setTimeout in case it's being called from a blur
36902             // handler. Without the setTimeout, the call to `content` would
36903             // wipe out the pending value change.
36904             setTimeout(function() {
36905                 showBlank = true;
36906                 content($wrap);
36907                 $list.selectAll('li:last-child input.key').node().focus();
36908             }, 0);
36909         }
36910     }
36911
36912     rawTagEditor.state = function(_) {
36913         if (!arguments.length) return state;
36914         state = _;
36915         return rawTagEditor;
36916     };
36917
36918     rawTagEditor.preset = function(_) {
36919         if (!arguments.length) return preset;
36920         preset = _;
36921         return rawTagEditor;
36922     };
36923
36924     rawTagEditor.tags = function(_) {
36925         if (!arguments.length) return tags;
36926         tags = _;
36927         return rawTagEditor;
36928     };
36929
36930     rawTagEditor.entityID = function(_) {
36931         if (!arguments.length) return id;
36932         id = _;
36933         return rawTagEditor;
36934     };
36935
36936     return d3.rebind(rawTagEditor, event, 'on');
36937 };
36938 iD.ui.Restore = function(context) {
36939     return function(selection) {
36940         if (!context.history().lock() || !context.history().restorableChanges())
36941             return;
36942
36943         var modal = iD.ui.modal(selection);
36944
36945         modal.select('.modal')
36946             .attr('class', 'modal fillL col6');
36947
36948         var introModal = modal.select('.content');
36949
36950         introModal.attr('class','cf');
36951
36952         introModal.append('div')
36953             .attr('class', 'modal-section')
36954             .append('h3')
36955             .text(t('restore.heading'));
36956
36957         introModal.append('div')
36958             .attr('class','modal-section')
36959             .append('p')
36960             .text(t('restore.description'));
36961
36962         var buttonWrap = introModal.append('div')
36963             .attr('class', 'modal-actions cf');
36964
36965         var restore = buttonWrap.append('button')
36966             .attr('class', 'restore col6')
36967             .text(t('restore.restore'))
36968             .on('click', function() {
36969                 context.history().restore();
36970                 modal.remove();
36971             });
36972
36973         buttonWrap.append('button')
36974             .attr('class', 'reset col6')
36975             .text(t('restore.reset'))
36976             .on('click', function() {
36977                 context.history().clearSaved();
36978                 modal.remove();
36979             });
36980
36981         restore.node().focus();
36982     };
36983 };
36984 iD.ui.Save = function(context) {
36985     var history = context.history(),
36986         key = iD.ui.cmd('⌘S');
36987
36988     function saving() {
36989         return context.mode().id === 'save';
36990     }
36991
36992     function save() {
36993         d3.event.preventDefault();
36994         if (!context.inIntro() && !saving() && history.hasChanges()) {
36995             context.enter(iD.modes.Save(context));
36996         }
36997     }
36998
36999     return function(selection) {
37000         var tooltip = bootstrap.tooltip()
37001             .placement('bottom')
37002             .html(true)
37003             .title(iD.ui.tooltipHtml(t('save.no_changes'), key));
37004
37005         var button = selection.append('button')
37006             .attr('class', 'save col12 disabled')
37007             .attr('tabindex', -1)
37008             .on('click', save)
37009             .call(tooltip);
37010
37011         button.append('span')
37012             .attr('class', 'label')
37013             .text(t('save.title'));
37014
37015         button.append('span')
37016             .attr('class', 'count')
37017             .text('0');
37018
37019         var keybinding = d3.keybinding('undo-redo')
37020             .on(key, save, true);
37021
37022         d3.select(document)
37023             .call(keybinding);
37024
37025         var numChanges = 0;
37026
37027         context.history().on('change.save', function() {
37028             var _ = history.difference().summary().length;
37029             if (_ === numChanges)
37030                 return;
37031             numChanges = _;
37032
37033             tooltip.title(iD.ui.tooltipHtml(t(numChanges > 0 ?
37034                     'save.help' : 'save.no_changes'), key));
37035
37036             button
37037                 .classed('disabled', numChanges === 0)
37038                 .classed('has-count', numChanges > 0);
37039
37040             button.select('span.count')
37041                 .text(numChanges);
37042         });
37043
37044         context.on('enter.save', function() {
37045             button.property('disabled', saving());
37046             if (saving()) button.call(tooltip.hide);
37047         });
37048     };
37049 };
37050 iD.ui.Scale = function(context) {
37051     var projection = context.projection,
37052         maxLength = 180,
37053         tickHeight = 8;
37054
37055     function scaleDefs(loc1, loc2) {
37056         var lat = (loc2[1] + loc1[1]) / 2,
37057             imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
37058             conversion = (imperial ? 3.28084 : 1),
37059             dist = iD.geo.lonToMeters(loc2[0] - loc1[0], lat) * conversion,
37060             scale = { dist: 0, px: 0, text: '' },
37061             buckets, i, val, dLon;
37062
37063         if (imperial) {
37064             buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
37065         } else {
37066             buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
37067         }
37068
37069         // determine a user-friendly endpoint for the scale
37070         for (i = 0; i < buckets.length; i++) {
37071             val = buckets[i];
37072             if (dist >= val) {
37073                 scale.dist = Math.floor(dist / val) * val;
37074                 break;
37075             }
37076         }
37077
37078         dLon = iD.geo.metersToLon(scale.dist / conversion, lat);
37079         scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
37080
37081         if (imperial) {
37082             if (scale.dist >= 5280) {
37083                 scale.dist /= 5280;
37084                 scale.text = String(scale.dist) + ' mi';
37085             } else {
37086                 scale.text = String(scale.dist) + ' ft';
37087             }
37088         } else {
37089             if (scale.dist >= 1000) {
37090                 scale.dist /= 1000;
37091                 scale.text = String(scale.dist) + ' km';
37092             } else {
37093                 scale.text = String(scale.dist) + ' m';
37094             }
37095         }
37096
37097         return scale;
37098     }
37099
37100     function update(selection) {
37101         // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
37102         var dims = context.map().dimensions(),
37103             loc1 = projection.invert([0, dims[1]]),
37104             loc2 = projection.invert([maxLength, dims[1]]),
37105             scale = scaleDefs(loc1, loc2);
37106
37107         selection.select('#scalepath')
37108             .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
37109
37110         selection.select('#scaletext')
37111             .attr('x', scale.px + 8)
37112             .attr('y', tickHeight)
37113             .text(scale.text);
37114     }
37115
37116     return function(selection) {
37117         var g = selection.append('svg')
37118             .attr('id', 'scale')
37119             .append('g')
37120             .attr('transform', 'translate(10,11)');
37121
37122         g.append('path').attr('id', 'scalepath');
37123         g.append('text').attr('id', 'scaletext');
37124
37125         update(selection);
37126
37127         context.map().on('move.scale', function() {
37128             update(selection);
37129         });
37130     };
37131 };
37132 iD.ui.SelectionList = function(context, selectedIDs) {
37133
37134     function selectEntity(entity) {
37135         context.enter(iD.modes.Select(context, [entity.id]).suppressMenu(true));
37136     }
37137
37138
37139     function selectionList(selection) {
37140         selection.classed('selection-list-pane', true);
37141
37142         var header = selection.append('div')
37143             .attr('class', 'header fillL cf');
37144
37145         header.append('h3')
37146             .text(t('inspector.multiselect'));
37147
37148         var listWrap = selection.append('div')
37149             .attr('class', 'inspector-body');
37150
37151         var list = listWrap.append('div')
37152             .attr('class', 'feature-list cf');
37153
37154         context.history().on('change.selection-list', drawList);
37155         drawList();
37156
37157         function drawList() {
37158             var entities = selectedIDs
37159                 .map(function(id) { return context.hasEntity(id); })
37160                 .filter(function(entity) { return entity; });
37161
37162             var items = list.selectAll('.feature-list-item')
37163                 .data(entities, iD.Entity.key);
37164
37165             var enter = items.enter().append('button')
37166                 .attr('class', 'feature-list-item')
37167                 .on('click', selectEntity);
37168
37169             // Enter
37170             var label = enter.append('div')
37171                 .attr('class', 'label')
37172                 .call(iD.svg.Icon('', 'pre-text'));
37173
37174             label.append('span')
37175                 .attr('class', 'entity-type');
37176
37177             label.append('span')
37178                 .attr('class', 'entity-name');
37179
37180             // Update
37181             items.selectAll('use')
37182                 .attr('href', function() {
37183                     var entity = this.parentNode.parentNode.__data__;
37184                     return '#icon-' + context.geometry(entity.id);
37185                 });
37186
37187             items.selectAll('.entity-type')
37188                 .text(function(entity) { return context.presets().match(entity, context.graph()).name(); });
37189
37190             items.selectAll('.entity-name')
37191                 .text(function(entity) { return iD.util.displayName(entity); });
37192
37193             // Exit
37194             items.exit()
37195                 .remove();
37196         }
37197     }
37198
37199     return selectionList;
37200
37201 };
37202 iD.ui.Sidebar = function(context) {
37203     var inspector = iD.ui.Inspector(context),
37204         current;
37205
37206     function sidebar(selection) {
37207         var featureListWrap = selection.append('div')
37208             .attr('class', 'feature-list-pane')
37209             .call(iD.ui.FeatureList(context));
37210
37211         selection.call(iD.ui.Notice(context));
37212
37213         var inspectorWrap = selection.append('div')
37214             .attr('class', 'inspector-hidden inspector-wrap fr');
37215
37216         function hover(id) {
37217             if (!current && context.hasEntity(id)) {
37218                 featureListWrap.classed('inspector-hidden', true);
37219                 inspectorWrap.classed('inspector-hidden', false)
37220                     .classed('inspector-hover', true);
37221
37222                 if (inspector.entityID() !== id || inspector.state() !== 'hover') {
37223                     inspector
37224                         .state('hover')
37225                         .entityID(id);
37226
37227                     inspectorWrap.call(inspector);
37228                 }
37229             } else if (!current) {
37230                 featureListWrap.classed('inspector-hidden', false);
37231                 inspectorWrap.classed('inspector-hidden', true);
37232                 inspector.state('hide');
37233             }
37234         }
37235
37236         sidebar.hover = _.throttle(hover, 200);
37237
37238         sidebar.select = function(id, newFeature) {
37239             if (!current && id) {
37240                 featureListWrap.classed('inspector-hidden', true);
37241                 inspectorWrap.classed('inspector-hidden', false)
37242                     .classed('inspector-hover', false);
37243
37244                 if (inspector.entityID() !== id || inspector.state() !== 'select') {
37245                     inspector
37246                         .state('select')
37247                         .entityID(id)
37248                         .newFeature(newFeature);
37249
37250                     inspectorWrap.call(inspector);
37251                 }
37252             } else if (!current) {
37253                 featureListWrap.classed('inspector-hidden', false);
37254                 inspectorWrap.classed('inspector-hidden', true);
37255                 inspector.state('hide');
37256             }
37257         };
37258
37259         sidebar.show = function(component) {
37260             featureListWrap.classed('inspector-hidden', true);
37261             inspectorWrap.classed('inspector-hidden', true);
37262             if (current) current.remove();
37263             current = selection.append('div')
37264                 .attr('class', 'sidebar-component')
37265                 .call(component);
37266         };
37267
37268         sidebar.hide = function() {
37269             featureListWrap.classed('inspector-hidden', false);
37270             inspectorWrap.classed('inspector-hidden', true);
37271             if (current) current.remove();
37272             current = null;
37273         };
37274     }
37275
37276     sidebar.hover = function() {};
37277     sidebar.hover.cancel = function() {};
37278     sidebar.select = function() {};
37279     sidebar.show = function() {};
37280     sidebar.hide = function() {};
37281
37282     return sidebar;
37283 };
37284 iD.ui.SourceSwitch = function(context) {
37285     var keys;
37286
37287     function click() {
37288         d3.event.preventDefault();
37289
37290         if (context.history().hasChanges() &&
37291             !window.confirm(t('source_switch.lose_changes'))) return;
37292
37293         var live = d3.select(this)
37294             .classed('live');
37295
37296         context.connection()
37297             .switch(live ? keys[1] : keys[0]);
37298
37299         context.enter(iD.modes.Browse(context));
37300         context.flush();
37301
37302         d3.select(this)
37303             .text(live ? t('source_switch.dev') : t('source_switch.live'))
37304             .classed('live', !live);
37305     }
37306
37307     var sourceSwitch = function(selection) {
37308         selection.append('a')
37309             .attr('href', '#')
37310             .text(t('source_switch.live'))
37311             .classed('live', true)
37312             .attr('tabindex', -1)
37313             .on('click', click);
37314     };
37315
37316     sourceSwitch.keys = function(_) {
37317         if (!arguments.length) return keys;
37318         keys = _;
37319         return sourceSwitch;
37320     };
37321
37322     return sourceSwitch;
37323 };
37324 iD.ui.Spinner = function(context) {
37325     var connection = context.connection();
37326
37327     return function(selection) {
37328         var img = selection.append('img')
37329             .attr('src', context.imagePath('loader-black.gif'))
37330             .style('opacity', 0);
37331
37332         connection.on('loading.spinner', function() {
37333             img.transition()
37334                 .style('opacity', 1);
37335         });
37336
37337         connection.on('loaded.spinner', function() {
37338             img.transition()
37339                 .style('opacity', 0);
37340         });
37341     };
37342 };
37343 iD.ui.Splash = function(context) {
37344     return function(selection) {
37345         if (context.storage('sawSplash'))
37346              return;
37347
37348         context.storage('sawSplash', true);
37349
37350         var modal = iD.ui.modal(selection);
37351
37352         modal.select('.modal')
37353             .attr('class', 'modal-splash modal col6');
37354
37355         var introModal = modal.select('.content')
37356             .append('div')
37357             .attr('class', 'fillL');
37358
37359         introModal.append('div')
37360             .attr('class','modal-section cf')
37361             .append('h3').text(t('splash.welcome'));
37362
37363         introModal.append('div')
37364             .attr('class','modal-section')
37365             .append('p')
37366             .html(t('splash.text', {
37367                 version: iD.version,
37368                 website: '<a href="http://ideditor.com/">ideditor.com</a>',
37369                 github: '<a href="https://github.com/openstreetmap/iD">github.com</a>'
37370             }));
37371
37372         var buttons = introModal.append('div').attr('class', 'modal-actions cf');
37373
37374         buttons.append('button')
37375             .attr('class', 'col6 walkthrough')
37376             .text(t('splash.walkthrough'))
37377             .on('click', function() {
37378                 d3.select(document.body).call(iD.ui.intro(context));
37379                 modal.close();
37380             });
37381
37382         buttons.append('button')
37383             .attr('class', 'col6 start')
37384             .text(t('splash.start'))
37385             .on('click', modal.close);
37386
37387         modal.select('button.close').attr('class','hide');
37388
37389     };
37390 };
37391 iD.ui.Status = function(context) {
37392     var connection = context.connection(),
37393         errCount = 0;
37394
37395     return function(selection) {
37396
37397         function update() {
37398
37399             connection.status(function(err, apiStatus) {
37400
37401                 selection.html('');
37402
37403                 if (err && errCount++ < 2) return;
37404
37405                 if (err) {
37406                     selection.text(t('status.error'));
37407
37408                 } else if (apiStatus === 'readonly') {
37409                     selection.text(t('status.readonly'));
37410
37411                 } else if (apiStatus === 'offline') {
37412                     selection.text(t('status.offline'));
37413                 }
37414
37415                 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
37416                 if (!err) errCount = 0;
37417
37418             });
37419         }
37420
37421         connection.on('auth', function() { update(selection); });
37422         window.setInterval(update, 90000);
37423         update(selection);
37424     };
37425 };
37426 iD.ui.Success = function(context) {
37427     var dispatch = d3.dispatch('cancel'),
37428         changeset;
37429
37430     function success(selection) {
37431         var message = (changeset.comment || t('success.edited_osm')).substring(0, 130) +
37432             ' ' + context.connection().changesetURL(changeset.id);
37433
37434         var header = selection.append('div')
37435             .attr('class', 'header fillL');
37436
37437         header.append('button')
37438             .attr('class', 'fr')
37439             .on('click', function() { dispatch.cancel(); })
37440             .call(iD.svg.Icon('#icon-close'));
37441
37442         header.append('h3')
37443             .text(t('success.just_edited'));
37444
37445         var body = selection.append('div')
37446             .attr('class', 'body save-success fillL');
37447
37448         body.append('p')
37449             .html(t('success.help_html'));
37450
37451         body.append('a')
37452             .attr('class', 'details')
37453             .attr('target', '_blank')
37454             .attr('tabindex', -1)
37455             .call(iD.svg.Icon('#icon-out-link', 'inline'))
37456             .attr('href', t('success.help_link_url'))
37457             .append('span')
37458             .text(t('success.help_link_text'));
37459
37460         var changesetURL = context.connection().changesetURL(changeset.id);
37461
37462         body.append('a')
37463             .attr('class', 'button col12 osm')
37464             .attr('target', '_blank')
37465             .attr('href', changesetURL)
37466             .text(t('success.view_on_osm'));
37467
37468         var sharing = {
37469             facebook: 'https://facebook.com/sharer/sharer.php?u=' + encodeURIComponent(changesetURL),
37470             twitter: 'https://twitter.com/intent/tweet?source=webclient&text=' + encodeURIComponent(message),
37471             google: 'https://plus.google.com/share?url=' + encodeURIComponent(changesetURL)
37472         };
37473
37474         body.selectAll('.button.social')
37475             .data(d3.entries(sharing))
37476             .enter()
37477             .append('a')
37478             .attr('class', 'button social col4')
37479             .attr('target', '_blank')
37480             .attr('href', function(d) { return d.value; })
37481             .call(bootstrap.tooltip()
37482                 .title(function(d) { return t('success.' + d.key); })
37483                 .placement('bottom'))
37484             .each(function(d) { d3.select(this).call(iD.svg.Icon('#logo-' + d.key, 'social')); });
37485     }
37486
37487     success.changeset = function(_) {
37488         if (!arguments.length) return changeset;
37489         changeset = _;
37490         return success;
37491     };
37492
37493     return d3.rebind(success, dispatch, 'on');
37494 };
37495 iD.ui.TagReference = function(tag, context) {
37496     var tagReference = {},
37497         button,
37498         body,
37499         loaded,
37500         showing;
37501
37502     function findLocal(data) {
37503         var locale = iD.detect().locale.toLowerCase(),
37504             localized;
37505
37506         localized = _.find(data, function(d) {
37507             return d.lang.toLowerCase() === locale;
37508         });
37509         if (localized) return localized;
37510
37511         // try the non-regional version of a language, like
37512         // 'en' if the language is 'en-US'
37513         if (locale.indexOf('-') !== -1) {
37514             var first = locale.split('-')[0];
37515             localized = _.find(data, function(d) {
37516                 return d.lang.toLowerCase() === first;
37517             });
37518             if (localized) return localized;
37519         }
37520
37521         // finally fall back to english
37522         return _.find(data, function(d) {
37523             return d.lang.toLowerCase() === 'en';
37524         });
37525     }
37526
37527     function load(param) {
37528         button.classed('tag-reference-loading', true);
37529
37530         context.taginfo().docs(param, function show(err, data) {
37531             var docs;
37532             if (!err && data) {
37533                 docs = findLocal(data);
37534             }
37535
37536             body.html('');
37537
37538             if (!docs || !docs.description) {
37539                 if (param.hasOwnProperty('value')) {
37540                     load(_.omit(param, 'value'));   // retry with key only
37541                 } else {
37542                     body.append('p').text(t('inspector.no_documentation_key'));
37543                     done();
37544                 }
37545                 return;
37546             }
37547
37548             if (docs.image && docs.image.thumb_url_prefix) {
37549                 body
37550                     .append('img')
37551                     .attr('class', 'wiki-image')
37552                     .attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix)
37553                     .on('load', function() { done(); })
37554                     .on('error', function() { d3.select(this).remove(); done(); });
37555             } else {
37556                 done();
37557             }
37558
37559             body
37560                 .append('p')
37561                 .text(docs.description);
37562
37563             body
37564                 .append('a')
37565                 .attr('target', '_blank')
37566                 .attr('href', 'https://wiki.openstreetmap.org/wiki/' + docs.title)
37567                 .call(iD.svg.Icon('#icon-out-link', 'inline'))
37568                 .append('span')
37569                 .text(t('inspector.reference'));
37570         });
37571     }
37572
37573     function done() {
37574         loaded = true;
37575
37576         button.classed('tag-reference-loading', false);
37577
37578         body.transition()
37579             .duration(200)
37580             .style('max-height', '200px')
37581             .style('opacity', '1');
37582
37583         showing = true;
37584     }
37585
37586     function hide(selection) {
37587         selection = selection || body.transition().duration(200);
37588
37589         selection
37590             .style('max-height', '0px')
37591             .style('opacity', '0');
37592
37593         showing = false;
37594     }
37595
37596     tagReference.button = function(selection) {
37597         button = selection.selectAll('.tag-reference-button')
37598             .data([0]);
37599
37600         button.enter()
37601             .append('button')
37602             .attr('class', 'tag-reference-button')
37603             .attr('tabindex', -1)
37604             .call(iD.svg.Icon('#icon-inspect'));
37605
37606         button.on('click', function () {
37607             d3.event.stopPropagation();
37608             d3.event.preventDefault();
37609             if (showing) {
37610                 hide();
37611             } else if (loaded) {
37612                 done();
37613             } else {
37614                 if (context.taginfo()) {
37615                     load(tag);
37616                 }
37617             }
37618         });
37619     };
37620
37621     tagReference.body = function(selection) {
37622         body = selection.selectAll('.tag-reference-body')
37623             .data([0]);
37624
37625         body.enter().append('div')
37626             .attr('class', 'tag-reference-body cf')
37627             .style('max-height', '0')
37628             .style('opacity', '0');
37629
37630         if (showing === false) {
37631             hide(body);
37632         }
37633     };
37634
37635     tagReference.showing = function(_) {
37636         if (!arguments.length) return showing;
37637         showing = _;
37638         return tagReference;
37639     };
37640
37641     return tagReference;
37642 };
37643 // toggles the visibility of ui elements, using a combination of the
37644 // hide class, which sets display=none, and a d3 transition for opacity.
37645 // this will cause blinking when called repeatedly, so check that the
37646 // value actually changes between calls.
37647 iD.ui.Toggle = function(show, callback) {
37648     return function(selection) {
37649         selection
37650             .style('opacity', show ? 0 : 1)
37651             .classed('hide', false)
37652             .transition()
37653             .style('opacity', show ? 1 : 0)
37654             .each('end', function() {
37655                 d3.select(this)
37656                     .classed('hide', !show)
37657                     .style('opacity', null);
37658                 if (callback) callback.apply(this);
37659             });
37660     };
37661 };
37662 iD.ui.UndoRedo = function(context) {
37663     var commands = [{
37664         id: 'undo',
37665         cmd: iD.ui.cmd('⌘Z'),
37666         action: function() { if (!(context.inIntro() || saving())) context.undo(); },
37667         annotation: function() { return context.history().undoAnnotation(); }
37668     }, {
37669         id: 'redo',
37670         cmd: iD.ui.cmd('⌘⇧Z'),
37671         action: function() {if (!(context.inIntro() || saving())) context.redo(); },
37672         annotation: function() { return context.history().redoAnnotation(); }
37673     }];
37674
37675     function saving() {
37676         return context.mode().id === 'save';
37677     }
37678
37679     return function(selection) {
37680         var tooltip = bootstrap.tooltip()
37681             .placement('bottom')
37682             .html(true)
37683             .title(function (d) {
37684                 return iD.ui.tooltipHtml(d.annotation() ?
37685                     t(d.id + '.tooltip', {action: d.annotation()}) :
37686                     t(d.id + '.nothing'), d.cmd);
37687             });
37688
37689         var buttons = selection.selectAll('button')
37690             .data(commands)
37691             .enter().append('button')
37692             .attr('class', 'col6 disabled')
37693             .on('click', function(d) { return d.action(); })
37694             .call(tooltip);
37695
37696         buttons.each(function(d) {
37697             d3.select(this)
37698                 .call(iD.svg.Icon('#icon-' + d.id));
37699         });
37700
37701         var keybinding = d3.keybinding('undo')
37702             .on(commands[0].cmd, function() { d3.event.preventDefault(); commands[0].action(); })
37703             .on(commands[1].cmd, function() { d3.event.preventDefault(); commands[1].action(); });
37704
37705         d3.select(document)
37706             .call(keybinding);
37707
37708         context.history()
37709             .on('change.undo_redo', update);
37710
37711         context
37712             .on('enter.undo_redo', update);
37713
37714         function update() {
37715             buttons
37716                 .property('disabled', saving())
37717                 .classed('disabled', function(d) { return !d.annotation(); })
37718                 .each(function() {
37719                     var selection = d3.select(this);
37720                     if (selection.property('tooltipVisible')) {
37721                         selection.call(tooltip.show);
37722                     }
37723                 });
37724         }
37725     };
37726 };
37727 iD.ui.ViewOnOSM = function(context) {
37728     var id;
37729
37730     function viewOnOSM(selection) {
37731         var entity = context.entity(id);
37732
37733         selection.style('display', entity.isNew() ? 'none' : null);
37734
37735         var $link = selection.selectAll('.view-on-osm')
37736             .data([0]);
37737
37738         $link.enter()
37739             .append('a')
37740             .attr('class', 'view-on-osm')
37741             .attr('target', '_blank')
37742             .call(iD.svg.Icon('#icon-out-link', 'inline'))
37743             .append('span')
37744             .text(t('inspector.view_on_osm'));
37745
37746         $link
37747             .attr('href', context.connection().entityURL(entity));
37748     }
37749
37750     viewOnOSM.entityID = function(_) {
37751         if (!arguments.length) return id;
37752         id = _;
37753         return viewOnOSM;
37754     };
37755
37756     return viewOnOSM;
37757 };
37758 iD.ui.Zoom = function(context) {
37759     var zooms = [{
37760         id: 'zoom-in',
37761         icon: 'plus',
37762         title: t('zoom.in'),
37763         action: context.zoomIn,
37764         key: '+'
37765     }, {
37766         id: 'zoom-out',
37767         icon: 'minus',
37768         title: t('zoom.out'),
37769         action: context.zoomOut,
37770         key: '-'
37771     }];
37772
37773     function zoomIn() {
37774         d3.event.preventDefault();
37775         if (!context.inIntro()) context.zoomIn();
37776     }
37777
37778     function zoomOut() {
37779         d3.event.preventDefault();
37780         if (!context.inIntro()) context.zoomOut();
37781     }
37782
37783     function zoomInFurther() {
37784         d3.event.preventDefault();
37785         if (!context.inIntro()) context.zoomInFurther();
37786     }
37787
37788     function zoomOutFurther() {
37789         d3.event.preventDefault();
37790         if (!context.inIntro()) context.zoomOutFurther();
37791     }
37792
37793
37794     return function(selection) {
37795         var button = selection.selectAll('button')
37796             .data(zooms)
37797             .enter().append('button')
37798             .attr('tabindex', -1)
37799             .attr('class', function(d) { return d.id; })
37800             .on('click.editor', function(d) { d.action(); })
37801             .call(bootstrap.tooltip()
37802                 .placement('left')
37803                 .html(true)
37804                 .title(function(d) {
37805                     return iD.ui.tooltipHtml(d.title, d.key);
37806                 }));
37807
37808         button.each(function(d) {
37809             d3.select(this)
37810                 .call(iD.svg.Icon('#icon-' + d.icon, 'light'));
37811         });
37812
37813         var keybinding = d3.keybinding('zoom');
37814
37815         _.each(['=','ffequals','plus','ffplus'], function(key) {
37816             keybinding.on(key, zoomIn);
37817             keybinding.on('⇧' + key, zoomIn);
37818             keybinding.on(iD.ui.cmd('⌘' + key), zoomInFurther);
37819             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomInFurther);
37820         });
37821         _.each(['-','ffminus','_','dash'], function(key) {
37822             keybinding.on(key, zoomOut);
37823             keybinding.on('⇧' + key, zoomOut);
37824             keybinding.on(iD.ui.cmd('⌘' + key), zoomOutFurther);
37825             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomOutFurther);
37826         });
37827
37828         d3.select(document)
37829             .call(keybinding);
37830     };
37831 };
37832 iD.ui.preset.access = function(field) {
37833     var dispatch = d3.dispatch('change'),
37834         items;
37835
37836     function access(selection) {
37837         var wrap = selection.selectAll('.preset-input-wrap')
37838             .data([0]);
37839
37840         wrap.enter().append('div')
37841             .attr('class', 'cf preset-input-wrap')
37842             .append('ul');
37843
37844         items = wrap.select('ul').selectAll('li')
37845             .data(field.keys);
37846
37847         // Enter
37848
37849         var enter = items.enter().append('li')
37850             .attr('class', function(d) { return 'cf preset-access-' + d; });
37851
37852         enter.append('span')
37853             .attr('class', 'col6 label preset-label-access')
37854             .attr('for', function(d) { return 'preset-input-access-' + d; })
37855             .text(function(d) { return field.t('types.' + d); });
37856
37857         enter.append('div')
37858             .attr('class', 'col6 preset-input-access-wrap')
37859             .append('input')
37860             .attr('type', 'text')
37861             .attr('class', 'preset-input-access')
37862             .attr('id', function(d) { return 'preset-input-access-' + d; })
37863             .each(function(d) {
37864                 d3.select(this)
37865                     .call(d3.combobox()
37866                         .data(access.options(d)));
37867             });
37868
37869         // Update
37870
37871         wrap.selectAll('.preset-input-access')
37872             .on('change', change)
37873             .on('blur', change);
37874     }
37875
37876     function change(d) {
37877         var tag = {};
37878         tag[d] = d3.select(this).value() || undefined;
37879         dispatch.change(tag);
37880     }
37881
37882     access.options = function(type) {
37883         var options = ['no', 'permissive', 'private', 'destination'];
37884
37885         if (type !== 'access') {
37886             options.unshift('yes');
37887             options.push('designated');
37888
37889             if (type === 'bicycle') {
37890                 options.push('dismount');
37891             }
37892         }
37893
37894         return options.map(function(option) {
37895             return {
37896                 title: field.t('options.' + option + '.description'),
37897                 value: option
37898             };
37899         });
37900     };
37901
37902     var placeholders = {
37903         footway: {
37904             foot: 'designated',
37905             motor_vehicle: 'no'
37906         },
37907         steps: {
37908             foot: 'yes',
37909             motor_vehicle: 'no',
37910             bicycle: 'no',
37911             horse: 'no'
37912         },
37913         pedestrian: {
37914             foot: 'yes',
37915             motor_vehicle: 'no'
37916         },
37917         cycleway: {
37918             motor_vehicle: 'no',
37919             bicycle: 'designated'
37920         },
37921         bridleway: {
37922             motor_vehicle: 'no',
37923             horse: 'designated'
37924         },
37925         path: {
37926             foot: 'yes',
37927             motor_vehicle: 'no',
37928             bicycle: 'yes',
37929             horse: 'yes'
37930         },
37931         motorway: {
37932             foot: 'no',
37933             motor_vehicle: 'yes',
37934             bicycle: 'no',
37935             horse: 'no'
37936         },
37937         trunk: {
37938             motor_vehicle: 'yes'
37939         },
37940         primary: {
37941             foot: 'yes',
37942             motor_vehicle: 'yes',
37943             bicycle: 'yes',
37944             horse: 'yes'
37945         },
37946         secondary: {
37947             foot: 'yes',
37948             motor_vehicle: 'yes',
37949             bicycle: 'yes',
37950             horse: 'yes'
37951         },
37952         tertiary: {
37953             foot: 'yes',
37954             motor_vehicle: 'yes',
37955             bicycle: 'yes',
37956             horse: 'yes'
37957         },
37958         residential: {
37959             foot: 'yes',
37960             motor_vehicle: 'yes',
37961             bicycle: 'yes',
37962             horse: 'yes'
37963         },
37964         unclassified: {
37965             foot: 'yes',
37966             motor_vehicle: 'yes',
37967             bicycle: 'yes',
37968             horse: 'yes'
37969         },
37970         service: {
37971             foot: 'yes',
37972             motor_vehicle: 'yes',
37973             bicycle: 'yes',
37974             horse: 'yes'
37975         },
37976         motorway_link: {
37977             foot: 'no',
37978             motor_vehicle: 'yes',
37979             bicycle: 'no',
37980             horse: 'no'
37981         },
37982         trunk_link: {
37983             motor_vehicle: 'yes'
37984         },
37985         primary_link: {
37986             foot: 'yes',
37987             motor_vehicle: 'yes',
37988             bicycle: 'yes',
37989             horse: 'yes'
37990         },
37991         secondary_link: {
37992             foot: 'yes',
37993             motor_vehicle: 'yes',
37994             bicycle: 'yes',
37995             horse: 'yes'
37996         },
37997         tertiary_link: {
37998             foot: 'yes',
37999             motor_vehicle: 'yes',
38000             bicycle: 'yes',
38001             horse: 'yes'
38002         }
38003     };
38004
38005     access.tags = function(tags) {
38006         items.selectAll('.preset-input-access')
38007             .value(function(d) { return tags[d] || ''; })
38008             .attr('placeholder', function() {
38009                 return tags.access ? tags.access : field.placeholder();
38010             });
38011
38012         // items.selectAll('#preset-input-access-access')
38013         //     .attr('placeholder', 'yes');
38014
38015         _.forEach(placeholders[tags.highway], function(v, k) {
38016             items.selectAll('#preset-input-access-' + k)
38017                 .attr('placeholder', function() { return (tags.access || v); });
38018         });
38019     };
38020
38021     access.focus = function() {
38022         items.selectAll('.preset-input-access')
38023             .node().focus();
38024     };
38025
38026     return d3.rebind(access, dispatch, 'on');
38027 };
38028 iD.ui.preset.address = function(field, context) {
38029     var dispatch = d3.dispatch('init', 'change'),
38030         wrap,
38031         entity,
38032         isInitialized;
38033
38034     var widths = {
38035         housenumber: 1/3,
38036         street: 2/3,
38037         city: 2/3,
38038         state: 1/4,
38039         postcode: 1/3
38040     };
38041
38042     function getStreets() {
38043         var extent = entity.extent(context.graph()),
38044             l = extent.center(),
38045             box = iD.geo.Extent(l).padByMeters(200);
38046
38047         return context.intersects(box)
38048             .filter(isAddressable)
38049             .map(function(d) {
38050                 var loc = context.projection([
38051                     (extent[0][0] + extent[1][0]) / 2,
38052                     (extent[0][1] + extent[1][1]) / 2]),
38053                     choice = iD.geo.chooseEdge(context.childNodes(d), loc, context.projection);
38054                 return {
38055                     title: d.tags.name,
38056                     value: d.tags.name,
38057                     dist: choice.distance
38058                 };
38059             }).sort(function(a, b) {
38060                 return a.dist - b.dist;
38061             });
38062
38063         function isAddressable(d) {
38064             return d.tags.highway && d.tags.name && d.type === 'way';
38065         }
38066     }
38067
38068     function getCities() {
38069         var extent = entity.extent(context.graph()),
38070             l = extent.center(),
38071             box = iD.geo.Extent(l).padByMeters(200);
38072
38073         return context.intersects(box)
38074             .filter(isAddressable)
38075             .map(function(d) {
38076                 return {
38077                     title: d.tags['addr:city'] || d.tags.name,
38078                     value: d.tags['addr:city'] || d.tags.name,
38079                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
38080                 };
38081             }).sort(function(a, b) {
38082                 return a.dist - b.dist;
38083             });
38084
38085         function isAddressable(d) {
38086             if (d.tags.name &&
38087                 (d.tags.admin_level === '8' || d.tags.border_type === 'city'))
38088                 return true;
38089
38090             if (d.tags.place && d.tags.name && (
38091                     d.tags.place === 'city' ||
38092                     d.tags.place === 'town' ||
38093                     d.tags.place === 'village'))
38094                 return true;
38095
38096             if (d.tags['addr:city']) return true;
38097
38098             return false;
38099         }
38100     }
38101
38102     function getPostCodes() {
38103         var extent = entity.extent(context.graph()),
38104             l = extent.center(),
38105             box = iD.geo.Extent(l).padByMeters(200);
38106
38107         return context.intersects(box)
38108             .filter(isAddressable)
38109             .map(function(d) {
38110                 return {
38111                     title: d.tags['addr:postcode'],
38112                     value: d.tags['addr:postcode'],
38113                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
38114                 };
38115             }).sort(function(a, b) {
38116                 return a.dist - b.dist;
38117             });
38118
38119         function isAddressable(d) {
38120             return d.tags['addr:postcode'];
38121         }
38122     }
38123
38124     function address(selection) {
38125         isInitialized = false;
38126
38127         wrap = selection.selectAll('.preset-input-wrap')
38128             .data([0]);
38129
38130         // Enter
38131
38132         wrap.enter()
38133             .append('div')
38134             .attr('class', 'preset-input-wrap');
38135
38136         var center = entity.extent(context.graph()).center(),
38137             addressFormat;
38138
38139         iD.services.nominatim().countryCode(center, function (err, countryCode) {
38140             addressFormat = _.find(iD.data.addressFormats, function (a) {
38141                 return a && a.countryCodes && _.contains(a.countryCodes, countryCode);
38142             }) || _.first(iD.data.addressFormats);
38143
38144             function row(r) {
38145                 // Normalize widths.
38146                 var total = _.reduce(r, function(sum, field) {
38147                     return sum + (widths[field] || 0.5);
38148                 }, 0);
38149
38150                 return r.map(function (field) {
38151                     return {
38152                         id: field,
38153                         width: (widths[field] || 0.5) / total
38154                     };
38155                 });
38156             }
38157
38158             wrap.selectAll('div')
38159                 .data(addressFormat.format)
38160                 .enter()
38161                 .append('div')
38162                 .attr('class', 'addr-row')
38163                 .selectAll('input')
38164                 .data(row)
38165                 .enter()
38166                 .append('input')
38167                 .property('type', 'text')
38168                 .attr('placeholder', function (d) { return field.t('placeholders.' + d.id); })
38169                 .attr('class', function (d) { return 'addr-' + d.id; })
38170                 .style('width', function (d) { return d.width * 100 + '%'; });
38171
38172             // Update
38173
38174             wrap.selectAll('.addr-street')
38175                 .call(d3.combobox()
38176                     .fetcher(function(value, callback) {
38177                         callback(getStreets());
38178                     }));
38179
38180             wrap.selectAll('.addr-city')
38181                 .call(d3.combobox()
38182                     .fetcher(function(value, callback) {
38183                         callback(getCities());
38184                     }));
38185
38186             wrap.selectAll('.addr-postcode')
38187                 .call(d3.combobox()
38188                     .fetcher(function(value, callback) {
38189                         callback(getPostCodes());
38190                     }));
38191
38192             wrap.selectAll('input')
38193                 .on('blur', change())
38194                 .on('change', change());
38195
38196             wrap.selectAll('input:not(.combobox-input)')
38197                 .on('input', change(true));
38198
38199             dispatch.init();
38200             isInitialized = true;
38201         });
38202     }
38203
38204     function change(onInput) {
38205         return function() {
38206             var tags = {};
38207
38208             wrap.selectAll('input')
38209                 .each(function (field) {
38210                     tags['addr:' + field.id] = this.value || undefined;
38211                 });
38212
38213             dispatch.change(tags, onInput);
38214         };
38215     }
38216
38217     function updateTags(tags) {
38218         wrap.selectAll('input')
38219             .value(function (field) {
38220                 return tags['addr:' + field.id] || '';
38221             });
38222     }
38223
38224     address.entity = function(_) {
38225         if (!arguments.length) return entity;
38226         entity = _;
38227         return address;
38228     };
38229
38230     address.tags = function(tags) {
38231         if (isInitialized) {
38232             updateTags(tags);
38233         } else {
38234             dispatch.on('init', function () {
38235                 updateTags(tags);
38236             });
38237         }
38238     };
38239
38240     address.focus = function() {
38241         var node = wrap.selectAll('input').node();
38242         if (node) node.focus();
38243     };
38244
38245     return d3.rebind(address, dispatch, 'on');
38246 };
38247 iD.ui.preset.check =
38248 iD.ui.preset.defaultcheck = function(field) {
38249     var dispatch = d3.dispatch('change'),
38250         options = field.strings && field.strings.options,
38251         values = [],
38252         texts = [],
38253         entity, value, box, text, label;
38254
38255     if (options) {
38256         for (var k in options) {
38257             values.push(k === 'undefined' ? undefined : k);
38258             texts.push(field.t('options.' + k, { 'default': options[k] }));
38259         }
38260     } else {
38261         values = [undefined, 'yes'];
38262         texts = [t('inspector.unknown'), t('inspector.check.yes')];
38263         if (field.type === 'check') {
38264             values.push('no');
38265             texts.push(t('inspector.check.no'));
38266         }
38267     }
38268
38269     var check = function(selection) {
38270         // hack: pretend oneway field is a oneway_yes field
38271         // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
38272         if (field.id === 'oneway') {
38273             for (var key in entity.tags) {
38274                 if (key in iD.oneWayTags && (entity.tags[key] in iD.oneWayTags[key])) {
38275                     texts[0] = t('presets.fields.oneway_yes.options.undefined');
38276                     break;
38277                 }
38278             }
38279         }
38280
38281         selection.classed('checkselect', 'true');
38282
38283         label = selection.selectAll('.preset-input-wrap')
38284             .data([0]);
38285
38286         var enter = label.enter().append('label')
38287             .attr('class', 'preset-input-wrap');
38288
38289         enter.append('input')
38290             .property('indeterminate', field.type === 'check')
38291             .attr('type', 'checkbox')
38292             .attr('id', 'preset-input-' + field.id);
38293
38294         enter.append('span')
38295             .text(texts[0])
38296             .attr('class', 'value');
38297
38298         box = label.select('input')
38299             .on('click', function() {
38300                 var t = {};
38301                 t[field.key] = values[(values.indexOf(value) + 1) % values.length];
38302                 dispatch.change(t);
38303                 d3.event.stopPropagation();
38304             });
38305
38306         text = label.select('span.value');
38307     };
38308
38309     check.entity = function(_) {
38310         if (!arguments.length) return entity;
38311         entity = _;
38312         return check;
38313     };
38314
38315     check.tags = function(tags) {
38316         value = tags[field.key];
38317         box.property('indeterminate', field.type === 'check' && !value);
38318         box.property('checked', value === 'yes');
38319         text.text(texts[values.indexOf(value)]);
38320         label.classed('set', !!value);
38321     };
38322
38323     check.focus = function() {
38324         box.node().focus();
38325     };
38326
38327     return d3.rebind(check, dispatch, 'on');
38328 };
38329 iD.ui.preset.combo =
38330 iD.ui.preset.typeCombo = function(field, context) {
38331     var dispatch = d3.dispatch('change'),
38332         optstrings = field.strings && field.strings.options,
38333         optarray = field.options,
38334         snake_case = (field.snake_case || (field.snake_case === undefined)),
38335         strings = {},
38336         input;
38337
38338     function snake(s) {
38339         return s.replace(/\s+/g, '_');
38340     }
38341
38342     function unsnake(s) {
38343         return s.replace(/_+/g, ' ');
38344     }
38345
38346     function clean(s) {
38347         return s.split(';')
38348             .map(function(s) { return s.trim(); })
38349             .join(';');
38350     }
38351
38352     function optString() {
38353         return _.find(_.keys(strings), function(k) {
38354                 return strings[k] === input.value();
38355             });
38356     }
38357
38358     function combo(selection) {
38359         var combobox = d3.combobox();
38360
38361         input = selection.selectAll('input')
38362             .data([0]);
38363
38364         var enter = input.enter()
38365             .append('input')
38366             .attr('type', 'text')
38367             .attr('id', 'preset-input-' + field.id);
38368
38369         if (optstrings) { enter.attr('readonly', 'readonly'); }
38370
38371         input
38372             .call(combobox)
38373             .on('change', change)
38374             .on('blur', change)
38375             .each(function() {
38376                 if (optstrings) {
38377                     _.each(optstrings, function(v, k) {
38378                         strings[k] = field.t('options.' + k, { 'default': v });
38379                     });
38380                     stringsLoaded();
38381                 } else if (optarray) {
38382                     _.each(optarray, function(k) {
38383                         strings[k] = (snake_case ? unsnake(k) : k);
38384                     });
38385                     stringsLoaded();
38386                 } else if (context.taginfo()) {
38387                     context.taginfo().values({key: field.key}, function(err, data) {
38388                         if (!err) {
38389                             _.each(_.pluck(data, 'value'), function(k) {
38390                                 strings[k] = (snake_case ? unsnake(k) : k);
38391                             });
38392                             stringsLoaded();
38393                         }
38394                     });
38395                 }
38396             });
38397
38398         function stringsLoaded() {
38399             var keys = _.keys(strings),
38400                 strs = [],
38401                 placeholders;
38402
38403             combobox.data(keys.map(function(k) {
38404                 var s = strings[k],
38405                     o = {};
38406                 o.title = o.value = s;
38407                 if (s.length < 20) { strs.push(s); }
38408                 return o;
38409             }));
38410
38411             placeholders = strs.length > 1 ? strs : keys;
38412             input.attr('placeholder', field.placeholder() ||
38413                 (placeholders.slice(0, 3).join(', ') + '...'));
38414         }
38415     }
38416
38417     function change() {
38418         var value = optString() || clean(input.value());
38419
38420         if (snake_case) {
38421             value = snake(value);
38422         }
38423         if (field.type === 'typeCombo' && !value) {
38424             value = 'yes';
38425         }
38426
38427         var t = {};
38428         t[field.key] = value || undefined;
38429         dispatch.change(t);
38430     }
38431
38432     combo.tags = function(tags) {
38433         var key = tags[field.key],
38434             optstring = optString(),
38435             value = strings[key] || key || '';
38436
38437         if (field.type === 'typeCombo' && value.toLowerCase() === 'yes') {
38438             value = '';
38439         }
38440         if (!optstring && snake_case) {
38441             value = unsnake(value);
38442         }
38443         input.value(value);
38444     };
38445
38446     combo.focus = function() {
38447         input.node().focus();
38448     };
38449
38450     return d3.rebind(combo, dispatch, 'on');
38451 };
38452 iD.ui.preset.cycleway = function(field) {
38453     var dispatch = d3.dispatch('change'),
38454         items;
38455
38456     function cycleway(selection) {
38457         var wrap = selection.selectAll('.preset-input-wrap')
38458             .data([0]);
38459
38460         wrap.enter().append('div')
38461             .attr('class', 'cf preset-input-wrap')
38462             .append('ul');
38463
38464         items = wrap.select('ul').selectAll('li')
38465             .data(field.keys);
38466
38467         // Enter
38468
38469         var enter = items.enter().append('li')
38470             .attr('class', function(d) { return 'cf preset-cycleway-' + d; });
38471
38472         enter.append('span')
38473             .attr('class', 'col6 label preset-label-cycleway')
38474             .attr('for', function(d) { return 'preset-input-cycleway-' + d; })
38475             .text(function(d) { return field.t('types.' + d); });
38476
38477         enter.append('div')
38478             .attr('class', 'col6 preset-input-cycleway-wrap')
38479             .append('input')
38480             .attr('type', 'text')
38481             .attr('class', 'preset-input-cycleway')
38482             .attr('id', function(d) { return 'preset-input-cycleway-' + d; })
38483             .each(function(d) {
38484                 d3.select(this)
38485                     .call(d3.combobox()
38486                         .data(cycleway.options(d)));
38487             });
38488
38489         // Update
38490
38491         wrap.selectAll('.preset-input-cycleway')
38492             .on('change', change)
38493             .on('blur', change);
38494     }
38495
38496     function change() {
38497         var inputs = d3.selectAll('.preset-input-cycleway')[0],
38498             left = d3.select(inputs[0]).value(),
38499             right = d3.select(inputs[1]).value(),
38500             tag = {};
38501         if (left === 'none' || left === '') { left = undefined; }
38502         if (right === 'none' || right === '') { right = undefined; }
38503
38504         // Always set both left and right as changing one can affect the other
38505         tag = {
38506             cycleway: undefined,
38507             'cycleway:left': left,
38508             'cycleway:right': right
38509         };
38510
38511         // If the left and right tags match, use the cycleway tag to tag both
38512         // sides the same way
38513         if (left === right) {
38514             tag = {
38515                 cycleway: left,
38516                 'cycleway:left': undefined,
38517                 'cycleway:right': undefined
38518             };
38519         }
38520
38521         dispatch.change(tag);
38522     }
38523
38524     cycleway.options = function() {
38525         return d3.keys(field.strings.options).map(function(option) {
38526             return {
38527                 title: field.t('options.' + option + '.description'),
38528                 value: option
38529             };
38530         });
38531     };
38532
38533     cycleway.tags = function(tags) {
38534         items.selectAll('.preset-input-cycleway')
38535             .value(function(d) {
38536                 // If cycleway is set, always return that
38537                 if (tags.cycleway) {
38538                     return tags.cycleway;
38539                 }
38540                 return tags[d] || '';
38541             })
38542             .attr('placeholder', field.placeholder());
38543     };
38544
38545     cycleway.focus = function() {
38546         items.selectAll('.preset-input-cycleway')
38547             .node().focus();
38548     };
38549
38550     return d3.rebind(cycleway, dispatch, 'on');
38551 };
38552 iD.ui.preset.text =
38553 iD.ui.preset.number =
38554 iD.ui.preset.tel =
38555 iD.ui.preset.email =
38556 iD.ui.preset.url = function(field) {
38557
38558     var dispatch = d3.dispatch('change'),
38559         input;
38560
38561     function i(selection) {
38562         input = selection.selectAll('input')
38563             .data([0]);
38564
38565         input.enter().append('input')
38566             .attr('type', field.type)
38567             .attr('id', 'preset-input-' + field.id)
38568             .attr('placeholder', field.placeholder() || t('inspector.unknown'));
38569
38570         input
38571             .on('input', change(true))
38572             .on('blur', change())
38573             .on('change', change());
38574
38575         if (field.type === 'number') {
38576             input.attr('type', 'text');
38577
38578             var spinControl = selection.selectAll('.spin-control')
38579                 .data([0]);
38580
38581             var enter = spinControl.enter().append('div')
38582                 .attr('class', 'spin-control');
38583
38584             enter.append('button')
38585                 .datum(1)
38586                 .attr('class', 'increment');
38587
38588             enter.append('button')
38589                 .datum(-1)
38590                 .attr('class', 'decrement');
38591
38592             spinControl.selectAll('button')
38593                 .on('click', function(d) {
38594                     d3.event.preventDefault();
38595                     var num = parseInt(input.node().value || 0, 10);
38596                     if (!isNaN(num)) input.node().value = num + d;
38597                     change()();
38598                 });
38599         }
38600     }
38601
38602     function change(onInput) {
38603         return function() {
38604             var t = {};
38605             t[field.key] = input.value() || undefined;
38606             dispatch.change(t, onInput);
38607         };
38608     }
38609
38610     i.tags = function(tags) {
38611         input.value(tags[field.key] || '');
38612     };
38613
38614     i.focus = function() {
38615         input.node().focus();
38616     };
38617
38618     return d3.rebind(i, dispatch, 'on');
38619 };
38620 iD.ui.preset.localized = function(field, context) {
38621     var dispatch = d3.dispatch('change', 'input'),
38622         wikipedia = iD.services.wikipedia(),
38623         input, localizedInputs, wikiTitles,
38624         entity;
38625
38626     function i(selection) {
38627         input = selection.selectAll('.localized-main')
38628             .data([0]);
38629
38630         input.enter().append('input')
38631             .attr('type', 'text')
38632             .attr('id', 'preset-input-' + field.id)
38633             .attr('class', 'localized-main')
38634             .attr('placeholder', field.placeholder());
38635
38636         if (field.id === 'name') {
38637             var preset = context.presets().match(entity, context.graph());
38638             input.call(d3.combobox().fetcher(
38639                 iD.util.SuggestNames(preset, iD.data.suggestions)
38640             ));
38641         }
38642
38643         input
38644             .on('input', change(true))
38645             .on('blur', change())
38646             .on('change', change());
38647
38648         var translateButton = selection.selectAll('.localized-add')
38649             .data([0]);
38650
38651         translateButton.enter()
38652             .append('button')
38653             .attr('class', 'button-input-action localized-add minor')
38654             .call(iD.svg.Icon('#icon-plus'))
38655             .call(bootstrap.tooltip()
38656                 .title(t('translate.translate'))
38657                 .placement('left'));
38658
38659         translateButton
38660             .on('click', addNew);
38661
38662         localizedInputs = selection.selectAll('.localized-wrap')
38663             .data([0]);
38664
38665         localizedInputs.enter().append('div')
38666             .attr('class', 'localized-wrap');
38667     }
38668
38669     function addNew() {
38670         d3.event.preventDefault();
38671         var data = localizedInputs.selectAll('div.entry').data();
38672         var defaultLang = iD.detect().locale.toLowerCase().split('-')[0];
38673         var langExists = _.find(data, function(datum) { return datum.lang === defaultLang;});
38674         var isLangEn = defaultLang.indexOf('en') > -1;
38675         if (isLangEn || langExists) {
38676           defaultLang = '';
38677         }
38678         data.push({ lang: defaultLang, value: '' });
38679         localizedInputs.call(render, data);
38680     }
38681
38682     function change(onInput) {
38683         return function() {
38684             var t = {};
38685             t[field.key] = d3.select(this).value() || undefined;
38686             dispatch.change(t, onInput);
38687         };
38688     }
38689
38690     function key(lang) { return field.key + ':' + lang; }
38691
38692     function changeLang(d) {
38693         var lang = d3.select(this).value(),
38694             t = {},
38695             language = _.find(iD.data.wikipedia, function(d) {
38696                 return d[0].toLowerCase() === lang.toLowerCase() ||
38697                     d[1].toLowerCase() === lang.toLowerCase();
38698             });
38699
38700         if (language) lang = language[2];
38701
38702         if (d.lang && d.lang !== lang) {
38703             t[key(d.lang)] = undefined;
38704         }
38705
38706         var value = d3.select(this.parentNode)
38707             .selectAll('.localized-value')
38708             .value();
38709
38710         if (lang && value) {
38711             t[key(lang)] = value;
38712         } else if (lang && wikiTitles && wikiTitles[d.lang]) {
38713             t[key(lang)] = wikiTitles[d.lang];
38714         }
38715
38716         d.lang = lang;
38717         dispatch.change(t);
38718     }
38719
38720     function changeValue(d) {
38721         if (!d.lang) return;
38722         var t = {};
38723         t[key(d.lang)] = d3.select(this).value() || undefined;
38724         dispatch.change(t);
38725     }
38726
38727     function fetcher(value, cb) {
38728         var v = value.toLowerCase();
38729
38730         cb(iD.data.wikipedia.filter(function(d) {
38731             return d[0].toLowerCase().indexOf(v) >= 0 ||
38732             d[1].toLowerCase().indexOf(v) >= 0 ||
38733             d[2].toLowerCase().indexOf(v) >= 0;
38734         }).map(function(d) {
38735             return { value: d[1] };
38736         }));
38737     }
38738
38739     function render(selection, data) {
38740         var wraps = selection.selectAll('div.entry').
38741             data(data, function(d) { return d.lang; });
38742
38743         var innerWrap = wraps.enter()
38744             .insert('div', ':first-child');
38745
38746         innerWrap.attr('class', 'entry')
38747             .each(function() {
38748                 var wrap = d3.select(this);
38749                 var langcombo = d3.combobox().fetcher(fetcher);
38750
38751                 var label = wrap.append('label')
38752                     .attr('class','form-label')
38753                     .text(t('translate.localized_translation_label'))
38754                     .attr('for','localized-lang');
38755
38756                 label.append('button')
38757                     .attr('class', 'minor remove')
38758                     .on('click', function(d){
38759                         d3.event.preventDefault();
38760                         var t = {};
38761                         t[key(d.lang)] = undefined;
38762                         dispatch.change(t);
38763                         d3.select(this.parentNode.parentNode)
38764                             .style('top','0')
38765                             .style('max-height','240px')
38766                             .transition()
38767                             .style('opacity', '0')
38768                             .style('max-height','0px')
38769                             .remove();
38770                     })
38771                     .call(iD.svg.Icon('#operation-delete'));
38772
38773                 wrap.append('input')
38774                     .attr('class', 'localized-lang')
38775                     .attr('type', 'text')
38776                     .attr('placeholder',t('translate.localized_translation_language'))
38777                     .on('blur', changeLang)
38778                     .on('change', changeLang)
38779                     .call(langcombo);
38780
38781                 wrap.append('input')
38782                     .on('blur', changeValue)
38783                     .on('change', changeValue)
38784                     .attr('type', 'text')
38785                     .attr('placeholder', t('translate.localized_translation_name'))
38786                     .attr('class', 'localized-value');
38787             });
38788
38789         innerWrap
38790             .style('margin-top', '0px')
38791             .style('max-height', '0px')
38792             .style('opacity', '0')
38793             .transition()
38794             .duration(200)
38795             .style('margin-top', '10px')
38796             .style('max-height', '240px')
38797             .style('opacity', '1')
38798             .each('end', function() {
38799                 d3.select(this)
38800                     .style('max-height', '')
38801                     .style('overflow', 'visible');
38802             });
38803
38804         wraps.exit()
38805             .transition()
38806             .duration(200)
38807             .style('max-height','0px')
38808             .style('opacity', '0')
38809             .style('top','-10px')
38810             .remove();
38811
38812         var entry = selection.selectAll('.entry');
38813
38814         entry.select('.localized-lang')
38815             .value(function(d) {
38816                 var lang = _.find(iD.data.wikipedia, function(lang) { return lang[2] === d.lang; });
38817                 return lang ? lang[1] : d.lang;
38818             });
38819
38820         entry.select('.localized-value')
38821             .value(function(d) { return d.value; });
38822     }
38823
38824     i.tags = function(tags) {
38825         // Fetch translations from wikipedia
38826         if (tags.wikipedia && !wikiTitles) {
38827             wikiTitles = {};
38828             var wm = tags.wikipedia.match(/([^:]+):(.+)/);
38829             if (wm && wm[0] && wm[1]) {
38830                 wikipedia.translations(wm[1], wm[2], function(d) {
38831                     wikiTitles = d;
38832                 });
38833             }
38834         }
38835
38836         input.value(tags[field.key] || '');
38837
38838         var postfixed = [], k, m;
38839         for (k in tags) {
38840             m = k.match(/^(.*):([a-zA-Z_-]+)$/);
38841             if (m && m[1] === field.key && m[2]) {
38842                 postfixed.push({ lang: m[2], value: tags[k] });
38843             }
38844         }
38845
38846         localizedInputs.call(render, postfixed.reverse());
38847     };
38848
38849     i.focus = function() {
38850         input.node().focus();
38851     };
38852
38853     i.entity = function(_) {
38854         entity = _;
38855     };
38856
38857     return d3.rebind(i, dispatch, 'on');
38858 };
38859 iD.ui.preset.maxspeed = function(field, context) {
38860     var dispatch = d3.dispatch('change'),
38861         entity,
38862         imperial,
38863         unitInput,
38864         combobox,
38865         input;
38866
38867     var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
38868         imperialValues = [20, 25, 30, 35, 40, 45, 50, 55, 65, 70];
38869
38870     function maxspeed(selection) {
38871         combobox = d3.combobox();
38872         var unitCombobox = d3.combobox().data(['km/h', 'mph'].map(comboValues));
38873
38874         input = selection.selectAll('#preset-input-' + field.id)
38875             .data([0]);
38876
38877         input.enter().append('input')
38878             .attr('type', 'text')
38879             .attr('id', 'preset-input-' + field.id)
38880             .attr('placeholder', field.placeholder());
38881
38882         input
38883             .call(combobox)
38884             .on('change', change)
38885             .on('blur', change);
38886
38887         var childNodes = context.graph().childNodes(context.entity(entity.id)),
38888             loc = childNodes[~~(childNodes.length/2)].loc;
38889
38890         imperial = _.any(iD.data.imperial.features, function(f) {
38891             return _.any(f.geometry.coordinates, function(d) {
38892                 return iD.geo.pointInPolygon(loc, d[0]);
38893             });
38894         });
38895
38896         unitInput = selection.selectAll('input.maxspeed-unit')
38897             .data([0]);
38898
38899         unitInput.enter().append('input')
38900             .attr('type', 'text')
38901             .attr('class', 'maxspeed-unit');
38902
38903         unitInput
38904             .on('blur', changeUnits)
38905             .on('change', changeUnits)
38906             .call(unitCombobox);
38907
38908         function changeUnits() {
38909             imperial = unitInput.value() === 'mph';
38910             unitInput.value(imperial ? 'mph' : 'km/h');
38911             setSuggestions();
38912             change();
38913         }
38914
38915     }
38916
38917     function setSuggestions() {
38918         combobox.data((imperial ? imperialValues : metricValues).map(comboValues));
38919         unitInput.value(imperial ? 'mph' : 'km/h');
38920     }
38921
38922     function comboValues(d) {
38923         return {
38924             value: d.toString(),
38925             title: d.toString()
38926         };
38927     }
38928
38929     function change() {
38930         var tag = {},
38931             value = input.value();
38932
38933         if (!value) {
38934             tag[field.key] = undefined;
38935         } else if (isNaN(value) || !imperial) {
38936             tag[field.key] = value;
38937         } else {
38938             tag[field.key] = value + ' mph';
38939         }
38940
38941         dispatch.change(tag);
38942     }
38943
38944     maxspeed.tags = function(tags) {
38945         var value = tags[field.key];
38946
38947         if (value && value.indexOf('mph') >= 0) {
38948             value = parseInt(value, 10);
38949             imperial = true;
38950         } else if (value) {
38951             imperial = false;
38952         }
38953
38954         setSuggestions();
38955
38956         input.value(value || '');
38957     };
38958
38959     maxspeed.focus = function() {
38960         input.node().focus();
38961     };
38962
38963     maxspeed.entity = function(_) {
38964         entity = _;
38965     };
38966
38967     return d3.rebind(maxspeed, dispatch, 'on');
38968 };
38969 iD.ui.preset.radio = function(field) {
38970     var dispatch = d3.dispatch('change'),
38971         labels, radios, placeholder;
38972
38973     function radio(selection) {
38974         selection.classed('preset-radio', true);
38975
38976         var wrap = selection.selectAll('.preset-input-wrap')
38977             .data([0]);
38978
38979         var buttonWrap = wrap.enter().append('div')
38980             .attr('class', 'preset-input-wrap toggle-list');
38981
38982         buttonWrap.append('span')
38983             .attr('class', 'placeholder');
38984
38985         placeholder = selection.selectAll('.placeholder');
38986
38987         labels = wrap.selectAll('label')
38988             .data(field.options || field.keys);
38989
38990         var enter = labels.enter().append('label');
38991
38992         enter.append('input')
38993             .attr('type', 'radio')
38994             .attr('name', field.id)
38995             .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
38996             .attr('checked', false);
38997
38998         enter.append('span')
38999             .text(function(d) { return field.t('options.' + d, { 'default': d }); });
39000
39001         radios = labels.selectAll('input')
39002             .on('change', change);
39003     }
39004
39005     function change() {
39006         var t = {};
39007         if (field.key) t[field.key] = undefined;
39008         radios.each(function(d) {
39009             var active = d3.select(this).property('checked');
39010             if (field.key) {
39011                 if (active) t[field.key] = d;
39012             } else {
39013                 t[d] = active ? 'yes' : undefined;
39014             }
39015         });
39016         dispatch.change(t);
39017     }
39018
39019     radio.tags = function(tags) {
39020         function checked(d) {
39021             if (field.key) {
39022                 return tags[field.key] === d;
39023             } else {
39024                 return !!(tags[d] && tags[d] !== 'no');
39025             }
39026         }
39027
39028         labels.classed('active', checked);
39029         radios.property('checked', checked);
39030         var selection = radios.filter(function() { return this.checked; });
39031         if (selection.empty()) {
39032             placeholder.text(t('inspector.none'));
39033         } else {
39034             placeholder.text(selection.attr('value'));
39035         }
39036     };
39037
39038     radio.focus = function() {
39039         radios.node().focus();
39040     };
39041
39042     return d3.rebind(radio, dispatch, 'on');
39043 };
39044 iD.ui.preset.restrictions = function(field, context) {
39045     var dispatch = d3.dispatch('change'),
39046         hover = iD.behavior.Hover(context),
39047         vertexID,
39048         fromNodeID;
39049
39050
39051     function restrictions(selection) {
39052         // if form field is hidden or has detached from dom, clean up.
39053         if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
39054             selection.call(restrictions.off);
39055             return;
39056         }
39057
39058         var wrap = selection.selectAll('.preset-input-wrap')
39059             .data([0]);
39060
39061         var enter = wrap.enter()
39062             .append('div')
39063             .attr('class', 'preset-input-wrap');
39064
39065         enter
39066             .append('div')
39067             .attr('class', 'restriction-help');
39068
39069
39070         var intersection = iD.geo.Intersection(context.graph(), vertexID),
39071             graph = intersection.graph,
39072             vertex = graph.entity(vertexID),
39073             filter = d3.functor(true),
39074             extent = iD.geo.Extent(),
39075             projection = iD.geo.RawMercator();
39076
39077         var d = wrap.dimensions(),
39078             c = [d[0] / 2, d[1] / 2],
39079             z = 24;
39080
39081         projection
39082             .scale(256 * Math.pow(2, z) / (2 * Math.PI));
39083
39084         var s = projection(vertex.loc);
39085
39086         projection
39087             .translate([c[0] - s[0], c[1] - s[1]])
39088             .clipExtent([[0, 0], d]);
39089
39090         var drawLayers = iD.svg.Layers(projection, context).only('osm').dimensions(d),
39091             drawVertices = iD.svg.Vertices(projection, context),
39092             drawLines = iD.svg.Lines(projection, context),
39093             drawTurns = iD.svg.Turns(projection, context);
39094
39095         enter
39096             .call(drawLayers)
39097             .selectAll('.surface')
39098             .call(hover);
39099
39100
39101         var surface = wrap.selectAll('.surface');
39102
39103         surface
39104             .dimensions(d)
39105             .call(drawVertices, graph, [vertex], filter, extent, z)
39106             .call(drawLines, graph, intersection.ways, filter)
39107             .call(drawTurns, graph, intersection.turns(fromNodeID));
39108
39109         surface
39110             .on('click.restrictions', click)
39111             .on('mouseover.restrictions', mouseover)
39112             .on('mouseout.restrictions', mouseout);
39113
39114         surface
39115             .selectAll('.selected')
39116             .classed('selected', false);
39117
39118         if (fromNodeID) {
39119             surface
39120                 .selectAll('.' + intersection.highways[fromNodeID].id)
39121                 .classed('selected', true);
39122         }
39123
39124         mouseout();
39125
39126         context.history()
39127             .on('change.restrictions', render);
39128
39129         d3.select(window)
39130             .on('resize.restrictions', function() {
39131                 wrap.dimensions(null);
39132                 render();
39133             });
39134
39135         function click() {
39136             var datum = d3.event.target.__data__;
39137             if (datum instanceof iD.Entity) {
39138                 fromNodeID = intersection.adjacentNodeId(datum.id);
39139                 render();
39140             } else if (datum instanceof iD.geo.Turn) {
39141                 if (datum.restriction) {
39142                     context.perform(
39143                         iD.actions.UnrestrictTurn(datum, projection),
39144                         t('operations.restriction.annotation.delete'));
39145                 } else {
39146                     context.perform(
39147                         iD.actions.RestrictTurn(datum, projection),
39148                         t('operations.restriction.annotation.create'));
39149                 }
39150             }
39151         }
39152
39153         function mouseover() {
39154             var datum = d3.event.target.__data__;
39155             if (datum instanceof iD.geo.Turn) {
39156                 var graph = context.graph(),
39157                     presets = context.presets(),
39158                     preset;
39159
39160                 if (datum.restriction) {
39161                     preset = presets.match(graph.entity(datum.restriction), graph);
39162                 } else {
39163                     preset = presets.item('type/restriction/' +
39164                         iD.geo.inferRestriction(
39165                             graph,
39166                             datum.from,
39167                             datum.via,
39168                             datum.to,
39169                             projection));
39170                 }
39171
39172                 wrap.selectAll('.restriction-help')
39173                     .text(t('operations.restriction.help.' +
39174                         (datum.restriction ? 'toggle_off' : 'toggle_on'),
39175                         {restriction: preset.name()}));
39176             }
39177         }
39178
39179         function mouseout() {
39180             wrap.selectAll('.restriction-help')
39181                 .text(t('operations.restriction.help.' +
39182                     (fromNodeID ? 'toggle' : 'select')));
39183         }
39184
39185         function render() {
39186             if (context.hasEntity(vertexID)) {
39187                 restrictions(selection);
39188             }
39189         }
39190     }
39191
39192     restrictions.entity = function(_) {
39193         if (!vertexID || vertexID !== _.id) {
39194             fromNodeID = null;
39195             vertexID = _.id;
39196         }
39197     };
39198
39199     restrictions.tags = function() {};
39200     restrictions.focus = function() {};
39201
39202     restrictions.off = function(selection) {
39203         selection.selectAll('.surface')
39204             .call(hover.off)
39205             .on('click.restrictions', null)
39206             .on('mouseover.restrictions', null)
39207             .on('mouseout.restrictions', null);
39208
39209         context.history()
39210             .on('change.restrictions', null);
39211
39212         d3.select(window)
39213             .on('resize.restrictions', null);
39214     };
39215
39216     return d3.rebind(restrictions, dispatch, 'on');
39217 };
39218 iD.ui.preset.textarea = function(field) {
39219     var dispatch = d3.dispatch('change'),
39220         input;
39221
39222     function i(selection) {
39223         input = selection.selectAll('textarea')
39224             .data([0]);
39225
39226         input.enter().append('textarea')
39227             .attr('id', 'preset-input-' + field.id)
39228             .attr('placeholder', field.placeholder() || t('inspector.unknown'))
39229             .attr('maxlength', 255);
39230
39231         input
39232             .on('input', change(true))
39233             .on('blur', change())
39234             .on('change', change());
39235     }
39236
39237     function change(onInput) {
39238         return function() {
39239             var t = {};
39240             t[field.key] = input.value() || undefined;
39241             dispatch.change(t, onInput);
39242         };
39243     }
39244
39245     i.tags = function(tags) {
39246         input.value(tags[field.key] || '');
39247     };
39248
39249     i.focus = function() {
39250         input.node().focus();
39251     };
39252
39253     return d3.rebind(i, dispatch, 'on');
39254 };
39255 iD.ui.preset.wikipedia = function(field, context) {
39256     var dispatch = d3.dispatch('change'),
39257         wikipedia = iD.services.wikipedia(),
39258         link, entity, lang, title;
39259
39260     function i(selection) {
39261         var langcombo = d3.combobox()
39262             .fetcher(function(value, cb) {
39263                 var v = value.toLowerCase();
39264
39265                 cb(iD.data.wikipedia.filter(function(d) {
39266                     return d[0].toLowerCase().indexOf(v) >= 0 ||
39267                         d[1].toLowerCase().indexOf(v) >= 0 ||
39268                         d[2].toLowerCase().indexOf(v) >= 0;
39269                 }).map(function(d) {
39270                     return { value: d[1] };
39271                 }));
39272             });
39273
39274         var titlecombo = d3.combobox()
39275             .fetcher(function(value, cb) {
39276
39277                 if (!value) value = context.entity(entity.id).tags.name || '';
39278                 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
39279
39280                 searchfn(language()[2], value, function(query, data) {
39281                     cb(data.map(function(d) {
39282                         return { value: d };
39283                     }));
39284                 });
39285             });
39286
39287         lang = selection.selectAll('input.wiki-lang')
39288             .data([0]);
39289
39290         lang.enter().append('input')
39291             .attr('type', 'text')
39292             .attr('class', 'wiki-lang')
39293             .value('English');
39294
39295         lang
39296             .call(langcombo)
39297             .on('blur', changeLang)
39298             .on('change', changeLang);
39299
39300         title = selection.selectAll('input.wiki-title')
39301             .data([0]);
39302
39303         title.enter().append('input')
39304             .attr('type', 'text')
39305             .attr('class', 'wiki-title')
39306             .attr('id', 'preset-input-' + field.id);
39307
39308         title
39309             .call(titlecombo)
39310             .on('blur', change)
39311             .on('change', change);
39312
39313         link = selection.selectAll('a.wiki-link')
39314             .data([0]);
39315
39316         link.enter().append('a')
39317             .attr('class', 'wiki-link button-input-action minor')
39318             .attr('target', '_blank')
39319             .call(iD.svg.Icon('#icon-out-link', 'inline'));
39320     }
39321
39322     function language() {
39323         var value = lang.value().toLowerCase();
39324         var locale = iD.detect().locale.toLowerCase();
39325         var localeLanguage;
39326         return _.find(iD.data.wikipedia, function(d) {
39327             if (d[2] === locale) localeLanguage = d;
39328             return d[0].toLowerCase() === value ||
39329                 d[1].toLowerCase() === value ||
39330                 d[2] === value;
39331         }) || localeLanguage || ['English', 'English', 'en'];
39332     }
39333
39334     function changeLang() {
39335         lang.value(language()[1]);
39336         change();
39337     }
39338
39339     function change() {
39340         var value = title.value(),
39341             m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/),
39342             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
39343             anchor;
39344
39345         if (l) {
39346             // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
39347             value = decodeURIComponent(m[2]).replace(/_/g, ' ');
39348             if (m[3]) {
39349                 try {
39350                     // Best-effort `anchordecode:` implementation
39351                     anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
39352                 } catch (e) {
39353                     anchor = decodeURIComponent(m[3]);
39354                 }
39355                 value += '#' + anchor.replace(/_/g, ' ');
39356             }
39357             value = value.slice(0, 1).toUpperCase() + value.slice(1);
39358             lang.value(l[1]);
39359             title.value(value);
39360         }
39361
39362         var t = {};
39363         t[field.key] = value ? language()[2] + ':' + value : undefined;
39364         dispatch.change(t);
39365     }
39366
39367     i.tags = function(tags) {
39368         var value = tags[field.key] || '',
39369             m = value.match(/([^:]+):([^#]+)(?:#(.+))?/),
39370             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
39371             anchor = m && m[3];
39372
39373         // value in correct format
39374         if (l) {
39375             lang.value(l[1]);
39376             title.value(m[2] + (anchor ? ('#' + anchor) : ''));
39377             if (anchor) {
39378                 try {
39379                     // Best-effort `anchorencode:` implementation
39380                     anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
39381                 } catch (e) {
39382                     anchor = anchor.replace(/ /g, '_');
39383                 }
39384             }
39385             link.attr('href', 'https://' + m[1] + '.wikipedia.org/wiki/' +
39386                       m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : ''));
39387
39388         // unrecognized value format
39389         } else {
39390             title.value(value);
39391             link.attr('href', 'https://en.wikipedia.org/wiki/Special:Search?search=' + value);
39392         }
39393     };
39394
39395     i.entity = function(_) {
39396         entity = _;
39397     };
39398
39399     i.focus = function() {
39400         title.node().focus();
39401     };
39402
39403     return d3.rebind(i, dispatch, 'on');
39404 };
39405 iD.ui.intro.area = function(context, reveal) {
39406     var event = d3.dispatch('done'),
39407         timeout;
39408
39409     var step = {
39410         title: 'intro.areas.title'
39411     };
39412
39413     step.enter = function() {
39414         var playground = [-85.63552, 41.94159],
39415             corner = [-85.63565411045074, 41.9417715536927];
39416         context.map().centerZoom(playground, 19);
39417         reveal('button.add-area',
39418             t('intro.areas.add', { button: iD.ui.intro.icon('#icon-area', 'pre-text') }),
39419             { tooltipClass: 'intro-areas-add' });
39420
39421         context.on('enter.intro', addArea);
39422
39423         function addArea(mode) {
39424             if (mode.id !== 'add-area') return;
39425             context.on('enter.intro', drawArea);
39426
39427             var padding = 120 * Math.pow(2, context.map().zoom() - 19);
39428             var pointBox = iD.ui.intro.pad(corner, padding, context);
39429             reveal(pointBox, t('intro.areas.corner'));
39430
39431             context.map().on('move.intro', function() {
39432                 padding = 120 * Math.pow(2, context.map().zoom() - 19);
39433                 pointBox = iD.ui.intro.pad(corner, padding, context);
39434                 reveal(pointBox, t('intro.areas.corner'), {duration: 0});
39435             });
39436         }
39437
39438         function drawArea(mode) {
39439             if (mode.id !== 'draw-area') return;
39440             context.on('enter.intro', enterSelect);
39441
39442             var padding = 150 * Math.pow(2, context.map().zoom() - 19);
39443             var pointBox = iD.ui.intro.pad(playground, padding, context);
39444             reveal(pointBox, t('intro.areas.place'));
39445
39446             context.map().on('move.intro', function() {
39447                 padding = 150 * Math.pow(2, context.map().zoom() - 19);
39448                 pointBox = iD.ui.intro.pad(playground, padding, context);
39449                 reveal(pointBox, t('intro.areas.place'), {duration: 0});
39450             });
39451         }
39452
39453         function enterSelect(mode) {
39454             if (mode.id !== 'select') return;
39455             context.map().on('move.intro', null);
39456             context.on('enter.intro', null);
39457
39458             timeout = setTimeout(function() {
39459                 reveal('.preset-search-input',
39460                     t('intro.areas.search',
39461                     { name: context.presets().item('leisure/playground').name() }));
39462                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
39463             }, 500);
39464         }
39465
39466         function keySearch() {
39467             var first = d3.select('.preset-list-item:first-child');
39468             if (first.classed('preset-leisure-playground')) {
39469                 reveal(first.select('.preset-list-button').node(), t('intro.areas.choose'));
39470                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
39471                 d3.select('.preset-search-input').on('keyup.intro', null);
39472             }
39473         }
39474
39475         function selectedPreset() {
39476             reveal('.pane',
39477                 t('intro.areas.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39478             context.on('exit.intro', event.done);
39479         }
39480     };
39481
39482     step.exit = function() {
39483         window.clearTimeout(timeout);
39484         context.on('enter.intro', null);
39485         context.on('exit.intro', null);
39486         context.history().on('change.intro', null);
39487         context.map().on('move.intro', null);
39488         d3.select('.preset-search-input').on('keyup.intro', null);
39489     };
39490
39491     return d3.rebind(step, event, 'on');
39492 };
39493 iD.ui.intro.line = function(context, reveal) {
39494     var event = d3.dispatch('done'),
39495         timeouts = [];
39496
39497     var step = {
39498         title: 'intro.lines.title'
39499     };
39500
39501     function timeout(f, t) {
39502         timeouts.push(window.setTimeout(f, t));
39503     }
39504
39505     function eventCancel() {
39506         d3.event.stopPropagation();
39507         d3.event.preventDefault();
39508     }
39509
39510     step.enter = function() {
39511         var centroid = [-85.62830, 41.95699];
39512         var midpoint = [-85.62975395449628, 41.95787501510204];
39513         var start = [-85.6297754121684, 41.95805253325314];
39514         var intersection = [-85.62974496187628, 41.95742515554585];
39515
39516         context.map().centerZoom(start, 18);
39517         reveal('button.add-line',
39518             t('intro.lines.add', { button: iD.ui.intro.icon('#icon-line', 'pre-text') }),
39519             { tooltipClass: 'intro-lines-add' });
39520
39521         context.on('enter.intro', addLine);
39522
39523         function addLine(mode) {
39524             if (mode.id !== 'add-line') return;
39525             context.on('enter.intro', drawLine);
39526
39527             var padding = 150 * Math.pow(2, context.map().zoom() - 18);
39528             var pointBox = iD.ui.intro.pad(start, padding, context);
39529             reveal(pointBox, t('intro.lines.start'));
39530
39531             context.map().on('move.intro', function() {
39532                 padding = 150 * Math.pow(2, context.map().zoom() - 18);
39533                 pointBox = iD.ui.intro.pad(start, padding, context);
39534                 reveal(pointBox, t('intro.lines.start'), {duration: 0});
39535             });
39536         }
39537
39538         function drawLine(mode) {
39539             if (mode.id !== 'draw-line') return;
39540             context.history().on('change.intro', addIntersection);
39541             context.on('enter.intro', retry);
39542
39543             var padding = 300 * Math.pow(2, context.map().zoom() - 19);
39544             var pointBox = iD.ui.intro.pad(midpoint, padding, context);
39545             reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}));
39546
39547             context.map().on('move.intro', function() {
39548                 padding = 300 * Math.pow(2, context.map().zoom() - 19);
39549                 pointBox = iD.ui.intro.pad(midpoint, padding, context);
39550                 reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}), {duration: 0});
39551             });
39552         }
39553
39554         // ended line before creating intersection
39555         function retry(mode) {
39556             if (mode.id !== 'select') return;
39557             var pointBox = iD.ui.intro.pad(intersection, 30, context),
39558                 ids = mode.selectedIDs();
39559             reveal(pointBox, t('intro.lines.restart', {name: t('intro.graph.flower_st')}));
39560             d3.select(window).on('mousedown.intro', eventCancel, true);
39561
39562             timeout(function() {
39563                 context.replace(iD.actions.DeleteMultiple(ids));
39564                 step.exit();
39565                 step.enter();
39566             }, 3000);
39567         }
39568
39569         function addIntersection(changes) {
39570             if ( _.any(changes.created(), function(d) {
39571                 return d.type === 'node' && context.graph().parentWays(d).length > 1;
39572             })) {
39573                 context.history().on('change.intro', null);
39574                 context.on('enter.intro', enterSelect);
39575
39576                 var padding = 900 * Math.pow(2, context.map().zoom() - 19);
39577                 var pointBox = iD.ui.intro.pad(centroid, padding, context);
39578                 reveal(pointBox, t('intro.lines.finish'));
39579
39580                 context.map().on('move.intro', function() {
39581                     padding = 900 * Math.pow(2, context.map().zoom() - 19);
39582                     pointBox = iD.ui.intro.pad(centroid, padding, context);
39583                     reveal(pointBox, t('intro.lines.finish'), {duration: 0});
39584                 });
39585             }
39586         }
39587
39588         function enterSelect(mode) {
39589             if (mode.id !== 'select') return;
39590             context.map().on('move.intro', null);
39591             context.on('enter.intro', null);
39592             d3.select('#curtain').style('pointer-events', 'all');
39593
39594             presetCategory();
39595         }
39596
39597         function presetCategory() {
39598             timeout(function() {
39599                 d3.select('#curtain').style('pointer-events', 'none');
39600                 var road = d3.select('.preset-category-road .preset-list-button');
39601                 reveal(road.node(), t('intro.lines.road'));
39602                 road.one('click.intro', roadCategory);
39603             }, 500);
39604         }
39605
39606         function roadCategory() {
39607             timeout(function() {
39608                 var grid = d3.select('.subgrid');
39609                 reveal(grid.node(), t('intro.lines.residential'));
39610                 grid.selectAll(':not(.preset-highway-residential) .preset-list-button')
39611                     .one('click.intro', retryPreset);
39612                 grid.selectAll('.preset-highway-residential .preset-list-button')
39613                     .one('click.intro', roadDetails);
39614             }, 500);
39615         }
39616
39617         // selected wrong road type
39618         function retryPreset() {
39619             timeout(function() {
39620                 var preset = d3.select('.entity-editor-pane .preset-list-button');
39621                 reveal(preset.node(), t('intro.lines.wrong_preset'));
39622                 preset.one('click.intro', presetCategory);
39623             }, 500);
39624         }
39625
39626         function roadDetails() {
39627             reveal('.pane',
39628                 t('intro.lines.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39629             context.on('exit.intro', event.done);
39630         }
39631
39632     };
39633
39634     step.exit = function() {
39635         d3.select(window).on('mousedown.intro', null, true);
39636         d3.select('#curtain').style('pointer-events', 'none');
39637         timeouts.forEach(window.clearTimeout);
39638         context.on('enter.intro', null);
39639         context.on('exit.intro', null);
39640         context.map().on('move.intro', null);
39641         context.history().on('change.intro', null);
39642     };
39643
39644     return d3.rebind(step, event, 'on');
39645 };
39646 iD.ui.intro.navigation = function(context, reveal) {
39647     var event = d3.dispatch('done'),
39648         timeouts = [];
39649
39650     var step = {
39651         title: 'intro.navigation.title'
39652     };
39653
39654     function set(f, t) {
39655         timeouts.push(window.setTimeout(f, t));
39656     }
39657
39658     function eventCancel() {
39659         d3.event.stopPropagation();
39660         d3.event.preventDefault();
39661     }
39662
39663     step.enter = function() {
39664         var rect = context.surfaceRect(),
39665             map = {
39666                 left: rect.left + 10,
39667                 top: rect.top + 70,
39668                 width: rect.width - 70,
39669                 height: rect.height - 170
39670             };
39671
39672         context.map().centerZoom([-85.63591, 41.94285], 19);
39673
39674         reveal(map, t('intro.navigation.drag'));
39675
39676         context.map().on('move.intro', _.debounce(function() {
39677             context.map().on('move.intro', null);
39678             townhall();
39679             context.on('enter.intro', inspectTownHall);
39680         }, 400));
39681
39682         function townhall() {
39683             var hall = [-85.63645945147184, 41.942986488012565];
39684
39685             var point = context.projection(hall);
39686             if (point[0] < 0 || point[0] > rect.width ||
39687                 point[1] < 0 || point[1] > rect.height) {
39688                 context.map().center(hall);
39689             }
39690
39691             var box = iD.ui.intro.pointBox(hall, context);
39692             reveal(box, t('intro.navigation.select'));
39693
39694             context.map().on('move.intro', function() {
39695                 var box = iD.ui.intro.pointBox(hall, context);
39696                 reveal(box, t('intro.navigation.select'), {duration: 0});
39697             });
39698         }
39699
39700         function inspectTownHall(mode) {
39701             if (mode.id !== 'select') return;
39702             context.on('enter.intro', null);
39703             context.map().on('move.intro', null);
39704             set(function() {
39705                 reveal('.entity-editor-pane',
39706                     t('intro.navigation.pane', { button: iD.ui.intro.icon('#icon-close', 'pre-text') }));
39707                 context.on('exit.intro', streetSearch);
39708             }, 700);
39709         }
39710
39711         function streetSearch() {
39712             context.on('exit.intro', null);
39713             reveal('.search-header input',
39714                 t('intro.navigation.search', { name: t('intro.graph.spring_st') }));
39715             d3.select('.search-header input').on('keyup.intro', searchResult);
39716         }
39717
39718         function searchResult() {
39719             var first = d3.select('.feature-list-item:nth-child(0n+2)'),  // skip No Results item
39720                 firstName = first.select('.entity-name'),
39721                 name = t('intro.graph.spring_st');
39722
39723             if (!firstName.empty() && firstName.text() === name) {
39724                 reveal(first.node(), t('intro.navigation.choose', { name: name }));
39725                 context.on('exit.intro', selectedStreet);
39726                 d3.select('.search-header input')
39727                     .on('keydown.intro', eventCancel, true)
39728                     .on('keyup.intro', null);
39729             }
39730         }
39731
39732         function selectedStreet() {
39733             var springSt = [-85.63585099140167, 41.942506848938926];
39734             context.map().center(springSt);
39735             context.on('exit.intro', event.done);
39736             set(function() {
39737                 reveal('.entity-editor-pane',
39738                     t('intro.navigation.chosen', {
39739                         name: t('intro.graph.spring_st'),
39740                         button: iD.ui.intro.icon('#icon-close', 'pre-text')
39741                     }));
39742             }, 400);
39743         }
39744     };
39745
39746     step.exit = function() {
39747         timeouts.forEach(window.clearTimeout);
39748         context.map().on('move.intro', null);
39749         context.on('enter.intro', null);
39750         context.on('exit.intro', null);
39751         d3.select('.search-header input')
39752             .on('keydown.intro', null)
39753             .on('keyup.intro', null);
39754     };
39755
39756     return d3.rebind(step, event, 'on');
39757 };
39758 iD.ui.intro.point = function(context, reveal) {
39759     var event = d3.dispatch('done'),
39760         timeouts = [];
39761
39762     var step = {
39763         title: 'intro.points.title'
39764     };
39765
39766     function setTimeout(f, t) {
39767         timeouts.push(window.setTimeout(f, t));
39768     }
39769
39770     function eventCancel() {
39771         d3.event.stopPropagation();
39772         d3.event.preventDefault();
39773     }
39774
39775     step.enter = function() {
39776         context.map().centerZoom([-85.63279, 41.94394], 19);
39777         reveal('button.add-point',
39778             t('intro.points.add', { button: iD.ui.intro.icon('#icon-point', 'pre-text') }),
39779             { tooltipClass: 'intro-points-add' });
39780
39781         var corner = [-85.632481,41.944094];
39782
39783         context.on('enter.intro', addPoint);
39784
39785         function addPoint(mode) {
39786             if (mode.id !== 'add-point') return;
39787             context.on('enter.intro', enterSelect);
39788
39789             var pointBox = iD.ui.intro.pad(corner, 150, context);
39790             reveal(pointBox, t('intro.points.place'));
39791
39792             context.map().on('move.intro', function() {
39793                 pointBox = iD.ui.intro.pad(corner, 150, context);
39794                 reveal(pointBox, t('intro.points.place'), {duration: 0});
39795             });
39796         }
39797
39798         function enterSelect(mode) {
39799             if (mode.id !== 'select') return;
39800             context.map().on('move.intro', null);
39801             context.on('enter.intro', null);
39802
39803             setTimeout(function() {
39804                 reveal('.preset-search-input',
39805                     t('intro.points.search', {name: context.presets().item('amenity/cafe').name()}));
39806                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
39807             }, 500);
39808         }
39809
39810         function keySearch() {
39811             var first = d3.select('.preset-list-item:first-child');
39812             if (first.classed('preset-amenity-cafe')) {
39813                 reveal(first.select('.preset-list-button').node(), t('intro.points.choose'));
39814                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
39815                 d3.select('.preset-search-input')
39816                     .on('keydown.intro', eventCancel, true)
39817                     .on('keyup.intro', null);
39818             }
39819         }
39820
39821         function selectedPreset() {
39822             setTimeout(function() {
39823                 reveal('.entity-editor-pane', t('intro.points.describe'), {tooltipClass: 'intro-points-describe'});
39824                 context.history().on('change.intro', closeEditor);
39825                 context.on('exit.intro', selectPoint);
39826             }, 400);
39827         }
39828
39829         function closeEditor() {
39830             d3.select('.preset-search-input').on('keydown.intro', null);
39831             context.history().on('change.intro', null);
39832             reveal('.entity-editor-pane',
39833                 t('intro.points.close', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39834         }
39835
39836         function selectPoint() {
39837             context.on('exit.intro', null);
39838             context.history().on('change.intro', null);
39839             context.on('enter.intro', enterReselect);
39840
39841             var pointBox = iD.ui.intro.pad(corner, 150, context);
39842             reveal(pointBox, t('intro.points.reselect'));
39843
39844             context.map().on('move.intro', function() {
39845                 pointBox = iD.ui.intro.pad(corner, 150, context);
39846                 reveal(pointBox, t('intro.points.reselect'), {duration: 0});
39847             });
39848         }
39849
39850         function enterReselect(mode) {
39851             if (mode.id !== 'select') return;
39852             context.map().on('move.intro', null);
39853             context.on('enter.intro', null);
39854
39855             setTimeout(function() {
39856                 reveal('.entity-editor-pane',
39857                     t('intro.points.fixname', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39858                 context.on('exit.intro', deletePoint);
39859             }, 500);
39860         }
39861
39862         function deletePoint() {
39863             context.on('exit.intro', null);
39864             context.on('enter.intro', enterDelete);
39865
39866             var pointBox = iD.ui.intro.pad(corner, 150, context);
39867             reveal(pointBox, t('intro.points.reselect_delete'));
39868
39869             context.map().on('move.intro', function() {
39870                 pointBox = iD.ui.intro.pad(corner, 150, context);
39871                 reveal(pointBox, t('intro.points.reselect_delete'), {duration: 0});
39872             });
39873         }
39874
39875         function enterDelete(mode) {
39876             if (mode.id !== 'select') return;
39877             context.map().on('move.intro', null);
39878             context.on('enter.intro', null);
39879             context.on('exit.intro', deletePoint);
39880             context.map().on('move.intro', deletePoint);
39881             context.history().on('change.intro', deleted);
39882
39883             setTimeout(function() {
39884                 var node = d3.select('.radial-menu-item-delete').node();
39885                 var pointBox = iD.ui.intro.pad(node.getBoundingClientRect(), 50, context);
39886                 reveal(pointBox,
39887                     t('intro.points.delete', { button: iD.ui.intro.icon('#operation-delete', 'pre-text') }));
39888             }, 300);
39889         }
39890
39891         function deleted(changed) {
39892             if (changed.deleted().length) event.done();
39893         }
39894
39895     };
39896
39897     step.exit = function() {
39898         timeouts.forEach(window.clearTimeout);
39899         context.on('exit.intro', null);
39900         context.on('enter.intro', null);
39901         context.map().on('move.intro', null);
39902         context.history().on('change.intro', null);
39903         d3.select('.preset-search-input')
39904             .on('keyup.intro', null)
39905             .on('keydown.intro', null);
39906     };
39907
39908     return d3.rebind(step, event, 'on');
39909 };
39910 iD.ui.intro.startEditing = function(context, reveal) {
39911     var event = d3.dispatch('done', 'startEditing'),
39912         modal,
39913         timeouts = [];
39914
39915     var step = {
39916         title: 'intro.startediting.title'
39917     };
39918
39919     function timeout(f, t) {
39920         timeouts.push(window.setTimeout(f, t));
39921     }
39922
39923     step.enter = function() {
39924         reveal('.map-control.help-control',
39925             t('intro.startediting.help', { button: iD.ui.intro.icon('#icon-help', 'pre-text') }));
39926
39927         timeout(function() {
39928             reveal('#bar button.save', t('intro.startediting.save'));
39929         }, 5000);
39930
39931         timeout(function() {
39932             reveal('#surface');
39933         }, 10000);
39934
39935         timeout(function() {
39936             modal = iD.ui.modal(context.container());
39937
39938             modal.select('.modal')
39939                 .attr('class', 'modal-splash modal col6');
39940
39941             modal.selectAll('.close').remove();
39942
39943             var startbutton = modal.select('.content')
39944                 .attr('class', 'fillL')
39945                     .append('button')
39946                         .attr('class', 'modal-section huge-modal-button')
39947                         .on('click', function() {
39948                             modal.remove();
39949                         });
39950
39951                 startbutton.append('div')
39952                     .attr('class','illustration');
39953                 startbutton.append('h2')
39954                     .text(t('intro.startediting.start'));
39955
39956             event.startEditing();
39957         }, 10500);
39958     };
39959
39960     step.exit = function() {
39961         if (modal) modal.remove();
39962         timeouts.forEach(window.clearTimeout);
39963     };
39964
39965     return d3.rebind(step, event, 'on');
39966 };
39967 iD.presets = function() {
39968
39969     // an iD.presets.Collection with methods for
39970     // loading new data and returning defaults
39971
39972     var all = iD.presets.Collection([]),
39973         defaults = { area: all, line: all, point: all, vertex: all, relation: all },
39974         fields = {},
39975         universal = [],
39976         recent = iD.presets.Collection([]);
39977
39978     // Index of presets by (geometry, tag key).
39979     var index = {
39980         point: {},
39981         vertex: {},
39982         line: {},
39983         area: {},
39984         relation: {}
39985     };
39986
39987     all.match = function(entity, resolver) {
39988         var geometry = entity.geometry(resolver),
39989             geometryMatches = index[geometry],
39990             best = -1,
39991             match;
39992
39993         for (var k in entity.tags) {
39994             var keyMatches = geometryMatches[k];
39995             if (!keyMatches) continue;
39996
39997             for (var i = 0; i < keyMatches.length; i++) {
39998                 var score = keyMatches[i].matchScore(entity);
39999                 if (score > best) {
40000                     best = score;
40001                     match = keyMatches[i];
40002                 }
40003             }
40004         }
40005
40006         return match || all.item(geometry);
40007     };
40008
40009     // Because of the open nature of tagging, iD will never have a complete
40010     // list of tags used in OSM, so we want it to have logic like "assume
40011     // that a closed way with an amenity tag is an area, unless the amenity
40012     // is one of these specific types". This function computes a structure
40013     // that allows testing of such conditions, based on the presets designated
40014     // as as supporting (or not supporting) the area geometry.
40015     //
40016     // The returned object L is a whitelist/blacklist of tags. A closed way
40017     // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
40018     // (see `iD.Way#isArea()`). In other words, the keys of L form the whitelist,
40019     // and the subkeys form the blacklist.
40020     all.areaKeys = function() {
40021         var areaKeys = {},
40022             ignore = ['barrier', 'highway', 'footway', 'railway', 'type'],
40023             presets = _.reject(all.collection, 'suggestion');
40024
40025         // whitelist
40026         presets.forEach(function(d) {
40027             for (var key in d.tags) break;
40028             if (!key) return;
40029             if (ignore.indexOf(key) !== -1) return;
40030
40031             if (d.geometry.indexOf('area') !== -1) {
40032                 areaKeys[key] = areaKeys[key] || {};
40033             }
40034         });
40035
40036         // blacklist
40037         presets.forEach(function(d) {
40038             for (var key in d.tags) break;
40039             if (!key) return;
40040             if (ignore.indexOf(key) !== -1) return;
40041
40042             var value = d.tags[key];
40043             if (d.geometry.indexOf('area') === -1 && key in areaKeys && value !== '*') {
40044                 areaKeys[key][value] = true;
40045             }
40046         });
40047
40048         return areaKeys;
40049     };
40050
40051     all.load = function(d) {
40052
40053         if (d.fields) {
40054             _.forEach(d.fields, function(d, id) {
40055                 fields[id] = iD.presets.Field(id, d);
40056                 if (d.universal) universal.push(fields[id]);
40057             });
40058         }
40059
40060         if (d.presets) {
40061             _.forEach(d.presets, function(d, id) {
40062                 all.collection.push(iD.presets.Preset(id, d, fields));
40063             });
40064         }
40065
40066         if (d.categories) {
40067             _.forEach(d.categories, function(d, id) {
40068                 all.collection.push(iD.presets.Category(id, d, all));
40069             });
40070         }
40071
40072         if (d.defaults) {
40073             var getItem = _.bind(all.item, all);
40074             defaults = {
40075                 area: iD.presets.Collection(d.defaults.area.map(getItem)),
40076                 line: iD.presets.Collection(d.defaults.line.map(getItem)),
40077                 point: iD.presets.Collection(d.defaults.point.map(getItem)),
40078                 vertex: iD.presets.Collection(d.defaults.vertex.map(getItem)),
40079                 relation: iD.presets.Collection(d.defaults.relation.map(getItem))
40080             };
40081         }
40082
40083         for (var i = 0; i < all.collection.length; i++) {
40084             var preset = all.collection[i],
40085                 geometry = preset.geometry;
40086
40087             for (var j = 0; j < geometry.length; j++) {
40088                 var g = index[geometry[j]];
40089                 for (var k in preset.tags) {
40090                     (g[k] = g[k] || []).push(preset);
40091                 }
40092             }
40093         }
40094
40095         return all;
40096     };
40097
40098     all.field = function(id) {
40099         return fields[id];
40100     };
40101
40102     all.universal = function() {
40103         return universal;
40104     };
40105
40106     all.defaults = function(geometry, n) {
40107         var rec = recent.matchGeometry(geometry).collection.slice(0, 4),
40108             def = _.uniq(rec.concat(defaults[geometry].collection)).slice(0, n - 1);
40109         return iD.presets.Collection(_.unique(rec.concat(def).concat(all.item(geometry))));
40110     };
40111
40112     all.choose = function(preset) {
40113         if (!preset.isFallback()) {
40114             recent = iD.presets.Collection(_.unique([preset].concat(recent.collection)));
40115         }
40116         return all;
40117     };
40118
40119     return all;
40120 };
40121 iD.presets.Category = function(id, category, all) {
40122     category = _.clone(category);
40123
40124     category.id = id;
40125
40126     category.members = iD.presets.Collection(category.members.map(function(id) {
40127         return all.item(id);
40128     }));
40129
40130     category.matchGeometry = function(geometry) {
40131         return category.geometry.indexOf(geometry) >= 0;
40132     };
40133
40134     category.matchScore = function() { return -1; };
40135
40136     category.name = function() {
40137         return t('presets.categories.' + id + '.name', {'default': id});
40138     };
40139
40140     category.terms = function() {
40141         return [];
40142     };
40143
40144     return category;
40145 };
40146 iD.presets.Collection = function(collection) {
40147
40148     var maxSearchResults = 50,
40149         maxSuggestionResults = 10;
40150
40151     var presets = {
40152
40153         collection: collection,
40154
40155         item: function(id) {
40156             return _.find(collection, function(d) {
40157                 return d.id === id;
40158             });
40159         },
40160
40161         matchGeometry: function(geometry) {
40162             return iD.presets.Collection(collection.filter(function(d) {
40163                 return d.matchGeometry(geometry);
40164             }));
40165         },
40166
40167         search: function(value, geometry) {
40168             if (!value) return this;
40169
40170             value = value.toLowerCase();
40171
40172             var searchable = _.filter(collection, function(a) {
40173                     return a.searchable !== false && a.suggestion !== true;
40174                 }),
40175                 suggestions = _.filter(collection, function(a) {
40176                     return a.suggestion === true;
40177                 });
40178
40179             function leading(a) {
40180                 var index = a.indexOf(value);
40181                 return index === 0 || a[index - 1] === ' ';
40182             }
40183
40184             // matches value to preset.name
40185             var leading_name = _.filter(searchable, function(a) {
40186                     return leading(a.name().toLowerCase());
40187                 }).sort(function(a, b) {
40188                     var i = a.name().toLowerCase().indexOf(value) - b.name().toLowerCase().indexOf(value);
40189                     if (i === 0) return a.name().length - b.name().length;
40190                     else return i;
40191                 });
40192
40193             // matches value to preset.terms values
40194             var leading_terms = _.filter(searchable, function(a) {
40195                     return _.any(a.terms() || [], leading);
40196                 });
40197
40198             // matches value to preset.tags values
40199             var leading_tag_values = _.filter(searchable, function(a) {
40200                     return _.any(_.without(_.values(a.tags || {}), '*'), leading);
40201                 });
40202
40203
40204             // finds close matches to value in preset.name
40205             var levenstein_name = searchable.map(function(a) {
40206                     return {
40207                         preset: a,
40208                         dist: iD.util.editDistance(value, a.name().toLowerCase())
40209                     };
40210                 }).filter(function(a) {
40211                     return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
40212                 }).sort(function(a, b) {
40213                     return a.dist - b.dist;
40214                 }).map(function(a) {
40215                     return a.preset;
40216                 });
40217
40218             // finds close matches to value in preset.terms
40219             var leventstein_terms = _.filter(searchable, function(a) {
40220                     return _.any(a.terms() || [], function(b) {
40221                         return iD.util.editDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
40222                     });
40223                 });
40224
40225             function suggestionName(name) {
40226                 var nameArray = name.split(' - ');
40227                 if (nameArray.length > 1) {
40228                     name = nameArray.slice(0, nameArray.length-1).join(' - ');
40229                 }
40230                 return name.toLowerCase();
40231             }
40232
40233             var leading_suggestions = _.filter(suggestions, function(a) {
40234                     return leading(suggestionName(a.name()));
40235                 }).sort(function(a, b) {
40236                     a = suggestionName(a.name());
40237                     b = suggestionName(b.name());
40238                     var i = a.indexOf(value) - b.indexOf(value);
40239                     if (i === 0) return a.length - b.length;
40240                     else return i;
40241                 });
40242
40243             var leven_suggestions = suggestions.map(function(a) {
40244                     return {
40245                         preset: a,
40246                         dist: iD.util.editDistance(value, suggestionName(a.name()))
40247                     };
40248                 }).filter(function(a) {
40249                     return a.dist + Math.min(value.length - suggestionName(a.preset.name()).length, 0) < 1;
40250                 }).sort(function(a, b) {
40251                     return a.dist - b.dist;
40252                 }).map(function(a) {
40253                     return a.preset;
40254                 });
40255
40256             var other = presets.item(geometry);
40257
40258             var results = leading_name.concat(
40259                             leading_terms,
40260                             leading_tag_values,
40261                             leading_suggestions.slice(0, maxSuggestionResults+5),
40262                             levenstein_name,
40263                             leventstein_terms,
40264                             leven_suggestions.slice(0, maxSuggestionResults)
40265                         ).slice(0, maxSearchResults-1);
40266
40267             return iD.presets.Collection(_.unique(
40268                     results.concat(other)
40269                 ));
40270         }
40271     };
40272
40273     return presets;
40274 };
40275 iD.presets.Field = function(id, field) {
40276     field = _.clone(field);
40277
40278     field.id = id;
40279
40280     field.matchGeometry = function(geometry) {
40281         return !field.geometry || field.geometry === geometry;
40282     };
40283
40284     field.t = function(scope, options) {
40285         return t('presets.fields.' + id + '.' + scope, options);
40286     };
40287
40288     field.label = function() {
40289         return field.t('label', {'default': id});
40290     };
40291
40292     var placeholder = field.placeholder;
40293     field.placeholder = function() {
40294         return field.t('placeholder', {'default': placeholder});
40295     };
40296
40297     return field;
40298 };
40299 iD.presets.Preset = function(id, preset, fields) {
40300     preset = _.clone(preset);
40301
40302     preset.id = id;
40303     preset.fields = (preset.fields || []).map(getFields);
40304     preset.geometry = (preset.geometry || []);
40305
40306     function getFields(f) {
40307         return fields[f];
40308     }
40309
40310     preset.matchGeometry = function(geometry) {
40311         return preset.geometry.indexOf(geometry) >= 0;
40312     };
40313
40314     var matchScore = preset.matchScore || 1;
40315     preset.matchScore = function(entity) {
40316         var tags = preset.tags,
40317             score = 0;
40318
40319         for (var t in tags) {
40320             if (entity.tags[t] === tags[t]) {
40321                 score += matchScore;
40322             } else if (tags[t] === '*' && t in entity.tags) {
40323                 score += matchScore / 2;
40324             } else {
40325                 return -1;
40326             }
40327         }
40328
40329         return score;
40330     };
40331
40332     preset.t = function(scope, options) {
40333         return t('presets.presets.' + id + '.' + scope, options);
40334     };
40335
40336     var name = preset.name;
40337     preset.name = function() {
40338         if (preset.suggestion) {
40339             id = id.split('/');
40340             id = id[0] + '/' + id[1];
40341             return name + ' - ' + t('presets.presets.' + id + '.name');
40342         }
40343         return preset.t('name', {'default': name});
40344     };
40345
40346     preset.terms = function() {
40347         return preset.t('terms', {'default': ''}).toLowerCase().trim().split(/\s*,+\s*/);
40348     };
40349
40350     preset.isFallback = function() {
40351         var tagCount = Object.keys(preset.tags).length;
40352         return tagCount === 0 || (tagCount === 1 && preset.tags.hasOwnProperty('area'));
40353     };
40354
40355     preset.reference = function(geometry) {
40356         var key = Object.keys(preset.tags)[0],
40357             value = preset.tags[key];
40358
40359         if (geometry === 'relation' && key === 'type') {
40360             return { rtype: value };
40361         } else if (value === '*') {
40362             return { key: key };
40363         } else {
40364             return { key: key, value: value };
40365         }
40366     };
40367
40368     var removeTags = preset.removeTags || preset.tags;
40369     preset.removeTags = function(tags, geometry) {
40370         tags = _.omit(tags, _.keys(removeTags));
40371
40372         for (var f in preset.fields) {
40373             var field = preset.fields[f];
40374             if (field.matchGeometry(geometry) && field.default === tags[field.key]) {
40375                 delete tags[field.key];
40376             }
40377         }
40378
40379         delete tags.area;
40380         return tags;
40381     };
40382
40383     var applyTags = preset.addTags || preset.tags;
40384     preset.applyTags = function(tags, geometry) {
40385         var k;
40386
40387         tags = _.clone(tags);
40388
40389         for (k in applyTags) {
40390             if (applyTags[k] === '*') {
40391                 tags[k] = 'yes';
40392             } else {
40393                 tags[k] = applyTags[k];
40394             }
40395         }
40396
40397         // Add area=yes if necessary.
40398         // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
40399         // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
40400         // 2. chosen preset doesn't have a key in areaKeys (`railway=station`)
40401         if (geometry === 'area') {
40402             var needsAreaTag = true;
40403             if (preset.geometry.indexOf('line') === -1) {
40404                 for (k in applyTags) {
40405                     if (k in iD.areaKeys) {
40406                         needsAreaTag = false;
40407                         break;
40408                     }
40409                 }
40410             }
40411             if (needsAreaTag) {
40412                 tags.area = 'yes';
40413             }
40414         }
40415
40416         for (var f in preset.fields) {
40417             var field = preset.fields[f];
40418             if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
40419                 tags[field.key] = field.default;
40420             }
40421         }
40422
40423         return tags;
40424     };
40425
40426     return preset;
40427 };
40428 iD.validations = {};
40429 iD.validations.DeprecatedTag = function() {
40430
40431     var validation = function(changes) {
40432         var warnings = [];
40433         for (var i = 0; i < changes.created.length; i++) {
40434             var change = changes.created[i],
40435                 deprecatedTags = change.deprecatedTags();
40436
40437             if (!_.isEmpty(deprecatedTags)) {
40438                 var tags = iD.util.tagText({ tags: deprecatedTags });
40439                 warnings.push({
40440                     id: 'deprecated_tags',
40441                     message: t('validations.deprecated_tags', { tags: tags }),
40442                     entity: change
40443                 });
40444             }
40445         }
40446         return warnings;
40447     };
40448
40449     return validation;
40450 };
40451 iD.validations.ManyDeletions = function() {
40452     var threshold = 100;
40453
40454     var validation = function(changes) {
40455         var warnings = [];
40456         if (changes.deleted.length > threshold) {
40457             warnings.push({
40458                 id: 'many_deletions',
40459                 message: t('validations.many_deletions', { n: changes.deleted.length })
40460             });
40461         }
40462         return warnings;
40463     };
40464
40465     return validation;
40466 };
40467 iD.validations.MissingTag = function() {
40468
40469     var validation = function(changes, graph) {
40470         var warnings = [];
40471         for (var i = 0; i < changes.created.length; i++) {
40472             var change = changes.created[i],
40473                 geometry = change.geometry(graph);
40474
40475             if ((geometry === 'point' || geometry === 'line' || geometry === 'area') && !change.isUsed(graph)) {
40476                 warnings.push({
40477                     id: 'missing_tag',
40478                     message: t('validations.untagged_' + geometry),
40479                     tooltip: t('validations.untagged_' + geometry + '_tooltip'),
40480                     entity: change
40481                 });
40482             }
40483         }
40484         return warnings;
40485     };
40486
40487     return validation;
40488 };
40489 iD.validations.TagSuggestsArea = function() {
40490
40491     // https://github.com/openstreetmap/josm/blob/mirror/src/org/
40492     // openstreetmap/josm/data/validation/tests/UnclosedWays.java#L80
40493     function tagSuggestsArea(tags) {
40494         if (_.isEmpty(tags)) return false;
40495
40496         var presence = ['landuse', 'amenities', 'tourism', 'shop'];
40497         for (var i = 0; i < presence.length; i++) {
40498             if (tags[presence[i]] !== undefined) {
40499                 return presence[i] + '=' + tags[presence[i]];
40500             }
40501         }
40502
40503         if (tags.building && tags.building === 'yes') return 'building=yes';
40504     }
40505
40506     var validation = function(changes, graph) {
40507         var warnings = [];
40508         for (var i = 0; i < changes.created.length; i++) {
40509             var change = changes.created[i],
40510                 geometry = change.geometry(graph),
40511                 suggestion = (geometry === 'line' ? tagSuggestsArea(change.tags) : undefined);
40512
40513             if (suggestion) {
40514                 warnings.push({
40515                     id: 'tag_suggests_area',
40516                     message: t('validations.tag_suggests_area', { tag: suggestion }),
40517                     entity: change
40518                 });
40519             }
40520         }
40521         return warnings;
40522     };
40523
40524     return validation;
40525 };
40526 })();
40527 window.locale = { _current: 'en' };
40528
40529 locale.current = function(_) {
40530     if (!arguments.length) return locale._current;
40531     if (locale[_] !== undefined) locale._current = _;
40532     else if (locale[_.split('-')[0]]) locale._current = _.split('-')[0];
40533     return locale;
40534 };
40535
40536 function t(s, o, loc) {
40537     loc = loc || locale._current;
40538
40539     var path = s.split(".").reverse(),
40540         rep = locale[loc];
40541
40542     while (rep !== undefined && path.length) rep = rep[path.pop()];
40543
40544     if (rep !== undefined) {
40545         if (o) for (var k in o) rep = rep.replace('{' + k + '}', o[k]);
40546         return rep;
40547     }
40548
40549     if (loc !== 'en') {
40550         return t(s, o, 'en');
40551     }
40552
40553     if (o && 'default' in o) {
40554         return o['default'];
40555     }
40556
40557     var missing = 'Missing ' + loc + ' translation: ' + s;
40558     if (typeof console !== "undefined") console.error(missing);
40559
40560     return missing;
40561 }
40562 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"},"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"},"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":"Fix alignment","imagery_source_faq":"Where does this imagery come from?","reset":"reset","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"},"major_roads":{"description":"Major Roads","tooltip":"Highways, Streets, etc."},"minor_roads":{"description":"Minor 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_type":{"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/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 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/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":""},"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/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/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":"Dock","terms":""},"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"]]}]};