]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
Merge remote-tracking branch 'openstreetmap/pull/954'
[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.2';
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 lasso;
24077
24078         function mousedown() {
24079             var button = 0;  // left
24080             if (d3.event.button === button && d3.event.shiftKey === true) {
24081                 lasso = null;
24082
24083                 selection
24084                     .on('mousemove.lasso', mousemove)
24085                     .on('mouseup.lasso', mouseup);
24086
24087                 d3.event.stopPropagation();
24088             }
24089         }
24090
24091         function mousemove() {
24092             if (!lasso) {
24093                 lasso = iD.ui.Lasso(context);
24094                 context.surface().call(lasso);
24095             }
24096
24097             lasso.p(context.mouse());
24098         }
24099
24100         function normalize(a, b) {
24101             return [
24102                 [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
24103                 [Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
24104         }
24105
24106         function lassoed() {
24107             if (!lasso) return [];
24108
24109             var graph = context.graph(),
24110                 bounds = lasso.extent().map(context.projection.invert),
24111                 extent = iD.geo.Extent(normalize(bounds[0], bounds[1]));
24112
24113             return _.pluck(context.intersects(extent).filter(function(entity) {
24114                 return entity.type === 'node' &&
24115                     iD.geo.pointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
24116                     !context.features().isHidden(entity, graph, entity.geometry(graph));
24117             }), 'id');
24118         }
24119
24120         function mouseup() {
24121             selection
24122                 .on('mousemove.lasso', null)
24123                 .on('mouseup.lasso', null);
24124
24125             if (!lasso) return;
24126
24127             var ids = lassoed();
24128             lasso.close();
24129
24130             if (ids.length) {
24131                 context.enter(iD.modes.Select(context, ids));
24132             }
24133         }
24134
24135         selection
24136             .on('mousedown.lasso', mousedown);
24137     };
24138
24139     behavior.off = function(selection) {
24140         selection.on('mousedown.lasso', null);
24141     };
24142
24143     return behavior;
24144 };
24145 iD.behavior.Paste = function(context) {
24146     var keybinding = d3.keybinding('paste');
24147
24148     function omitTag(v, k) {
24149         return (
24150             k === 'phone' ||
24151             k === 'fax' ||
24152             k === 'email' ||
24153             k === 'website' ||
24154             k === 'url' ||
24155             k === 'note' ||
24156             k === 'description' ||
24157             k.indexOf('name') !== -1 ||
24158             k.indexOf('wiki') === 0 ||
24159             k.indexOf('addr:') === 0 ||
24160             k.indexOf('contact:') === 0
24161         );
24162     }
24163
24164     function doPaste() {
24165         d3.event.preventDefault();
24166         if (context.inIntro()) return;
24167
24168         var baseGraph = context.graph(),
24169             mouse = context.mouse(),
24170             projection = context.projection,
24171             viewport = iD.geo.Extent(projection.clipExtent()).polygon();
24172
24173         if (!iD.geo.pointInPolygon(mouse, viewport)) return;
24174
24175         var extent = iD.geo.Extent(),
24176             oldIDs = context.copyIDs(),
24177             oldGraph = context.copyGraph(),
24178             newIDs = [];
24179
24180         if (!oldIDs.length) return;
24181
24182         var action = iD.actions.CopyEntities(oldIDs, oldGraph);
24183         context.perform(action);
24184
24185         var copies = action.copies();
24186         for (var id in copies) {
24187             var oldEntity = oldGraph.entity(id),
24188                 newEntity = copies[id];
24189
24190             extent._extend(oldEntity.extent(oldGraph));
24191             newIDs.push(newEntity.id);
24192             context.perform(iD.actions.ChangeTags(newEntity.id, _.omit(newEntity.tags, omitTag)));
24193         }
24194
24195         // Put pasted objects where mouse pointer is..
24196         var center = projection(extent.center()),
24197             delta = [ mouse[0] - center[0], mouse[1] - center[1] ];
24198
24199         context.perform(iD.actions.Move(newIDs, delta, projection));
24200         context.enter(iD.modes.Move(context, newIDs, baseGraph));
24201     }
24202
24203     function paste() {
24204         keybinding.on(iD.ui.cmd('⌘V'), doPaste);
24205         d3.select(document).call(keybinding);
24206         return paste;
24207     }
24208
24209     paste.off = function() {
24210         d3.select(document).call(keybinding.off);
24211     };
24212
24213     return paste;
24214 };
24215 iD.behavior.Select = function(context) {
24216     function keydown() {
24217         if (d3.event && d3.event.shiftKey) {
24218             context.surface()
24219                 .classed('behavior-multiselect', true);
24220         }
24221     }
24222
24223     function keyup() {
24224         if (!d3.event || !d3.event.shiftKey) {
24225             context.surface()
24226                 .classed('behavior-multiselect', false);
24227         }
24228     }
24229
24230     function click() {
24231         var datum = d3.event.target.__data__,
24232             lasso = d3.select('#surface .lasso').node(),
24233             mode = context.mode();
24234
24235         if (!(datum instanceof iD.Entity)) {
24236             if (!d3.event.shiftKey && !lasso && mode.id !== 'browse')
24237                 context.enter(iD.modes.Browse(context));
24238
24239         } else if (!d3.event.shiftKey && !lasso) {
24240             // Avoid re-entering Select mode with same entity.
24241             if (context.selectedIDs().length !== 1 || context.selectedIDs()[0] !== datum.id) {
24242                 context.enter(iD.modes.Select(context, [datum.id]));
24243             } else {
24244                 mode.suppressMenu(false).reselect();
24245             }
24246         } else if (context.selectedIDs().indexOf(datum.id) >= 0) {
24247             var selectedIDs = _.without(context.selectedIDs(), datum.id);
24248             context.enter(selectedIDs.length ?
24249                 iD.modes.Select(context, selectedIDs) :
24250                 iD.modes.Browse(context));
24251
24252         } else {
24253             context.enter(iD.modes.Select(context, context.selectedIDs().concat([datum.id])));
24254         }
24255     }
24256
24257     var behavior = function(selection) {
24258         d3.select(window)
24259             .on('keydown.select', keydown)
24260             .on('keyup.select', keyup);
24261
24262         selection.on('click.select', click);
24263
24264         keydown();
24265     };
24266
24267     behavior.off = function(selection) {
24268         d3.select(window)
24269             .on('keydown.select', null)
24270             .on('keyup.select', null);
24271
24272         selection.on('click.select', null);
24273
24274         keyup();
24275     };
24276
24277     return behavior;
24278 };
24279 iD.behavior.Tail = function() {
24280     var text,
24281         container,
24282         xmargin = 25,
24283         tooltipSize = [0, 0],
24284         selectionSize = [0, 0];
24285
24286     function tail(selection) {
24287         if (!text) return;
24288
24289         d3.select(window)
24290             .on('resize.tail', function() { selectionSize = selection.dimensions(); });
24291
24292         function show() {
24293             container.style('display', 'block');
24294             tooltipSize = container.dimensions();
24295         }
24296
24297         function mousemove() {
24298             if (container.style('display') === 'none') show();
24299             var xoffset = ((d3.event.clientX + tooltipSize[0] + xmargin) > selectionSize[0]) ?
24300                 -tooltipSize[0] - xmargin : xmargin;
24301             container.classed('left', xoffset > 0);
24302             iD.util.setTransform(container, d3.event.clientX + xoffset, d3.event.clientY);
24303         }
24304
24305         function mouseleave() {
24306             if (d3.event.relatedTarget !== container.node()) {
24307                 container.style('display', 'none');
24308             }
24309         }
24310
24311         function mouseenter() {
24312             if (d3.event.relatedTarget !== container.node()) {
24313                 show();
24314             }
24315         }
24316
24317         container = d3.select(document.body)
24318             .append('div')
24319             .style('display', 'none')
24320             .attr('class', 'tail tooltip-inner');
24321
24322         container.append('div')
24323             .text(text);
24324
24325         selection
24326             .on('mousemove.tail', mousemove)
24327             .on('mouseenter.tail', mouseenter)
24328             .on('mouseleave.tail', mouseleave);
24329
24330         container
24331             .on('mousemove.tail', mousemove);
24332
24333         tooltipSize = container.dimensions();
24334         selectionSize = selection.dimensions();
24335     }
24336
24337     tail.off = function(selection) {
24338         if (!text) return;
24339
24340         container
24341             .on('mousemove.tail', null)
24342             .remove();
24343
24344         selection
24345             .on('mousemove.tail', null)
24346             .on('mouseenter.tail', null)
24347             .on('mouseleave.tail', null);
24348
24349         d3.select(window)
24350             .on('resize.tail', null);
24351     };
24352
24353     tail.text = function(_) {
24354         if (!arguments.length) return text;
24355         text = _;
24356         return tail;
24357     };
24358
24359     return tail;
24360 };
24361 iD.modes = {};
24362 iD.modes.AddArea = function(context) {
24363     var mode = {
24364         id: 'add-area',
24365         button: 'area',
24366         title: t('modes.add_area.title'),
24367         description: t('modes.add_area.description'),
24368         key: '3'
24369     };
24370
24371     var behavior = iD.behavior.AddWay(context)
24372             .tail(t('modes.add_area.tail'))
24373             .on('start', start)
24374             .on('startFromWay', startFromWay)
24375             .on('startFromNode', startFromNode),
24376         defaultTags = {area: 'yes'};
24377
24378     function start(loc) {
24379         var graph = context.graph(),
24380             node = iD.Node({loc: loc}),
24381             way = iD.Way({tags: defaultTags});
24382
24383         context.perform(
24384             iD.actions.AddEntity(node),
24385             iD.actions.AddEntity(way),
24386             iD.actions.AddVertex(way.id, node.id),
24387             iD.actions.AddVertex(way.id, node.id));
24388
24389         context.enter(iD.modes.DrawArea(context, way.id, graph));
24390     }
24391
24392     function startFromWay(loc, edge) {
24393         var graph = context.graph(),
24394             node = iD.Node({loc: loc}),
24395             way = iD.Way({tags: defaultTags});
24396
24397         context.perform(
24398             iD.actions.AddEntity(node),
24399             iD.actions.AddEntity(way),
24400             iD.actions.AddVertex(way.id, node.id),
24401             iD.actions.AddVertex(way.id, node.id),
24402             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24403
24404         context.enter(iD.modes.DrawArea(context, way.id, graph));
24405     }
24406
24407     function startFromNode(node) {
24408         var graph = context.graph(),
24409             way = iD.Way({tags: defaultTags});
24410
24411         context.perform(
24412             iD.actions.AddEntity(way),
24413             iD.actions.AddVertex(way.id, node.id),
24414             iD.actions.AddVertex(way.id, node.id));
24415
24416         context.enter(iD.modes.DrawArea(context, way.id, graph));
24417     }
24418
24419     mode.enter = function() {
24420         context.install(behavior);
24421     };
24422
24423     mode.exit = function() {
24424         context.uninstall(behavior);
24425     };
24426
24427     return mode;
24428 };
24429 iD.modes.AddLine = function(context) {
24430     var mode = {
24431         id: 'add-line',
24432         button: 'line',
24433         title: t('modes.add_line.title'),
24434         description: t('modes.add_line.description'),
24435         key: '2'
24436     };
24437
24438     var behavior = iD.behavior.AddWay(context)
24439         .tail(t('modes.add_line.tail'))
24440         .on('start', start)
24441         .on('startFromWay', startFromWay)
24442         .on('startFromNode', startFromNode);
24443
24444     function start(loc) {
24445         var baseGraph = context.graph(),
24446             node = iD.Node({loc: loc}),
24447             way = iD.Way();
24448
24449         context.perform(
24450             iD.actions.AddEntity(node),
24451             iD.actions.AddEntity(way),
24452             iD.actions.AddVertex(way.id, node.id));
24453
24454         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24455     }
24456
24457     function startFromWay(loc, edge) {
24458         var baseGraph = context.graph(),
24459             node = iD.Node({loc: loc}),
24460             way = iD.Way();
24461
24462         context.perform(
24463             iD.actions.AddEntity(node),
24464             iD.actions.AddEntity(way),
24465             iD.actions.AddVertex(way.id, node.id),
24466             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24467
24468         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24469     }
24470
24471     function startFromNode(node) {
24472         var baseGraph = context.graph(),
24473             way = iD.Way();
24474
24475         context.perform(
24476             iD.actions.AddEntity(way),
24477             iD.actions.AddVertex(way.id, node.id));
24478
24479         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24480     }
24481
24482     mode.enter = function() {
24483         context.install(behavior);
24484     };
24485
24486     mode.exit = function() {
24487         context.uninstall(behavior);
24488     };
24489
24490     return mode;
24491 };
24492 iD.modes.AddPoint = function(context) {
24493     var mode = {
24494         id: 'add-point',
24495         button: 'point',
24496         title: t('modes.add_point.title'),
24497         description: t('modes.add_point.description'),
24498         key: '1'
24499     };
24500
24501     var behavior = iD.behavior.Draw(context)
24502         .tail(t('modes.add_point.tail'))
24503         .on('click', add)
24504         .on('clickWay', addWay)
24505         .on('clickNode', addNode)
24506         .on('cancel', cancel)
24507         .on('finish', cancel);
24508
24509     function add(loc) {
24510         var node = iD.Node({loc: loc});
24511
24512         context.perform(
24513             iD.actions.AddEntity(node),
24514             t('operations.add.annotation.point'));
24515
24516         context.enter(
24517             iD.modes.Select(context, [node.id])
24518                 .suppressMenu(true)
24519                 .newFeature(true));
24520     }
24521
24522     function addWay(loc) {
24523         add(loc);
24524     }
24525
24526     function addNode(node) {
24527         add(node.loc);
24528     }
24529
24530     function cancel() {
24531         context.enter(iD.modes.Browse(context));
24532     }
24533
24534     mode.enter = function() {
24535         context.install(behavior);
24536     };
24537
24538     mode.exit = function() {
24539         context.uninstall(behavior);
24540     };
24541
24542     return mode;
24543 };
24544 iD.modes.Browse = function(context) {
24545     var mode = {
24546         button: 'browse',
24547         id: 'browse',
24548         title: t('modes.browse.title'),
24549         description: t('modes.browse.description')
24550     }, sidebar;
24551
24552     var behaviors = [
24553         iD.behavior.Paste(context),
24554         iD.behavior.Hover(context)
24555             .on('hover', context.ui().sidebar.hover),
24556         iD.behavior.Select(context),
24557         iD.behavior.Lasso(context),
24558         iD.modes.DragNode(context).behavior];
24559
24560     mode.enter = function() {
24561         behaviors.forEach(function(behavior) {
24562             context.install(behavior);
24563         });
24564
24565         // Get focus on the body.
24566         if (document.activeElement && document.activeElement.blur) {
24567             document.activeElement.blur();
24568         }
24569
24570         if (sidebar) {
24571             context.ui().sidebar.show(sidebar);
24572         } else {
24573             context.ui().sidebar.select(null);
24574         }
24575     };
24576
24577     mode.exit = function() {
24578         context.ui().sidebar.hover.cancel();
24579         behaviors.forEach(function(behavior) {
24580             context.uninstall(behavior);
24581         });
24582
24583         if (sidebar) {
24584             context.ui().sidebar.hide();
24585         }
24586     };
24587
24588     mode.sidebar = function(_) {
24589         if (!arguments.length) return sidebar;
24590         sidebar = _;
24591         return mode;
24592     };
24593
24594     return mode;
24595 };
24596 iD.modes.DragNode = function(context) {
24597     var mode = {
24598         id: 'drag-node',
24599         button: 'browse'
24600     };
24601
24602     var nudgeInterval,
24603         activeIDs,
24604         wasMidpoint,
24605         cancelled,
24606         selectedIDs = [],
24607         hover = iD.behavior.Hover(context)
24608             .altDisables(true)
24609             .on('hover', context.ui().sidebar.hover),
24610         edit = iD.behavior.Edit(context);
24611
24612     function edge(point, size) {
24613         var pad = [30, 100, 30, 100];
24614         if (point[0] > size[0] - pad[0]) return [-10, 0];
24615         else if (point[0] < pad[2]) return [10, 0];
24616         else if (point[1] > size[1] - pad[1]) return [0, -10];
24617         else if (point[1] < pad[3]) return [0, 10];
24618         return null;
24619     }
24620
24621     function startNudge(nudge) {
24622         if (nudgeInterval) window.clearInterval(nudgeInterval);
24623         nudgeInterval = window.setInterval(function() {
24624             context.pan(nudge);
24625         }, 50);
24626     }
24627
24628     function stopNudge() {
24629         if (nudgeInterval) window.clearInterval(nudgeInterval);
24630         nudgeInterval = null;
24631     }
24632
24633     function moveAnnotation(entity) {
24634         return t('operations.move.annotation.' + entity.geometry(context.graph()));
24635     }
24636
24637     function connectAnnotation(entity) {
24638         return t('operations.connect.annotation.' + entity.geometry(context.graph()));
24639     }
24640
24641     function origin(entity) {
24642         return context.projection(entity.loc);
24643     }
24644
24645     function start(entity) {
24646         cancelled = d3.event.sourceEvent.shiftKey ||
24647             context.features().hasHiddenConnections(entity, context.graph());
24648
24649         if (cancelled) return behavior.cancel();
24650
24651         wasMidpoint = entity.type === 'midpoint';
24652         if (wasMidpoint) {
24653             var midpoint = entity;
24654             entity = iD.Node();
24655             context.perform(iD.actions.AddMidpoint(midpoint, entity));
24656
24657              var vertex = context.surface()
24658                 .selectAll('.' + entity.id);
24659              behavior.target(vertex.node(), entity);
24660
24661         } else {
24662             context.perform(
24663                 iD.actions.Noop());
24664         }
24665
24666         activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
24667         activeIDs.push(entity.id);
24668
24669         context.enter(mode);
24670     }
24671
24672     function datum() {
24673         if (d3.event.sourceEvent.altKey) {
24674             return {};
24675         }
24676
24677         return d3.event.sourceEvent.target.__data__ || {};
24678     }
24679
24680     // via https://gist.github.com/shawnbot/4166283
24681     function childOf(p, c) {
24682         if (p === c) return false;
24683         while (c && c !== p) c = c.parentNode;
24684         return c === p;
24685     }
24686
24687     function move(entity) {
24688         if (cancelled) return;
24689         d3.event.sourceEvent.stopPropagation();
24690
24691         var nudge = childOf(context.container().node(),
24692             d3.event.sourceEvent.toElement) &&
24693             edge(d3.event.point, context.map().dimensions());
24694
24695         if (nudge) startNudge(nudge);
24696         else stopNudge();
24697
24698         var loc = context.projection.invert(d3.event.point);
24699
24700         var d = datum();
24701         if (d.type === 'node' && d.id !== entity.id) {
24702             loc = d.loc;
24703         } else if (d.type === 'way' && !d3.select(d3.event.sourceEvent.target).classed('fill')) {
24704             loc = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection).loc;
24705         }
24706
24707         context.replace(
24708             iD.actions.MoveNode(entity.id, loc),
24709             moveAnnotation(entity));
24710     }
24711
24712     function end(entity) {
24713         if (cancelled) return;
24714
24715         var d = datum();
24716
24717         if (d.type === 'way') {
24718             var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection);
24719             context.replace(
24720                 iD.actions.AddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity),
24721                 connectAnnotation(d));
24722
24723         } else if (d.type === 'node' && d.id !== entity.id) {
24724             context.replace(
24725                 iD.actions.Connect([d.id, entity.id]),
24726                 connectAnnotation(d));
24727
24728         } else if (wasMidpoint) {
24729             context.replace(
24730                 iD.actions.Noop(),
24731                 t('operations.add.annotation.vertex'));
24732
24733         } else {
24734             context.replace(
24735                 iD.actions.Noop(),
24736                 moveAnnotation(entity));
24737         }
24738
24739         var reselection = selectedIDs.filter(function(id) {
24740             return context.graph().hasEntity(id);
24741         });
24742
24743         if (reselection.length) {
24744             context.enter(
24745                 iD.modes.Select(context, reselection)
24746                     .suppressMenu(true));
24747         } else {
24748             context.enter(iD.modes.Browse(context));
24749         }
24750     }
24751
24752     function cancel() {
24753         behavior.cancel();
24754         context.enter(iD.modes.Browse(context));
24755     }
24756
24757     function setActiveElements() {
24758         context.surface().selectAll(iD.util.entitySelector(activeIDs))
24759             .classed('active', true);
24760     }
24761
24762     var behavior = iD.behavior.drag()
24763         .delegate('g.node, g.point, g.midpoint')
24764         .surface(context.surface().node())
24765         .origin(origin)
24766         .on('start', start)
24767         .on('move', move)
24768         .on('end', end);
24769
24770     mode.enter = function() {
24771         context.install(hover);
24772         context.install(edit);
24773
24774         context.history()
24775             .on('undone.drag-node', cancel);
24776
24777         context.map()
24778             .on('drawn.drag-node', setActiveElements);
24779
24780         setActiveElements();
24781     };
24782
24783     mode.exit = function() {
24784         context.ui().sidebar.hover.cancel();
24785         context.uninstall(hover);
24786         context.uninstall(edit);
24787
24788         context.history()
24789             .on('undone.drag-node', null);
24790
24791         context.map()
24792             .on('drawn.drag-node', null);
24793
24794         context.surface()
24795             .selectAll('.active')
24796             .classed('active', false);
24797
24798         stopNudge();
24799     };
24800
24801     mode.selectedIDs = function(_) {
24802         if (!arguments.length) return selectedIDs;
24803         selectedIDs = _;
24804         return mode;
24805     };
24806
24807     mode.behavior = behavior;
24808
24809     return mode;
24810 };
24811 iD.modes.DrawArea = function(context, wayId, baseGraph) {
24812     var mode = {
24813         button: 'area',
24814         id: 'draw-area'
24815     };
24816
24817     var behavior;
24818
24819     mode.enter = function() {
24820         var way = context.entity(wayId),
24821             headId = way.nodes[way.nodes.length - 2],
24822             tailId = way.first();
24823
24824         behavior = iD.behavior.DrawWay(context, wayId, -1, mode, baseGraph)
24825             .tail(t('modes.draw_area.tail'));
24826
24827         var addNode = behavior.addNode;
24828
24829         behavior.addNode = function(node) {
24830             if (node.id === headId || node.id === tailId) {
24831                 behavior.finish();
24832             } else {
24833                 addNode(node);
24834             }
24835         };
24836
24837         context.install(behavior);
24838     };
24839
24840     mode.exit = function() {
24841         context.uninstall(behavior);
24842     };
24843
24844     mode.selectedIDs = function() {
24845         return [wayId];
24846     };
24847
24848     return mode;
24849 };
24850 iD.modes.DrawLine = function(context, wayId, baseGraph, affix) {
24851     var mode = {
24852         button: 'line',
24853         id: 'draw-line'
24854     };
24855
24856     var behavior;
24857
24858     mode.enter = function() {
24859         var way = context.entity(wayId),
24860             index = (affix === 'prefix') ? 0 : undefined,
24861             headId = (affix === 'prefix') ? way.first() : way.last();
24862
24863         behavior = iD.behavior.DrawWay(context, wayId, index, mode, baseGraph)
24864             .tail(t('modes.draw_line.tail'));
24865
24866         var addNode = behavior.addNode;
24867
24868         behavior.addNode = function(node) {
24869             if (node.id === headId) {
24870                 behavior.finish();
24871             } else {
24872                 addNode(node);
24873             }
24874         };
24875
24876         context.install(behavior);
24877     };
24878
24879     mode.exit = function() {
24880         context.uninstall(behavior);
24881     };
24882
24883     mode.selectedIDs = function() {
24884         return [wayId];
24885     };
24886
24887     return mode;
24888 };
24889 iD.modes.Move = function(context, entityIDs, baseGraph) {
24890     var mode = {
24891         id: 'move',
24892         button: 'browse'
24893     };
24894
24895     var keybinding = d3.keybinding('move'),
24896         edit = iD.behavior.Edit(context),
24897         annotation = entityIDs.length === 1 ?
24898             t('operations.move.annotation.' + context.geometry(entityIDs[0])) :
24899             t('operations.move.annotation.multiple'),
24900         cache,
24901         origin,
24902         nudgeInterval;
24903
24904     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
24905
24906     function edge(point, size) {
24907         var pad = [30, 100, 30, 100];
24908         if (point[0] > size[0] - pad[0]) return [-10, 0];
24909         else if (point[0] < pad[2]) return [10, 0];
24910         else if (point[1] > size[1] - pad[1]) return [0, -10];
24911         else if (point[1] < pad[3]) return [0, 10];
24912         return null;
24913     }
24914
24915     function startNudge(nudge) {
24916         if (nudgeInterval) window.clearInterval(nudgeInterval);
24917         nudgeInterval = window.setInterval(function() {
24918             context.pan(nudge);
24919
24920             var currMouse = context.mouse(),
24921                 origMouse = context.projection(origin),
24922                 delta = vecSub(vecSub(currMouse, origMouse), nudge),
24923                 action = iD.actions.Move(entityIDs, delta, context.projection, cache);
24924
24925             context.overwrite(action, annotation);
24926
24927         }, 50);
24928     }
24929
24930     function stopNudge() {
24931         if (nudgeInterval) window.clearInterval(nudgeInterval);
24932         nudgeInterval = null;
24933     }
24934
24935     function move() {
24936         var currMouse = context.mouse(),
24937             origMouse = context.projection(origin),
24938             delta = vecSub(currMouse, origMouse),
24939             action = iD.actions.Move(entityIDs, delta, context.projection, cache);
24940
24941         context.overwrite(action, annotation);
24942
24943         var nudge = edge(currMouse, context.map().dimensions());
24944         if (nudge) startNudge(nudge);
24945         else stopNudge();
24946     }
24947
24948     function finish() {
24949         d3.event.stopPropagation();
24950         context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
24951         stopNudge();
24952     }
24953
24954     function cancel() {
24955         if (baseGraph) {
24956             while (context.graph() !== baseGraph) context.pop();
24957             context.enter(iD.modes.Browse(context));
24958         } else {
24959             context.pop();
24960             context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
24961         }
24962         stopNudge();
24963     }
24964
24965     function undone() {
24966         context.enter(iD.modes.Browse(context));
24967     }
24968
24969     mode.enter = function() {
24970         origin = context.map().mouseCoordinates();
24971         cache = {};
24972
24973         context.install(edit);
24974
24975         context.perform(
24976             iD.actions.Noop(),
24977             annotation);
24978
24979         context.surface()
24980             .on('mousemove.move', move)
24981             .on('click.move', finish);
24982
24983         context.history()
24984             .on('undone.move', undone);
24985
24986         keybinding
24987             .on('⎋', cancel)
24988             .on('↩', finish);
24989
24990         d3.select(document)
24991             .call(keybinding);
24992     };
24993
24994     mode.exit = function() {
24995         stopNudge();
24996
24997         context.uninstall(edit);
24998
24999         context.surface()
25000             .on('mousemove.move', null)
25001             .on('click.move', null);
25002
25003         context.history()
25004             .on('undone.move', null);
25005
25006         keybinding.off();
25007     };
25008
25009     return mode;
25010 };
25011 iD.modes.RotateWay = function(context, wayId) {
25012     var mode = {
25013         id: 'rotate-way',
25014         button: 'browse'
25015     };
25016
25017     var keybinding = d3.keybinding('rotate-way'),
25018         edit = iD.behavior.Edit(context);
25019
25020     mode.enter = function() {
25021         context.install(edit);
25022
25023         var annotation = t('operations.rotate.annotation.' + context.geometry(wayId)),
25024             way = context.graph().entity(wayId),
25025             nodes = _.uniq(context.graph().childNodes(way)),
25026             points = nodes.map(function(n) { return context.projection(n.loc); }),
25027             pivot = d3.geom.polygon(points).centroid(),
25028             angle;
25029
25030         context.perform(
25031             iD.actions.Noop(),
25032             annotation);
25033
25034         function rotate() {
25035
25036             var mousePoint = context.mouse(),
25037                 newAngle = Math.atan2(mousePoint[1] - pivot[1], mousePoint[0] - pivot[0]);
25038
25039             if (typeof angle === 'undefined') angle = newAngle;
25040
25041             context.replace(
25042                 iD.actions.RotateWay(wayId, pivot, newAngle - angle, context.projection),
25043                 annotation);
25044
25045             angle = newAngle;
25046         }
25047
25048         function finish() {
25049             d3.event.stopPropagation();
25050             context.enter(iD.modes.Select(context, [wayId])
25051                 .suppressMenu(true));
25052         }
25053
25054         function cancel() {
25055             context.pop();
25056             context.enter(iD.modes.Select(context, [wayId])
25057                 .suppressMenu(true));
25058         }
25059
25060         function undone() {
25061             context.enter(iD.modes.Browse(context));
25062         }
25063
25064         context.surface()
25065             .on('mousemove.rotate-way', rotate)
25066             .on('click.rotate-way', finish);
25067
25068         context.history()
25069             .on('undone.rotate-way', undone);
25070
25071         keybinding
25072             .on('⎋', cancel)
25073             .on('↩', finish);
25074
25075         d3.select(document)
25076             .call(keybinding);
25077     };
25078
25079     mode.exit = function() {
25080         context.uninstall(edit);
25081
25082         context.surface()
25083             .on('mousemove.rotate-way', null)
25084             .on('click.rotate-way', null);
25085
25086         context.history()
25087             .on('undone.rotate-way', null);
25088
25089         keybinding.off();
25090     };
25091
25092     return mode;
25093 };
25094 iD.modes.Save = function(context) {
25095     var ui = iD.ui.Commit(context)
25096             .on('cancel', cancel)
25097             .on('save', save);
25098
25099     function cancel() {
25100         context.enter(iD.modes.Browse(context));
25101     }
25102
25103     function save(e, tryAgain) {
25104         function withChildNodes(ids, graph) {
25105             return _.uniq(_.reduce(ids, function(result, id) {
25106                 var e = graph.entity(id);
25107                 if (e.type === 'way') {
25108                     try {
25109                         var cn = graph.childNodes(e);
25110                         result.push.apply(result, _.pluck(_.filter(cn, 'version'), 'id'));
25111                     } catch(err) {
25112                         /* eslint-disable no-console */
25113                         if (typeof console !== 'undefined') console.error(err);
25114                         /* eslint-enable no-console */
25115                     }
25116                 }
25117                 return result;
25118             }, _.clone(ids)));
25119         }
25120
25121         var loading = iD.ui.Loading(context).message(t('save.uploading')).blocking(true),
25122             history = context.history(),
25123             origChanges = history.changes(iD.actions.DiscardTags(history.difference())),
25124             localGraph = context.graph(),
25125             remoteGraph = iD.Graph(history.base(), true),
25126             modified = _.filter(history.difference().summary(), {changeType: 'modified'}),
25127             toCheck = _.pluck(_.pluck(modified, 'entity'), 'id'),
25128             toLoad = withChildNodes(toCheck, localGraph),
25129             conflicts = [],
25130             errors = [];
25131
25132         if (!tryAgain) history.perform(iD.actions.Noop());  // checkpoint
25133         context.container().call(loading);
25134
25135         if (toCheck.length) {
25136             context.connection().loadMultiple(toLoad, loaded);
25137         } else {
25138             finalize();
25139         }
25140
25141
25142         // Reload modified entities into an alternate graph and check for conflicts..
25143         function loaded(err, result) {
25144             if (errors.length) return;
25145
25146             if (err) {
25147                 errors.push({
25148                     msg: err.responseText,
25149                     details: [ t('save.status_code', { code: err.status }) ]
25150                 });
25151                 showErrors();
25152
25153             } else {
25154                 var loadMore = [];
25155                 _.each(result.data, function(entity) {
25156                     remoteGraph.replace(entity);
25157                     toLoad = _.without(toLoad, entity.id);
25158
25159                     // Because loadMultiple doesn't download /full like loadEntity,
25160                     // need to also load children that aren't already being checked..
25161                     if (!entity.visible) return;
25162                     if (entity.type === 'way') {
25163                         loadMore.push.apply(loadMore,
25164                             _.difference(entity.nodes, toCheck, toLoad, loadMore));
25165                     } else if (entity.type === 'relation' && entity.isMultipolygon()) {
25166                         loadMore.push.apply(loadMore,
25167                             _.difference(_.pluck(entity.members, 'id'), toCheck, toLoad, loadMore));
25168                     }
25169                 });
25170
25171                 if (loadMore.length) {
25172                     toLoad.push.apply(toLoad, loadMore);
25173                     context.connection().loadMultiple(loadMore, loaded);
25174                 }
25175
25176                 if (!toLoad.length) {
25177                     checkConflicts();
25178                 }
25179             }
25180         }
25181
25182
25183         function checkConflicts() {
25184             function choice(id, text, action) {
25185                 return { id: id, text: text, action: function() { history.replace(action); } };
25186             }
25187             function formatUser(d) {
25188                 return '<a href="' + context.connection().userURL(d) + '" target="_blank">' + d + '</a>';
25189             }
25190             function entityName(entity) {
25191                 return iD.util.displayName(entity) || (iD.util.displayType(entity.id) + ' ' + entity.id);
25192             }
25193
25194             function compareVersions(local, remote) {
25195                 if (local.version !== remote.version) return false;
25196
25197                 if (local.type === 'way') {
25198                     var children = _.union(local.nodes, remote.nodes);
25199
25200                     for (var i = 0; i < children.length; i++) {
25201                         var a = localGraph.hasEntity(children[i]),
25202                             b = remoteGraph.hasEntity(children[i]);
25203
25204                         if (a && b && a.version !== b.version) return false;
25205                     }
25206                 }
25207
25208                 return true;
25209             }
25210
25211             _.each(toCheck, function(id) {
25212                 var local = localGraph.entity(id),
25213                     remote = remoteGraph.entity(id);
25214
25215                 if (compareVersions(local, remote)) return;
25216
25217                 var action = iD.actions.MergeRemoteChanges,
25218                     merge = action(id, localGraph, remoteGraph, formatUser);
25219
25220                 history.replace(merge);
25221
25222                 var mergeConflicts = merge.conflicts();
25223                 if (!mergeConflicts.length) return;  // merged safely
25224
25225                 var forceLocal = action(id, localGraph, remoteGraph).withOption('force_local'),
25226                     forceRemote = action(id, localGraph, remoteGraph).withOption('force_remote'),
25227                     keepMine = t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore')),
25228                     keepTheirs = t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
25229
25230                 conflicts.push({
25231                     id: id,
25232                     name: entityName(local),
25233                     details: mergeConflicts,
25234                     chosen: 1,
25235                     choices: [
25236                         choice(id, keepMine, forceLocal),
25237                         choice(id, keepTheirs, forceRemote)
25238                     ]
25239                 });
25240             });
25241
25242             finalize();
25243         }
25244
25245
25246         function finalize() {
25247             if (conflicts.length) {
25248                 conflicts.sort(function(a,b) { return b.id.localeCompare(a.id); });
25249                 showConflicts();
25250             } else if (errors.length) {
25251                 showErrors();
25252             } else {
25253                 var changes = history.changes(iD.actions.DiscardTags(history.difference()));
25254                 if (changes.modified.length || changes.created.length || changes.deleted.length) {
25255                     context.connection().putChangeset(
25256                         changes,
25257                         e.comment,
25258                         history.imageryUsed(),
25259                         function(err, changeset_id) {
25260                             if (err) {
25261                                 errors.push({
25262                                     msg: err.responseText,
25263                                     details: [ t('save.status_code', { code: err.status }) ]
25264                                 });
25265                                 showErrors();
25266                             } else {
25267                                 history.clearSaved();
25268                                 success(e, changeset_id);
25269                                 // Add delay to allow for postgres replication #1646 #2678
25270                                 window.setTimeout(function() {
25271                                     loading.close();
25272                                     context.flush();
25273                                 }, 2500);
25274                             }
25275                         });
25276                 } else {        // changes were insignificant or reverted by user
25277                     loading.close();
25278                     context.flush();
25279                     cancel();
25280                 }
25281             }
25282         }
25283
25284
25285         function showConflicts() {
25286             var selection = context.container()
25287                 .select('#sidebar')
25288                 .append('div')
25289                 .attr('class','sidebar-component');
25290
25291             loading.close();
25292
25293             selection.call(iD.ui.Conflicts(context)
25294                 .list(conflicts)
25295                 .on('download', function() {
25296                     var data = JXON.stringify(context.connection().osmChangeJXON('CHANGEME', origChanges)),
25297                         win = window.open('data:text/xml,' + encodeURIComponent(data), '_blank');
25298                     win.focus();
25299                 })
25300                 .on('cancel', function() {
25301                     history.pop();
25302                     selection.remove();
25303                 })
25304                 .on('save', function() {
25305                     for (var i = 0; i < conflicts.length; i++) {
25306                         if (conflicts[i].chosen === 1) {  // user chose "keep theirs"
25307                             var entity = context.hasEntity(conflicts[i].id);
25308                             if (entity && entity.type === 'way') {
25309                                 var children = _.uniq(entity.nodes);
25310                                 for (var j = 0; j < children.length; j++) {
25311                                     history.replace(iD.actions.Revert(children[j]));
25312                                 }
25313                             }
25314                             history.replace(iD.actions.Revert(conflicts[i].id));
25315                         }
25316                     }
25317
25318                     selection.remove();
25319                     save(e, true);
25320                 })
25321             );
25322         }
25323
25324
25325         function showErrors() {
25326             var selection = iD.ui.confirm(context.container());
25327
25328             history.pop();
25329             loading.close();
25330
25331             selection
25332                 .select('.modal-section.header')
25333                 .append('h3')
25334                 .text(t('save.error'));
25335
25336             addErrors(selection, errors);
25337             selection.okButton();
25338         }
25339
25340
25341         function addErrors(selection, data) {
25342             var message = selection
25343                 .select('.modal-section.message-text');
25344
25345             var items = message
25346                 .selectAll('.error-container')
25347                 .data(data);
25348
25349             var enter = items.enter()
25350                 .append('div')
25351                 .attr('class', 'error-container');
25352
25353             enter
25354                 .append('a')
25355                 .attr('class', 'error-description')
25356                 .attr('href', '#')
25357                 .classed('hide-toggle', true)
25358                 .text(function(d) { return d.msg || t('save.unknown_error_details'); })
25359                 .on('click', function() {
25360                     var error = d3.select(this),
25361                         detail = d3.select(this.nextElementSibling),
25362                         exp = error.classed('expanded');
25363
25364                     detail.style('display', exp ? 'none' : 'block');
25365                     error.classed('expanded', !exp);
25366
25367                     d3.event.preventDefault();
25368                 });
25369
25370             var details = enter
25371                 .append('div')
25372                 .attr('class', 'error-detail-container')
25373                 .style('display', 'none');
25374
25375             details
25376                 .append('ul')
25377                 .attr('class', 'error-detail-list')
25378                 .selectAll('li')
25379                 .data(function(d) { return d.details || []; })
25380                 .enter()
25381                 .append('li')
25382                 .attr('class', 'error-detail-item')
25383                 .text(function(d) { return d; });
25384
25385             items.exit()
25386                 .remove();
25387         }
25388
25389     }
25390
25391
25392     function success(e, changeset_id) {
25393         context.enter(iD.modes.Browse(context)
25394             .sidebar(iD.ui.Success(context)
25395                 .changeset({
25396                     id: changeset_id,
25397                     comment: e.comment
25398                 })
25399                 .on('cancel', function() {
25400                     context.ui().sidebar.hide();
25401                 })));
25402     }
25403
25404     var mode = {
25405         id: 'save'
25406     };
25407
25408     mode.enter = function() {
25409         context.connection().authenticate(function(err) {
25410             if (err) {
25411                 cancel();
25412             } else {
25413                 context.ui().sidebar.show(ui);
25414             }
25415         });
25416     };
25417
25418     mode.exit = function() {
25419         context.ui().sidebar.hide();
25420     };
25421
25422     return mode;
25423 };
25424 iD.modes.Select = function(context, selectedIDs) {
25425     var mode = {
25426         id: 'select',
25427         button: 'browse'
25428     };
25429
25430     var keybinding = d3.keybinding('select'),
25431         timeout = null,
25432         behaviors = [
25433             iD.behavior.Copy(context),
25434             iD.behavior.Paste(context),
25435             iD.behavior.Breathe(context),
25436             iD.behavior.Hover(context),
25437             iD.behavior.Select(context),
25438             iD.behavior.Lasso(context),
25439             iD.modes.DragNode(context)
25440                 .selectedIDs(selectedIDs)
25441                 .behavior],
25442         inspector,
25443         radialMenu,
25444         newFeature = false,
25445         suppressMenu = false;
25446
25447     var wrap = context.container()
25448         .select('.inspector-wrap');
25449
25450
25451     function singular() {
25452         if (selectedIDs.length === 1) {
25453             return context.hasEntity(selectedIDs[0]);
25454         }
25455     }
25456
25457     function closeMenu() {
25458         if (radialMenu) {
25459             context.surface().call(radialMenu.close);
25460         }
25461     }
25462
25463     function positionMenu() {
25464         if (suppressMenu || !radialMenu) { return; }
25465
25466         var entity = singular();
25467         if (entity && context.geometry(entity.id) === 'relation') {
25468             suppressMenu = true;
25469         } else if (entity && entity.type === 'node') {
25470             radialMenu.center(context.projection(entity.loc));
25471         } else {
25472             var point = context.mouse(),
25473                 viewport = iD.geo.Extent(context.projection.clipExtent()).polygon();
25474             if (iD.geo.pointInPolygon(point, viewport)) {
25475                 radialMenu.center(point);
25476             } else {
25477                 suppressMenu = true;
25478             }
25479         }
25480     }
25481
25482     function showMenu() {
25483         closeMenu();
25484         if (!suppressMenu && radialMenu) {
25485             context.surface().call(radialMenu);
25486         }
25487     }
25488
25489     function toggleMenu() {
25490         if (d3.select('.radial-menu').empty()) {
25491             showMenu();
25492         } else {
25493             closeMenu();
25494         }
25495     }
25496
25497     mode.selectedIDs = function() {
25498         return selectedIDs;
25499     };
25500
25501     mode.reselect = function() {
25502         var surfaceNode = context.surface().node();
25503         if (surfaceNode.focus) { // FF doesn't support it
25504             surfaceNode.focus();
25505         }
25506
25507         positionMenu();
25508         showMenu();
25509     };
25510
25511     mode.newFeature = function(_) {
25512         if (!arguments.length) return newFeature;
25513         newFeature = _;
25514         return mode;
25515     };
25516
25517     mode.suppressMenu = function(_) {
25518         if (!arguments.length) return suppressMenu;
25519         suppressMenu = _;
25520         return mode;
25521     };
25522
25523     mode.enter = function() {
25524         function update() {
25525             closeMenu();
25526             if (_.any(selectedIDs, function(id) { return !context.hasEntity(id); })) {
25527                 // Exit mode if selected entity gets undone
25528                 context.enter(iD.modes.Browse(context));
25529             }
25530         }
25531
25532         function dblclick() {
25533             var target = d3.select(d3.event.target),
25534                 datum = target.datum();
25535
25536             if (datum instanceof iD.Way && !target.classed('fill')) {
25537                 var choice = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection),
25538                     node = iD.Node();
25539
25540                 var prev = datum.nodes[choice.index - 1],
25541                     next = datum.nodes[choice.index];
25542
25543                 context.perform(
25544                     iD.actions.AddMidpoint({loc: choice.loc, edge: [prev, next]}, node),
25545                     t('operations.add.annotation.vertex'));
25546
25547                 d3.event.preventDefault();
25548                 d3.event.stopPropagation();
25549             }
25550         }
25551
25552         function selectElements(drawn) {
25553             var entity = singular();
25554             if (entity && context.geometry(entity.id) === 'relation') {
25555                 suppressMenu = true;
25556                 return;
25557             }
25558
25559             var selection = context.surface()
25560                     .selectAll(iD.util.entityOrMemberSelector(selectedIDs, context.graph()));
25561
25562             if (selection.empty()) {
25563                 if (drawn) {  // Exit mode if selected DOM elements have disappeared..
25564                     context.enter(iD.modes.Browse(context));
25565                 }
25566             } else {
25567                 selection
25568                     .classed('selected', true);
25569             }
25570         }
25571
25572         function esc() {
25573             if (!context.inIntro()) {
25574                 context.enter(iD.modes.Browse(context));
25575             }
25576         }
25577
25578
25579         behaviors.forEach(function(behavior) {
25580             context.install(behavior);
25581         });
25582
25583         var operations = _.without(d3.values(iD.operations), iD.operations.Delete)
25584                 .map(function(o) { return o(selectedIDs, context); })
25585                 .filter(function(o) { return o.available(); });
25586
25587         operations.unshift(iD.operations.Delete(selectedIDs, context));
25588
25589         keybinding
25590             .on('⎋', esc, true)
25591             .on('space', toggleMenu);
25592
25593         operations.forEach(function(operation) {
25594             operation.keys.forEach(function(key) {
25595                 keybinding.on(key, function() {
25596                     if (!(context.inIntro() || operation.disabled())) {
25597                         operation();
25598                     }
25599                 });
25600             });
25601         });
25602
25603         d3.select(document)
25604             .call(keybinding);
25605
25606         radialMenu = iD.ui.RadialMenu(context, operations);
25607
25608         context.ui().sidebar
25609             .select(singular() ? singular().id : null, newFeature);
25610
25611         context.history()
25612             .on('undone.select', update)
25613             .on('redone.select', update);
25614
25615         context.map()
25616             .on('move.select', closeMenu)
25617             .on('drawn.select', selectElements);
25618
25619         selectElements();
25620
25621         var show = d3.event && !suppressMenu;
25622
25623         if (show) {
25624             positionMenu();
25625         }
25626
25627         timeout = window.setTimeout(function() {
25628             if (show) {
25629                 showMenu();
25630             }
25631
25632             context.surface()
25633                 .on('dblclick.select', dblclick);
25634         }, 200);
25635
25636         if (selectedIDs.length > 1) {
25637             var entities = iD.ui.SelectionList(context, selectedIDs);
25638             context.ui().sidebar.show(entities);
25639         }
25640     };
25641
25642     mode.exit = function() {
25643         if (timeout) window.clearTimeout(timeout);
25644
25645         if (inspector) wrap.call(inspector.close);
25646
25647         behaviors.forEach(function(behavior) {
25648             context.uninstall(behavior);
25649         });
25650
25651         keybinding.off();
25652         closeMenu();
25653         radialMenu = undefined;
25654
25655         context.history()
25656             .on('undone.select', null)
25657             .on('redone.select', null);
25658
25659         context.surface()
25660             .on('dblclick.select', null)
25661             .selectAll('.selected')
25662             .classed('selected', false);
25663
25664         context.map().on('drawn.select', null);
25665         context.ui().sidebar.hide();
25666     };
25667
25668     return mode;
25669 };
25670 iD.operations = {};
25671 iD.operations.Circularize = function(selectedIDs, context) {
25672     var entityId = selectedIDs[0],
25673         entity = context.entity(entityId),
25674         extent = entity.extent(context.graph()),
25675         geometry = context.geometry(entityId),
25676         action = iD.actions.Circularize(entityId, context.projection);
25677
25678     var operation = function() {
25679         var annotation = t('operations.circularize.annotation.' + geometry);
25680         context.perform(action, annotation);
25681     };
25682
25683     operation.available = function() {
25684         return selectedIDs.length === 1 &&
25685             entity.type === 'way' &&
25686             _.uniq(entity.nodes).length > 1;
25687     };
25688
25689     operation.disabled = function() {
25690         var reason;
25691         if (extent.percentContainedIn(context.extent()) < 0.8) {
25692             reason = 'too_large';
25693         } else if (context.hasHiddenConnections(entityId)) {
25694             reason = 'connected_to_hidden';
25695         }
25696         return action.disabled(context.graph()) || reason;
25697     };
25698
25699     operation.tooltip = function() {
25700         var disable = operation.disabled();
25701         return disable ?
25702             t('operations.circularize.' + disable) :
25703             t('operations.circularize.description.' + geometry);
25704     };
25705
25706     operation.id = 'circularize';
25707     operation.keys = [t('operations.circularize.key')];
25708     operation.title = t('operations.circularize.title');
25709
25710     return operation;
25711 };
25712 iD.operations.Continue = function(selectedIDs, context) {
25713     var graph = context.graph(),
25714         entities = selectedIDs.map(function(id) { return graph.entity(id); }),
25715         geometries = _.extend({line: [], vertex: []},
25716             _.groupBy(entities, function(entity) { return entity.geometry(graph); })),
25717         vertex = geometries.vertex[0];
25718
25719     function candidateWays() {
25720         return graph.parentWays(vertex).filter(function(parent) {
25721             return parent.geometry(graph) === 'line' &&
25722                 parent.affix(vertex.id) &&
25723                 (geometries.line.length === 0 || geometries.line[0] === parent);
25724         });
25725     }
25726
25727     var operation = function() {
25728         var candidate = candidateWays()[0];
25729         context.enter(iD.modes.DrawLine(
25730             context,
25731             candidate.id,
25732             context.graph(),
25733             candidate.affix(vertex.id)));
25734     };
25735
25736     operation.available = function() {
25737         return geometries.vertex.length === 1 && geometries.line.length <= 1 &&
25738             !context.features().hasHiddenConnections(vertex, context.graph());
25739     };
25740
25741     operation.disabled = function() {
25742         var candidates = candidateWays();
25743         if (candidates.length === 0)
25744             return 'not_eligible';
25745         if (candidates.length > 1)
25746             return 'multiple';
25747     };
25748
25749     operation.tooltip = function() {
25750         var disable = operation.disabled();
25751         return disable ?
25752             t('operations.continue.' + disable) :
25753             t('operations.continue.description');
25754     };
25755
25756     operation.id = 'continue';
25757     operation.keys = [t('operations.continue.key')];
25758     operation.title = t('operations.continue.title');
25759
25760     return operation;
25761 };
25762 iD.operations.Delete = function(selectedIDs, context) {
25763     var action = iD.actions.DeleteMultiple(selectedIDs);
25764
25765     var operation = function() {
25766         var annotation,
25767             nextSelectedID;
25768
25769         if (selectedIDs.length > 1) {
25770             annotation = t('operations.delete.annotation.multiple', {n: selectedIDs.length});
25771
25772         } else {
25773             var id = selectedIDs[0],
25774                 entity = context.entity(id),
25775                 geometry = context.geometry(id),
25776                 parents = context.graph().parentWays(entity),
25777                 parent = parents[0];
25778
25779             annotation = t('operations.delete.annotation.' + geometry);
25780
25781             // Select the next closest node in the way.
25782             if (geometry === 'vertex' && parents.length === 1 && parent.nodes.length > 2) {
25783                 var nodes = parent.nodes,
25784                     i = nodes.indexOf(id);
25785
25786                 if (i === 0) {
25787                     i++;
25788                 } else if (i === nodes.length - 1) {
25789                     i--;
25790                 } else {
25791                     var a = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc),
25792                         b = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
25793                     i = a < b ? i - 1 : i + 1;
25794                 }
25795
25796                 nextSelectedID = nodes[i];
25797             }
25798         }
25799
25800         if (nextSelectedID && context.hasEntity(nextSelectedID)) {
25801             context.enter(iD.modes.Select(context, [nextSelectedID]));
25802         } else {
25803             context.enter(iD.modes.Browse(context));
25804         }
25805
25806         context.perform(
25807             action,
25808             annotation);
25809     };
25810
25811     operation.available = function() {
25812         return true;
25813     };
25814
25815     operation.disabled = function() {
25816         var reason;
25817         if (_.any(selectedIDs, context.hasHiddenConnections)) {
25818             reason = 'connected_to_hidden';
25819         }
25820         return action.disabled(context.graph()) || reason;
25821     };
25822
25823     operation.tooltip = function() {
25824         var disable = operation.disabled();
25825         return disable ?
25826             t('operations.delete.' + disable) :
25827             t('operations.delete.description');
25828     };
25829
25830     operation.id = 'delete';
25831     operation.keys = [iD.ui.cmd('⌘⌫'), iD.ui.cmd('⌘⌦')];
25832     operation.title = t('operations.delete.title');
25833
25834     return operation;
25835 };
25836 iD.operations.Disconnect = function(selectedIDs, context) {
25837     var vertices = _.filter(selectedIDs, function vertex(entityId) {
25838         return context.geometry(entityId) === 'vertex';
25839     });
25840
25841     var entityId = vertices[0],
25842         action = iD.actions.Disconnect(entityId);
25843
25844     if (selectedIDs.length > 1) {
25845         action.limitWays(_.without(selectedIDs, entityId));
25846     }
25847
25848     var operation = function() {
25849         context.perform(action, t('operations.disconnect.annotation'));
25850     };
25851
25852     operation.available = function() {
25853         return vertices.length === 1;
25854     };
25855
25856     operation.disabled = function() {
25857         var reason;
25858         if (_.any(selectedIDs, context.hasHiddenConnections)) {
25859             reason = 'connected_to_hidden';
25860         }
25861         return action.disabled(context.graph()) || reason;
25862     };
25863
25864     operation.tooltip = function() {
25865         var disable = operation.disabled();
25866         return disable ?
25867             t('operations.disconnect.' + disable) :
25868             t('operations.disconnect.description');
25869     };
25870
25871     operation.id = 'disconnect';
25872     operation.keys = [t('operations.disconnect.key')];
25873     operation.title = t('operations.disconnect.title');
25874
25875     return operation;
25876 };
25877 iD.operations.Merge = function(selectedIDs, context) {
25878     var join = iD.actions.Join(selectedIDs),
25879         merge = iD.actions.Merge(selectedIDs),
25880         mergePolygon = iD.actions.MergePolygon(selectedIDs);
25881
25882     var operation = function() {
25883         var annotation = t('operations.merge.annotation', {n: selectedIDs.length}),
25884             action;
25885
25886         if (!join.disabled(context.graph())) {
25887             action = join;
25888         } else if (!merge.disabled(context.graph())) {
25889             action = merge;
25890         } else {
25891             action = mergePolygon;
25892         }
25893
25894         context.perform(action, annotation);
25895         context.enter(iD.modes.Select(context, selectedIDs.filter(function(id) { return context.hasEntity(id); }))
25896             .suppressMenu(true));
25897     };
25898
25899     operation.available = function() {
25900         return selectedIDs.length >= 2;
25901     };
25902
25903     operation.disabled = function() {
25904         return join.disabled(context.graph()) &&
25905             merge.disabled(context.graph()) &&
25906             mergePolygon.disabled(context.graph());
25907     };
25908
25909     operation.tooltip = function() {
25910         var j = join.disabled(context.graph()),
25911             m = merge.disabled(context.graph()),
25912             p = mergePolygon.disabled(context.graph());
25913
25914         if (j === 'restriction' && m && p)
25915             return t('operations.merge.restriction', {relation: context.presets().item('type/restriction').name()});
25916
25917         if (p === 'incomplete_relation' && j && m)
25918             return t('operations.merge.incomplete_relation');
25919
25920         if (j && m && p)
25921             return t('operations.merge.' + j);
25922
25923         return t('operations.merge.description');
25924     };
25925
25926     operation.id = 'merge';
25927     operation.keys = [t('operations.merge.key')];
25928     operation.title = t('operations.merge.title');
25929
25930     return operation;
25931 };
25932 iD.operations.Move = function(selectedIDs, context) {
25933     var extent = selectedIDs.reduce(function(extent, id) {
25934             return extent.extend(context.entity(id).extent(context.graph()));
25935         }, iD.geo.Extent());
25936
25937     var operation = function() {
25938         context.enter(iD.modes.Move(context, selectedIDs));
25939     };
25940
25941     operation.available = function() {
25942         return selectedIDs.length > 1 ||
25943             context.entity(selectedIDs[0]).type !== 'node';
25944     };
25945
25946     operation.disabled = function() {
25947         var reason;
25948         if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
25949             reason = 'too_large';
25950         } else if (_.any(selectedIDs, context.hasHiddenConnections)) {
25951             reason = 'connected_to_hidden';
25952         }
25953         return iD.actions.Move(selectedIDs).disabled(context.graph()) || reason;
25954     };
25955
25956     operation.tooltip = function() {
25957         var disable = operation.disabled();
25958         return disable ?
25959             t('operations.move.' + disable) :
25960             t('operations.move.description');
25961     };
25962
25963     operation.id = 'move';
25964     operation.keys = [t('operations.move.key')];
25965     operation.title = t('operations.move.title');
25966
25967     return operation;
25968 };
25969 iD.operations.Orthogonalize = function(selectedIDs, context) {
25970     var entityId = selectedIDs[0],
25971         entity = context.entity(entityId),
25972         extent = entity.extent(context.graph()),
25973         geometry = context.geometry(entityId),
25974         action = iD.actions.Orthogonalize(entityId, context.projection);
25975
25976     var operation = function() {
25977         var annotation = t('operations.orthogonalize.annotation.' + geometry);
25978         context.perform(action, annotation);
25979     };
25980
25981     operation.available = function() {
25982         return selectedIDs.length === 1 &&
25983             entity.type === 'way' &&
25984             entity.isClosed() &&
25985             _.uniq(entity.nodes).length > 2;
25986     };
25987
25988     operation.disabled = function() {
25989         var reason;
25990         if (extent.percentContainedIn(context.extent()) < 0.8) {
25991             reason = 'too_large';
25992         } else if (context.hasHiddenConnections(entityId)) {
25993             reason = 'connected_to_hidden';
25994         }
25995         return action.disabled(context.graph()) || reason;
25996     };
25997
25998     operation.tooltip = function() {
25999         var disable = operation.disabled();
26000         return disable ?
26001             t('operations.orthogonalize.' + disable) :
26002             t('operations.orthogonalize.description.' + geometry);
26003     };
26004
26005     operation.id = 'orthogonalize';
26006     operation.keys = [t('operations.orthogonalize.key')];
26007     operation.title = t('operations.orthogonalize.title');
26008
26009     return operation;
26010 };
26011 iD.operations.Reverse = function(selectedIDs, context) {
26012     var entityId = selectedIDs[0];
26013
26014     var operation = function() {
26015         context.perform(
26016             iD.actions.Reverse(entityId),
26017             t('operations.reverse.annotation'));
26018     };
26019
26020     operation.available = function() {
26021         return selectedIDs.length === 1 &&
26022             context.geometry(entityId) === 'line';
26023     };
26024
26025     operation.disabled = function() {
26026         return false;
26027     };
26028
26029     operation.tooltip = function() {
26030         return t('operations.reverse.description');
26031     };
26032
26033     operation.id = 'reverse';
26034     operation.keys = [t('operations.reverse.key')];
26035     operation.title = t('operations.reverse.title');
26036
26037     return operation;
26038 };
26039 iD.operations.Rotate = function(selectedIDs, context) {
26040     var entityId = selectedIDs[0],
26041         entity = context.entity(entityId),
26042         extent = entity.extent(context.graph()),
26043         geometry = context.geometry(entityId);
26044
26045     var operation = function() {
26046         context.enter(iD.modes.RotateWay(context, entityId));
26047     };
26048
26049     operation.available = function() {
26050         if (selectedIDs.length !== 1 || entity.type !== 'way')
26051             return false;
26052         if (geometry === 'area')
26053             return true;
26054         if (entity.isClosed() &&
26055             context.graph().parentRelations(entity).some(function(r) { return r.isMultipolygon(); }))
26056             return true;
26057         return false;
26058     };
26059
26060     operation.disabled = function() {
26061         if (extent.percentContainedIn(context.extent()) < 0.8) {
26062             return 'too_large';
26063         } else if (context.hasHiddenConnections(entityId)) {
26064             return 'connected_to_hidden';
26065         } else {
26066             return false;
26067         }
26068     };
26069
26070     operation.tooltip = function() {
26071         var disable = operation.disabled();
26072         return disable ?
26073             t('operations.rotate.' + disable) :
26074             t('operations.rotate.description');
26075     };
26076
26077     operation.id = 'rotate';
26078     operation.keys = [t('operations.rotate.key')];
26079     operation.title = t('operations.rotate.title');
26080
26081     return operation;
26082 };
26083 iD.operations.Split = function(selectedIDs, context) {
26084     var vertices = _.filter(selectedIDs, function vertex(entityId) {
26085         return context.geometry(entityId) === 'vertex';
26086     });
26087
26088     var entityId = vertices[0],
26089         action = iD.actions.Split(entityId);
26090
26091     if (selectedIDs.length > 1) {
26092         action.limitWays(_.without(selectedIDs, entityId));
26093     }
26094
26095     var operation = function() {
26096         var annotation;
26097
26098         var ways = action.ways(context.graph());
26099         if (ways.length === 1) {
26100             annotation = t('operations.split.annotation.' + context.geometry(ways[0].id));
26101         } else {
26102             annotation = t('operations.split.annotation.multiple', {n: ways.length});
26103         }
26104
26105         var difference = context.perform(action, annotation);
26106         context.enter(iD.modes.Select(context, difference.extantIDs()));
26107     };
26108
26109     operation.available = function() {
26110         return vertices.length === 1;
26111     };
26112
26113     operation.disabled = function() {
26114         var reason;
26115         if (_.any(selectedIDs, context.hasHiddenConnections)) {
26116             reason = 'connected_to_hidden';
26117         }
26118         return action.disabled(context.graph()) || reason;
26119     };
26120
26121     operation.tooltip = function() {
26122         var disable = operation.disabled();
26123         if (disable) {
26124             return t('operations.split.' + disable);
26125         }
26126
26127         var ways = action.ways(context.graph());
26128         if (ways.length === 1) {
26129             return t('operations.split.description.' + context.geometry(ways[0].id));
26130         } else {
26131             return t('operations.split.description.multiple');
26132         }
26133     };
26134
26135     operation.id = 'split';
26136     operation.keys = [t('operations.split.key')];
26137     operation.title = t('operations.split.title');
26138
26139     return operation;
26140 };
26141 iD.operations.Straighten = function(selectedIDs, context) {
26142     var entityId = selectedIDs[0],
26143         action = iD.actions.Straighten(entityId, context.projection);
26144
26145     function operation() {
26146         var annotation = t('operations.straighten.annotation');
26147         context.perform(action, annotation);
26148     }
26149
26150     operation.available = function() {
26151         var entity = context.entity(entityId);
26152         return selectedIDs.length === 1 &&
26153             entity.type === 'way' &&
26154             !entity.isClosed() &&
26155             _.uniq(entity.nodes).length > 2;
26156     };
26157
26158     operation.disabled = function() {
26159         var reason;
26160         if (context.hasHiddenConnections(entityId)) {
26161             reason = 'connected_to_hidden';
26162         }
26163         return action.disabled(context.graph()) || reason;
26164     };
26165
26166     operation.tooltip = function() {
26167         var disable = operation.disabled();
26168         return disable ?
26169             t('operations.straighten.' + disable) :
26170             t('operations.straighten.description');
26171     };
26172
26173     operation.id = 'straighten';
26174     operation.keys = [t('operations.straighten.key')];
26175     operation.title = t('operations.straighten.title');
26176
26177     return operation;
26178 };
26179 iD.Connection = function(useHttps) {
26180     if (typeof useHttps !== 'boolean') {
26181       useHttps = window.location.protocol === 'https:';
26182     }
26183
26184     var event = d3.dispatch('authenticating', 'authenticated', 'auth', 'loading', 'loaded'),
26185         protocol = useHttps ? 'https:' : 'http:',
26186         url = protocol + '//www.openstreetmap.org',
26187         connection = {},
26188         inflight = {},
26189         loadedTiles = {},
26190         tileZoom = 16,
26191         oauth = osmAuth({
26192             url: protocol + '//www.openstreetmap.org',
26193             oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
26194             oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
26195             loading: authenticating,
26196             done: authenticated
26197         }),
26198         ndStr = 'nd',
26199         tagStr = 'tag',
26200         memberStr = 'member',
26201         nodeStr = 'node',
26202         wayStr = 'way',
26203         relationStr = 'relation',
26204         userDetails,
26205         off;
26206
26207
26208     connection.changesetURL = function(changesetId) {
26209         return url + '/changeset/' + changesetId;
26210     };
26211
26212     connection.changesetsURL = function(center, zoom) {
26213         var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
26214         return url + '/history#map=' +
26215             Math.floor(zoom) + '/' +
26216             center[1].toFixed(precision) + '/' +
26217             center[0].toFixed(precision);
26218     };
26219
26220     connection.entityURL = function(entity) {
26221         return url + '/' + entity.type + '/' + entity.osmId();
26222     };
26223
26224     connection.userURL = function(username) {
26225         return url + '/user/' + username;
26226     };
26227
26228     connection.loadFromURL = function(url, callback) {
26229         function done(err, dom) {
26230             return callback(err, parse(dom));
26231         }
26232         return d3.xml(url).get(done);
26233     };
26234
26235     connection.loadEntity = function(id, callback) {
26236         var type = iD.Entity.id.type(id),
26237             osmID = iD.Entity.id.toOSM(id);
26238
26239         connection.loadFromURL(
26240             url + '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''),
26241             function(err, entities) {
26242                 if (callback) callback(err, {data: entities});
26243             });
26244     };
26245
26246     connection.loadEntityVersion = function(id, version, callback) {
26247         var type = iD.Entity.id.type(id),
26248             osmID = iD.Entity.id.toOSM(id);
26249
26250         connection.loadFromURL(
26251             url + '/api/0.6/' + type + '/' + osmID + '/' + version,
26252             function(err, entities) {
26253                 if (callback) callback(err, {data: entities});
26254             });
26255     };
26256
26257     connection.loadMultiple = function(ids, callback) {
26258         _.each(_.groupBy(_.uniq(ids), iD.Entity.id.type), function(v, k) {
26259             var type = k + 's',
26260                 osmIDs = _.map(v, iD.Entity.id.toOSM);
26261
26262             _.each(_.chunk(osmIDs, 150), function(arr) {
26263                 connection.loadFromURL(
26264                     url + '/api/0.6/' + type + '?' + type + '=' + arr.join(),
26265                     function(err, entities) {
26266                         if (callback) callback(err, {data: entities});
26267                     });
26268             });
26269         });
26270     };
26271
26272     function authenticating() {
26273         event.authenticating();
26274     }
26275
26276     function authenticated() {
26277         event.authenticated();
26278     }
26279
26280     function getLoc(attrs) {
26281         var lon = attrs.lon && attrs.lon.value,
26282             lat = attrs.lat && attrs.lat.value;
26283         return [parseFloat(lon), parseFloat(lat)];
26284     }
26285
26286     function getNodes(obj) {
26287         var elems = obj.getElementsByTagName(ndStr),
26288             nodes = new Array(elems.length);
26289         for (var i = 0, l = elems.length; i < l; i++) {
26290             nodes[i] = 'n' + elems[i].attributes.ref.value;
26291         }
26292         return nodes;
26293     }
26294
26295     function getTags(obj) {
26296         var elems = obj.getElementsByTagName(tagStr),
26297             tags = {};
26298         for (var i = 0, l = elems.length; i < l; i++) {
26299             var attrs = elems[i].attributes;
26300             tags[attrs.k.value] = attrs.v.value;
26301         }
26302         return tags;
26303     }
26304
26305     function getMembers(obj) {
26306         var elems = obj.getElementsByTagName(memberStr),
26307             members = new Array(elems.length);
26308         for (var i = 0, l = elems.length; i < l; i++) {
26309             var attrs = elems[i].attributes;
26310             members[i] = {
26311                 id: attrs.type.value[0] + attrs.ref.value,
26312                 type: attrs.type.value,
26313                 role: attrs.role.value
26314             };
26315         }
26316         return members;
26317     }
26318
26319     function getVisible(attrs) {
26320         return (!attrs.visible || attrs.visible.value !== 'false');
26321     }
26322
26323     var parsers = {
26324         node: function nodeData(obj) {
26325             var attrs = obj.attributes;
26326             return new iD.Node({
26327                 id: iD.Entity.id.fromOSM(nodeStr, attrs.id.value),
26328                 loc: getLoc(attrs),
26329                 version: attrs.version.value,
26330                 user: attrs.user && attrs.user.value,
26331                 tags: getTags(obj),
26332                 visible: getVisible(attrs)
26333             });
26334         },
26335
26336         way: function wayData(obj) {
26337             var attrs = obj.attributes;
26338             return new iD.Way({
26339                 id: iD.Entity.id.fromOSM(wayStr, attrs.id.value),
26340                 version: attrs.version.value,
26341                 user: attrs.user && attrs.user.value,
26342                 tags: getTags(obj),
26343                 nodes: getNodes(obj),
26344                 visible: getVisible(attrs)
26345             });
26346         },
26347
26348         relation: function relationData(obj) {
26349             var attrs = obj.attributes;
26350             return new iD.Relation({
26351                 id: iD.Entity.id.fromOSM(relationStr, attrs.id.value),
26352                 version: attrs.version.value,
26353                 user: attrs.user && attrs.user.value,
26354                 tags: getTags(obj),
26355                 members: getMembers(obj),
26356                 visible: getVisible(attrs)
26357             });
26358         }
26359     };
26360
26361     function parse(dom) {
26362         if (!dom || !dom.childNodes) return;
26363
26364         var root = dom.childNodes[0],
26365             children = root.childNodes,
26366             entities = [];
26367
26368         for (var i = 0, l = children.length; i < l; i++) {
26369             var child = children[i],
26370                 parser = parsers[child.nodeName];
26371             if (parser) {
26372                 entities.push(parser(child));
26373             }
26374         }
26375
26376         return entities;
26377     }
26378
26379     connection.authenticated = function() {
26380         return oauth.authenticated();
26381     };
26382
26383     // Generate Changeset XML. Returns a string.
26384     connection.changesetJXON = function(tags) {
26385         return {
26386             osm: {
26387                 changeset: {
26388                     tag: _.map(tags, function(value, key) {
26389                         return { '@k': key, '@v': value };
26390                     }),
26391                     '@version': 0.6,
26392                     '@generator': 'iD'
26393                 }
26394             }
26395         };
26396     };
26397
26398     // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
26399     // XML. Returns a string.
26400     connection.osmChangeJXON = function(changeset_id, changes) {
26401         function nest(x, order) {
26402             var groups = {};
26403             for (var i = 0; i < x.length; i++) {
26404                 var tagName = Object.keys(x[i])[0];
26405                 if (!groups[tagName]) groups[tagName] = [];
26406                 groups[tagName].push(x[i][tagName]);
26407             }
26408             var ordered = {};
26409             order.forEach(function(o) {
26410                 if (groups[o]) ordered[o] = groups[o];
26411             });
26412             return ordered;
26413         }
26414
26415         function rep(entity) {
26416             return entity.asJXON(changeset_id);
26417         }
26418
26419         return {
26420             osmChange: {
26421                 '@version': 0.6,
26422                 '@generator': 'iD',
26423                 'create': nest(changes.created.map(rep), ['node', 'way', 'relation']),
26424                 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
26425                 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true})
26426             }
26427         };
26428     };
26429
26430     connection.changesetTags = function(comment, imageryUsed) {
26431         var detected = iD.detect(),
26432             tags = {
26433                 created_by: 'iD ' + iD.version,
26434                 imagery_used: imageryUsed.join(';').substr(0, 255),
26435                 host: (window.location.origin + window.location.pathname).substr(0, 255),
26436                 locale: detected.locale
26437             };
26438
26439         if (comment) {
26440             tags.comment = comment.substr(0, 255);
26441         }
26442
26443         return tags;
26444     };
26445
26446     connection.putChangeset = function(changes, comment, imageryUsed, callback) {
26447         oauth.xhr({
26448                 method: 'PUT',
26449                 path: '/api/0.6/changeset/create',
26450                 options: { header: { 'Content-Type': 'text/xml' } },
26451                 content: JXON.stringify(connection.changesetJXON(connection.changesetTags(comment, imageryUsed)))
26452             }, function(err, changeset_id) {
26453                 if (err) return callback(err);
26454                 oauth.xhr({
26455                     method: 'POST',
26456                     path: '/api/0.6/changeset/' + changeset_id + '/upload',
26457                     options: { header: { 'Content-Type': 'text/xml' } },
26458                     content: JXON.stringify(connection.osmChangeJXON(changeset_id, changes))
26459                 }, function(err) {
26460                     if (err) return callback(err);
26461                     // POST was successful, safe to call the callback.
26462                     // Still attempt to close changeset, but ignore response because #2667
26463                     // Add delay to allow for postgres replication #1646 #2678
26464                     window.setTimeout(function() { callback(null, changeset_id); }, 2500);
26465                     oauth.xhr({
26466                         method: 'PUT',
26467                         path: '/api/0.6/changeset/' + changeset_id + '/close',
26468                         options: { header: { 'Content-Type': 'text/xml' } }
26469                     }, d3.functor(true));
26470                 });
26471             });
26472     };
26473
26474     connection.userDetails = function(callback) {
26475         if (userDetails) {
26476             callback(undefined, userDetails);
26477             return;
26478         }
26479
26480         function done(err, user_details) {
26481             if (err) return callback(err);
26482
26483             var u = user_details.getElementsByTagName('user')[0],
26484                 img = u.getElementsByTagName('img'),
26485                 image_url = '';
26486
26487             if (img && img[0] && img[0].getAttribute('href')) {
26488                 image_url = img[0].getAttribute('href');
26489             }
26490
26491             userDetails = {
26492                 display_name: u.attributes.display_name.value,
26493                 image_url: image_url,
26494                 id: u.attributes.id.value
26495             };
26496
26497             callback(undefined, userDetails);
26498         }
26499
26500         oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
26501     };
26502
26503     connection.userChangesets = function(callback) {
26504         connection.userDetails(function(err, user) {
26505             if (err) return callback(err);
26506
26507             function done(changesets) {
26508                 callback(undefined, Array.prototype.map.call(changesets.getElementsByTagName('changeset'),
26509                     function (changeset) {
26510                         return { tags: getTags(changeset) };
26511                     }));
26512             }
26513
26514             d3.xml(url + '/api/0.6/changesets?user=' + user.id).get()
26515                 .on('load', done)
26516                 .on('error', callback);
26517         });
26518     };
26519
26520     connection.status = function(callback) {
26521         function done(capabilities) {
26522             var apiStatus = capabilities.getElementsByTagName('status');
26523             callback(undefined, apiStatus[0].getAttribute('api'));
26524         }
26525         d3.xml(url + '/api/capabilities').get()
26526             .on('load', done)
26527             .on('error', callback);
26528     };
26529
26530     function abortRequest(i) { i.abort(); }
26531
26532     connection.tileZoom = function(_) {
26533         if (!arguments.length) return tileZoom;
26534         tileZoom = _;
26535         return connection;
26536     };
26537
26538     connection.loadTiles = function(projection, dimensions, callback) {
26539
26540         if (off) return;
26541
26542         var s = projection.scale() * 2 * Math.PI,
26543             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
26544             ts = 256 * Math.pow(2, z - tileZoom),
26545             origin = [
26546                 s / 2 - projection.translate()[0],
26547                 s / 2 - projection.translate()[1]];
26548
26549         var tiles = d3.geo.tile()
26550             .scaleExtent([tileZoom, tileZoom])
26551             .scale(s)
26552             .size(dimensions)
26553             .translate(projection.translate())()
26554             .map(function(tile) {
26555                 var x = tile[0] * ts - origin[0],
26556                     y = tile[1] * ts - origin[1];
26557
26558                 return {
26559                     id: tile.toString(),
26560                     extent: iD.geo.Extent(
26561                         projection.invert([x, y + ts]),
26562                         projection.invert([x + ts, y]))
26563                 };
26564             });
26565
26566         function bboxUrl(tile) {
26567             return url + '/api/0.6/map?bbox=' + tile.extent.toParam();
26568         }
26569
26570         _.filter(inflight, function(v, i) {
26571             var wanted = _.find(tiles, function(tile) {
26572                 return i === tile.id;
26573             });
26574             if (!wanted) delete inflight[i];
26575             return !wanted;
26576         }).map(abortRequest);
26577
26578         tiles.forEach(function(tile) {
26579             var id = tile.id;
26580
26581             if (loadedTiles[id] || inflight[id]) return;
26582
26583             if (_.isEmpty(inflight)) {
26584                 event.loading();
26585             }
26586
26587             inflight[id] = connection.loadFromURL(bboxUrl(tile), function(err, parsed) {
26588                 loadedTiles[id] = true;
26589                 delete inflight[id];
26590
26591                 if (callback) callback(err, _.extend({data: parsed}, tile));
26592
26593                 if (_.isEmpty(inflight)) {
26594                     event.loaded();
26595                 }
26596             });
26597         });
26598     };
26599
26600     connection.switch = function(options) {
26601         url = options.url;
26602         oauth.options(_.extend({
26603             loading: authenticating,
26604             done: authenticated
26605         }, options));
26606         event.auth();
26607         connection.flush();
26608         return connection;
26609     };
26610
26611     connection.toggle = function(_) {
26612         off = !_;
26613         return connection;
26614     };
26615
26616     connection.flush = function() {
26617         userDetails = undefined;
26618         _.forEach(inflight, abortRequest);
26619         loadedTiles = {};
26620         inflight = {};
26621         return connection;
26622     };
26623
26624     connection.loadedTiles = function(_) {
26625         if (!arguments.length) return loadedTiles;
26626         loadedTiles = _;
26627         return connection;
26628     };
26629
26630     connection.logout = function() {
26631         userDetails = undefined;
26632         oauth.logout();
26633         event.auth();
26634         return connection;
26635     };
26636
26637     connection.authenticate = function(callback) {
26638         userDetails = undefined;
26639         function done(err, res) {
26640             event.auth();
26641             if (callback) callback(err, res);
26642         }
26643         return oauth.authenticate(done);
26644     };
26645
26646     return d3.rebind(connection, event, 'on');
26647 };
26648 /*
26649     iD.Difference represents the difference between two graphs.
26650     It knows how to calculate the set of entities that were
26651     created, modified, or deleted, and also contains the logic
26652     for recursively extending a difference to the complete set
26653     of entities that will require a redraw, taking into account
26654     child and parent relationships.
26655  */
26656 iD.Difference = function(base, head) {
26657     var changes = {}, length = 0;
26658
26659     function changed(h, b) {
26660         return h !== b && !_.isEqual(_.omit(h, 'v'), _.omit(b, 'v'));
26661     }
26662
26663     _.each(head.entities, function(h, id) {
26664         var b = base.entities[id];
26665         if (changed(h, b)) {
26666             changes[id] = {base: b, head: h};
26667             length++;
26668         }
26669     });
26670
26671     _.each(base.entities, function(b, id) {
26672         var h = head.entities[id];
26673         if (!changes[id] && changed(h, b)) {
26674             changes[id] = {base: b, head: h};
26675             length++;
26676         }
26677     });
26678
26679     function addParents(parents, result) {
26680         for (var i = 0; i < parents.length; i++) {
26681             var parent = parents[i];
26682
26683             if (parent.id in result)
26684                 continue;
26685
26686             result[parent.id] = parent;
26687             addParents(head.parentRelations(parent), result);
26688         }
26689     }
26690
26691     var difference = {};
26692
26693     difference.length = function() {
26694         return length;
26695     };
26696
26697     difference.changes = function() {
26698         return changes;
26699     };
26700
26701     difference.extantIDs = function() {
26702         var result = [];
26703         _.each(changes, function(change, id) {
26704             if (change.head) result.push(id);
26705         });
26706         return result;
26707     };
26708
26709     difference.modified = function() {
26710         var result = [];
26711         _.each(changes, function(change) {
26712             if (change.base && change.head) result.push(change.head);
26713         });
26714         return result;
26715     };
26716
26717     difference.created = function() {
26718         var result = [];
26719         _.each(changes, function(change) {
26720             if (!change.base && change.head) result.push(change.head);
26721         });
26722         return result;
26723     };
26724
26725     difference.deleted = function() {
26726         var result = [];
26727         _.each(changes, function(change) {
26728             if (change.base && !change.head) result.push(change.base);
26729         });
26730         return result;
26731     };
26732
26733     difference.summary = function() {
26734         var relevant = {};
26735
26736         function addEntity(entity, graph, changeType) {
26737             relevant[entity.id] = {
26738                 entity: entity,
26739                 graph: graph,
26740                 changeType: changeType
26741             };
26742         }
26743
26744         function addParents(entity) {
26745             var parents = head.parentWays(entity);
26746             for (var j = parents.length - 1; j >= 0; j--) {
26747                 var parent = parents[j];
26748                 if (!(parent.id in relevant)) addEntity(parent, head, 'modified');
26749             }
26750         }
26751
26752         _.each(changes, function(change) {
26753             if (change.head && change.head.geometry(head) !== 'vertex') {
26754                 addEntity(change.head, head, change.base ? 'modified' : 'created');
26755
26756             } else if (change.base && change.base.geometry(base) !== 'vertex') {
26757                 addEntity(change.base, base, 'deleted');
26758
26759             } else if (change.base && change.head) { // modified vertex
26760                 var moved    = !_.isEqual(change.base.loc,  change.head.loc),
26761                     retagged = !_.isEqual(change.base.tags, change.head.tags);
26762
26763                 if (moved) {
26764                     addParents(change.head);
26765                 }
26766
26767                 if (retagged || (moved && change.head.hasInterestingTags())) {
26768                     addEntity(change.head, head, 'modified');
26769                 }
26770
26771             } else if (change.head && change.head.hasInterestingTags()) { // created vertex
26772                 addEntity(change.head, head, 'created');
26773
26774             } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
26775                 addEntity(change.base, base, 'deleted');
26776             }
26777         });
26778
26779         return d3.values(relevant);
26780     };
26781
26782     difference.complete = function(extent) {
26783         var result = {}, id, change;
26784
26785         for (id in changes) {
26786             change = changes[id];
26787
26788             var h = change.head,
26789                 b = change.base,
26790                 entity = h || b;
26791
26792             if (extent &&
26793                 (!h || !h.intersects(extent, head)) &&
26794                 (!b || !b.intersects(extent, base)))
26795                 continue;
26796
26797             result[id] = h;
26798
26799             if (entity.type === 'way') {
26800                 var nh = h ? h.nodes : [],
26801                     nb = b ? b.nodes : [],
26802                     diff, i;
26803
26804                 diff = _.difference(nh, nb);
26805                 for (i = 0; i < diff.length; i++) {
26806                     result[diff[i]] = head.hasEntity(diff[i]);
26807                 }
26808
26809                 diff = _.difference(nb, nh);
26810                 for (i = 0; i < diff.length; i++) {
26811                     result[diff[i]] = head.hasEntity(diff[i]);
26812                 }
26813             }
26814
26815             addParents(head.parentWays(entity), result);
26816             addParents(head.parentRelations(entity), result);
26817         }
26818
26819         return result;
26820     };
26821
26822     return difference;
26823 };
26824 iD.Entity = function(attrs) {
26825     // For prototypal inheritance.
26826     if (this instanceof iD.Entity) return;
26827
26828     // Create the appropriate subtype.
26829     if (attrs && attrs.type) {
26830         return iD.Entity[attrs.type].apply(this, arguments);
26831     } else if (attrs && attrs.id) {
26832         return iD.Entity[iD.Entity.id.type(attrs.id)].apply(this, arguments);
26833     }
26834
26835     // Initialize a generic Entity (used only in tests).
26836     return (new iD.Entity()).initialize(arguments);
26837 };
26838
26839 iD.Entity.id = function(type) {
26840     return iD.Entity.id.fromOSM(type, iD.Entity.id.next[type]--);
26841 };
26842
26843 iD.Entity.id.next = {node: -1, way: -1, relation: -1};
26844
26845 iD.Entity.id.fromOSM = function(type, id) {
26846     return type[0] + id;
26847 };
26848
26849 iD.Entity.id.toOSM = function(id) {
26850     return id.slice(1);
26851 };
26852
26853 iD.Entity.id.type = function(id) {
26854     return {'n': 'node', 'w': 'way', 'r': 'relation'}[id[0]];
26855 };
26856
26857 // A function suitable for use as the second argument to d3.selection#data().
26858 iD.Entity.key = function(entity) {
26859     return entity.id + 'v' + (entity.v || 0);
26860 };
26861
26862 iD.Entity.prototype = {
26863     tags: {},
26864
26865     initialize: function(sources) {
26866         for (var i = 0; i < sources.length; ++i) {
26867             var source = sources[i];
26868             for (var prop in source) {
26869                 if (Object.prototype.hasOwnProperty.call(source, prop)) {
26870                     if (source[prop] === undefined) {
26871                         delete this[prop];
26872                     } else {
26873                         this[prop] = source[prop];
26874                     }
26875                 }
26876             }
26877         }
26878
26879         if (!this.id && this.type) {
26880             this.id = iD.Entity.id(this.type);
26881         }
26882         if (!this.hasOwnProperty('visible')) {
26883             this.visible = true;
26884         }
26885
26886         if (iD.debug) {
26887             Object.freeze(this);
26888             Object.freeze(this.tags);
26889
26890             if (this.loc) Object.freeze(this.loc);
26891             if (this.nodes) Object.freeze(this.nodes);
26892             if (this.members) Object.freeze(this.members);
26893         }
26894
26895         return this;
26896     },
26897
26898     copy: function(resolver, copies) {
26899         if (copies[this.id])
26900             return copies[this.id];
26901
26902         var copy = iD.Entity(this, {id: undefined, user: undefined, version: undefined});
26903         copies[this.id] = copy;
26904
26905         return copy;
26906     },
26907
26908     osmId: function() {
26909         return iD.Entity.id.toOSM(this.id);
26910     },
26911
26912     isNew: function() {
26913         return this.osmId() < 0;
26914     },
26915
26916     update: function(attrs) {
26917         return iD.Entity(this, attrs, {v: 1 + (this.v || 0)});
26918     },
26919
26920     mergeTags: function(tags) {
26921         var merged = _.clone(this.tags), changed = false;
26922         for (var k in tags) {
26923             var t1 = merged[k],
26924                 t2 = tags[k];
26925             if (!t1) {
26926                 changed = true;
26927                 merged[k] = t2;
26928             } else if (t1 !== t2) {
26929                 changed = true;
26930                 merged[k] = _.union(t1.split(/;\s*/), t2.split(/;\s*/)).join(';');
26931             }
26932         }
26933         return changed ? this.update({tags: merged}) : this;
26934     },
26935
26936     intersects: function(extent, resolver) {
26937         return this.extent(resolver).intersects(extent);
26938     },
26939
26940     isUsed: function(resolver) {
26941         return _.without(Object.keys(this.tags), 'area').length > 0 ||
26942             resolver.parentRelations(this).length > 0;
26943     },
26944
26945     hasInterestingTags: function() {
26946         return _.keys(this.tags).some(iD.interestingTag);
26947     },
26948
26949     isHighwayIntersection: function() {
26950         return false;
26951     },
26952
26953     deprecatedTags: function() {
26954         var tags = _.pairs(this.tags);
26955         var deprecated = {};
26956
26957         iD.data.deprecated.forEach(function(d) {
26958             var match = _.pairs(d.old)[0];
26959             tags.forEach(function(t) {
26960                 if (t[0] === match[0] &&
26961                     (t[1] === match[1] || match[1] === '*')) {
26962                     deprecated[t[0]] = t[1];
26963                 }
26964             });
26965         });
26966
26967         return deprecated;
26968     }
26969 };
26970 iD.Graph = function(other, mutable) {
26971     if (!(this instanceof iD.Graph)) return new iD.Graph(other, mutable);
26972
26973     if (other instanceof iD.Graph) {
26974         var base = other.base();
26975         this.entities = _.assign(Object.create(base.entities), other.entities);
26976         this._parentWays = _.assign(Object.create(base.parentWays), other._parentWays);
26977         this._parentRels = _.assign(Object.create(base.parentRels), other._parentRels);
26978
26979     } else {
26980         this.entities = Object.create({});
26981         this._parentWays = Object.create({});
26982         this._parentRels = Object.create({});
26983         this.rebase(other || [], [this]);
26984     }
26985
26986     this.transients = {};
26987     this._childNodes = {};
26988     this.frozen = !mutable;
26989 };
26990
26991 iD.Graph.prototype = {
26992     hasEntity: function(id) {
26993         return this.entities[id];
26994     },
26995
26996     entity: function(id) {
26997         var entity = this.entities[id];
26998         if (!entity) {
26999             throw new Error('entity ' + id + ' not found');
27000         }
27001         return entity;
27002     },
27003
27004     transient: function(entity, key, fn) {
27005         var id = entity.id,
27006             transients = this.transients[id] ||
27007             (this.transients[id] = {});
27008
27009         if (transients[key] !== undefined) {
27010             return transients[key];
27011         }
27012
27013         transients[key] = fn.call(entity);
27014
27015         return transients[key];
27016     },
27017
27018     parentWays: function(entity) {
27019         var parents = this._parentWays[entity.id],
27020             result = [];
27021
27022         if (parents) {
27023             for (var i = 0; i < parents.length; i++) {
27024                 result.push(this.entity(parents[i]));
27025             }
27026         }
27027         return result;
27028     },
27029
27030     isPoi: function(entity) {
27031         var parentWays = this._parentWays[entity.id];
27032         return !parentWays || parentWays.length === 0;
27033     },
27034
27035     isShared: function(entity) {
27036         var parentWays = this._parentWays[entity.id];
27037         return parentWays && parentWays.length > 1;
27038     },
27039
27040     parentRelations: function(entity) {
27041         var parents = this._parentRels[entity.id],
27042             result = [];
27043
27044         if (parents) {
27045             for (var i = 0; i < parents.length; i++) {
27046                 result.push(this.entity(parents[i]));
27047             }
27048         }
27049         return result;
27050     },
27051
27052     childNodes: function(entity) {
27053         if (this._childNodes[entity.id]) return this._childNodes[entity.id];
27054         if (!entity.nodes) return [];
27055
27056         var nodes = [];
27057         for (var i = 0; i < entity.nodes.length; i++) {
27058             nodes[i] = this.entity(entity.nodes[i]);
27059         }
27060
27061         if (iD.debug) Object.freeze(nodes);
27062
27063         this._childNodes[entity.id] = nodes;
27064         return this._childNodes[entity.id];
27065     },
27066
27067     base: function() {
27068         return {
27069             'entities': iD.util.getPrototypeOf(this.entities),
27070             'parentWays': iD.util.getPrototypeOf(this._parentWays),
27071             'parentRels': iD.util.getPrototypeOf(this._parentRels)
27072         };
27073     },
27074
27075     // Unlike other graph methods, rebase mutates in place. This is because it
27076     // is used only during the history operation that merges newly downloaded
27077     // data into each state. To external consumers, it should appear as if the
27078     // graph always contained the newly downloaded data.
27079     rebase: function(entities, stack, force) {
27080         var base = this.base(),
27081             i, j, k, id;
27082
27083         for (i = 0; i < entities.length; i++) {
27084             var entity = entities[i];
27085
27086             if (!entity.visible || (!force && base.entities[entity.id]))
27087                 continue;
27088
27089             // Merging data into the base graph
27090             base.entities[entity.id] = entity;
27091             this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
27092
27093             // Restore provisionally-deleted nodes that are discovered to have an extant parent
27094             if (entity.type === 'way') {
27095                 for (j = 0; j < entity.nodes.length; j++) {
27096                     id = entity.nodes[j];
27097                     for (k = 1; k < stack.length; k++) {
27098                         var ents = stack[k].entities;
27099                         if (ents.hasOwnProperty(id) && ents[id] === undefined) {
27100                             delete ents[id];
27101                         }
27102                     }
27103                 }
27104             }
27105         }
27106
27107         for (i = 0; i < stack.length; i++) {
27108             stack[i]._updateRebased();
27109         }
27110     },
27111
27112     _updateRebased: function() {
27113         var base = this.base(),
27114             i, k, child, id, keys;
27115
27116         keys = Object.keys(this._parentWays);
27117         for (i = 0; i < keys.length; i++) {
27118             child = keys[i];
27119             if (base.parentWays[child]) {
27120                 for (k = 0; k < base.parentWays[child].length; k++) {
27121                     id = base.parentWays[child][k];
27122                     if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentWays[child], id)) {
27123                         this._parentWays[child].push(id);
27124                     }
27125                 }
27126             }
27127         }
27128
27129         keys = Object.keys(this._parentRels);
27130         for (i = 0; i < keys.length; i++) {
27131             child = keys[i];
27132             if (base.parentRels[child]) {
27133                 for (k = 0; k < base.parentRels[child].length; k++) {
27134                     id = base.parentRels[child][k];
27135                     if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentRels[child], id)) {
27136                         this._parentRels[child].push(id);
27137                     }
27138                 }
27139             }
27140         }
27141
27142         this.transients = {};
27143
27144         // this._childNodes is not updated, under the assumption that
27145         // ways are always downloaded with their child nodes.
27146     },
27147
27148     // Updates calculated properties (parentWays, parentRels) for the specified change
27149     _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
27150
27151         parentWays = parentWays || this._parentWays;
27152         parentRels = parentRels || this._parentRels;
27153
27154         var type = entity && entity.type || oldentity && oldentity.type,
27155             removed, added, ways, rels, i;
27156
27157
27158         if (type === 'way') {
27159
27160             // Update parentWays
27161             if (oldentity && entity) {
27162                 removed = _.difference(oldentity.nodes, entity.nodes);
27163                 added = _.difference(entity.nodes, oldentity.nodes);
27164             } else if (oldentity) {
27165                 removed = oldentity.nodes;
27166                 added = [];
27167             } else if (entity) {
27168                 removed = [];
27169                 added = entity.nodes;
27170             }
27171             for (i = 0; i < removed.length; i++) {
27172                 parentWays[removed[i]] = _.without(parentWays[removed[i]], oldentity.id);
27173             }
27174             for (i = 0; i < added.length; i++) {
27175                 ways = _.without(parentWays[added[i]], entity.id);
27176                 ways.push(entity.id);
27177                 parentWays[added[i]] = ways;
27178             }
27179
27180         } else if (type === 'relation') {
27181
27182             // Update parentRels
27183             if (oldentity && entity) {
27184                 removed = _.difference(oldentity.members, entity.members);
27185                 added = _.difference(entity.members, oldentity);
27186             } else if (oldentity) {
27187                 removed = oldentity.members;
27188                 added = [];
27189             } else if (entity) {
27190                 removed = [];
27191                 added = entity.members;
27192             }
27193             for (i = 0; i < removed.length; i++) {
27194                 parentRels[removed[i].id] = _.without(parentRels[removed[i].id], oldentity.id);
27195             }
27196             for (i = 0; i < added.length; i++) {
27197                 rels = _.without(parentRels[added[i].id], entity.id);
27198                 rels.push(entity.id);
27199                 parentRels[added[i].id] = rels;
27200             }
27201         }
27202     },
27203
27204     replace: function(entity) {
27205         if (this.entities[entity.id] === entity)
27206             return this;
27207
27208         return this.update(function() {
27209             this._updateCalculated(this.entities[entity.id], entity);
27210             this.entities[entity.id] = entity;
27211         });
27212     },
27213
27214     remove: function(entity) {
27215         return this.update(function() {
27216             this._updateCalculated(entity, undefined);
27217             this.entities[entity.id] = undefined;
27218         });
27219     },
27220
27221     revert: function(id) {
27222         var baseEntity = this.base().entities[id],
27223             headEntity = this.entities[id];
27224
27225         if (headEntity === baseEntity)
27226             return this;
27227
27228         return this.update(function() {
27229             this._updateCalculated(headEntity, baseEntity);
27230             delete this.entities[id];
27231         });
27232     },
27233
27234     update: function() {
27235         var graph = this.frozen ? iD.Graph(this, true) : this;
27236
27237         for (var i = 0; i < arguments.length; i++) {
27238             arguments[i].call(graph, graph);
27239         }
27240
27241         if (this.frozen) graph.frozen = true;
27242
27243         return graph;
27244     },
27245
27246     // Obliterates any existing entities
27247     load: function(entities) {
27248         var base = this.base();
27249         this.entities = Object.create(base.entities);
27250
27251         for (var i in entities) {
27252             this.entities[i] = entities[i];
27253             this._updateCalculated(base.entities[i], this.entities[i]);
27254         }
27255
27256         return this;
27257     }
27258 };
27259 iD.History = function(context) {
27260     var stack, index, tree,
27261         imageryUsed = ['Bing'],
27262         dispatch = d3.dispatch('change', 'undone', 'redone'),
27263         lock = iD.util.SessionMutex('lock');
27264
27265     function perform(actions) {
27266         actions = Array.prototype.slice.call(actions);
27267
27268         var annotation;
27269
27270         if (!_.isFunction(_.last(actions))) {
27271             annotation = actions.pop();
27272         }
27273
27274         var graph = stack[index].graph;
27275         for (var i = 0; i < actions.length; i++) {
27276             graph = actions[i](graph);
27277         }
27278
27279         return {
27280             graph: graph,
27281             annotation: annotation,
27282             imageryUsed: imageryUsed
27283         };
27284     }
27285
27286     function change(previous) {
27287         var difference = iD.Difference(previous, history.graph());
27288         dispatch.change(difference);
27289         return difference;
27290     }
27291
27292     // iD uses namespaced keys so multiple installations do not conflict
27293     function getKey(n) {
27294         return 'iD_' + window.location.origin + '_' + n;
27295     }
27296
27297     var history = {
27298         graph: function() {
27299             return stack[index].graph;
27300         },
27301
27302         base: function() {
27303             return stack[0].graph;
27304         },
27305
27306         merge: function(entities, extent) {
27307             stack[0].graph.rebase(entities, _.pluck(stack, 'graph'), false);
27308             tree.rebase(entities, false);
27309
27310             dispatch.change(undefined, extent);
27311         },
27312
27313         perform: function() {
27314             var previous = stack[index].graph;
27315
27316             stack = stack.slice(0, index + 1);
27317             stack.push(perform(arguments));
27318             index++;
27319
27320             return change(previous);
27321         },
27322
27323         replace: function() {
27324             var previous = stack[index].graph;
27325
27326             // assert(index == stack.length - 1)
27327             stack[index] = perform(arguments);
27328
27329             return change(previous);
27330         },
27331
27332         pop: function() {
27333             var previous = stack[index].graph;
27334
27335             if (index > 0) {
27336                 index--;
27337                 stack.pop();
27338                 return change(previous);
27339             }
27340         },
27341
27342         // Same as calling pop and then perform
27343         overwrite: function() {
27344             var previous = stack[index].graph;
27345
27346             if (index > 0) {
27347                 index--;
27348                 stack.pop();
27349             }
27350             stack = stack.slice(0, index + 1);
27351             stack.push(perform(arguments));
27352             index++;
27353
27354             return change(previous);
27355         },
27356
27357         undo: function() {
27358             var previous = stack[index].graph;
27359
27360             // Pop to the next annotated state.
27361             while (index > 0) {
27362                 index--;
27363                 if (stack[index].annotation) break;
27364             }
27365
27366             dispatch.undone();
27367             return change(previous);
27368         },
27369
27370         redo: function() {
27371             var previous = stack[index].graph;
27372
27373             while (index < stack.length - 1) {
27374                 index++;
27375                 if (stack[index].annotation) break;
27376             }
27377
27378             dispatch.redone();
27379             return change(previous);
27380         },
27381
27382         undoAnnotation: function() {
27383             var i = index;
27384             while (i >= 0) {
27385                 if (stack[i].annotation) return stack[i].annotation;
27386                 i--;
27387             }
27388         },
27389
27390         redoAnnotation: function() {
27391             var i = index + 1;
27392             while (i <= stack.length - 1) {
27393                 if (stack[i].annotation) return stack[i].annotation;
27394                 i++;
27395             }
27396         },
27397
27398         intersects: function(extent) {
27399             return tree.intersects(extent, stack[index].graph);
27400         },
27401
27402         difference: function() {
27403             var base = stack[0].graph,
27404                 head = stack[index].graph;
27405             return iD.Difference(base, head);
27406         },
27407
27408         changes: function(action) {
27409             var base = stack[0].graph,
27410                 head = stack[index].graph;
27411
27412             if (action) {
27413                 head = action(head);
27414             }
27415
27416             var difference = iD.Difference(base, head);
27417
27418             return {
27419                 modified: difference.modified(),
27420                 created: difference.created(),
27421                 deleted: difference.deleted()
27422             };
27423         },
27424
27425         validate: function(changes) {
27426             return _(iD.validations)
27427                 .map(function(fn) { return fn()(changes, stack[index].graph); })
27428                 .flatten()
27429                 .value();
27430         },
27431
27432         hasChanges: function() {
27433             return this.difference().length() > 0;
27434         },
27435
27436         imageryUsed: function(sources) {
27437             if (sources) {
27438                 imageryUsed = sources;
27439                 return history;
27440             } else {
27441                 return _(stack.slice(1, index + 1))
27442                     .pluck('imageryUsed')
27443                     .flatten()
27444                     .unique()
27445                     .without(undefined, 'Custom')
27446                     .value();
27447             }
27448         },
27449
27450         reset: function() {
27451             stack = [{graph: iD.Graph()}];
27452             index = 0;
27453             tree = iD.Tree(stack[0].graph);
27454             dispatch.change();
27455             return history;
27456         },
27457
27458         toJSON: function() {
27459             if (!this.hasChanges()) return;
27460
27461             var allEntities = {},
27462                 baseEntities = {},
27463                 base = stack[0];
27464
27465             var s = stack.map(function(i) {
27466                 var modified = [], deleted = [];
27467
27468                 _.forEach(i.graph.entities, function(entity, id) {
27469                     if (entity) {
27470                         var key = iD.Entity.key(entity);
27471                         allEntities[key] = entity;
27472                         modified.push(key);
27473                     } else {
27474                         deleted.push(id);
27475                     }
27476
27477                     // make sure that the originals of changed or deleted entities get merged
27478                     // into the base of the stack after restoring the data from JSON.
27479                     if (id in base.graph.entities) {
27480                         baseEntities[id] = base.graph.entities[id];
27481                     }
27482                     // get originals of parent entities too
27483                     _.forEach(base.graph._parentWays[id], function(parentId) {
27484                         if (parentId in base.graph.entities) {
27485                             baseEntities[parentId] = base.graph.entities[parentId];
27486                         }
27487                     });
27488                 });
27489
27490                 var x = {};
27491
27492                 if (modified.length) x.modified = modified;
27493                 if (deleted.length) x.deleted = deleted;
27494                 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
27495                 if (i.annotation) x.annotation = i.annotation;
27496
27497                 return x;
27498             });
27499
27500             return JSON.stringify({
27501                 version: 3,
27502                 entities: _.values(allEntities),
27503                 baseEntities: _.values(baseEntities),
27504                 stack: s,
27505                 nextIDs: iD.Entity.id.next,
27506                 index: index
27507             });
27508         },
27509
27510         fromJSON: function(json, loadChildNodes) {
27511             var h = JSON.parse(json),
27512                 loadComplete = true;
27513
27514             iD.Entity.id.next = h.nextIDs;
27515             index = h.index;
27516
27517             if (h.version === 2 || h.version === 3) {
27518                 var allEntities = {};
27519
27520                 h.entities.forEach(function(entity) {
27521                     allEntities[iD.Entity.key(entity)] = iD.Entity(entity);
27522                 });
27523
27524                 if (h.version === 3) {
27525                     // This merges originals for changed entities into the base of
27526                     // the stack even if the current stack doesn't have them (for
27527                     // example when iD has been restarted in a different region)
27528                     var baseEntities = h.baseEntities.map(function(d) { return iD.Entity(d); });
27529                     stack[0].graph.rebase(baseEntities, _.pluck(stack, 'graph'), true);
27530                     tree.rebase(baseEntities, true);
27531
27532                     // When we restore a modified way, we also need to fetch any missing
27533                     // childnodes that would normally have been downloaded with it.. #2142
27534                     if (loadChildNodes) {
27535                         var missing =  _(baseEntities)
27536                                 .filter('type', 'way')
27537                                 .pluck('nodes')
27538                                 .flatten()
27539                                 .uniq()
27540                                 .reject(function(n) { return stack[0].graph.hasEntity(n); })
27541                                 .value();
27542
27543                         if (!_.isEmpty(missing)) {
27544                             loadComplete = false;
27545                             context.redrawEnable(false);
27546
27547                             var loading = iD.ui.Loading(context).blocking(true);
27548                             context.container().call(loading);
27549
27550                             var childNodesLoaded = function(err, result) {
27551                                 if (!err) {
27552                                     var visible = _.groupBy(result.data, 'visible');
27553                                     if (!_.isEmpty(visible.true)) {
27554                                         missing = _.difference(missing, _.pluck(visible.true, 'id'));
27555                                         stack[0].graph.rebase(visible.true, _.pluck(stack, 'graph'), true);
27556                                         tree.rebase(visible.true, true);
27557                                     }
27558
27559                                     // fetch older versions of nodes that were deleted..
27560                                     _.each(visible.false, function(entity) {
27561                                         context.connection()
27562                                             .loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
27563                                     });
27564                                 }
27565
27566                                 if (err || _.isEmpty(missing)) {
27567                                     loading.close();
27568                                     context.redrawEnable(true);
27569                                     dispatch.change();
27570                                 }
27571                             };
27572
27573                             context.connection().loadMultiple(missing, childNodesLoaded);
27574                         }
27575                     }
27576                 }
27577
27578                 stack = h.stack.map(function(d) {
27579                     var entities = {}, entity;
27580
27581                     if (d.modified) {
27582                         d.modified.forEach(function(key) {
27583                             entity = allEntities[key];
27584                             entities[entity.id] = entity;
27585                         });
27586                     }
27587
27588                     if (d.deleted) {
27589                         d.deleted.forEach(function(id) {
27590                             entities[id] = undefined;
27591                         });
27592                     }
27593
27594                     return {
27595                         graph: iD.Graph(stack[0].graph).load(entities),
27596                         annotation: d.annotation,
27597                         imageryUsed: d.imageryUsed
27598                     };
27599                 });
27600
27601             } else { // original version
27602                 stack = h.stack.map(function(d) {
27603                     var entities = {};
27604
27605                     for (var i in d.entities) {
27606                         var entity = d.entities[i];
27607                         entities[i] = entity === 'undefined' ? undefined : iD.Entity(entity);
27608                     }
27609
27610                     d.graph = iD.Graph(stack[0].graph).load(entities);
27611                     return d;
27612                 });
27613             }
27614
27615             if (loadComplete) {
27616                 dispatch.change();
27617             }
27618
27619             return history;
27620         },
27621
27622         save: function() {
27623             if (lock.locked()) context.storage(getKey('saved_history'), history.toJSON() || null);
27624             return history;
27625         },
27626
27627         clearSaved: function() {
27628             context.debouncedSave.cancel();
27629             if (lock.locked()) context.storage(getKey('saved_history'), null);
27630             return history;
27631         },
27632
27633         lock: function() {
27634             return lock.lock();
27635         },
27636
27637         unlock: function() {
27638             lock.unlock();
27639         },
27640
27641         // is iD not open in another window and it detects that
27642         // there's a history stored in localStorage that's recoverable?
27643         restorableChanges: function() {
27644             return lock.locked() && !!context.storage(getKey('saved_history'));
27645         },
27646
27647         // load history from a version stored in localStorage
27648         restore: function() {
27649             if (!lock.locked()) return;
27650
27651             var json = context.storage(getKey('saved_history'));
27652             if (json) history.fromJSON(json, true);
27653         },
27654
27655         _getKey: getKey
27656
27657     };
27658
27659     history.reset();
27660
27661     return d3.rebind(history, dispatch, 'on');
27662 };
27663 iD.Node = iD.Entity.node = function iD_Node() {
27664     if (!(this instanceof iD_Node)) {
27665         return (new iD_Node()).initialize(arguments);
27666     } else if (arguments.length) {
27667         this.initialize(arguments);
27668     }
27669 };
27670
27671 iD.Node.prototype = Object.create(iD.Entity.prototype);
27672
27673 _.extend(iD.Node.prototype, {
27674     type: 'node',
27675
27676     extent: function() {
27677         return new iD.geo.Extent(this.loc);
27678     },
27679
27680     geometry: function(graph) {
27681         return graph.transient(this, 'geometry', function() {
27682             return graph.isPoi(this) ? 'point' : 'vertex';
27683         });
27684     },
27685
27686     move: function(loc) {
27687         return this.update({loc: loc});
27688     },
27689
27690     isIntersection: function(resolver) {
27691         return resolver.transient(this, 'isIntersection', function() {
27692             return resolver.parentWays(this).filter(function(parent) {
27693                 return (parent.tags.highway ||
27694                     parent.tags.waterway ||
27695                     parent.tags.railway ||
27696                     parent.tags.aeroway) &&
27697                     parent.geometry(resolver) === 'line';
27698             }).length > 1;
27699         });
27700     },
27701
27702     isHighwayIntersection: function(resolver) {
27703         return resolver.transient(this, 'isHighwayIntersection', function() {
27704             return resolver.parentWays(this).filter(function(parent) {
27705                 return parent.tags.highway && parent.geometry(resolver) === 'line';
27706             }).length > 1;
27707         });
27708     },
27709
27710     asJXON: function(changeset_id) {
27711         var r = {
27712             node: {
27713                 '@id': this.osmId(),
27714                 '@lon': this.loc[0],
27715                 '@lat': this.loc[1],
27716                 '@version': (this.version || 0),
27717                 tag: _.map(this.tags, function(v, k) {
27718                     return { keyAttributes: { k: k, v: v } };
27719                 })
27720             }
27721         };
27722         if (changeset_id) r.node['@changeset'] = changeset_id;
27723         return r;
27724     },
27725
27726     asGeoJSON: function() {
27727         return {
27728             type: 'Point',
27729             coordinates: this.loc
27730         };
27731     }
27732 });
27733 iD.Relation = iD.Entity.relation = function iD_Relation() {
27734     if (!(this instanceof iD_Relation)) {
27735         return (new iD_Relation()).initialize(arguments);
27736     } else if (arguments.length) {
27737         this.initialize(arguments);
27738     }
27739 };
27740
27741 iD.Relation.prototype = Object.create(iD.Entity.prototype);
27742
27743 iD.Relation.creationOrder = function(a, b) {
27744     var aId = parseInt(iD.Entity.id.toOSM(a.id), 10);
27745     var bId = parseInt(iD.Entity.id.toOSM(b.id), 10);
27746
27747     if (aId < 0 || bId < 0) return aId - bId;
27748     return bId - aId;
27749 };
27750
27751 _.extend(iD.Relation.prototype, {
27752     type: 'relation',
27753     members: [],
27754
27755     copy: function(resolver, copies) {
27756         if (copies[this.id])
27757             return copies[this.id];
27758
27759         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
27760
27761         var members = this.members.map(function(member) {
27762             return _.extend({}, member, {id: resolver.entity(member.id).copy(resolver, copies).id});
27763         });
27764
27765         copy = copy.update({members: members});
27766         copies[this.id] = copy;
27767
27768         return copy;
27769     },
27770
27771     extent: function(resolver, memo) {
27772         return resolver.transient(this, 'extent', function() {
27773             if (memo && memo[this.id]) return iD.geo.Extent();
27774             memo = memo || {};
27775             memo[this.id] = true;
27776
27777             var extent = iD.geo.Extent();
27778             for (var i = 0; i < this.members.length; i++) {
27779                 var member = resolver.hasEntity(this.members[i].id);
27780                 if (member) {
27781                     extent._extend(member.extent(resolver, memo));
27782                 }
27783             }
27784             return extent;
27785         });
27786     },
27787
27788     geometry: function(graph) {
27789         return graph.transient(this, 'geometry', function() {
27790             return this.isMultipolygon() ? 'area' : 'relation';
27791         });
27792     },
27793
27794     isDegenerate: function() {
27795         return this.members.length === 0;
27796     },
27797
27798     // Return an array of members, each extended with an 'index' property whose value
27799     // is the member index.
27800     indexedMembers: function() {
27801         var result = new Array(this.members.length);
27802         for (var i = 0; i < this.members.length; i++) {
27803             result[i] = _.extend({}, this.members[i], {index: i});
27804         }
27805         return result;
27806     },
27807
27808     // Return the first member with the given role. A copy of the member object
27809     // is returned, extended with an 'index' property whose value is the member index.
27810     memberByRole: function(role) {
27811         for (var i = 0; i < this.members.length; i++) {
27812             if (this.members[i].role === role) {
27813                 return _.extend({}, this.members[i], {index: i});
27814             }
27815         }
27816     },
27817
27818     // Return the first member with the given id. A copy of the member object
27819     // is returned, extended with an 'index' property whose value is the member index.
27820     memberById: function(id) {
27821         for (var i = 0; i < this.members.length; i++) {
27822             if (this.members[i].id === id) {
27823                 return _.extend({}, this.members[i], {index: i});
27824             }
27825         }
27826     },
27827
27828     // Return the first member with the given id and role. A copy of the member object
27829     // is returned, extended with an 'index' property whose value is the member index.
27830     memberByIdAndRole: function(id, role) {
27831         for (var i = 0; i < this.members.length; i++) {
27832             if (this.members[i].id === id && this.members[i].role === role) {
27833                 return _.extend({}, this.members[i], {index: i});
27834             }
27835         }
27836     },
27837
27838     addMember: function(member, index) {
27839         var members = this.members.slice();
27840         members.splice(index === undefined ? members.length : index, 0, member);
27841         return this.update({members: members});
27842     },
27843
27844     updateMember: function(member, index) {
27845         var members = this.members.slice();
27846         members.splice(index, 1, _.extend({}, members[index], member));
27847         return this.update({members: members});
27848     },
27849
27850     removeMember: function(index) {
27851         var members = this.members.slice();
27852         members.splice(index, 1);
27853         return this.update({members: members});
27854     },
27855
27856     removeMembersWithID: function(id) {
27857         var members = _.reject(this.members, function(m) { return m.id === id; });
27858         return this.update({members: members});
27859     },
27860
27861     // Wherever a member appears with id `needle.id`, replace it with a member
27862     // with id `replacement.id`, type `replacement.type`, and the original role,
27863     // unless a member already exists with that id and role. Return an updated
27864     // relation.
27865     replaceMember: function(needle, replacement) {
27866         if (!this.memberById(needle.id))
27867             return this;
27868
27869         var members = [];
27870
27871         for (var i = 0; i < this.members.length; i++) {
27872             var member = this.members[i];
27873             if (member.id !== needle.id) {
27874                 members.push(member);
27875             } else if (!this.memberByIdAndRole(replacement.id, member.role)) {
27876                 members.push({id: replacement.id, type: replacement.type, role: member.role});
27877             }
27878         }
27879
27880         return this.update({members: members});
27881     },
27882
27883     asJXON: function(changeset_id) {
27884         var r = {
27885             relation: {
27886                 '@id': this.osmId(),
27887                 '@version': this.version || 0,
27888                 member: _.map(this.members, function(member) {
27889                     return { keyAttributes: { type: member.type, role: member.role, ref: iD.Entity.id.toOSM(member.id) } };
27890                 }),
27891                 tag: _.map(this.tags, function(v, k) {
27892                     return { keyAttributes: { k: k, v: v } };
27893                 })
27894             }
27895         };
27896         if (changeset_id) r.relation['@changeset'] = changeset_id;
27897         return r;
27898     },
27899
27900     asGeoJSON: function(resolver) {
27901         return resolver.transient(this, 'GeoJSON', function () {
27902             if (this.isMultipolygon()) {
27903                 return {
27904                     type: 'MultiPolygon',
27905                     coordinates: this.multipolygon(resolver)
27906                 };
27907             } else {
27908                 return {
27909                     type: 'FeatureCollection',
27910                     properties: this.tags,
27911                     features: this.members.map(function (member) {
27912                         return _.extend({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
27913                     })
27914                 };
27915             }
27916         });
27917     },
27918
27919     area: function(resolver) {
27920         return resolver.transient(this, 'area', function() {
27921             return d3.geo.area(this.asGeoJSON(resolver));
27922         });
27923     },
27924
27925     isMultipolygon: function() {
27926         return this.tags.type === 'multipolygon';
27927     },
27928
27929     isComplete: function(resolver) {
27930         for (var i = 0; i < this.members.length; i++) {
27931             if (!resolver.hasEntity(this.members[i].id)) {
27932                 return false;
27933             }
27934         }
27935         return true;
27936     },
27937
27938     isRestriction: function() {
27939         return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
27940     },
27941
27942     // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
27943     // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
27944     //
27945     // This corresponds to the structure needed for rendering a multipolygon path using a
27946     // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
27947     //
27948     // In the case of invalid geometries, this function will still return a result which
27949     // includes the nodes of all way members, but some Nds may be unclosed and some inner
27950     // rings not matched with the intended outer ring.
27951     //
27952     multipolygon: function(resolver) {
27953         var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
27954             inners = this.members.filter(function(m) { return 'inner' === m.role; });
27955
27956         outers = iD.geo.joinWays(outers, resolver);
27957         inners = iD.geo.joinWays(inners, resolver);
27958
27959         outers = outers.map(function(outer) { return _.pluck(outer.nodes, 'loc'); });
27960         inners = inners.map(function(inner) { return _.pluck(inner.nodes, 'loc'); });
27961
27962         var result = outers.map(function(o) {
27963             // Heuristic for detecting counterclockwise winding order. Assumes
27964             // that OpenStreetMap polygons are not hemisphere-spanning.
27965             return [d3.geo.area({type: 'Polygon', coordinates: [o]}) > 2 * Math.PI ? o.reverse() : o];
27966         });
27967
27968         function findOuter(inner) {
27969             var o, outer;
27970
27971             for (o = 0; o < outers.length; o++) {
27972                 outer = outers[o];
27973                 if (iD.geo.polygonContainsPolygon(outer, inner))
27974                     return o;
27975             }
27976
27977             for (o = 0; o < outers.length; o++) {
27978                 outer = outers[o];
27979                 if (iD.geo.polygonIntersectsPolygon(outer, inner))
27980                     return o;
27981             }
27982         }
27983
27984         for (var i = 0; i < inners.length; i++) {
27985             var inner = inners[i];
27986
27987             if (d3.geo.area({type: 'Polygon', coordinates: [inner]}) < 2 * Math.PI) {
27988                 inner = inner.reverse();
27989             }
27990
27991             var o = findOuter(inners[i]);
27992             if (o !== undefined)
27993                 result[o].push(inners[i]);
27994             else
27995                 result.push([inners[i]]); // Invalid geometry
27996         }
27997
27998         return result;
27999     }
28000 });
28001 iD.oneWayTags = {
28002     'aerialway': {
28003         'chair_lift': true,
28004         'mixed_lift': true,
28005         't-bar': true,
28006         'j-bar': true,
28007         'platter': true,
28008         'rope_tow': true,
28009         'magic_carpet': true,
28010         'yes': true
28011     },
28012     'highway': {
28013         'motorway': true,
28014         'motorway_link': true
28015     },
28016     'junction': {
28017         'roundabout': true
28018     },
28019     'man_made': {
28020         'piste:halfpipe': true
28021     },
28022     'piste:type': {
28023         'downhill': true,
28024         'sled': true,
28025         'yes': true
28026     },
28027     'waterway': {
28028         'river': true,
28029         'stream': true
28030     }
28031 };
28032
28033 iD.pavedTags = {
28034     'surface': {
28035         'paved': true,
28036         'asphalt': true,
28037         'concrete': true
28038     },
28039     'tracktype': {
28040         'grade1': true
28041     }
28042 };
28043
28044 iD.interestingTag = function (key) {
28045     return key !== 'attribution' &&
28046         key !== 'created_by' &&
28047         key !== 'source' &&
28048         key !== 'odbl' &&
28049         key.indexOf('tiger:') !== 0;
28050
28051 };
28052 iD.Tree = function(head) {
28053     var rtree = rbush(),
28054         rectangles = {};
28055
28056     function entityRectangle(entity) {
28057         var rect = entity.extent(head).rectangle();
28058         rect.id = entity.id;
28059         rectangles[entity.id] = rect;
28060         return rect;
28061     }
28062
28063     function updateParents(entity, insertions, memo) {
28064         head.parentWays(entity).forEach(function(way) {
28065             if (rectangles[way.id]) {
28066                 rtree.remove(rectangles[way.id]);
28067                 insertions[way.id] = way;
28068             }
28069             updateParents(way, insertions, memo);
28070         });
28071
28072         head.parentRelations(entity).forEach(function(relation) {
28073             if (memo[entity.id]) return;
28074             memo[entity.id] = true;
28075             if (rectangles[relation.id]) {
28076                 rtree.remove(rectangles[relation.id]);
28077                 insertions[relation.id] = relation;
28078             }
28079             updateParents(relation, insertions, memo);
28080         });
28081     }
28082
28083     var tree = {};
28084
28085     tree.rebase = function(entities, force) {
28086         var insertions = {};
28087
28088         for (var i = 0; i < entities.length; i++) {
28089             var entity = entities[i];
28090
28091             if (!entity.visible)
28092                 continue;
28093
28094             if (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id]) {
28095                 if (!force) {
28096                     continue;
28097                 } else if (rectangles[entity.id]) {
28098                     rtree.remove(rectangles[entity.id]);
28099                 }
28100             }
28101
28102             insertions[entity.id] = entity;
28103             updateParents(entity, insertions, {});
28104         }
28105
28106         rtree.load(_.map(insertions, entityRectangle));
28107
28108         return tree;
28109     };
28110
28111     tree.intersects = function(extent, graph) {
28112         if (graph !== head) {
28113             var diff = iD.Difference(head, graph),
28114                 insertions = {};
28115
28116             head = graph;
28117
28118             diff.deleted().forEach(function(entity) {
28119                 rtree.remove(rectangles[entity.id]);
28120                 delete rectangles[entity.id];
28121             });
28122
28123             diff.modified().forEach(function(entity) {
28124                 rtree.remove(rectangles[entity.id]);
28125                 insertions[entity.id] = entity;
28126                 updateParents(entity, insertions, {});
28127             });
28128
28129             diff.created().forEach(function(entity) {
28130                 insertions[entity.id] = entity;
28131             });
28132
28133             rtree.load(_.map(insertions, entityRectangle));
28134         }
28135
28136         return rtree.search(extent.rectangle()).map(function(rect) {
28137             return head.entity(rect.id);
28138         });
28139     };
28140
28141     return tree;
28142 };
28143 iD.Way = iD.Entity.way = function iD_Way() {
28144     if (!(this instanceof iD_Way)) {
28145         return (new iD_Way()).initialize(arguments);
28146     } else if (arguments.length) {
28147         this.initialize(arguments);
28148     }
28149 };
28150
28151 iD.Way.prototype = Object.create(iD.Entity.prototype);
28152
28153 _.extend(iD.Way.prototype, {
28154     type: 'way',
28155     nodes: [],
28156
28157     copy: function(resolver, copies) {
28158         if (copies[this.id])
28159             return copies[this.id];
28160
28161         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
28162
28163         var nodes = this.nodes.map(function(id) {
28164             return resolver.entity(id).copy(resolver, copies).id;
28165         });
28166
28167         copy = copy.update({nodes: nodes});
28168         copies[this.id] = copy;
28169
28170         return copy;
28171     },
28172
28173     extent: function(resolver) {
28174         return resolver.transient(this, 'extent', function() {
28175             var extent = iD.geo.Extent();
28176             for (var i = 0; i < this.nodes.length; i++) {
28177                 var node = resolver.hasEntity(this.nodes[i]);
28178                 if (node) {
28179                     extent._extend(node.extent());
28180                 }
28181             }
28182             return extent;
28183         });
28184     },
28185
28186     first: function() {
28187         return this.nodes[0];
28188     },
28189
28190     last: function() {
28191         return this.nodes[this.nodes.length - 1];
28192     },
28193
28194     contains: function(node) {
28195         return this.nodes.indexOf(node) >= 0;
28196     },
28197
28198     affix: function(node) {
28199         if (this.nodes[0] === node) return 'prefix';
28200         if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
28201     },
28202
28203     layer: function() {
28204         // explicit layer tag, clamp between -10, 10..
28205         if (this.tags.layer !== undefined) {
28206             return Math.max(-10, Math.min(+(this.tags.layer), 10));
28207         }
28208
28209         // implied layer tag..
28210         if (this.tags.location === 'overground') return 1;
28211         if (this.tags.location === 'underground') return -1;
28212         if (this.tags.location === 'underwater') return -10;
28213
28214         if (this.tags.power === 'line') return 10;
28215         if (this.tags.power === 'minor_line') return 10;
28216         if (this.tags.aerialway) return 10;
28217         if (this.tags.bridge) return 1;
28218         if (this.tags.cutting) return -1;
28219         if (this.tags.tunnel) return -1;
28220         if (this.tags.waterway) return -1;
28221         if (this.tags.man_made === 'pipeline') return -10;
28222         if (this.tags.boundary) return -10;
28223         return 0;
28224     },
28225
28226     isOneWay: function() {
28227         // explicit oneway tag..
28228         if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; }
28229         if (['no', '0'].indexOf(this.tags.oneway) !== -1) { return false; }
28230
28231         // implied oneway tag..
28232         for (var key in this.tags) {
28233             if (key in iD.oneWayTags && (this.tags[key] in iD.oneWayTags[key]))
28234                 return true;
28235         }
28236         return false;
28237     },
28238
28239     isClosed: function() {
28240         return this.nodes.length > 0 && this.first() === this.last();
28241     },
28242
28243     isConvex: function(resolver) {
28244         if (!this.isClosed() || this.isDegenerate()) return null;
28245
28246         var nodes = _.uniq(resolver.childNodes(this)),
28247             coords = _.pluck(nodes, 'loc'),
28248             curr = 0, prev = 0;
28249
28250         for (var i = 0; i < coords.length; i++) {
28251             var o = coords[(i+1) % coords.length],
28252                 a = coords[i],
28253                 b = coords[(i+2) % coords.length],
28254                 res = iD.geo.cross(o, a, b);
28255
28256             curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
28257             if (curr === 0) {
28258                 continue;
28259             } else if (prev && curr !== prev) {
28260                 return false;
28261             }
28262             prev = curr;
28263         }
28264         return true;
28265     },
28266
28267     isArea: function() {
28268         if (this.tags.area === 'yes')
28269             return true;
28270         if (!this.isClosed() || this.tags.area === 'no')
28271             return false;
28272         for (var key in this.tags)
28273             if (key in iD.areaKeys && !(this.tags[key] in iD.areaKeys[key]))
28274                 return true;
28275         return false;
28276     },
28277
28278     isDegenerate: function() {
28279         return _.uniq(this.nodes).length < (this.isArea() ? 3 : 2);
28280     },
28281
28282     areAdjacent: function(n1, n2) {
28283         for (var i = 0; i < this.nodes.length; i++) {
28284             if (this.nodes[i] === n1) {
28285                 if (this.nodes[i - 1] === n2) return true;
28286                 if (this.nodes[i + 1] === n2) return true;
28287             }
28288         }
28289         return false;
28290     },
28291
28292     geometry: function(graph) {
28293         return graph.transient(this, 'geometry', function() {
28294             return this.isArea() ? 'area' : 'line';
28295         });
28296     },
28297
28298     addNode: function(id, index) {
28299         var nodes = this.nodes.slice();
28300         nodes.splice(index === undefined ? nodes.length : index, 0, id);
28301         return this.update({nodes: nodes});
28302     },
28303
28304     updateNode: function(id, index) {
28305         var nodes = this.nodes.slice();
28306         nodes.splice(index, 1, id);
28307         return this.update({nodes: nodes});
28308     },
28309
28310     replaceNode: function(needle, replacement) {
28311         if (this.nodes.indexOf(needle) < 0)
28312             return this;
28313
28314         var nodes = this.nodes.slice();
28315         for (var i = 0; i < nodes.length; i++) {
28316             if (nodes[i] === needle) {
28317                 nodes[i] = replacement;
28318             }
28319         }
28320         return this.update({nodes: nodes});
28321     },
28322
28323     removeNode: function(id) {
28324         var nodes = [];
28325
28326         for (var i = 0; i < this.nodes.length; i++) {
28327             var node = this.nodes[i];
28328             if (node !== id && nodes[nodes.length - 1] !== node) {
28329                 nodes.push(node);
28330             }
28331         }
28332
28333         // Preserve circularity
28334         if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] !== nodes[0]) {
28335             nodes.push(nodes[0]);
28336         }
28337
28338         return this.update({nodes: nodes});
28339     },
28340
28341     asJXON: function(changeset_id) {
28342         var r = {
28343             way: {
28344                 '@id': this.osmId(),
28345                 '@version': this.version || 0,
28346                 nd: _.map(this.nodes, function(id) {
28347                     return { keyAttributes: { ref: iD.Entity.id.toOSM(id) } };
28348                 }),
28349                 tag: _.map(this.tags, function(v, k) {
28350                     return { keyAttributes: { k: k, v: v } };
28351                 })
28352             }
28353         };
28354         if (changeset_id) r.way['@changeset'] = changeset_id;
28355         return r;
28356     },
28357
28358     asGeoJSON: function(resolver) {
28359         return resolver.transient(this, 'GeoJSON', function() {
28360             var coordinates = _.pluck(resolver.childNodes(this), 'loc');
28361             if (this.isArea() && this.isClosed()) {
28362                 return {
28363                     type: 'Polygon',
28364                     coordinates: [coordinates]
28365                 };
28366             } else {
28367                 return {
28368                     type: 'LineString',
28369                     coordinates: coordinates
28370                 };
28371             }
28372         });
28373     },
28374
28375     area: function(resolver) {
28376         return resolver.transient(this, 'area', function() {
28377             var nodes = resolver.childNodes(this);
28378
28379             var json = {
28380                 type: 'Polygon',
28381                 coordinates: [_.pluck(nodes, 'loc')]
28382             };
28383
28384             if (!this.isClosed() && nodes.length) {
28385                 json.coordinates[0].push(nodes[0].loc);
28386             }
28387
28388             var area = d3.geo.area(json);
28389
28390             // Heuristic for detecting counterclockwise winding order. Assumes
28391             // that OpenStreetMap polygons are not hemisphere-spanning.
28392             if (area > 2 * Math.PI) {
28393                 json.coordinates[0] = json.coordinates[0].reverse();
28394                 area = d3.geo.area(json);
28395             }
28396
28397             return isNaN(area) ? 0 : area;
28398         });
28399     }
28400 });
28401 iD.Background = function(context) {
28402     var dispatch = d3.dispatch('change'),
28403         baseLayer = iD.TileLayer().projection(context.projection),
28404         overlayLayers = [],
28405         backgroundSources;
28406
28407
28408     function findSource(id) {
28409         return _.find(backgroundSources, function(d) {
28410             return d.id && d.id === id;
28411         });
28412     }
28413
28414
28415     function background(selection) {
28416         var base = selection.selectAll('.layer-background')
28417             .data([0]);
28418
28419         base.enter()
28420             .insert('div', '.layer-data')
28421             .attr('class', 'layer layer-background');
28422
28423         base.call(baseLayer);
28424
28425         var overlays = selection.selectAll('.layer-overlay')
28426             .data(overlayLayers, function(d) { return d.source().name(); });
28427
28428         overlays.enter()
28429             .insert('div', '.layer-data')
28430             .attr('class', 'layer layer-overlay');
28431
28432         overlays.each(function(layer) {
28433             d3.select(this).call(layer);
28434         });
28435
28436         overlays.exit()
28437             .remove();
28438     }
28439
28440
28441     background.updateImagery = function() {
28442         var b = background.baseLayerSource(),
28443             o = overlayLayers.map(function (d) { return d.source().id; }).join(','),
28444             q = iD.util.stringQs(location.hash.substring(1));
28445
28446         var id = b.id;
28447         if (id === 'custom') {
28448             id = 'custom:' + b.template;
28449         }
28450
28451         if (id) {
28452             q.background = id;
28453         } else {
28454             delete q.background;
28455         }
28456
28457         if (o) {
28458             q.overlays = o;
28459         } else {
28460             delete q.overlays;
28461         }
28462
28463         location.replace('#' + iD.util.qsString(q, true));
28464
28465         var imageryUsed = [b.imageryUsed()];
28466
28467         overlayLayers.forEach(function (d) {
28468             var source = d.source();
28469             if (!source.isLocatorOverlay()) {
28470                 imageryUsed.push(source.imageryUsed());
28471             }
28472         });
28473
28474         var gpx = context.layers().layer('gpx');
28475         if (gpx && gpx.enabled() && gpx.hasGpx()) {
28476             imageryUsed.push('Local GPX');
28477         }
28478
28479         context.history().imageryUsed(imageryUsed);
28480     };
28481
28482     background.sources = function(extent) {
28483         return backgroundSources.filter(function(source) {
28484             return source.intersects(extent);
28485         });
28486     };
28487
28488     background.dimensions = function(_) {
28489         baseLayer.dimensions(_);
28490
28491         overlayLayers.forEach(function(layer) {
28492             layer.dimensions(_);
28493         });
28494     };
28495
28496     background.baseLayerSource = function(d) {
28497         if (!arguments.length) return baseLayer.source();
28498
28499         baseLayer.source(d);
28500         dispatch.change();
28501         background.updateImagery();
28502
28503         return background;
28504     };
28505
28506     background.bing = function() {
28507         background.baseLayerSource(findSource('Bing'));
28508     };
28509
28510     background.showsLayer = function(d) {
28511         return d === baseLayer.source() ||
28512             (d.id === 'custom' && baseLayer.source().id === 'custom') ||
28513             overlayLayers.some(function(l) { return l.source() === d; });
28514     };
28515
28516     background.overlayLayerSources = function() {
28517         return overlayLayers.map(function (l) { return l.source(); });
28518     };
28519
28520     background.toggleOverlayLayer = function(d) {
28521         var layer;
28522
28523         for (var i = 0; i < overlayLayers.length; i++) {
28524             layer = overlayLayers[i];
28525             if (layer.source() === d) {
28526                 overlayLayers.splice(i, 1);
28527                 dispatch.change();
28528                 background.updateImagery();
28529                 return;
28530             }
28531         }
28532
28533         layer = iD.TileLayer()
28534             .source(d)
28535             .projection(context.projection)
28536             .dimensions(baseLayer.dimensions());
28537
28538         overlayLayers.push(layer);
28539         dispatch.change();
28540         background.updateImagery();
28541     };
28542
28543     background.nudge = function(d, zoom) {
28544         baseLayer.source().nudge(d, zoom);
28545         dispatch.change();
28546         return background;
28547     };
28548
28549     background.offset = function(d) {
28550         if (!arguments.length) return baseLayer.source().offset();
28551         baseLayer.source().offset(d);
28552         dispatch.change();
28553         return background;
28554     };
28555
28556     background.load = function(imagery) {
28557         function parseMap(qmap) {
28558             if (!qmap) return false;
28559             var args = qmap.split('/').map(Number);
28560             if (args.length < 3 || args.some(isNaN)) return false;
28561             return iD.geo.Extent([args[1], args[2]]);
28562         }
28563
28564         var q = iD.util.stringQs(location.hash.substring(1)),
28565             chosen = q.background || q.layer,
28566             extent = parseMap(q.map),
28567             best;
28568
28569         backgroundSources = imagery.map(function(source) {
28570             if (source.type === 'bing') {
28571                 return iD.BackgroundSource.Bing(source, dispatch);
28572             } else {
28573                 return iD.BackgroundSource(source);
28574             }
28575         });
28576
28577         backgroundSources.unshift(iD.BackgroundSource.None());
28578
28579         if (!chosen && extent) {
28580             best = _.find(this.sources(extent), function(s) { return s.best(); });
28581         }
28582
28583         if (chosen && chosen.indexOf('custom:') === 0) {
28584             background.baseLayerSource(iD.BackgroundSource.Custom(chosen.replace(/^custom:/, '')));
28585         } else {
28586             background.baseLayerSource(findSource(chosen) || best || findSource('Bing') || backgroundSources[1] || backgroundSources[0]);
28587         }
28588
28589         var locator = _.find(backgroundSources, function(d) {
28590             return d.overlay && d.default;
28591         });
28592
28593         if (locator) {
28594             background.toggleOverlayLayer(locator);
28595         }
28596
28597         var overlays = (q.overlays || '').split(',');
28598         overlays.forEach(function(overlay) {
28599             overlay = findSource(overlay);
28600             if (overlay) background.toggleOverlayLayer(overlay);
28601         });
28602
28603         if (q.gpx) {
28604             var gpx = context.layers().layer('gpx');
28605             if (gpx) { gpx.url(q.gpx); }
28606         }
28607     };
28608
28609     return d3.rebind(background, dispatch, 'on');
28610 };
28611 iD.BackgroundSource = function(data) {
28612     var source = _.clone(data),
28613         offset = [0, 0],
28614         name = source.name,
28615         best = !!source.best;
28616
28617     source.scaleExtent = data.scaleExtent || [0, 20];
28618     source.overzoom = data.overzoom !== false;
28619
28620     source.offset = function(_) {
28621         if (!arguments.length) return offset;
28622         offset = _;
28623         return source;
28624     };
28625
28626     source.nudge = function(_, zoomlevel) {
28627         offset[0] += _[0] / Math.pow(2, zoomlevel);
28628         offset[1] += _[1] / Math.pow(2, zoomlevel);
28629         return source;
28630     };
28631
28632     source.name = function() {
28633         return name;
28634     };
28635
28636     source.best = function() {
28637         return best;
28638     };
28639
28640     source.imageryUsed = function() {
28641         return source.id || name;
28642     };
28643
28644     source.url = function(coord) {
28645         return data.template
28646             .replace('{x}', coord[0])
28647             .replace('{y}', coord[1])
28648             // TMS-flipped y coordinate
28649             .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
28650             .replace(/\{z(oom)?\}/, coord[2])
28651             .replace(/\{switch:([^}]+)\}/, function(s, r) {
28652                 var subdomains = r.split(',');
28653                 return subdomains[(coord[0] + coord[1]) % subdomains.length];
28654             })
28655             .replace('{u}', function() {
28656                 var u = '';
28657                 for (var zoom = coord[2]; zoom > 0; zoom--) {
28658                     var b = 0;
28659                     var mask = 1 << (zoom - 1);
28660                     if ((coord[0] & mask) !== 0) b++;
28661                     if ((coord[1] & mask) !== 0) b += 2;
28662                     u += b.toString();
28663                 }
28664                 return u;
28665             });
28666     };
28667
28668     source.intersects = function(extent) {
28669         extent = extent.polygon();
28670         return !data.polygon || data.polygon.some(function(polygon) {
28671             return iD.geo.polygonIntersectsPolygon(polygon, extent, true);
28672         });
28673     };
28674
28675     source.validZoom = function(z) {
28676         return source.scaleExtent[0] <= z &&
28677             (source.overzoom || source.scaleExtent[1] > z);
28678     };
28679
28680     source.isLocatorOverlay = function() {
28681         return name === 'Locator Overlay';
28682     };
28683
28684     source.copyrightNotices = function() {};
28685
28686     return source;
28687 };
28688
28689 iD.BackgroundSource.Bing = function(data, dispatch) {
28690     // http://msdn.microsoft.com/en-us/library/ff701716.aspx
28691     // http://msdn.microsoft.com/en-us/library/ff701701.aspx
28692
28693     data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
28694
28695     var bing = iD.BackgroundSource(data),
28696         key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU', // Same as P2 and JOSM
28697         url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' +
28698             key + '&jsonp={callback}',
28699         providers = [];
28700
28701     d3.jsonp(url, function(json) {
28702         providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
28703             return {
28704                 attribution: provider.attribution,
28705                 areas: provider.coverageAreas.map(function(area) {
28706                     return {
28707                         zoom: [area.zoomMin, area.zoomMax],
28708                         extent: iD.geo.Extent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
28709                     };
28710                 })
28711             };
28712         });
28713         dispatch.change();
28714     });
28715
28716     bing.copyrightNotices = function(zoom, extent) {
28717         zoom = Math.min(zoom, 21);
28718         return providers.filter(function(provider) {
28719             return _.any(provider.areas, function(area) {
28720                 return extent.intersects(area.extent) &&
28721                     area.zoom[0] <= zoom &&
28722                     area.zoom[1] >= zoom;
28723             });
28724         }).map(function(provider) {
28725             return provider.attribution;
28726         }).join(', ');
28727     };
28728
28729     bing.logo = 'bing_maps.png';
28730     bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
28731
28732     return bing;
28733 };
28734
28735 iD.BackgroundSource.None = function() {
28736     var source = iD.BackgroundSource({id: 'none', template: ''});
28737
28738     source.name = function() {
28739         return t('background.none');
28740     };
28741
28742     source.imageryUsed = function() {
28743         return 'None';
28744     };
28745
28746     return source;
28747 };
28748
28749 iD.BackgroundSource.Custom = function(template) {
28750     var source = iD.BackgroundSource({id: 'custom', template: template});
28751
28752     source.name = function() {
28753         return t('background.custom');
28754     };
28755
28756     source.imageryUsed = function() {
28757         return 'Custom (' + template + ')';
28758     };
28759
28760     return source;
28761 };
28762 iD.Features = function(context) {
28763     var traffic_roads = {
28764         'motorway': true,
28765         'motorway_link': true,
28766         'trunk': true,
28767         'trunk_link': true,
28768         'primary': true,
28769         'primary_link': true,
28770         'secondary': true,
28771         'secondary_link': true,
28772         'tertiary': true,
28773         'tertiary_link': true,
28774         'residential': true,
28775         'unclassified': true,
28776         'living_street': true
28777     };
28778
28779     var service_roads = {
28780         'service': true,
28781         'road': true,
28782         'track': true
28783     };
28784
28785     var paths = {
28786         'path': true,
28787         'footway': true,
28788         'cycleway': true,
28789         'bridleway': true,
28790         'steps': true,
28791         'pedestrian': true,
28792         'corridor': true
28793     };
28794
28795     var past_futures = {
28796         'proposed': true,
28797         'construction': true,
28798         'abandoned': true,
28799         'dismantled': true,
28800         'disused': true,
28801         'razed': true,
28802         'demolished': true,
28803         'obliterated': true
28804     };
28805
28806     var dispatch = d3.dispatch('change', 'redraw'),
28807         _cullFactor = 1,
28808         _cache = {},
28809         _features = {},
28810         _stats = {},
28811         _keys = [],
28812         _hidden = [];
28813
28814     function update() {
28815         _hidden = features.hidden();
28816         dispatch.change();
28817         dispatch.redraw();
28818     }
28819
28820     function defineFeature(k, filter, max) {
28821         _keys.push(k);
28822         _features[k] = {
28823             filter: filter,
28824             enabled: true,   // whether the user wants it enabled..
28825             count: 0,
28826             currentMax: (max || Infinity),
28827             defaultMax: (max || Infinity),
28828             enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
28829             disable: function() { this.enabled = false; this.currentMax = 0; },
28830             hidden: function() { return !context.editable() || this.count > this.currentMax * _cullFactor; },
28831             autoHidden: function() { return this.hidden() && this.currentMax > 0; }
28832         };
28833     }
28834
28835
28836     defineFeature('points', function isPoint(entity, resolver, geometry) {
28837         return geometry === 'point';
28838     }, 200);
28839
28840     defineFeature('traffic_roads', function isTrafficRoad(entity) {
28841         return traffic_roads[entity.tags.highway];
28842     });
28843
28844     defineFeature('service_roads', function isServiceRoad(entity) {
28845         return service_roads[entity.tags.highway];
28846     });
28847
28848     defineFeature('paths', function isPath(entity) {
28849         return paths[entity.tags.highway];
28850     });
28851
28852     defineFeature('buildings', function isBuilding(entity) {
28853         return (
28854             !!entity.tags['building:part'] ||
28855             (!!entity.tags.building && entity.tags.building !== 'no') ||
28856             entity.tags.amenity === 'shelter' ||
28857             entity.tags.parking === 'multi-storey' ||
28858             entity.tags.parking === 'sheds' ||
28859             entity.tags.parking === 'carports' ||
28860             entity.tags.parking === 'garage_boxes'
28861         );
28862     }, 250);
28863
28864     defineFeature('landuse', function isLanduse(entity, resolver, geometry) {
28865         return geometry === 'area' &&
28866             !_features.buildings.filter(entity) &&
28867             !_features.water.filter(entity);
28868     });
28869
28870     defineFeature('boundaries', function isBoundary(entity) {
28871         return !!entity.tags.boundary;
28872     });
28873
28874     defineFeature('water', function isWater(entity) {
28875         return (
28876             !!entity.tags.waterway ||
28877             entity.tags.natural === 'water' ||
28878             entity.tags.natural === 'coastline' ||
28879             entity.tags.natural === 'bay' ||
28880             entity.tags.landuse === 'pond' ||
28881             entity.tags.landuse === 'basin' ||
28882             entity.tags.landuse === 'reservoir' ||
28883             entity.tags.landuse === 'salt_pond'
28884         );
28885     });
28886
28887     defineFeature('rail', function isRail(entity) {
28888         return (
28889             !!entity.tags.railway ||
28890             entity.tags.landuse === 'railway'
28891         ) && !(
28892             traffic_roads[entity.tags.highway] ||
28893             service_roads[entity.tags.highway] ||
28894             paths[entity.tags.highway]
28895         );
28896     });
28897
28898     defineFeature('power', function isPower(entity) {
28899         return !!entity.tags.power;
28900     });
28901
28902     // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
28903     defineFeature('past_future', function isPastFuture(entity) {
28904         if (
28905             traffic_roads[entity.tags.highway] ||
28906             service_roads[entity.tags.highway] ||
28907             paths[entity.tags.highway]
28908         ) { return false; }
28909
28910         var strings = Object.keys(entity.tags);
28911
28912         for (var i = 0; i < strings.length; i++) {
28913             var s = strings[i];
28914             if (past_futures[s] || past_futures[entity.tags[s]]) { return true; }
28915         }
28916         return false;
28917     });
28918
28919     // Lines or areas that don't match another feature filter.
28920     // IMPORTANT: The 'others' feature must be the last one defined,
28921     //   so that code in getMatches can skip this test if `hasMatch = true`
28922     defineFeature('others', function isOther(entity, resolver, geometry) {
28923         return (geometry === 'line' || geometry === 'area');
28924     });
28925
28926
28927     function features() {}
28928
28929     features.features = function() {
28930         return _features;
28931     };
28932
28933     features.keys = function() {
28934         return _keys;
28935     };
28936
28937     features.enabled = function(k) {
28938         if (!arguments.length) {
28939             return _.filter(_keys, function(k) { return _features[k].enabled; });
28940         }
28941         return _features[k] && _features[k].enabled;
28942     };
28943
28944     features.disabled = function(k) {
28945         if (!arguments.length) {
28946             return _.reject(_keys, function(k) { return _features[k].enabled; });
28947         }
28948         return _features[k] && !_features[k].enabled;
28949     };
28950
28951     features.hidden = function(k) {
28952         if (!arguments.length) {
28953             return _.filter(_keys, function(k) { return _features[k].hidden(); });
28954         }
28955         return _features[k] && _features[k].hidden();
28956     };
28957
28958     features.autoHidden = function(k) {
28959         if (!arguments.length) {
28960             return _.filter(_keys, function(k) { return _features[k].autoHidden(); });
28961         }
28962         return _features[k] && _features[k].autoHidden();
28963     };
28964
28965     features.enable = function(k) {
28966         if (_features[k] && !_features[k].enabled) {
28967             _features[k].enable();
28968             update();
28969         }
28970     };
28971
28972     features.disable = function(k) {
28973         if (_features[k] && _features[k].enabled) {
28974             _features[k].disable();
28975             update();
28976         }
28977     };
28978
28979     features.toggle = function(k) {
28980         if (_features[k]) {
28981             (function(f) { return f.enabled ? f.disable() : f.enable(); }(_features[k]));
28982             update();
28983         }
28984     };
28985
28986     features.resetStats = function() {
28987         _.each(_features, function(f) { f.count = 0; });
28988         dispatch.change();
28989     };
28990
28991     features.gatherStats = function(d, resolver, dimensions) {
28992         var needsRedraw = false,
28993             type = _.groupBy(d, function(ent) { return ent.type; }),
28994             entities = [].concat(type.relation || [], type.way || [], type.node || []),
28995             currHidden, geometry, matches;
28996
28997         _.each(_features, function(f) { f.count = 0; });
28998
28999         // adjust the threshold for point/building culling based on viewport size..
29000         // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
29001         _cullFactor = dimensions[0] * dimensions[1] / 1000000;
29002
29003         for (var i = 0; i < entities.length; i++) {
29004             geometry = entities[i].geometry(resolver);
29005             if (!(geometry === 'vertex' || geometry === 'relation')) {
29006                 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
29007                 for (var j = 0; j < matches.length; j++) {
29008                     _features[matches[j]].count++;
29009                 }
29010             }
29011         }
29012
29013         currHidden = features.hidden();
29014         if (currHidden !== _hidden) {
29015             _hidden = currHidden;
29016             needsRedraw = true;
29017             dispatch.change();
29018         }
29019
29020         return needsRedraw;
29021     };
29022
29023     features.stats = function() {
29024         _.each(_keys, function(k) { _stats[k] = _features[k].count; });
29025         return _stats;
29026     };
29027
29028     features.clear = function(d) {
29029         for (var i = 0; i < d.length; i++) {
29030             features.clearEntity(d[i]);
29031         }
29032     };
29033
29034     features.clearEntity = function(entity) {
29035         delete _cache[iD.Entity.key(entity)];
29036     };
29037
29038     features.reset = function() {
29039         _cache = {};
29040     };
29041
29042     features.getMatches = function(entity, resolver, geometry) {
29043         if (geometry === 'vertex' || geometry === 'relation') return {};
29044
29045         var ent = iD.Entity.key(entity);
29046         if (!_cache[ent]) {
29047             _cache[ent] = {};
29048         }
29049
29050         if (!_cache[ent].matches) {
29051             var matches = {},
29052                 hasMatch = false;
29053
29054             for (var i = 0; i < _keys.length; i++) {
29055                 if (_keys[i] === 'others') {
29056                     if (hasMatch) continue;
29057
29058                     // Multipolygon members:
29059                     // If an entity...
29060                     //   1. is a way that hasn't matched other "interesting" feature rules,
29061                     //   2. and it belongs to a single parent multipolygon relation
29062                     // ...then match whatever feature rules the parent multipolygon has matched.
29063                     // see #2548, #2887
29064                     //
29065                     // IMPORTANT:
29066                     // For this to work, getMatches must be called on relations before ways.
29067                     //
29068                     if (entity.type === 'way') {
29069                         var parents = features.getParents(entity, resolver, geometry);
29070                         if (parents.length === 1 && parents[0].isMultipolygon()) {
29071                             var pkey = iD.Entity.key(parents[0]);
29072                             if (_cache[pkey] && _cache[pkey].matches) {
29073                                 matches = _.clone(_cache[pkey].matches);
29074                                 continue;
29075                             }
29076                         }
29077                     }
29078                 }
29079
29080                 if (_features[_keys[i]].filter(entity, resolver, geometry)) {
29081                     matches[_keys[i]] = hasMatch = true;
29082                 }
29083             }
29084             _cache[ent].matches = matches;
29085         }
29086
29087         return _cache[ent].matches;
29088     };
29089
29090     features.getParents = function(entity, resolver, geometry) {
29091         if (geometry === 'point') return [];
29092
29093         var ent = iD.Entity.key(entity);
29094         if (!_cache[ent]) {
29095             _cache[ent] = {};
29096         }
29097
29098         if (!_cache[ent].parents) {
29099             var parents = [];
29100             if (geometry === 'vertex') {
29101                 parents = resolver.parentWays(entity);
29102             } else {   // 'line', 'area', 'relation'
29103                 parents = resolver.parentRelations(entity);
29104             }
29105             _cache[ent].parents = parents;
29106         }
29107         return _cache[ent].parents;
29108     };
29109
29110     features.isHiddenFeature = function(entity, resolver, geometry) {
29111         if (!_hidden.length) return false;
29112         if (!entity.version) return false;
29113
29114         var matches = features.getMatches(entity, resolver, geometry);
29115
29116         for (var i = 0; i < _hidden.length; i++) {
29117             if (matches[_hidden[i]]) return true;
29118         }
29119         return false;
29120     };
29121
29122     features.isHiddenChild = function(entity, resolver, geometry) {
29123         if (!_hidden.length) return false;
29124         if (!entity.version || geometry === 'point') return false;
29125
29126         var parents = features.getParents(entity, resolver, geometry);
29127         if (!parents.length) return false;
29128
29129         for (var i = 0; i < parents.length; i++) {
29130             if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
29131                 return false;
29132             }
29133         }
29134         return true;
29135     };
29136
29137     features.hasHiddenConnections = function(entity, resolver) {
29138         if (!_hidden.length) return false;
29139         var childNodes, connections;
29140
29141         if (entity.type === 'midpoint') {
29142             childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
29143             connections = [];
29144         } else {
29145             childNodes = entity.nodes ? resolver.childNodes(entity) : [];
29146             connections = features.getParents(entity, resolver, entity.geometry(resolver));
29147         }
29148
29149         // gather ways connected to child nodes..
29150         connections = _.reduce(childNodes, function(result, e) {
29151             return resolver.isShared(e) ? _.union(result, resolver.parentWays(e)) : result;
29152         }, connections);
29153
29154         return connections.length ? _.any(connections, function(e) {
29155             return features.isHidden(e, resolver, e.geometry(resolver));
29156         }) : false;
29157     };
29158
29159     features.isHidden = function(entity, resolver, geometry) {
29160         if (!_hidden.length) return false;
29161         if (!entity.version) return false;
29162
29163         var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
29164         return fn(entity, resolver, geometry);
29165     };
29166
29167     features.filter = function(d, resolver) {
29168         if (!_hidden.length) return d;
29169
29170         var result = [];
29171         for (var i = 0; i < d.length; i++) {
29172             var entity = d[i];
29173             if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
29174                 result.push(entity);
29175             }
29176         }
29177         return result;
29178     };
29179
29180     return d3.rebind(features, dispatch, 'on');
29181 };
29182 iD.Map = function(context) {
29183     var dimensions = [1, 1],
29184         dispatch = d3.dispatch('move', 'drawn'),
29185         projection = context.projection,
29186         zoom = d3.behavior.zoom()
29187             .translate(projection.translate())
29188             .scale(projection.scale() * 2 * Math.PI)
29189             .scaleExtent([1024, 256 * Math.pow(2, 24)])
29190             .on('zoom', zoomPan),
29191         dblclickEnabled = true,
29192         redrawEnabled = true,
29193         transformStart,
29194         transformed = false,
29195         minzoom = 0,
29196         drawLayers = iD.svg.Layers(projection, context),
29197         drawPoints = iD.svg.Points(projection, context),
29198         drawVertices = iD.svg.Vertices(projection, context),
29199         drawLines = iD.svg.Lines(projection),
29200         drawAreas = iD.svg.Areas(projection),
29201         drawMidpoints = iD.svg.Midpoints(projection, context),
29202         drawLabels = iD.svg.Labels(projection, context),
29203         supersurface,
29204         wrapper,
29205         surface,
29206         mouse,
29207         mousemove;
29208
29209     function map(selection) {
29210         context.history()
29211             .on('change.map', redraw);
29212         context.background()
29213             .on('change.map', redraw);
29214         context.features()
29215             .on('redraw.map', redraw);
29216         drawLayers
29217             .on('change.map', function() {
29218                 context.background().updateImagery();
29219                 redraw();
29220             });
29221
29222         selection
29223             .on('dblclick.map', dblClick)
29224             .call(zoom);
29225
29226         supersurface = selection.append('div')
29227             .attr('id', 'supersurface')
29228             .call(iD.util.setTransform, 0, 0);
29229
29230         // Need a wrapper div because Opera can't cope with an absolutely positioned
29231         // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
29232         wrapper = supersurface
29233             .append('div')
29234             .attr('class', 'layer layer-data');
29235
29236         map.surface = surface = wrapper
29237             .call(drawLayers)
29238             .selectAll('.surface')
29239             .attr('id', 'surface');
29240
29241         surface
29242             .on('mousedown.zoom', function() {
29243                 if (d3.event.button === 2) {
29244                     d3.event.stopPropagation();
29245                 }
29246             }, true)
29247             .on('mouseup.zoom', function() {
29248                 if (resetTransform()) redraw();
29249             })
29250             .on('mousemove.map', function() {
29251                 mousemove = d3.event;
29252             })
29253             .on('mouseover.vertices', function() {
29254                 if (map.editable() && !transformed) {
29255                     var hover = d3.event.target.__data__;
29256                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29257                     dispatch.drawn({full: false});
29258                 }
29259             })
29260             .on('mouseout.vertices', function() {
29261                 if (map.editable() && !transformed) {
29262                     var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__;
29263                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29264                     dispatch.drawn({full: false});
29265                 }
29266             });
29267
29268
29269         supersurface
29270             .call(context.background());
29271
29272
29273         context.on('enter.map', function() {
29274             if (map.editable() && !transformed) {
29275                 var all = context.intersects(map.extent()),
29276                     filter = d3.functor(true),
29277                     graph = context.graph();
29278
29279                 all = context.features().filter(all, graph);
29280                 surface
29281                     .call(drawVertices, graph, all, filter, map.extent(), map.zoom())
29282                     .call(drawMidpoints, graph, all, filter, map.trimmedExtent());
29283                 dispatch.drawn({full: false});
29284             }
29285         });
29286
29287         map.dimensions(selection.dimensions());
29288
29289         drawLabels.supersurface(supersurface);
29290     }
29291
29292     function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }
29293
29294     function drawVector(difference, extent) {
29295         var graph = context.graph(),
29296             features = context.features(),
29297             all = context.intersects(map.extent()),
29298             data, filter;
29299
29300         if (difference) {
29301             var complete = difference.complete(map.extent());
29302             data = _.compact(_.values(complete));
29303             filter = function(d) { return d.id in complete; };
29304             features.clear(data);
29305
29306         } else {
29307             // force a full redraw if gatherStats detects that a feature
29308             // should be auto-hidden (e.g. points or buildings)..
29309             if (features.gatherStats(all, graph, dimensions)) {
29310                 extent = undefined;
29311             }
29312
29313             if (extent) {
29314                 data = context.intersects(map.extent().intersection(extent));
29315                 var set = d3.set(_.pluck(data, 'id'));
29316                 filter = function(d) { return set.has(d.id); };
29317
29318             } else {
29319                 data = all;
29320                 filter = d3.functor(true);
29321             }
29322         }
29323
29324         data = features.filter(data, graph);
29325
29326         surface
29327             .call(drawVertices, graph, data, filter, map.extent(), map.zoom())
29328             .call(drawLines, graph, data, filter)
29329             .call(drawAreas, graph, data, filter)
29330             .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
29331             .call(drawLabels, graph, data, filter, dimensions, !difference && !extent)
29332             .call(drawPoints, graph, data, filter);
29333
29334         dispatch.drawn({full: true});
29335     }
29336
29337     function editOff() {
29338         context.features().resetStats();
29339         surface.selectAll('.layer-osm *').remove();
29340         dispatch.drawn({full: true});
29341     }
29342
29343     function dblClick() {
29344         if (!dblclickEnabled) {
29345             d3.event.preventDefault();
29346             d3.event.stopImmediatePropagation();
29347         }
29348     }
29349
29350     function zoomPan() {
29351         if (Math.log(d3.event.scale) / Math.LN2 - 8 < minzoom) {
29352             surface.interrupt();
29353             iD.ui.flash(context.container())
29354                 .select('.content')
29355                 .text(t('cannot_zoom'));
29356             setZoom(context.minEditableZoom(), true);
29357             queueRedraw();
29358             dispatch.move(map);
29359             return;
29360         }
29361
29362         projection
29363             .translate(d3.event.translate)
29364             .scale(d3.event.scale / (2 * Math.PI));
29365
29366         var scale = d3.event.scale / transformStart[0],
29367             tX = (d3.event.translate[0] / scale - transformStart[1][0]) * scale,
29368             tY = (d3.event.translate[1] / scale - transformStart[1][1]) * scale;
29369
29370         transformed = true;
29371         iD.util.setTransform(supersurface, tX, tY, scale);
29372         queueRedraw();
29373
29374         dispatch.move(map);
29375     }
29376
29377     function resetTransform() {
29378         if (!transformed) return false;
29379
29380         surface.selectAll('.radial-menu').interrupt().remove();
29381         iD.util.setTransform(supersurface, 0, 0);
29382         transformed = false;
29383         return true;
29384     }
29385
29386     function redraw(difference, extent) {
29387         if (!surface || !redrawEnabled) return;
29388
29389         clearTimeout(timeoutId);
29390
29391         // If we are in the middle of a zoom/pan, we can't do differenced redraws.
29392         // It would result in artifacts where differenced entities are redrawn with
29393         // one transform and unchanged entities with another.
29394         if (resetTransform()) {
29395             difference = extent = undefined;
29396         }
29397
29398         var zoom = String(~~map.zoom());
29399         if (surface.attr('data-zoom') !== zoom) {
29400             surface.attr('data-zoom', zoom)
29401                 .classed('low-zoom', zoom <= 16);
29402         }
29403
29404         if (!difference) {
29405             supersurface.call(context.background());
29406         }
29407
29408         // OSM
29409         if (map.editable()) {
29410             context.loadTiles(projection, dimensions);
29411             drawVector(difference, extent);
29412         } else {
29413             editOff();
29414         }
29415
29416         wrapper
29417             .call(drawLayers);
29418
29419         transformStart = [
29420             projection.scale() * 2 * Math.PI,
29421             projection.translate().slice()];
29422
29423         return map;
29424     }
29425
29426     var timeoutId;
29427     function queueRedraw() {
29428         clearTimeout(timeoutId);
29429         timeoutId = setTimeout(function() { redraw(); }, 300);
29430     }
29431
29432     function pointLocation(p) {
29433         var translate = projection.translate(),
29434             scale = projection.scale() * 2 * Math.PI;
29435         return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
29436     }
29437
29438     function locationPoint(l) {
29439         var translate = projection.translate(),
29440             scale = projection.scale() * 2 * Math.PI;
29441         return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
29442     }
29443
29444     map.mouse = function() {
29445         var e = mousemove || d3.event, s;
29446         while ((s = e.sourceEvent)) e = s;
29447         return mouse(e);
29448     };
29449
29450     map.mouseCoordinates = function() {
29451         return projection.invert(map.mouse());
29452     };
29453
29454     map.dblclickEnable = function(_) {
29455         if (!arguments.length) return dblclickEnabled;
29456         dblclickEnabled = _;
29457         return map;
29458     };
29459
29460     map.redrawEnable = function(_) {
29461         if (!arguments.length) return redrawEnabled;
29462         redrawEnabled = _;
29463         return map;
29464     };
29465
29466     function interpolateZoom(_) {
29467         var k = projection.scale(),
29468             t = projection.translate();
29469
29470         surface.node().__chart__ = {
29471             x: t[0],
29472             y: t[1],
29473             k: k * 2 * Math.PI
29474         };
29475
29476         setZoom(_);
29477         projection.scale(k).translate(t);  // undo setZoom projection changes
29478
29479         zoom.event(surface.transition());
29480     }
29481
29482     function setZoom(_, force) {
29483         if (_ === map.zoom() && !force)
29484             return false;
29485         var scale = 256 * Math.pow(2, _),
29486             center = pxCenter(),
29487             l = pointLocation(center);
29488         scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
29489         projection.scale(scale / (2 * Math.PI));
29490         zoom.scale(scale);
29491         var t = projection.translate();
29492         l = locationPoint(l);
29493         t[0] += center[0] - l[0];
29494         t[1] += center[1] - l[1];
29495         projection.translate(t);
29496         zoom.translate(projection.translate());
29497         return true;
29498     }
29499
29500     function setCenter(_) {
29501         var c = map.center();
29502         if (_[0] === c[0] && _[1] === c[1])
29503             return false;
29504         var t = projection.translate(),
29505             pxC = pxCenter(),
29506             ll = projection(_);
29507         projection.translate([
29508             t[0] - ll[0] + pxC[0],
29509             t[1] - ll[1] + pxC[1]]);
29510         zoom.translate(projection.translate());
29511         return true;
29512     }
29513
29514     map.pan = function(d) {
29515         var t = projection.translate();
29516         t[0] += d[0];
29517         t[1] += d[1];
29518         projection.translate(t);
29519         zoom.translate(projection.translate());
29520         dispatch.move(map);
29521         return redraw();
29522     };
29523
29524     map.dimensions = function(_) {
29525         if (!arguments.length) return dimensions;
29526         var center = map.center();
29527         dimensions = _;
29528         drawLayers.dimensions(dimensions);
29529         context.background().dimensions(dimensions);
29530         projection.clipExtent([[0, 0], dimensions]);
29531         mouse = iD.util.fastMouse(supersurface.node());
29532         setCenter(center);
29533         return redraw();
29534     };
29535
29536     function zoomIn(integer) {
29537       interpolateZoom(~~map.zoom() + integer);
29538     }
29539
29540     function zoomOut(integer) {
29541       interpolateZoom(~~map.zoom() - integer);
29542     }
29543
29544     map.zoomIn = function() { zoomIn(1); };
29545     map.zoomInFurther = function() { zoomIn(4); };
29546
29547     map.zoomOut = function() { zoomOut(1); };
29548     map.zoomOutFurther = function() { zoomOut(4); };
29549
29550     map.center = function(loc) {
29551         if (!arguments.length) {
29552             return projection.invert(pxCenter());
29553         }
29554
29555         if (setCenter(loc)) {
29556             dispatch.move(map);
29557         }
29558
29559         return redraw();
29560     };
29561
29562     map.zoom = function(z) {
29563         if (!arguments.length) {
29564             return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
29565         }
29566
29567         if (z < minzoom) {
29568             surface.interrupt();
29569             iD.ui.flash(context.container())
29570                 .select('.content')
29571                 .text(t('cannot_zoom'));
29572             z = context.minEditableZoom();
29573         }
29574
29575         if (setZoom(z)) {
29576             dispatch.move(map);
29577         }
29578
29579         return redraw();
29580     };
29581
29582     map.zoomTo = function(entity, zoomLimits) {
29583         var extent = entity.extent(context.graph());
29584         if (!isFinite(extent.area())) return;
29585
29586         var zoom = map.trimmedExtentZoom(extent);
29587         zoomLimits = zoomLimits || [context.minEditableZoom(), 20];
29588         map.centerZoom(extent.center(), Math.min(Math.max(zoom, zoomLimits[0]), zoomLimits[1]));
29589     };
29590
29591     map.centerZoom = function(loc, z) {
29592         var centered = setCenter(loc),
29593             zoomed   = setZoom(z);
29594
29595         if (centered || zoomed) {
29596             dispatch.move(map);
29597         }
29598
29599         return redraw();
29600     };
29601
29602     map.centerEase = function(loc) {
29603         var from = map.center().slice(),
29604             t = 0,
29605             stop;
29606
29607         surface.one('mousedown.ease', function() {
29608             stop = true;
29609         });
29610
29611         d3.timer(function() {
29612             if (stop) return true;
29613             map.center(iD.geo.interp(from, loc, (t += 1) / 10));
29614             return t === 10;
29615         }, 20);
29616         return map;
29617     };
29618
29619     map.extent = function(_) {
29620         if (!arguments.length) {
29621             return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
29622                                  projection.invert([dimensions[0], 0]));
29623         } else {
29624             var extent = iD.geo.Extent(_);
29625             map.centerZoom(extent.center(), map.extentZoom(extent));
29626         }
29627     };
29628
29629     map.trimmedExtent = function(_) {
29630         if (!arguments.length) {
29631             var headerY = 60, footerY = 30, pad = 10;
29632             return new iD.geo.Extent(projection.invert([pad, dimensions[1] - footerY - pad]),
29633                     projection.invert([dimensions[0] - pad, headerY + pad]));
29634         } else {
29635             var extent = iD.geo.Extent(_);
29636             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
29637         }
29638     };
29639
29640     function calcZoom(extent, dim) {
29641         var tl = projection([extent[0][0], extent[1][1]]),
29642             br = projection([extent[1][0], extent[0][1]]);
29643
29644         // Calculate maximum zoom that fits extent
29645         var hFactor = (br[0] - tl[0]) / dim[0],
29646             vFactor = (br[1] - tl[1]) / dim[1],
29647             hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
29648             vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
29649             newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
29650
29651         return newZoom;
29652     }
29653
29654     map.extentZoom = function(_) {
29655         return calcZoom(iD.geo.Extent(_), dimensions);
29656     };
29657
29658     map.trimmedExtentZoom = function(_) {
29659         var trimY = 120, trimX = 40,
29660             trimmed = [dimensions[0] - trimX, dimensions[1] - trimY];
29661         return calcZoom(iD.geo.Extent(_), trimmed);
29662     };
29663
29664     map.editable = function() {
29665         return map.zoom() >= context.minEditableZoom();
29666     };
29667
29668     map.minzoom = function(_) {
29669         if (!arguments.length) return minzoom;
29670         minzoom = _;
29671         return map;
29672     };
29673
29674     map.layers = drawLayers;
29675
29676     return d3.rebind(map, dispatch, 'on');
29677 };
29678 iD.TileLayer = function() {
29679     var tileSize = 256,
29680         tile = d3.geo.tile(),
29681         projection,
29682         cache = {},
29683         tileOrigin,
29684         z,
29685         transformProp = iD.util.prefixCSSProperty('Transform'),
29686         source = d3.functor('');
29687
29688     function tileSizeAtZoom(d, z) {
29689         return (tileSize * Math.pow(2, z - d[2])) / tileSize;
29690     }
29691
29692     function atZoom(t, distance) {
29693         var power = Math.pow(2, distance);
29694         return [
29695             Math.floor(t[0] * power),
29696             Math.floor(t[1] * power),
29697             t[2] + distance];
29698     }
29699
29700     function lookUp(d) {
29701         for (var up = -1; up > -d[2]; up--) {
29702             var tile = atZoom(d, up);
29703             if (cache[source.url(tile)] !== false) {
29704                 return tile;
29705             }
29706         }
29707     }
29708
29709     function uniqueBy(a, n) {
29710         var o = [], seen = {};
29711         for (var i = 0; i < a.length; i++) {
29712             if (seen[a[i][n]] === undefined) {
29713                 o.push(a[i]);
29714                 seen[a[i][n]] = true;
29715             }
29716         }
29717         return o;
29718     }
29719
29720     function addSource(d) {
29721         d.push(source.url(d));
29722         return d;
29723     }
29724
29725     // Update tiles based on current state of `projection`.
29726     function background(selection) {
29727         tile.scale(projection.scale() * 2 * Math.PI)
29728             .translate(projection.translate());
29729
29730         tileOrigin = [
29731             projection.scale() * Math.PI - projection.translate()[0],
29732             projection.scale() * Math.PI - projection.translate()[1]];
29733
29734         z = Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.log(2) - 8, 0);
29735
29736         render(selection);
29737     }
29738
29739     // Derive the tiles onscreen, remove those offscreen and position them.
29740     // Important that this part not depend on `projection` because it's
29741     // rentered when tiles load/error (see #644).
29742     function render(selection) {
29743         var requests = [];
29744
29745         if (source.validZoom(z)) {
29746             tile().forEach(function(d) {
29747                 addSource(d);
29748                 if (d[3] === '') return;
29749                 if (typeof d[3] !== 'string') return; // Workaround for chrome crash https://github.com/openstreetmap/iD/issues/2295
29750                 requests.push(d);
29751                 if (cache[d[3]] === false && lookUp(d)) {
29752                     requests.push(addSource(lookUp(d)));
29753                 }
29754             });
29755
29756             requests = uniqueBy(requests, 3).filter(function(r) {
29757                 // don't re-request tiles which have failed in the past
29758                 return cache[r[3]] !== false;
29759             });
29760         }
29761
29762         var pixelOffset = [
29763             source.offset()[0] * Math.pow(2, z),
29764             source.offset()[1] * Math.pow(2, z)
29765         ];
29766
29767         function load(d) {
29768             cache[d[3]] = true;
29769             d3.select(this)
29770                 .on('error', null)
29771                 .on('load', null)
29772                 .classed('tile-loaded', true);
29773             render(selection);
29774         }
29775
29776         function error(d) {
29777             cache[d[3]] = false;
29778             d3.select(this)
29779                 .on('error', null)
29780                 .on('load', null)
29781                 .remove();
29782             render(selection);
29783         }
29784
29785         function imageTransform(d) {
29786             var _ts = tileSize * Math.pow(2, z - d[2]);
29787             var scale = tileSizeAtZoom(d, z);
29788             return 'translate(' +
29789                 ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0]) + 'px,' +
29790                 ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1]) + 'px)' +
29791                 'scale(' + scale + ',' + scale + ')';
29792         }
29793
29794         var image = selection
29795             .selectAll('img')
29796             .data(requests, function(d) { return d[3]; });
29797
29798         image.exit()
29799             .style(transformProp, imageTransform)
29800             .classed('tile-removing', true)
29801             .each(function() {
29802                 var tile = d3.select(this);
29803                 window.setTimeout(function() {
29804                     if (tile.classed('tile-removing')) {
29805                         tile.remove();
29806                     }
29807                 }, 300);
29808             });
29809
29810         image.enter().append('img')
29811             .attr('class', 'tile')
29812             .attr('src', function(d) { return d[3]; })
29813             .on('error', error)
29814             .on('load', load);
29815
29816         image
29817             .style(transformProp, imageTransform)
29818             .classed('tile-removing', false);
29819     }
29820
29821     background.projection = function(_) {
29822         if (!arguments.length) return projection;
29823         projection = _;
29824         return background;
29825     };
29826
29827     background.dimensions = function(_) {
29828         if (!arguments.length) return tile.size();
29829         tile.size(_);
29830         return background;
29831     };
29832
29833     background.source = function(_) {
29834         if (!arguments.length) return source;
29835         source = _;
29836         cache = {};
29837         tile.scaleExtent(source.scaleExtent);
29838         return background;
29839     };
29840
29841     return background;
29842 };
29843 iD.svg = {
29844     PointTransform: function(projection) {
29845         return function(entity) {
29846             // http://jsperf.com/short-array-join
29847             var pt = projection(entity.loc);
29848             return 'translate(' + pt[0] + ',' + pt[1] + ')';
29849         };
29850     },
29851
29852     Path: function(projection, graph, polygon) {
29853         var cache = {},
29854             clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
29855             project = projection.stream,
29856             path = d3.geo.path()
29857                 .projection({stream: function(output) { return polygon ? project(output) : project(clip(output)); }});
29858
29859         return function(entity) {
29860             if (entity.id in cache) {
29861                 return cache[entity.id];
29862             } else {
29863                 return cache[entity.id] = path(entity.asGeoJSON(graph));
29864             }
29865         };
29866     },
29867
29868     OneWaySegments: function(projection, graph, dt) {
29869         return function(entity) {
29870             var a,
29871                 b,
29872                 i = 0,
29873                 offset = dt,
29874                 segments = [],
29875                 clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
29876                 coordinates = graph.childNodes(entity).map(function(n) {
29877                     return n.loc;
29878                 });
29879
29880             if (entity.tags.oneway === '-1') coordinates.reverse();
29881
29882             d3.geo.stream({
29883                 type: 'LineString',
29884                 coordinates: coordinates
29885             }, projection.stream(clip({
29886                 lineStart: function() {},
29887                 lineEnd: function() {
29888                     a = null;
29889                 },
29890                 point: function(x, y) {
29891                     b = [x, y];
29892
29893                     if (a) {
29894                         var span = iD.geo.euclideanDistance(a, b) - offset;
29895
29896                         if (span >= 0) {
29897                             var angle = Math.atan2(b[1] - a[1], b[0] - a[0]),
29898                                 dx = dt * Math.cos(angle),
29899                                 dy = dt * Math.sin(angle),
29900                                 p = [a[0] + offset * Math.cos(angle),
29901                                      a[1] + offset * Math.sin(angle)];
29902
29903                             var segment = 'M' + a[0] + ',' + a[1] +
29904                                           'L' + p[0] + ',' + p[1];
29905
29906                             for (span -= dt; span >= 0; span -= dt) {
29907                                 p[0] += dx;
29908                                 p[1] += dy;
29909                                 segment += 'L' + p[0] + ',' + p[1];
29910                             }
29911
29912                             segment += 'L' + b[0] + ',' + b[1];
29913                             segments.push({id: entity.id, index: i, d: segment});
29914                         }
29915
29916                         offset = -span;
29917                         i++;
29918                     }
29919
29920                     a = b;
29921                 }
29922             })));
29923
29924             return segments;
29925         };
29926     },
29927
29928     RelationMemberTags: function(graph) {
29929         return function(entity) {
29930             var tags = entity.tags;
29931             graph.parentRelations(entity).forEach(function(relation) {
29932                 var type = relation.tags.type;
29933                 if (type === 'multipolygon' || type === 'boundary') {
29934                     tags = _.extend({}, relation.tags, tags);
29935                 }
29936             });
29937             return tags;
29938         };
29939     }
29940 };
29941 iD.svg.Areas = function(projection) {
29942     // Patterns only work in Firefox when set directly on element.
29943     // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
29944     var patterns = {
29945         wetland: 'wetland',
29946         beach: 'beach',
29947         scrub: 'scrub',
29948         construction: 'construction',
29949         military: 'construction',
29950         cemetery: 'cemetery',
29951         grave_yard: 'cemetery',
29952         meadow: 'meadow',
29953         farm: 'farmland',
29954         farmland: 'farmland',
29955         orchard: 'orchard'
29956     };
29957
29958     var patternKeys = ['landuse', 'natural', 'amenity'];
29959
29960     function setPattern(d) {
29961         for (var i = 0; i < patternKeys.length; i++) {
29962             if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) {
29963                 this.style.fill = this.style.stroke = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")';
29964                 return;
29965             }
29966         }
29967         this.style.fill = this.style.stroke = '';
29968     }
29969
29970     return function drawAreas(surface, graph, entities, filter) {
29971         var path = iD.svg.Path(projection, graph, true),
29972             areas = {},
29973             multipolygon;
29974
29975         for (var i = 0; i < entities.length; i++) {
29976             var entity = entities[i];
29977             if (entity.geometry(graph) !== 'area') continue;
29978
29979             multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph);
29980             if (multipolygon) {
29981                 areas[multipolygon.id] = {
29982                     entity: multipolygon.mergeTags(entity.tags),
29983                     area: Math.abs(entity.area(graph))
29984                 };
29985             } else if (!areas[entity.id]) {
29986                 areas[entity.id] = {
29987                     entity: entity,
29988                     area: Math.abs(entity.area(graph))
29989                 };
29990             }
29991         }
29992
29993         areas = d3.values(areas).filter(function hasPath(a) { return path(a.entity); });
29994         areas.sort(function areaSort(a, b) { return b.area - a.area; });
29995         areas = _.pluck(areas, 'entity');
29996
29997         var strokes = areas.filter(function(area) {
29998             return area.type === 'way';
29999         });
30000
30001         var data = {
30002             clip: areas,
30003             shadow: strokes,
30004             stroke: strokes,
30005             fill: areas
30006         };
30007
30008         var clipPaths = surface.selectAll('defs').selectAll('.clipPath')
30009            .filter(filter)
30010            .data(data.clip, iD.Entity.key);
30011
30012         clipPaths.enter()
30013            .append('clipPath')
30014            .attr('class', 'clipPath')
30015            .attr('id', function(entity) { return entity.id + '-clippath'; })
30016            .append('path');
30017
30018         clipPaths.selectAll('path')
30019            .attr('d', path);
30020
30021         clipPaths.exit()
30022            .remove();
30023
30024         var areagroup = surface
30025             .selectAll('.layer-areas')
30026             .selectAll('g.areagroup')
30027             .data(['fill', 'shadow', 'stroke']);
30028
30029         areagroup.enter()
30030             .append('g')
30031             .attr('class', function(d) { return 'layer areagroup area-' + d; });
30032
30033         var paths = areagroup
30034             .selectAll('path')
30035             .filter(filter)
30036             .data(function(layer) { return data[layer]; }, iD.Entity.key);
30037
30038         // Remove exiting areas first, so they aren't included in the `fills`
30039         // array used for sorting below (https://github.com/openstreetmap/iD/issues/1903).
30040         paths.exit()
30041             .remove();
30042
30043         var fills = surface.selectAll('.area-fill path.area')[0];
30044
30045         var bisect = d3.bisector(function(node) {
30046             return -node.__data__.area(graph);
30047         }).left;
30048
30049         function sortedByArea(entity) {
30050             if (this.__data__ === 'fill') {
30051                 return fills[bisect(fills, -entity.area(graph))];
30052             }
30053         }
30054
30055         paths.enter()
30056             .insert('path', sortedByArea)
30057             .each(function(entity) {
30058                 var layer = this.parentNode.__data__;
30059
30060                 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
30061
30062                 if (layer === 'fill') {
30063                     this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)');
30064                     setPattern.apply(this, arguments);
30065                 }
30066             })
30067             .call(iD.svg.TagClasses());
30068
30069         paths
30070             .attr('d', path);
30071     };
30072 };
30073 /*
30074     A standalone SVG element that contains only a `defs` sub-element. To be
30075     used once globally, since defs IDs must be unique within a document.
30076 */
30077 iD.svg.Defs = function(context) {
30078
30079     function SVGSpriteDefinition(id, href) {
30080         return function(defs) {
30081             d3.xml(href, 'image/svg+xml', function(err, svg) {
30082                 if (err) return;
30083                 defs.node().appendChild(
30084                     d3.select(svg.documentElement).attr('id', id).node()
30085                 );
30086             });
30087         };
30088     }
30089
30090     return function drawDefs(selection) {
30091         var defs = selection.append('defs');
30092
30093         // marker
30094         defs.append('marker')
30095             .attr({
30096                 id: 'oneway-marker',
30097                 viewBox: '0 0 10 10',
30098                 refY: 2.5,
30099                 refX: 5,
30100                 markerWidth: 2,
30101                 markerHeight: 2,
30102                 markerUnits: 'strokeWidth',
30103                 orient: 'auto'
30104             })
30105             .append('path')
30106             .attr('class', 'oneway')
30107             .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')
30108             .attr('stroke', 'none')
30109             .attr('fill', '#000')
30110             .attr('opacity', '0.5');
30111
30112         // patterns
30113         var patterns = defs.selectAll('pattern')
30114             .data([
30115                 // pattern name, pattern image name
30116                 ['wetland', 'wetland'],
30117                 ['construction', 'construction'],
30118                 ['cemetery', 'cemetery'],
30119                 ['orchard', 'orchard'],
30120                 ['farmland', 'farmland'],
30121                 ['beach', 'dots'],
30122                 ['scrub', 'dots'],
30123                 ['meadow', 'dots']
30124             ])
30125             .enter()
30126             .append('pattern')
30127             .attr({
30128                 id: function (d) {
30129                     return 'pattern-' + d[0];
30130                 },
30131                 width: 32,
30132                 height: 32,
30133                 patternUnits: 'userSpaceOnUse'
30134             });
30135
30136         patterns.append('rect')
30137             .attr({
30138                 x: 0,
30139                 y: 0,
30140                 width: 32,
30141                 height: 32,
30142                 'class': function (d) {
30143                     return 'pattern-color-' + d[0];
30144                 }
30145             });
30146
30147         patterns.append('image')
30148             .attr({
30149                 x: 0,
30150                 y: 0,
30151                 width: 32,
30152                 height: 32
30153             })
30154             .attr('xlink:href', function (d) {
30155                 return context.imagePath('pattern/' + d[1] + '.png');
30156             });
30157
30158         // clip paths
30159         defs.selectAll()
30160             .data([12, 18, 20, 32, 45])
30161             .enter().append('clipPath')
30162             .attr('id', function (d) {
30163                 return 'clip-square-' + d;
30164             })
30165             .append('rect')
30166             .attr('x', 0)
30167             .attr('y', 0)
30168             .attr('width', function (d) {
30169                 return d;
30170             })
30171             .attr('height', function (d) {
30172                 return d;
30173             });
30174
30175         defs.call(SVGSpriteDefinition(
30176             'iD-sprite',
30177             context.imagePath('iD-sprite.svg')));
30178
30179         defs.call(SVGSpriteDefinition(
30180             'maki-sprite',
30181             context.imagePath('maki-sprite.svg')));
30182     };
30183 };
30184 iD.svg.Gpx = function(projection, context, dispatch) {
30185     var showLabels = true,
30186         layer;
30187
30188     function init() {
30189         if (iD.svg.Gpx.initialized) return;  // run once
30190
30191         iD.svg.Gpx.geojson = {};
30192         iD.svg.Gpx.enabled = true;
30193
30194         function over() {
30195             d3.event.stopPropagation();
30196             d3.event.preventDefault();
30197             d3.event.dataTransfer.dropEffect = 'copy';
30198         }
30199
30200         d3.select('body')
30201             .attr('dropzone', 'copy')
30202             .on('drop.localgpx', function() {
30203                 d3.event.stopPropagation();
30204                 d3.event.preventDefault();
30205                 if (!iD.detect().filedrop) return;
30206                 drawGpx.files(d3.event.dataTransfer.files);
30207             })
30208             .on('dragenter.localgpx', over)
30209             .on('dragexit.localgpx', over)
30210             .on('dragover.localgpx', over);
30211
30212         iD.svg.Gpx.initialized = true;
30213     }
30214
30215
30216     function drawGpx(surface) {
30217         var geojson = iD.svg.Gpx.geojson,
30218             enabled = iD.svg.Gpx.enabled;
30219
30220         layer = surface.selectAll('.layer-gpx')
30221             .data(enabled ? [0] : []);
30222
30223         layer.enter()
30224             .append('g')
30225             .attr('class', 'layer-gpx');
30226
30227         layer.exit()
30228             .remove();
30229
30230
30231         var paths = layer
30232             .selectAll('path')
30233             .data([geojson]);
30234
30235         paths.enter()
30236             .append('path')
30237             .attr('class', 'gpx');
30238
30239         paths.exit()
30240             .remove();
30241
30242         var path = d3.geo.path()
30243             .projection(projection);
30244
30245         paths
30246             .attr('d', path);
30247
30248
30249         var labels = layer.selectAll('text')
30250             .data(showLabels && geojson.features ? geojson.features : []);
30251
30252         labels.enter()
30253             .append('text')
30254             .attr('class', 'gpx');
30255
30256         labels.exit()
30257             .remove();
30258
30259         labels
30260             .text(function(d) {
30261                 return d.properties.desc || d.properties.name;
30262             })
30263             .attr('x', function(d) {
30264                 var centroid = path.centroid(d);
30265                 return centroid[0] + 7;
30266             })
30267             .attr('y', function(d) {
30268                 var centroid = path.centroid(d);
30269                 return centroid[1];
30270             });
30271
30272     }
30273
30274     function toDom(x) {
30275         return (new DOMParser()).parseFromString(x, 'text/xml');
30276     }
30277
30278     drawGpx.showLabels = function(_) {
30279         if (!arguments.length) return showLabels;
30280         showLabels = _;
30281         return this;
30282     };
30283
30284     drawGpx.enabled = function(_) {
30285         if (!arguments.length) return iD.svg.Gpx.enabled;
30286         iD.svg.Gpx.enabled = _;
30287         dispatch.change();
30288         return this;
30289     };
30290
30291     drawGpx.hasGpx = function() {
30292         var geojson = iD.svg.Gpx.geojson;
30293         return (!(_.isEmpty(geojson) || _.isEmpty(geojson.features)));
30294     };
30295
30296     drawGpx.geojson = function(gj) {
30297         if (!arguments.length) return iD.svg.Gpx.geojson;
30298         if (_.isEmpty(gj) || _.isEmpty(gj.features)) return this;
30299         iD.svg.Gpx.geojson = gj;
30300         dispatch.change();
30301         return this;
30302     };
30303
30304     drawGpx.url = function(url) {
30305         d3.text(url, function(err, data) {
30306             if (!err) {
30307                 drawGpx.geojson(toGeoJSON.gpx(toDom(data)));
30308             }
30309         });
30310         return this;
30311     };
30312
30313     drawGpx.files = function(fileList) {
30314         var f = fileList[0],
30315             reader = new FileReader();
30316
30317         reader.onload = function(e) {
30318             drawGpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom();
30319         };
30320
30321         reader.readAsText(f);
30322         return this;
30323     };
30324
30325     drawGpx.fitZoom = function() {
30326         if (!this.hasGpx()) return this;
30327         var geojson = iD.svg.Gpx.geojson;
30328
30329         var map = context.map(),
30330             viewport = map.trimmedExtent().polygon(),
30331             coords = _.reduce(geojson.features, function(coords, feature) {
30332                 var c = feature.geometry.coordinates;
30333                 return _.union(coords, feature.geometry.type === 'Point' ? [c] : c);
30334             }, []);
30335
30336         if (!iD.geo.polygonIntersectsPolygon(viewport, coords, true)) {
30337             var extent = iD.geo.Extent(d3.geo.bounds(geojson));
30338             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
30339         }
30340
30341         return this;
30342     };
30343
30344     init();
30345     return drawGpx;
30346 };
30347 iD.svg.Icon = function(name, svgklass, useklass) {
30348     return function drawIcon(selection) {
30349         selection.selectAll('svg')
30350             .data([0])
30351             .enter()
30352             .append('svg')
30353             .attr('class', 'icon ' + (svgklass || ''))
30354             .append('use')
30355             .attr('xlink:href', name)
30356             .attr('class', useklass);
30357     };
30358 };
30359 iD.svg.Labels = function(projection, context) {
30360     var path = d3.geo.path().projection(projection);
30361
30362     // Replace with dict and iterate over entities tags instead?
30363     var label_stack = [
30364         ['line', 'aeroway'],
30365         ['line', 'highway'],
30366         ['line', 'railway'],
30367         ['line', 'waterway'],
30368         ['area', 'aeroway'],
30369         ['area', 'amenity'],
30370         ['area', 'building'],
30371         ['area', 'historic'],
30372         ['area', 'leisure'],
30373         ['area', 'man_made'],
30374         ['area', 'natural'],
30375         ['area', 'shop'],
30376         ['area', 'tourism'],
30377         ['point', 'aeroway'],
30378         ['point', 'amenity'],
30379         ['point', 'building'],
30380         ['point', 'historic'],
30381         ['point', 'leisure'],
30382         ['point', 'man_made'],
30383         ['point', 'natural'],
30384         ['point', 'shop'],
30385         ['point', 'tourism'],
30386         ['line', 'name'],
30387         ['area', 'name'],
30388         ['point', 'name']
30389     ];
30390
30391     var default_size = 12;
30392
30393     var font_sizes = label_stack.map(function(d) {
30394         var style = iD.util.getStyle('text.' + d[0] + '.tag-' + d[1]),
30395             m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30396         if (m) return parseInt(m[1], 10);
30397
30398         style = iD.util.getStyle('text.' + d[0]);
30399         m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30400         if (m) return parseInt(m[1], 10);
30401
30402         return default_size;
30403     });
30404
30405     var iconSize = 18;
30406
30407     var pointOffsets = [
30408         [15, -11, 'start'], // right
30409         [10, -11, 'start'], // unused right now
30410         [-15, -11, 'end']
30411     ];
30412
30413     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25,
30414         75, 20, 80, 15, 95, 10, 90, 5, 95];
30415
30416
30417     var noIcons = ['building', 'landuse', 'natural'];
30418     function blacklisted(preset) {
30419         return _.any(noIcons, function(s) {
30420             return preset.id.indexOf(s) >= 0;
30421         });
30422     }
30423
30424     function get(array, prop) {
30425         return function(d, i) { return array[i][prop]; };
30426     }
30427
30428     var textWidthCache = {};
30429
30430     function textWidth(text, size, elem) {
30431         var c = textWidthCache[size];
30432         if (!c) c = textWidthCache[size] = {};
30433
30434         if (c[text]) {
30435             return c[text];
30436
30437         } else if (elem) {
30438             c[text] = elem.getComputedTextLength();
30439             return c[text];
30440
30441         } else {
30442             var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
30443             if (str === null) {
30444                 return size / 3 * 2 * text.length;
30445             } else {
30446                 return size / 3 * (2 * text.length + str.length);
30447             }
30448         }
30449     }
30450
30451     function drawLineLabels(group, entities, filter, classes, labels) {
30452         var texts = group.selectAll('text.' + classes)
30453             .filter(filter)
30454             .data(entities, iD.Entity.key);
30455
30456         texts.enter()
30457             .append('text')
30458             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
30459             .append('textPath')
30460             .attr('class', 'textpath');
30461
30462
30463         texts.selectAll('.textpath')
30464             .filter(filter)
30465             .data(entities, iD.Entity.key)
30466             .attr({
30467                 'startOffset': '50%',
30468                 'xlink:href': function(d) { return '#labelpath-' + d.id; }
30469             })
30470             .text(iD.util.displayName);
30471
30472         texts.exit().remove();
30473     }
30474
30475     function drawLinePaths(group, entities, filter, classes, labels) {
30476         var halos = group.selectAll('path')
30477             .filter(filter)
30478             .data(entities, iD.Entity.key);
30479
30480         halos.enter()
30481             .append('path')
30482             .style('stroke-width', get(labels, 'font-size'))
30483             .attr('id', function(d) { return 'labelpath-' + d.id; })
30484             .attr('class', classes);
30485
30486         halos.attr('d', get(labels, 'lineString'));
30487
30488         halos.exit().remove();
30489     }
30490
30491     function drawPointLabels(group, entities, filter, classes, labels) {
30492         var texts = group.selectAll('text.' + classes)
30493             .filter(filter)
30494             .data(entities, iD.Entity.key);
30495
30496         texts.enter()
30497             .append('text')
30498             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; });
30499
30500         texts.attr('x', get(labels, 'x'))
30501             .attr('y', get(labels, 'y'))
30502             .style('text-anchor', get(labels, 'textAnchor'))
30503             .text(iD.util.displayName)
30504             .each(function(d, i) { textWidth(iD.util.displayName(d), labels[i].height, this); });
30505
30506         texts.exit().remove();
30507         return texts;
30508     }
30509
30510     function drawAreaLabels(group, entities, filter, classes, labels) {
30511         entities = entities.filter(hasText);
30512         labels = labels.filter(hasText);
30513         return drawPointLabels(group, entities, filter, classes, labels);
30514
30515         function hasText(d, i) {
30516             return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
30517         }
30518     }
30519
30520     function drawAreaIcons(group, entities, filter, classes, labels) {
30521         var icons = group.selectAll('use')
30522             .filter(filter)
30523             .data(entities, iD.Entity.key);
30524
30525         icons.enter()
30526             .append('use')
30527             .attr('class', 'icon areaicon')
30528             .attr('width', '18px')
30529             .attr('height', '18px');
30530
30531         icons.attr('transform', get(labels, 'transform'))
30532             .attr('xlink:href', function(d) {
30533                 var icon = context.presets().match(d, context.graph()).icon;
30534                 return '#' + icon + (icon === 'hairdresser' ? '-24': '-18');    // workaround: maki hairdresser-18 broken?
30535             });
30536
30537
30538         icons.exit().remove();
30539     }
30540
30541     function reverse(p) {
30542         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
30543         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
30544     }
30545
30546     function lineString(nodes) {
30547         return 'M' + nodes.join('L');
30548     }
30549
30550     function subpath(nodes, from, to) {
30551         function segmentLength(i) {
30552             var dx = nodes[i][0] - nodes[i + 1][0];
30553             var dy = nodes[i][1] - nodes[i + 1][1];
30554             return Math.sqrt(dx * dx + dy * dy);
30555         }
30556
30557         var sofar = 0,
30558             start, end, i0, i1;
30559         for (var i = 0; i < nodes.length - 1; i++) {
30560             var current = segmentLength(i);
30561             var portion;
30562             if (!start && sofar + current >= from) {
30563                 portion = (from - sofar) / current;
30564                 start = [
30565                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30566                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30567                 ];
30568                 i0 = i + 1;
30569             }
30570             if (!end && sofar + current >= to) {
30571                 portion = (to - sofar) / current;
30572                 end = [
30573                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30574                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30575                 ];
30576                 i1 = i + 1;
30577             }
30578             sofar += current;
30579
30580         }
30581         var ret = nodes.slice(i0, i1);
30582         ret.unshift(start);
30583         ret.push(end);
30584         return ret;
30585
30586     }
30587
30588     function hideOnMouseover() {
30589         var layers = d3.select(this)
30590             .selectAll('.layer-label, .layer-halo');
30591
30592         layers.selectAll('.proximate')
30593             .classed('proximate', false);
30594
30595         var mouse = context.mouse(),
30596             pad = 50,
30597             rect = [mouse[0] - pad, mouse[1] - pad, mouse[0] + pad, mouse[1] + pad],
30598             ids = _.pluck(rtree.search(rect), 'id');
30599
30600         if (!ids.length) return;
30601         layers.selectAll('.' + ids.join(', .'))
30602             .classed('proximate', true);
30603     }
30604
30605     var rtree = rbush(),
30606         rectangles = {};
30607
30608     function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
30609         var hidePoints = !surface.selectAll('.node.point').node();
30610
30611         var labelable = [], i, k, entity;
30612         for (i = 0; i < label_stack.length; i++) labelable.push([]);
30613
30614         if (fullRedraw) {
30615             rtree.clear();
30616             rectangles = {};
30617         } else {
30618             for (i = 0; i < entities.length; i++) {
30619                 rtree.remove(rectangles[entities[i].id]);
30620             }
30621         }
30622
30623         // Split entities into groups specified by label_stack
30624         for (i = 0; i < entities.length; i++) {
30625             entity = entities[i];
30626             var geometry = entity.geometry(graph);
30627
30628             if (geometry === 'vertex')
30629                 continue;
30630             if (hidePoints && geometry === 'point')
30631                 continue;
30632
30633             var preset = geometry === 'area' && context.presets().match(entity, graph),
30634                 icon = preset && !blacklisted(preset) && preset.icon;
30635
30636             if (!icon && !iD.util.displayName(entity))
30637                 continue;
30638
30639             for (k = 0; k < label_stack.length; k++) {
30640                 if (geometry === label_stack[k][0] && entity.tags[label_stack[k][1]]) {
30641                     labelable[k].push(entity);
30642                     break;
30643                 }
30644             }
30645         }
30646
30647         var positions = {
30648             point: [],
30649             line: [],
30650             area: []
30651         };
30652
30653         var labelled = {
30654             point: [],
30655             line: [],
30656             area: []
30657         };
30658
30659         // Try and find a valid label for labellable entities
30660         for (k = 0; k < labelable.length; k++) {
30661             var font_size = font_sizes[k];
30662             for (i = 0; i < labelable[k].length; i++) {
30663                 entity = labelable[k][i];
30664                 var name = iD.util.displayName(entity),
30665                     width = name && textWidth(name, font_size),
30666                     p;
30667                 if (entity.geometry(graph) === 'point') {
30668                     p = getPointLabel(entity, width, font_size);
30669                 } else if (entity.geometry(graph) === 'line') {
30670                     p = getLineLabel(entity, width, font_size);
30671                 } else if (entity.geometry(graph) === 'area') {
30672                     p = getAreaLabel(entity, width, font_size);
30673                 }
30674                 if (p) {
30675                     p.classes = entity.geometry(graph) + ' tag-' + label_stack[k][1];
30676                     positions[entity.geometry(graph)].push(p);
30677                     labelled[entity.geometry(graph)].push(entity);
30678                 }
30679             }
30680         }
30681
30682         function getPointLabel(entity, width, height) {
30683             var coord = projection(entity.loc),
30684                 m = 5,  // margin
30685                 offset = pointOffsets[0],
30686                 p = {
30687                     height: height,
30688                     width: width,
30689                     x: coord[0] + offset[0],
30690                     y: coord[1] + offset[1],
30691                     textAnchor: offset[2]
30692                 };
30693             var rect = [p.x - m, p.y - m, p.x + width + m, p.y + height + m];
30694             if (tryInsert(rect, entity.id)) return p;
30695         }
30696
30697
30698         function getLineLabel(entity, width, height) {
30699             var nodes = _.pluck(graph.childNodes(entity), 'loc').map(projection),
30700                 length = iD.geo.pathLength(nodes);
30701             if (length < width + 20) return;
30702
30703             for (var i = 0; i < lineOffsets.length; i++) {
30704                 var offset = lineOffsets[i],
30705                     middle = offset / 100 * length,
30706                     start = middle - width/2;
30707                 if (start < 0 || start + width > length) continue;
30708                 var sub = subpath(nodes, start, start + width),
30709                     rev = reverse(sub),
30710                     rect = [
30711                         Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
30712                         Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
30713                         Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
30714                         Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
30715                     ];
30716                 if (rev) sub = sub.reverse();
30717                 if (tryInsert(rect, entity.id)) return {
30718                     'font-size': height + 2,
30719                     lineString: lineString(sub),
30720                     startOffset: offset + '%'
30721                 };
30722             }
30723         }
30724
30725         function getAreaLabel(entity, width, height) {
30726             var centroid = path.centroid(entity.asGeoJSON(graph, true)),
30727                 extent = entity.extent(graph),
30728                 entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
30729                 rect;
30730
30731             if (isNaN(centroid[0]) || entitywidth < 20) return;
30732
30733             var iconX = centroid[0] - (iconSize/2),
30734                 iconY = centroid[1] - (iconSize/2),
30735                 textOffset = iconSize + 5;
30736
30737             var p = {
30738                 transform: 'translate(' + iconX + ',' + iconY + ')'
30739             };
30740
30741             if (width && entitywidth >= width + 20) {
30742                 p.x = centroid[0];
30743                 p.y = centroid[1] + textOffset;
30744                 p.textAnchor = 'middle';
30745                 p.height = height;
30746                 rect = [p.x - width/2, p.y, p.x + width/2, p.y + height + textOffset];
30747             } else {
30748                 rect = [iconX, iconY, iconX + iconSize, iconY + iconSize];
30749             }
30750
30751             if (tryInsert(rect, entity.id)) return p;
30752
30753         }
30754
30755         function tryInsert(rect, id) {
30756             // Check that label is visible
30757             if (rect[0] < 0 || rect[1] < 0 || rect[2] > dimensions[0] ||
30758                 rect[3] > dimensions[1]) return false;
30759             var v = rtree.search(rect).length === 0;
30760             if (v) {
30761                 rect.id = id;
30762                 rtree.insert(rect);
30763                 rectangles[id] = rect;
30764             }
30765             return v;
30766         }
30767
30768         var label = surface.selectAll('.layer-label'),
30769             halo = surface.selectAll('.layer-halo');
30770
30771         // points
30772         drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
30773         drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
30774
30775         // lines
30776         drawLinePaths(halo, labelled.line, filter, '', positions.line);
30777         drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
30778         drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
30779
30780         // areas
30781         drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
30782         drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
30783         drawAreaIcons(label, labelled.area, filter, 'arealabel-icon', positions.area);
30784     }
30785
30786     drawLabels.supersurface = function(supersurface) {
30787         supersurface
30788             .on('mousemove.hidelabels', hideOnMouseover)
30789             .on('mousedown.hidelabels', function () {
30790                 supersurface.on('mousemove.hidelabels', null);
30791             })
30792             .on('mouseup.hidelabels', function () {
30793                 supersurface.on('mousemove.hidelabels', hideOnMouseover);
30794             });
30795     };
30796
30797     return drawLabels;
30798 };
30799 iD.svg.Layers = function(projection, context) {
30800     var dispatch = d3.dispatch('change'),
30801         svg = d3.select(null),
30802         layers = [
30803             { id: 'osm', layer: iD.svg.Osm(projection, context, dispatch) },
30804             { id: 'gpx', layer: iD.svg.Gpx(projection, context, dispatch) },
30805             { id: 'mapillary-images', layer: iD.svg.MapillaryImages(projection, context, dispatch) },
30806             { id: 'mapillary-signs',  layer: iD.svg.MapillarySigns(projection, context, dispatch) }
30807         ];
30808
30809
30810     function drawLayers(selection) {
30811         svg = selection.selectAll('.surface')
30812             .data([0]);
30813
30814         svg.enter()
30815             .append('svg')
30816             .attr('class', 'surface')
30817             .append('defs');
30818
30819         var groups = svg.selectAll('.data-layer')
30820             .data(layers);
30821
30822         groups.enter()
30823             .append('g')
30824             .attr('class', function(d) { return 'data-layer data-layer-' + d.id; });
30825
30826         groups
30827             .each(function(d) { d3.select(this).call(d.layer); });
30828
30829         groups.exit()
30830             .remove();
30831     }
30832
30833     drawLayers.all = function() {
30834         return layers;
30835     };
30836
30837     drawLayers.layer = function(id) {
30838         var obj = _.find(layers, 'id', id);
30839         return obj && obj.layer;
30840     };
30841
30842     drawLayers.only = function(what) {
30843         var arr = [].concat(what);
30844         drawLayers.remove(_.difference(_.pluck(layers, 'id'), arr));
30845         return this;
30846     };
30847
30848     drawLayers.remove = function(what) {
30849         var arr = [].concat(what);
30850         arr.forEach(function(id) {
30851             layers = _.reject(layers, 'id', id);
30852         });
30853         dispatch.change();
30854         return this;
30855     };
30856
30857     drawLayers.add = function(what) {
30858         var arr = [].concat(what);
30859         arr.forEach(function(obj) {
30860             if ('id' in obj && 'layer' in obj) {
30861                 layers.push(obj);
30862             }
30863         });
30864         dispatch.change();
30865         return this;
30866     };
30867
30868     drawLayers.dimensions = function(_) {
30869         if (!arguments.length) return svg.dimensions();
30870         svg.dimensions(_);
30871         layers.forEach(function(obj) {
30872             if (obj.layer.dimensions) {
30873                 obj.layer.dimensions(_);
30874             }
30875         });
30876         return this;
30877     };
30878
30879
30880     return d3.rebind(drawLayers, dispatch, 'on');
30881 };
30882 iD.svg.Lines = function(projection) {
30883
30884     var highway_stack = {
30885         motorway: 0,
30886         motorway_link: 1,
30887         trunk: 2,
30888         trunk_link: 3,
30889         primary: 4,
30890         primary_link: 5,
30891         secondary: 6,
30892         tertiary: 7,
30893         unclassified: 8,
30894         residential: 9,
30895         service: 10,
30896         footway: 11
30897     };
30898
30899     function waystack(a, b) {
30900         var as = 0, bs = 0;
30901
30902         if (a.tags.highway) { as -= highway_stack[a.tags.highway]; }
30903         if (b.tags.highway) { bs -= highway_stack[b.tags.highway]; }
30904         return as - bs;
30905     }
30906
30907     return function drawLines(surface, graph, entities, filter) {
30908         var ways = [], pathdata = {}, onewaydata = {},
30909             getPath = iD.svg.Path(projection, graph);
30910
30911         for (var i = 0; i < entities.length; i++) {
30912             var entity = entities[i],
30913                 outer = iD.geo.simpleMultipolygonOuterMember(entity, graph);
30914             if (outer) {
30915                 ways.push(entity.mergeTags(outer.tags));
30916             } else if (entity.geometry(graph) === 'line') {
30917                 ways.push(entity);
30918             }
30919         }
30920
30921         ways = ways.filter(getPath);
30922
30923         pathdata = _.groupBy(ways, function(way) { return way.layer(); });
30924
30925         _.forOwn(pathdata, function(v, k) {
30926             onewaydata[k] = _(v)
30927                 .filter(function(d) { return d.isOneWay(); })
30928                 .map(iD.svg.OneWaySegments(projection, graph, 35))
30929                 .flatten()
30930                 .valueOf();
30931         });
30932
30933         var layergroup = surface
30934             .selectAll('.layer-lines')
30935             .selectAll('g.layergroup')
30936             .data(d3.range(-10, 11));
30937
30938         layergroup.enter()
30939             .append('g')
30940             .attr('class', function(d) { return 'layer layergroup layer' + String(d); });
30941
30942
30943         var linegroup = layergroup
30944             .selectAll('g.linegroup')
30945             .data(['shadow', 'casing', 'stroke']);
30946
30947         linegroup.enter()
30948             .append('g')
30949             .attr('class', function(d) { return 'layer linegroup line-' + d; });
30950
30951
30952         var lines = linegroup
30953             .selectAll('path')
30954             .filter(filter)
30955             .data(
30956                 function() { return pathdata[this.parentNode.parentNode.__data__] || []; },
30957                 iD.Entity.key
30958             );
30959
30960         // Optimization: call simple TagClasses only on enter selection. This
30961         // works because iD.Entity.key is defined to include the entity v attribute.
30962         lines.enter()
30963             .append('path')
30964             .attr('class', function(d) { return 'way line ' + this.parentNode.__data__ + ' ' + d.id; })
30965             .call(iD.svg.TagClasses());
30966
30967         lines
30968             .sort(waystack)
30969             .attr('d', getPath)
30970             .call(iD.svg.TagClasses().tags(iD.svg.RelationMemberTags(graph)));
30971
30972         lines.exit()
30973             .remove();
30974
30975
30976         var onewaygroup = layergroup
30977             .selectAll('g.onewaygroup')
30978             .data(['oneway']);
30979
30980         onewaygroup.enter()
30981             .append('g')
30982             .attr('class', 'layer onewaygroup');
30983
30984
30985         var oneways = onewaygroup
30986             .selectAll('path')
30987             .filter(filter)
30988             .data(
30989                 function() { return onewaydata[this.parentNode.parentNode.__data__] || []; },
30990                 function(d) { return [d.id, d.index]; }
30991             );
30992
30993         oneways.enter()
30994             .append('path')
30995             .attr('class', 'oneway')
30996             .attr('marker-mid', 'url(#oneway-marker)');
30997
30998         oneways
30999             .attr('d', function(d) { return d.d; });
31000
31001         if (iD.detect().ie) {
31002             oneways.each(function() { this.parentNode.insertBefore(this, this); });
31003         }
31004
31005         oneways.exit()
31006             .remove();
31007
31008     };
31009 };
31010 iD.svg.MapillaryImages = function(projection, context, dispatch) {
31011     var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
31012         minZoom = 12,
31013         layer = d3.select(null),
31014         _mapillary;
31015
31016
31017     function init() {
31018         if (iD.svg.MapillaryImages.initialized) return;  // run once
31019         iD.svg.MapillaryImages.enabled = false;
31020         iD.svg.MapillaryImages.initialized = true;
31021     }
31022
31023     function getMapillary() {
31024         if (iD.services.mapillary && !_mapillary) {
31025             _mapillary = iD.services.mapillary().on('loadedImages', debouncedRedraw);
31026         } else if (!iD.services.mapillary && _mapillary) {
31027             _mapillary = null;
31028         }
31029         return _mapillary;
31030     }
31031
31032     function showThumbnail(image) {
31033         var mapillary = getMapillary();
31034         if (!mapillary) return;
31035
31036         var thumb = mapillary.selectedThumbnail(),
31037             posX = projection(image.loc)[0],
31038             width = layer.dimensions()[0],
31039             position = (posX < width / 2) ? 'right' : 'left';
31040
31041         if (thumb) {
31042             d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31043                 .classed('selected', function(d) { return d.key === thumb.key; });
31044         }
31045
31046         mapillary.showThumbnail(image.key, position);
31047     }
31048
31049     function hideThumbnail() {
31050         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31051             .classed('selected', false);
31052
31053         var mapillary = getMapillary();
31054         if (mapillary) {
31055             mapillary.hideThumbnail();
31056         }
31057     }
31058
31059     function showLayer() {
31060         editOn();
31061         layer
31062             .style('opacity', 0)
31063             .transition()
31064             .duration(500)
31065             .style('opacity', 1)
31066             .each('end', debouncedRedraw);
31067     }
31068
31069     function hideLayer() {
31070         debouncedRedraw.cancel();
31071         hideThumbnail();
31072         layer
31073             .transition()
31074             .duration(500)
31075             .style('opacity', 0)
31076             .each('end', editOff);
31077     }
31078
31079     function editOn() {
31080         layer.style('display', 'block');
31081     }
31082
31083     function editOff() {
31084         layer.selectAll('.viewfield-group').remove();
31085         layer.style('display', 'none');
31086     }
31087
31088     function transform(d) {
31089         var t = iD.svg.PointTransform(projection)(d);
31090         if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
31091         return t;
31092     }
31093
31094     function update() {
31095         var mapillary = getMapillary(),
31096             data = (mapillary ? mapillary.images(projection, layer.dimensions()) : []);
31097
31098         var markers = layer.selectAll('.viewfield-group')
31099             .data(data, function(d) { return d.key; });
31100
31101         // Enter
31102         var enter = markers.enter()
31103             .append('g')
31104             .attr('class', 'viewfield-group');
31105
31106         enter.append('path')
31107             .attr('class', 'viewfield')
31108             .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
31109             .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');
31110
31111         enter.append('circle')
31112             .attr('dx', '0')
31113             .attr('dy', '0')
31114             .attr('r', '6');
31115
31116         // Exit
31117         markers.exit()
31118             .remove();
31119
31120         // Update
31121         markers
31122             .attr('transform', transform);
31123     }
31124
31125     function drawImages(selection) {
31126         var enabled = iD.svg.MapillaryImages.enabled,
31127             mapillary = getMapillary();
31128
31129         layer = selection.selectAll('.layer-mapillary-images')
31130             .data(mapillary ? [0] : []);
31131
31132         layer.enter()
31133             .append('g')
31134             .attr('class', 'layer-mapillary-images')
31135             .style('display', enabled ? 'block' : 'none')
31136             .on('click', function() {   // deselect/select
31137                 var mapillary = getMapillary();
31138                 if (!mapillary) return;
31139                 var d = d3.event.target.__data__,
31140                     thumb = mapillary.selectedThumbnail();
31141                 if (thumb && thumb.key === d.key) {
31142                     hideThumbnail();
31143                 } else {
31144                     mapillary.selectedThumbnail(d);
31145                     context.map().centerEase(d.loc);
31146                     showThumbnail(d);
31147                 }
31148             })
31149             .on('mouseover', function() {
31150                 var mapillary = getMapillary();
31151                 if (!mapillary) return;
31152                 showThumbnail(d3.event.target.__data__);
31153             })
31154             .on('mouseout', function() {
31155                 var mapillary = getMapillary();
31156                 if (!mapillary) return;
31157                 var thumb = mapillary.selectedThumbnail();
31158                 if (thumb) {
31159                     showThumbnail(thumb);
31160                 } else {
31161                     hideThumbnail();
31162                 }
31163             });
31164
31165         layer.exit()
31166             .remove();
31167
31168         if (enabled) {
31169             if (mapillary && ~~context.map().zoom() >= minZoom) {
31170                 editOn();
31171                 update();
31172                 mapillary.loadImages(projection, layer.dimensions());
31173             } else {
31174                 editOff();
31175             }
31176         }
31177     }
31178
31179     drawImages.enabled = function(_) {
31180         if (!arguments.length) return iD.svg.MapillaryImages.enabled;
31181         iD.svg.MapillaryImages.enabled = _;
31182         if (iD.svg.MapillaryImages.enabled) {
31183             showLayer();
31184         } else {
31185             hideLayer();
31186         }
31187         dispatch.change();
31188         return this;
31189     };
31190
31191     drawImages.supported = function() {
31192         return !!getMapillary();
31193     };
31194
31195     drawImages.dimensions = function(_) {
31196         if (!arguments.length) return layer.dimensions();
31197         layer.dimensions(_);
31198         return this;
31199     };
31200
31201     init();
31202     return drawImages;
31203 };
31204 iD.svg.MapillarySigns = function(projection, context, dispatch) {
31205     var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
31206         minZoom = 12,
31207         layer = d3.select(null),
31208         _mapillary;
31209
31210
31211     function init() {
31212         if (iD.svg.MapillarySigns.initialized) return;  // run once
31213         iD.svg.MapillarySigns.enabled = false;
31214         iD.svg.MapillarySigns.initialized = true;
31215     }
31216
31217     function getMapillary() {
31218         if (iD.services.mapillary && !_mapillary) {
31219             _mapillary = iD.services.mapillary().on('loadedSigns', debouncedRedraw);
31220         } else if (!iD.services.mapillary && _mapillary) {
31221             _mapillary = null;
31222         }
31223         return _mapillary;
31224     }
31225
31226     function showThumbnail(image) {
31227         var mapillary = getMapillary();
31228         if (!mapillary) return;
31229
31230         var thumb = mapillary.selectedThumbnail(),
31231             posX = projection(image.loc)[0],
31232             width = layer.dimensions()[0],
31233             position = (posX < width / 2) ? 'right' : 'left';
31234
31235         if (thumb) {
31236             d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31237                 .classed('selected', function(d) { return d.key === thumb.key; });
31238         }
31239
31240         mapillary.showThumbnail(image.key, position);
31241     }
31242
31243     function hideThumbnail() {
31244         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31245             .classed('selected', false);
31246
31247         var mapillary = getMapillary();
31248         if (mapillary) {
31249             mapillary.hideThumbnail();
31250         }
31251     }
31252
31253     function showLayer() {
31254         editOn();
31255         debouncedRedraw();
31256     }
31257
31258     function hideLayer() {
31259         debouncedRedraw.cancel();
31260         hideThumbnail();
31261         editOff();
31262     }
31263
31264     function editOn() {
31265         layer.style('display', 'block');
31266     }
31267
31268     function editOff() {
31269         layer.selectAll('.icon-sign').remove();
31270         layer.style('display', 'none');
31271     }
31272
31273     function update() {
31274         var mapillary = getMapillary(),
31275             data = (mapillary ? mapillary.signs(projection, layer.dimensions()) : []);
31276
31277         var signs = layer.selectAll('.icon-sign')
31278             .data(data, function(d) { return d.key; });
31279
31280         // Enter
31281         var enter = signs.enter()
31282             .append('foreignObject')
31283             .attr('class', 'icon-sign')
31284             .attr('width', '32px')      // for Firefox
31285             .attr('height', '32px');    // for Firefox
31286
31287         enter
31288             .append('xhtml:body')
31289             .html(mapillary.signHTML);
31290
31291         enter
31292             .on('click', function(d) {   // deselect/select
31293                 var mapillary = getMapillary();
31294                 if (!mapillary) return;
31295                 var thumb = mapillary.selectedThumbnail();
31296                 if (thumb && thumb.key === d.key) {
31297                     hideThumbnail();
31298                 } else {
31299                     mapillary.selectedThumbnail(d);
31300                     context.map().centerEase(d.loc);
31301                     showThumbnail(d);
31302                 }
31303             })
31304             .on('mouseover', showThumbnail)
31305             .on('mouseout', function() {
31306                 var mapillary = getMapillary();
31307                 if (!mapillary) return;
31308                 var thumb = mapillary.selectedThumbnail();
31309                 if (thumb) {
31310                     showThumbnail(thumb);
31311                 } else {
31312                     hideThumbnail();
31313                 }
31314             });
31315
31316         // Exit
31317         signs.exit()
31318             .remove();
31319
31320         // Update
31321         signs
31322             .attr('transform', iD.svg.PointTransform(projection));
31323     }
31324
31325     function drawSigns(selection) {
31326         var enabled = iD.svg.MapillarySigns.enabled,
31327             mapillary = getMapillary();
31328
31329         layer = selection.selectAll('.layer-mapillary-signs')
31330             .data(mapillary ? [0] : []);
31331
31332         layer.enter()
31333             .append('g')
31334             .attr('class', 'layer-mapillary-signs')
31335             .style('display', enabled ? 'block' : 'none')
31336             .attr('transform', 'translate(-16, -16)');  // center signs on loc
31337
31338         layer.exit()
31339             .remove();
31340
31341         if (enabled) {
31342             if (mapillary && ~~context.map().zoom() >= minZoom) {
31343                 editOn();
31344                 update();
31345                 mapillary.loadSigns(context, projection, layer.dimensions());
31346             } else {
31347                 editOff();
31348             }
31349         }
31350     }
31351
31352     drawSigns.enabled = function(_) {
31353         if (!arguments.length) return iD.svg.MapillarySigns.enabled;
31354         iD.svg.MapillarySigns.enabled = _;
31355         if (iD.svg.MapillarySigns.enabled) {
31356             showLayer();
31357         } else {
31358             hideLayer();
31359         }
31360         dispatch.change();
31361         return this;
31362     };
31363
31364     drawSigns.supported = function() {
31365         var mapillary = getMapillary();
31366         return (mapillary && mapillary.signsSupported());
31367     };
31368
31369     drawSigns.dimensions = function(_) {
31370         if (!arguments.length) return layer.dimensions();
31371         layer.dimensions(_);
31372         return this;
31373     };
31374
31375     init();
31376     return drawSigns;
31377 };
31378 iD.svg.Midpoints = function(projection, context) {
31379     return function drawMidpoints(surface, graph, entities, filter, extent) {
31380         var poly = extent.polygon(),
31381             midpoints = {};
31382
31383         for (var i = 0; i < entities.length; i++) {
31384             var entity = entities[i];
31385
31386             if (entity.type !== 'way')
31387                 continue;
31388             if (!filter(entity))
31389                 continue;
31390             if (context.selectedIDs().indexOf(entity.id) < 0)
31391                 continue;
31392
31393             var nodes = graph.childNodes(entity);
31394             for (var j = 0; j < nodes.length - 1; j++) {
31395
31396                 var a = nodes[j],
31397                     b = nodes[j + 1],
31398                     id = [a.id, b.id].sort().join('-');
31399
31400                 if (midpoints[id]) {
31401                     midpoints[id].parents.push(entity);
31402                 } else {
31403                     if (iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
31404                         var point = iD.geo.interp(a.loc, b.loc, 0.5),
31405                             loc = null;
31406
31407                         if (extent.intersects(point)) {
31408                             loc = point;
31409                         } else {
31410                             for (var k = 0; k < 4; k++) {
31411                                 point = iD.geo.lineIntersection([a.loc, b.loc], [poly[k], poly[k+1]]);
31412                                 if (point &&
31413                                     iD.geo.euclideanDistance(projection(a.loc), projection(point)) > 20 &&
31414                                     iD.geo.euclideanDistance(projection(b.loc), projection(point)) > 20)
31415                                 {
31416                                     loc = point;
31417                                     break;
31418                                 }
31419                             }
31420                         }
31421
31422                         if (loc) {
31423                             midpoints[id] = {
31424                                 type: 'midpoint',
31425                                 id: id,
31426                                 loc: loc,
31427                                 edge: [a.id, b.id],
31428                                 parents: [entity]
31429                             };
31430                         }
31431                     }
31432                 }
31433             }
31434         }
31435
31436         function midpointFilter(d) {
31437             if (midpoints[d.id])
31438                 return true;
31439
31440             for (var i = 0; i < d.parents.length; i++)
31441                 if (filter(d.parents[i]))
31442                     return true;
31443
31444             return false;
31445         }
31446
31447         var groups = surface.selectAll('.layer-hit').selectAll('g.midpoint')
31448             .filter(midpointFilter)
31449             .data(_.values(midpoints), function(d) { return d.id; });
31450
31451         var enter = groups.enter()
31452             .insert('g', ':first-child')
31453             .attr('class', 'midpoint');
31454
31455         enter.append('polygon')
31456             .attr('points', '-6,8 10,0 -6,-8')
31457             .attr('class', 'shadow');
31458
31459         enter.append('polygon')
31460             .attr('points', '-3,4 5,0 -3,-4')
31461             .attr('class', 'fill');
31462
31463         groups
31464             .attr('transform', function(d) {
31465                 var translate = iD.svg.PointTransform(projection),
31466                     a = context.entity(d.edge[0]),
31467                     b = context.entity(d.edge[1]),
31468                     angle = Math.round(iD.geo.angle(a, b, projection) * (180 / Math.PI));
31469                 return translate(d) + ' rotate(' + angle + ')';
31470             })
31471             .call(iD.svg.TagClasses().tags(
31472                 function(d) { return d.parents[0].tags; }
31473             ));
31474
31475         // Propagate data bindings.
31476         groups.select('polygon.shadow');
31477         groups.select('polygon.fill');
31478
31479         groups.exit()
31480             .remove();
31481     };
31482 };
31483 iD.svg.Osm = function() {
31484     return function drawOsm(selection) {
31485         var layers = selection.selectAll('.layer-osm')
31486             .data(['areas', 'lines', 'hit', 'halo', 'label']);
31487
31488         layers.enter().append('g')
31489             .attr('class', function(d) { return 'layer-osm layer-' + d; });
31490     };
31491 };
31492 iD.svg.Points = function(projection, context) {
31493     function markerPath(selection, klass) {
31494         selection
31495             .attr('class', klass)
31496             .attr('transform', 'translate(-8, -23)')
31497             .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');
31498     }
31499
31500     function sortY(a, b) {
31501         return b.loc[1] - a.loc[1];
31502     }
31503
31504     return function drawPoints(surface, graph, entities, filter) {
31505         var wireframe = surface.classed('fill-wireframe'),
31506             points = wireframe ? [] : _.filter(entities, function(e) {
31507                 return e.geometry(graph) === 'point';
31508             });
31509
31510         points.sort(sortY);
31511
31512         var groups = surface.selectAll('.layer-hit').selectAll('g.point')
31513             .filter(filter)
31514             .data(points, iD.Entity.key);
31515
31516         var group = groups.enter()
31517             .append('g')
31518             .attr('class', function(d) { return 'node point ' + d.id; })
31519             .order();
31520
31521         group.append('path')
31522             .call(markerPath, 'shadow');
31523
31524         group.append('path')
31525             .call(markerPath, 'stroke');
31526
31527         group.append('use')
31528             .attr('transform', 'translate(-6, -20)')
31529             .attr('class', 'icon')
31530             .attr('width', '12px')
31531             .attr('height', '12px');
31532
31533         groups.attr('transform', iD.svg.PointTransform(projection))
31534             .call(iD.svg.TagClasses());
31535
31536         // Selecting the following implicitly
31537         // sets the data (point entity) on the element
31538         groups.select('.shadow');
31539         groups.select('.stroke');
31540         groups.select('.icon')
31541             .attr('xlink:href', function(entity) {
31542                 var preset = context.presets().match(entity, graph);
31543                 return preset.icon ? '#' + preset.icon + '-12' : '';
31544             });
31545
31546         groups.exit()
31547             .remove();
31548     };
31549 };
31550 iD.svg.TagClasses = function() {
31551     var primaries = [
31552             'building', 'highway', 'railway', 'waterway', 'aeroway',
31553             'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse',
31554             'leisure', 'place'
31555         ],
31556         statuses = [
31557             'proposed', 'construction', 'disused', 'abandoned', 'dismantled',
31558             'razed', 'demolished', 'obliterated'
31559         ],
31560         secondaries = [
31561             'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
31562             'surface', 'tracktype', 'crossing'
31563         ],
31564         tagClassRe = /^tag-/,
31565         tags = function(entity) { return entity.tags; };
31566
31567
31568     var tagClasses = function(selection) {
31569         selection.each(function tagClassesEach(entity) {
31570             var value = this.className,
31571                 classes, primary, status;
31572
31573             if (value.baseVal !== undefined) value = value.baseVal;
31574
31575             classes = value.trim().split(/\s+/).filter(function(name) {
31576                 return name.length && !tagClassRe.test(name);
31577             }).join(' ');
31578
31579             var t = tags(entity), i, k, v;
31580
31581             // pick at most one primary classification tag..
31582             for (i = 0; i < primaries.length; i++) {
31583                 k = primaries[i];
31584                 v = t[k];
31585                 if (!v || v === 'no') continue;
31586
31587                 primary = k;
31588                 if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
31589                     status = v;
31590                     classes += ' tag-' + k;
31591                 } else {
31592                     classes += ' tag-' + k + ' tag-' + k + '-' + v;
31593                 }
31594
31595                 break;
31596             }
31597
31598             // add at most one status tag, only if relates to primary tag..
31599             if (!status) {
31600                 for (i = 0; i < statuses.length; i++) {
31601                     k = statuses[i];
31602                     v = t[k];
31603                     if (!v || v === 'no') continue;
31604
31605                     if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
31606                         status = k;
31607                     }
31608                     else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
31609                         status = k;
31610                     } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
31611                         status = k;
31612                         primary = v;
31613                         classes += ' tag-' + v;
31614                     }  // else ignore e.g.  `highway=path + abandoned=railway`
31615
31616                     if (status) break;
31617                 }
31618             }
31619
31620             if (status) {
31621                 classes += ' tag-status tag-status-' + status;
31622             }
31623
31624             // add any secondary (structure) tags
31625             for (i = 0; i < secondaries.length; i++) {
31626                 k = secondaries[i];
31627                 v = t[k];
31628                 if (!v || v === 'no') continue;
31629                 classes += ' tag-' + k + ' tag-' + k + '-' + v;
31630             }
31631
31632             // For highways, look for surface tagging..
31633             if (primary === 'highway') {
31634                 var paved = (t.highway !== 'track');
31635                 for (k in t) {
31636                     v = t[k];
31637                     if (k in iD.pavedTags) {
31638                         paved = !!iD.pavedTags[k][v];
31639                         break;
31640                     }
31641                 }
31642                 if (!paved) {
31643                     classes += ' tag-unpaved';
31644                 }
31645             }
31646
31647             classes = classes.trim();
31648
31649             if (classes !== value) {
31650                 d3.select(this).attr('class', classes);
31651             }
31652         });
31653     };
31654
31655     tagClasses.tags = function(_) {
31656         if (!arguments.length) return tags;
31657         tags = _;
31658         return tagClasses;
31659     };
31660
31661     return tagClasses;
31662 };
31663 iD.svg.Turns = function(projection) {
31664     return function drawTurns(surface, graph, turns) {
31665         function key(turn) {
31666             return [turn.from.node + turn.via.node + turn.to.node].join('-');
31667         }
31668
31669         function icon(turn) {
31670             var u = turn.u ? '-u' : '';
31671             if (!turn.restriction)
31672                 return '#turn-yes' + u;
31673             var restriction = graph.entity(turn.restriction).tags.restriction;
31674             return '#turn-' +
31675                 (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u;
31676         }
31677
31678         var groups = surface.selectAll('.layer-hit').selectAll('g.turn')
31679             .data(turns, key);
31680
31681         // Enter
31682         var enter = groups.enter().append('g')
31683             .attr('class', 'turn');
31684
31685         var nEnter = enter.filter(function (turn) { return !turn.u; });
31686
31687         nEnter.append('rect')
31688             .attr('transform', 'translate(-22, -12)')
31689             .attr('width', '44')
31690             .attr('height', '24');
31691
31692         nEnter.append('use')
31693             .attr('transform', 'translate(-22, -12)')
31694             .attr('width', '44')
31695             .attr('height', '24');
31696
31697
31698         var uEnter = enter.filter(function (turn) { return turn.u; });
31699
31700         uEnter.append('circle')
31701             .attr('r', '16');
31702
31703         uEnter.append('use')
31704             .attr('transform', 'translate(-16, -16)')
31705             .attr('width', '32')
31706             .attr('height', '32');
31707
31708
31709         // Update
31710         groups
31711             .attr('transform', function (turn) {
31712                 var v = graph.entity(turn.via.node),
31713                     t = graph.entity(turn.to.node),
31714                     a = iD.geo.angle(v, t, projection),
31715                     p = projection(v.loc),
31716                     r = turn.u ? 0 : 60;
31717
31718                 return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' +
31719                     'rotate(' + a * 180 / Math.PI + ')';
31720             });
31721
31722         groups.select('use')
31723             .attr('xlink:href', icon);
31724
31725         groups.select('rect');
31726         groups.select('circle');
31727
31728
31729         // Exit
31730         groups.exit()
31731             .remove();
31732
31733         return this;
31734     };
31735 };
31736 iD.svg.Vertices = function(projection, context) {
31737     var radiuses = {
31738         //       z16-, z17, z18+, tagged
31739         shadow: [6,    7.5,   7.5,  11.5],
31740         stroke: [2.5,  3.5,   3.5,  7],
31741         fill:   [1,    1.5,   1.5,  1.5]
31742     };
31743
31744     var hover;
31745
31746     function siblingAndChildVertices(ids, graph, extent) {
31747         var vertices = {};
31748
31749         function addChildVertices(entity) {
31750             if (!context.features().isHiddenFeature(entity, graph, entity.geometry(graph))) {
31751                 var i;
31752                 if (entity.type === 'way') {
31753                     for (i = 0; i < entity.nodes.length; i++) {
31754                         addChildVertices(graph.entity(entity.nodes[i]));
31755                     }
31756                 } else if (entity.type === 'relation') {
31757                     for (i = 0; i < entity.members.length; i++) {
31758                         var member = context.hasEntity(entity.members[i].id);
31759                         if (member) {
31760                             addChildVertices(member);
31761                         }
31762                     }
31763                 } else if (entity.intersects(extent, graph)) {
31764                     vertices[entity.id] = entity;
31765                 }
31766             }
31767         }
31768
31769         ids.forEach(function(id) {
31770             var entity = context.hasEntity(id);
31771             if (entity && entity.type === 'node') {
31772                 vertices[entity.id] = entity;
31773                 context.graph().parentWays(entity).forEach(function(entity) {
31774                     addChildVertices(entity);
31775                 });
31776             } else if (entity) {
31777                 addChildVertices(entity);
31778             }
31779         });
31780
31781         return vertices;
31782     }
31783
31784     function draw(selection, vertices, klass, graph, zoom) {
31785         var icons = {},
31786             z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
31787
31788         var groups = selection
31789             .data(vertices, iD.Entity.key);
31790
31791         function icon(entity) {
31792             if (entity.id in icons) return icons[entity.id];
31793             icons[entity.id] =
31794                 entity.hasInterestingTags() &&
31795                 context.presets().match(entity, graph).icon;
31796             return icons[entity.id];
31797         }
31798
31799         function setClass(klass) {
31800             return function(entity) {
31801                 this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id);
31802             };
31803         }
31804
31805         function setAttributes(selection) {
31806             ['shadow','stroke','fill'].forEach(function(klass) {
31807                 var rads = radiuses[klass];
31808                 selection.selectAll('.' + klass)
31809                     .each(function(entity) {
31810                         var i = z && icon(entity),
31811                             c = i ? 0.5 : 0,
31812                             r = rads[i ? 3 : z];
31813                         this.setAttribute('cx', c);
31814                         this.setAttribute('cy', -c);
31815                         this.setAttribute('r', r);
31816                         if (i && klass === 'fill') {
31817                             this.setAttribute('visibility', 'hidden');
31818                         } else {
31819                             this.removeAttribute('visibility');
31820                         }
31821                     });
31822             });
31823
31824             selection.selectAll('use')
31825                 .each(function() {
31826                     if (z) {
31827                         this.removeAttribute('visibility');
31828                     } else {
31829                         this.setAttribute('visibility', 'hidden');
31830                     }
31831                 });
31832         }
31833
31834         var enter = groups.enter()
31835             .append('g')
31836             .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; });
31837
31838         enter.append('circle')
31839             .each(setClass('shadow'));
31840
31841         enter.append('circle')
31842             .each(setClass('stroke'));
31843
31844         // Vertices with icons get a `use`.
31845         enter.filter(function(d) { return icon(d); })
31846             .append('use')
31847             .attr('transform', 'translate(-6, -6)')
31848             .attr('xlink:href', function(d) { return '#' + icon(d) + '-12'; })
31849             .attr('width', '12px')
31850             .attr('height', '12px')
31851             .each(setClass('icon'));
31852
31853         // Vertices with tags get a fill.
31854         enter.filter(function(d) { return d.hasInterestingTags(); })
31855             .append('circle')
31856             .each(setClass('fill'));
31857
31858         groups
31859             .attr('transform', iD.svg.PointTransform(projection))
31860             .classed('shared', function(entity) { return graph.isShared(entity); })
31861             .call(setAttributes);
31862
31863         groups.exit()
31864             .remove();
31865     }
31866
31867     function drawVertices(surface, graph, entities, filter, extent, zoom) {
31868         var selected = siblingAndChildVertices(context.selectedIDs(), graph, extent),
31869             wireframe = surface.classed('fill-wireframe'),
31870             vertices = [];
31871
31872         for (var i = 0; i < entities.length; i++) {
31873             var entity = entities[i],
31874                 geometry = entity.geometry(graph);
31875
31876             if (wireframe && geometry === 'point') {
31877                 vertices.push(entity);
31878                 continue;
31879             }
31880
31881             if (geometry !== 'vertex')
31882                 continue;
31883
31884             if (entity.id in selected ||
31885                 entity.hasInterestingTags() ||
31886                 entity.isIntersection(graph)) {
31887                 vertices.push(entity);
31888             }
31889         }
31890
31891         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-persistent')
31892             .filter(filter)
31893             .call(draw, vertices, 'vertex-persistent', graph, zoom);
31894
31895         drawHover(surface, graph, extent, zoom);
31896     }
31897
31898     function drawHover(surface, graph, extent, zoom) {
31899         var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent) : {};
31900
31901         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-hover')
31902             .call(draw, d3.values(hovered), 'vertex-hover', graph, zoom);
31903     }
31904
31905     drawVertices.drawHover = function(surface, graph, target, extent, zoom) {
31906         if (target === hover) return;
31907         hover = target;
31908         drawHover(surface, graph, extent, zoom);
31909     };
31910
31911     return drawVertices;
31912 };
31913 iD.ui = function(context) {
31914     function render(container) {
31915         var map = context.map();
31916
31917         if (iD.detect().opera) container.classed('opera', true);
31918
31919         var hash = iD.behavior.Hash(context);
31920
31921         hash();
31922
31923         if (!hash.hadHash) {
31924             map.centerZoom([0, 0], 2);
31925         }
31926
31927         container.append('svg')
31928             .attr('id', 'defs')
31929             .call(iD.svg.Defs(context));
31930
31931         container.append('div')
31932             .attr('id', 'sidebar')
31933             .attr('class', 'col4')
31934             .call(ui.sidebar);
31935
31936         var content = container.append('div')
31937             .attr('id', 'content');
31938
31939         var bar = content.append('div')
31940             .attr('id', 'bar')
31941             .attr('class', 'fillD');
31942
31943         content.append('div')
31944             .attr('id', 'map')
31945             .call(map);
31946
31947         content
31948             .call(iD.ui.MapInMap(context));
31949
31950         content.append('div')
31951             .call(iD.ui.Info(context));
31952
31953         bar.append('div')
31954             .attr('class', 'spacer col4');
31955
31956         var limiter = bar.append('div')
31957             .attr('class', 'limiter');
31958
31959         limiter.append('div')
31960             .attr('class', 'button-wrap joined col3')
31961             .call(iD.ui.Modes(context), limiter);
31962
31963         limiter.append('div')
31964             .attr('class', 'button-wrap joined col1')
31965             .call(iD.ui.UndoRedo(context));
31966
31967         limiter.append('div')
31968             .attr('class', 'button-wrap col1')
31969             .call(iD.ui.Save(context));
31970
31971         bar.append('div')
31972             .attr('class', 'full-screen')
31973             .call(iD.ui.FullScreen(context));
31974
31975         bar.append('div')
31976             .attr('class', 'spinner')
31977             .call(iD.ui.Spinner(context));
31978
31979         var controls = bar.append('div')
31980             .attr('class', 'map-controls');
31981
31982         controls.append('div')
31983             .attr('class', 'map-control zoombuttons')
31984             .call(iD.ui.Zoom(context));
31985
31986         controls.append('div')
31987             .attr('class', 'map-control geolocate-control')
31988             .call(iD.ui.Geolocate(context));
31989
31990         controls.append('div')
31991             .attr('class', 'map-control background-control')
31992             .call(iD.ui.Background(context));
31993
31994         controls.append('div')
31995             .attr('class', 'map-control map-data-control')
31996             .call(iD.ui.MapData(context));
31997
31998         controls.append('div')
31999             .attr('class', 'map-control help-control')
32000             .call(iD.ui.Help(context));
32001
32002         var about = content.append('div')
32003             .attr('id', 'about');
32004
32005         about.append('div')
32006             .attr('id', 'attrib')
32007             .call(iD.ui.Attribution(context));
32008
32009         var footer = about.append('div')
32010             .attr('id', 'footer')
32011             .attr('class', 'fillD');
32012
32013         footer.append('div')
32014             .attr('class', 'api-status')
32015             .call(iD.ui.Status(context));
32016
32017         footer.append('div')
32018             .attr('id', 'scale-block')
32019             .call(iD.ui.Scale(context));
32020
32021         var aboutList = footer.append('div')
32022             .attr('id', 'info-block')
32023             .append('ul')
32024             .attr('id', 'about-list');
32025
32026         if (!context.embed()) {
32027             aboutList.call(iD.ui.Account(context));
32028         }
32029
32030         aboutList.append('li')
32031             .append('a')
32032             .attr('target', '_blank')
32033             .attr('tabindex', -1)
32034             .attr('href', 'https://github.com/openstreetmap/iD')
32035             .text(iD.version);
32036
32037         var issueLinks = aboutList.append('li');
32038
32039         issueLinks.append('a')
32040             .attr('target', '_blank')
32041             .attr('tabindex', -1)
32042             .attr('href', 'https://github.com/openstreetmap/iD/issues')
32043             .call(iD.svg.Icon('#icon-bug', 'light'))
32044             .call(bootstrap.tooltip()
32045                 .title(t('report_a_bug'))
32046                 .placement('top')
32047             );
32048
32049         issueLinks.append('a')
32050             .attr('target', '_blank')
32051             .attr('tabindex', -1)
32052             .attr('href', 'https://github.com/openstreetmap/iD/blob/master/CONTRIBUTING.md#translating')
32053             .call(iD.svg.Icon('#icon-translate', 'light'))
32054             .call(bootstrap.tooltip()
32055                 .title(t('help_translate'))
32056                 .placement('top')
32057             );
32058
32059         aboutList.append('li')
32060             .attr('class', 'feature-warning')
32061             .attr('tabindex', -1)
32062             .call(iD.ui.FeatureInfo(context));
32063
32064         aboutList.append('li')
32065             .attr('class', 'user-list')
32066             .attr('tabindex', -1)
32067             .call(iD.ui.Contributors(context));
32068
32069         window.onbeforeunload = function() {
32070             return context.save();
32071         };
32072
32073         window.onunload = function() {
32074             context.history().unlock();
32075         };
32076
32077         var mapDimensions = map.dimensions();
32078
32079         d3.select(window).on('resize.editor', function() {
32080             mapDimensions = content.dimensions(null);
32081             map.dimensions(mapDimensions);
32082         });
32083
32084         function pan(d) {
32085             return function() {
32086                 d3.event.preventDefault();
32087                 if (!context.inIntro()) context.pan(d);
32088             };
32089         }
32090
32091         // pan amount
32092         var pa = 10;
32093
32094         var keybinding = d3.keybinding('main')
32095             .on('⌫', function() { d3.event.preventDefault(); })
32096             .on('←', pan([pa, 0]))
32097             .on('↑', pan([0, pa]))
32098             .on('→', pan([-pa, 0]))
32099             .on('↓', pan([0, -pa]))
32100             .on('⇧←', pan([mapDimensions[0], 0]))
32101             .on('⇧↑', pan([0, mapDimensions[1]]))
32102             .on('⇧→', pan([-mapDimensions[0], 0]))
32103             .on('⇧↓', pan([0, -mapDimensions[1]]))
32104             .on(iD.ui.cmd('⌘←'), pan([mapDimensions[0], 0]))
32105             .on(iD.ui.cmd('⌘↑'), pan([0, mapDimensions[1]]))
32106             .on(iD.ui.cmd('⌘→'), pan([-mapDimensions[0], 0]))
32107             .on(iD.ui.cmd('⌘↓'), pan([0, -mapDimensions[1]]));
32108
32109         d3.select(document)
32110             .call(keybinding);
32111
32112         context.enter(iD.modes.Browse(context));
32113
32114         context.container()
32115             .call(iD.ui.Splash(context))
32116             .call(iD.ui.Restore(context));
32117
32118         var authenticating = iD.ui.Loading(context)
32119             .message(t('loading_auth'));
32120
32121         context.connection()
32122             .on('authenticating.ui', function() {
32123                 context.container()
32124                     .call(authenticating);
32125             })
32126             .on('authenticated.ui', function() {
32127                 authenticating.close();
32128             });
32129     }
32130
32131     function ui(container) {
32132         context.container(container);
32133         context.loadLocale(function() {
32134             render(container);
32135         });
32136     }
32137
32138     ui.sidebar = iD.ui.Sidebar(context);
32139
32140     return ui;
32141 };
32142
32143 iD.ui.tooltipHtml = function(text, key) {
32144     var s = '<span>' + text + '</span>';
32145     if (key) {
32146         s += '<div class="keyhint-wrap">' +
32147             '<span> ' + (t('tooltip_keyhint')) + ' </span>' +
32148             '<span class="keyhint"> ' + key + '</span></div>';
32149     }
32150     return s;
32151 };
32152 iD.ui.Account = function(context) {
32153     var connection = context.connection();
32154
32155     function update(selection) {
32156         if (!connection.authenticated()) {
32157             selection.selectAll('#userLink, #logoutLink')
32158                 .classed('hide', true);
32159             return;
32160         }
32161
32162         connection.userDetails(function(err, details) {
32163             var userLink = selection.select('#userLink'),
32164                 logoutLink = selection.select('#logoutLink');
32165
32166             userLink.html('');
32167             logoutLink.html('');
32168
32169             if (err) return;
32170
32171             selection.selectAll('#userLink, #logoutLink')
32172                 .classed('hide', false);
32173
32174             // Link
32175             userLink.append('a')
32176                 .attr('href', connection.userURL(details.display_name))
32177                 .attr('target', '_blank');
32178
32179             // Add thumbnail or dont
32180             if (details.image_url) {
32181                 userLink.append('img')
32182                     .attr('class', 'icon pre-text user-icon')
32183                     .attr('src', details.image_url);
32184             } else {
32185                 userLink
32186                     .call(iD.svg.Icon('#icon-avatar', 'pre-text light'));
32187             }
32188
32189             // Add user name
32190             userLink.append('span')
32191                 .attr('class', 'label')
32192                 .text(details.display_name);
32193
32194             logoutLink.append('a')
32195                 .attr('class', 'logout')
32196                 .attr('href', '#')
32197                 .text(t('logout'))
32198                 .on('click.logout', function() {
32199                     d3.event.preventDefault();
32200                     connection.logout();
32201                 });
32202         });
32203     }
32204
32205     return function(selection) {
32206         selection.append('li')
32207             .attr('id', 'logoutLink')
32208             .classed('hide', true);
32209
32210         selection.append('li')
32211             .attr('id', 'userLink')
32212             .classed('hide', true);
32213
32214         connection.on('auth.account', function() { update(selection); });
32215         update(selection);
32216     };
32217 };
32218 iD.ui.Attribution = function(context) {
32219     var selection;
32220
32221     function attribution(data, klass) {
32222         var div = selection.selectAll('.' + klass)
32223             .data([0]);
32224
32225         div.enter()
32226             .append('div')
32227             .attr('class', klass);
32228
32229         var background = div.selectAll('.attribution')
32230             .data(data, function(d) { return d.name(); });
32231
32232         background.enter()
32233             .append('span')
32234             .attr('class', 'attribution')
32235             .each(function(d) {
32236                 if (d.terms_html) {
32237                     d3.select(this)
32238                         .html(d.terms_html);
32239                     return;
32240                 }
32241
32242                 var source = d.terms_text || d.id || d.name();
32243
32244                 if (d.logo) {
32245                     source = '<img class="source-image" src="' + context.imagePath(d.logo) + '">';
32246                 }
32247
32248                 if (d.terms_url) {
32249                     d3.select(this)
32250                         .append('a')
32251                         .attr('href', d.terms_url)
32252                         .attr('target', '_blank')
32253                         .html(source);
32254                 } else {
32255                     d3.select(this)
32256                         .text(source);
32257                 }
32258             });
32259
32260         background.exit()
32261             .remove();
32262
32263         var copyright = background.selectAll('.copyright-notice')
32264             .data(function(d) {
32265                 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
32266                 return notice ? [notice] : [];
32267             });
32268
32269         copyright.enter()
32270             .append('span')
32271             .attr('class', 'copyright-notice');
32272
32273         copyright.text(String);
32274
32275         copyright.exit()
32276             .remove();
32277     }
32278
32279     function update() {
32280         attribution([context.background().baseLayerSource()], 'base-layer-attribution');
32281         attribution(context.background().overlayLayerSources().filter(function (s) {
32282             return s.validZoom(context.map().zoom());
32283         }), 'overlay-layer-attribution');
32284     }
32285
32286     return function(select) {
32287         selection = select;
32288
32289         context.background()
32290             .on('change.attribution', update);
32291
32292         context.map()
32293             .on('move.attribution', _.throttle(update, 400, {leading: false}));
32294
32295         update();
32296     };
32297 };
32298 iD.ui.Background = function(context) {
32299     var key = 'B',
32300         opacities = [1, 0.75, 0.5, 0.25],
32301         directions = [
32302             ['left', [1, 0]],
32303             ['top', [0, -1]],
32304             ['right', [-1, 0]],
32305             ['bottom', [0, 1]]],
32306         opacityDefault = (context.storage('background-opacity') !== null) ?
32307             (+context.storage('background-opacity')) : 1.0,
32308         customTemplate = context.storage('background-custom-template') || '';
32309
32310     // Can be 0 from <1.3.0 use or due to issue #1923.
32311     if (opacityDefault === 0) opacityDefault = 1.0;
32312
32313     function background(selection) {
32314
32315         function sortSources(a, b) {
32316             return a.best() ? -1
32317                 : b.best() ? 1
32318                 : a.id === 'none' ? 1
32319                 : b.id === 'none' ? -1
32320                 : d3.ascending(a, b);
32321         }
32322
32323         function setOpacity(d) {
32324             var bg = context.container().selectAll('.layer-background')
32325                 .transition()
32326                 .style('opacity', d)
32327                 .attr('data-opacity', d);
32328
32329             if (!iD.detect().opera) {
32330                 iD.util.setTransform(bg, 0, 0);
32331             }
32332
32333             opacityList.selectAll('li')
32334                 .classed('active', function(_) { return _ === d; });
32335
32336             context.storage('background-opacity', d);
32337         }
32338
32339         function selectLayer() {
32340             function active(d) {
32341                 return context.background().showsLayer(d);
32342             }
32343
32344             content.selectAll('.layer, .custom_layer')
32345                 .classed('active', active)
32346                 .selectAll('input')
32347                 .property('checked', active);
32348         }
32349
32350         function clickSetSource(d) {
32351             d3.event.preventDefault();
32352             context.background().baseLayerSource(d);
32353             selectLayer();
32354         }
32355
32356         function editCustom() {
32357             d3.event.preventDefault();
32358             var template = window.prompt(t('background.custom_prompt'), customTemplate);
32359             if (!template ||
32360                 template.indexOf('google.com') !== -1 ||
32361                 template.indexOf('googleapis.com') !== -1 ||
32362                 template.indexOf('google.ru') !== -1) {
32363                 selectLayer();
32364                 return;
32365             }
32366             setCustom(template);
32367         }
32368
32369         function setCustom(template) {
32370             context.background().baseLayerSource(iD.BackgroundSource.Custom(template));
32371             selectLayer();
32372             context.storage('background-custom-template', template);
32373         }
32374
32375         function clickSetOverlay(d) {
32376             d3.event.preventDefault();
32377             context.background().toggleOverlayLayer(d);
32378             selectLayer();
32379         }
32380
32381         function drawList(layerList, type, change, filter) {
32382             var sources = context.background()
32383                 .sources(context.map().extent())
32384                 .filter(filter);
32385
32386             var layerLinks = layerList.selectAll('li.layer')
32387                 .data(sources, function(d) { return d.name(); })
32388                 .sort(sortSources);
32389
32390             var enter = layerLinks.enter()
32391                 .insert('li', '.custom_layer')
32392                 .attr('class', 'layer')
32393                 .classed('best', function(d) { return d.best(); });
32394
32395             // only set tooltips for layers with tooltips
32396             enter.filter(function(d) { return d.description; })
32397                 .call(bootstrap.tooltip()
32398                     .title(function(d) { return d.description; })
32399                     .placement('top'));
32400
32401             enter.filter(function(d) { return d.best(); })
32402                 .append('div')
32403                 .attr('class', 'best')
32404                 .call(bootstrap.tooltip()
32405                     .title(t('background.best_imagery'))
32406                     .placement('left'))
32407                 .append('span')
32408                 .html('&#9733;');
32409
32410             var label = enter.append('label');
32411
32412             label.append('input')
32413                 .attr('type', type)
32414                 .attr('name', 'layers')
32415                 .on('change', change);
32416
32417             label.append('span')
32418                 .text(function(d) { return d.name(); });
32419
32420             layerLinks.exit()
32421                 .remove();
32422
32423             layerList.style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
32424         }
32425
32426         function update() {
32427             backgroundList.call(drawList, 'radio', clickSetSource, function(d) { return !d.overlay; });
32428             overlayList.call(drawList, 'checkbox', clickSetOverlay, function(d) { return d.overlay; });
32429
32430             selectLayer();
32431
32432             var source = context.background().baseLayerSource();
32433             if (source.id === 'custom') {
32434                 customTemplate = source.template;
32435             }
32436         }
32437
32438         function clickNudge(d) {
32439             var timeout = window.setTimeout(function() {
32440                     interval = window.setInterval(nudge, 100);
32441                 }, 500),
32442                 interval;
32443
32444             d3.select(this).on('mouseup', function() {
32445                 window.clearInterval(interval);
32446                 window.clearTimeout(timeout);
32447                 nudge();
32448             });
32449
32450             function nudge() {
32451                 var offset = context.background()
32452                     .nudge(d[1], context.map().zoom())
32453                     .offset();
32454                 resetButton.classed('disabled', offset[0] === 0 && offset[1] === 0);
32455             }
32456         }
32457
32458         function hide() { setVisible(false); }
32459
32460         function toggle() {
32461             if (d3.event) d3.event.preventDefault();
32462             tooltip.hide(button);
32463             setVisible(!button.classed('active'));
32464         }
32465
32466         function setVisible(show) {
32467             if (show !== shown) {
32468                 button.classed('active', show);
32469                 shown = show;
32470
32471                 if (show) {
32472                     selection.on('mousedown.background-inside', function() {
32473                         return d3.event.stopPropagation();
32474                     });
32475                     content.style('display', 'block')
32476                         .style('right', '-300px')
32477                         .transition()
32478                         .duration(200)
32479                         .style('right', '0px');
32480                 } else {
32481                     content.style('display', 'block')
32482                         .style('right', '0px')
32483                         .transition()
32484                         .duration(200)
32485                         .style('right', '-300px')
32486                         .each('end', function() {
32487                             d3.select(this).style('display', 'none');
32488                         });
32489                     selection.on('mousedown.background-inside', null);
32490                 }
32491             }
32492         }
32493
32494
32495         var content = selection.append('div')
32496                 .attr('class', 'fillL map-overlay col3 content hide'),
32497             tooltip = bootstrap.tooltip()
32498                 .placement('left')
32499                 .html(true)
32500                 .title(iD.ui.tooltipHtml(t('background.description'), key)),
32501             button = selection.append('button')
32502                 .attr('tabindex', -1)
32503                 .on('click', toggle)
32504                 .call(iD.svg.Icon('#icon-layers', 'light'))
32505                 .call(tooltip),
32506             shown = false;
32507
32508         var opa = content.append('div')
32509                 .attr('class', 'opacity-options-wrapper');
32510
32511         opa.append('h4')
32512             .text(t('background.title'));
32513
32514         var opacityList = opa.append('ul')
32515             .attr('class', 'opacity-options');
32516
32517         opacityList.selectAll('div.opacity')
32518             .data(opacities)
32519             .enter()
32520             .append('li')
32521             .attr('data-original-title', function(d) {
32522                 return t('background.percent_brightness', { opacity: (d * 100) });
32523             })
32524             .on('click.set-opacity', setOpacity)
32525             .html('<div class="select-box"></div>')
32526             .call(bootstrap.tooltip()
32527                 .placement('left'))
32528             .append('div')
32529             .attr('class', 'opacity')
32530             .style('opacity', function(d) { return 1.25 - d; });
32531
32532         var backgroundList = content.append('ul')
32533             .attr('class', 'layer-list');
32534
32535         var custom = backgroundList.append('li')
32536             .attr('class', 'custom_layer')
32537             .datum(iD.BackgroundSource.Custom());
32538
32539         custom.append('button')
32540             .attr('class', 'layer-browse')
32541             .call(bootstrap.tooltip()
32542                 .title(t('background.custom_button'))
32543                 .placement('left'))
32544             .on('click', editCustom)
32545             .call(iD.svg.Icon('#icon-search'));
32546
32547         var label = custom.append('label');
32548
32549         label.append('input')
32550             .attr('type', 'radio')
32551             .attr('name', 'layers')
32552             .on('change', function () {
32553                 if (customTemplate) {
32554                     setCustom(customTemplate);
32555                 } else {
32556                     editCustom();
32557                 }
32558             });
32559
32560         label.append('span')
32561             .text(t('background.custom'));
32562
32563         content.append('div')
32564           .attr('class', 'imagery-faq')
32565           .append('a')
32566           .attr('target', '_blank')
32567           .attr('tabindex', -1)
32568           .call(iD.svg.Icon('#icon-out-link', 'inline'))
32569           .attr('href', 'https://github.com/openstreetmap/iD/blob/master/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
32570           .append('span')
32571           .text(t('background.imagery_source_faq'));
32572
32573         var overlayList = content.append('ul')
32574             .attr('class', 'layer-list');
32575
32576         var controls = content.append('div')
32577             .attr('class', 'controls-list');
32578
32579         var minimapLabel = controls
32580             .append('label')
32581             .call(bootstrap.tooltip()
32582                 .html(true)
32583                 .title(iD.ui.tooltipHtml(t('background.minimap.tooltip'), '/'))
32584                 .placement('top')
32585             );
32586
32587         minimapLabel.classed('minimap-toggle', true)
32588             .append('input')
32589             .attr('type', 'checkbox')
32590             .on('change', function() {
32591                 iD.ui.MapInMap.toggle();
32592                 d3.event.preventDefault();
32593             });
32594
32595         minimapLabel.append('span')
32596             .text(t('background.minimap.description'));
32597
32598         var adjustments = content.append('div')
32599             .attr('class', 'adjustments');
32600
32601         adjustments.append('a')
32602             .text(t('background.fix_misalignment'))
32603             .attr('href', '#')
32604             .classed('hide-toggle', true)
32605             .classed('expanded', false)
32606             .on('click', function() {
32607                 var exp = d3.select(this).classed('expanded');
32608                 nudgeContainer.style('display', exp ? 'none' : 'block');
32609                 d3.select(this).classed('expanded', !exp);
32610                 d3.event.preventDefault();
32611             });
32612
32613         var nudgeContainer = adjustments.append('div')
32614             .attr('class', 'nudge-container cf')
32615             .style('display', 'none');
32616
32617         nudgeContainer.selectAll('button')
32618             .data(directions).enter()
32619             .append('button')
32620             .attr('class', function(d) { return d[0] + ' nudge'; })
32621             .on('mousedown', clickNudge);
32622
32623         var resetButton = nudgeContainer
32624             .append('button')
32625             .attr('class', 'reset disabled')
32626             .on('click', function () {
32627                 context.background().offset([0, 0]);
32628                 resetButton.classed('disabled', true);
32629             })
32630             .call(iD.svg.Icon('#icon-undo'));
32631
32632         context.map()
32633             .on('move.background-update', _.debounce(update, 1000));
32634
32635         context.background()
32636             .on('change.background-update', update);
32637
32638         update();
32639         setOpacity(opacityDefault);
32640
32641         var keybinding = d3.keybinding('background')
32642             .on(key, toggle)
32643             .on('F', hide)
32644             .on('H', hide);
32645
32646         d3.select(document)
32647             .call(keybinding);
32648
32649         context.surface().on('mousedown.background-outside', hide);
32650         context.container().on('mousedown.background-outside', hide);
32651     }
32652
32653     return background;
32654 };
32655 // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
32656 // For example, ⌘Z -> Ctrl+Z
32657 iD.ui.cmd = function(code) {
32658     if (iD.detect().os === 'mac') {
32659         return code;
32660     }
32661
32662     if (iD.detect().os === 'win') {
32663         if (code === '⌘⇧Z') return 'Ctrl+Y';
32664     }
32665
32666     var result = '',
32667         replacements = {
32668             '⌘': 'Ctrl',
32669             '⇧': 'Shift',
32670             '⌥': 'Alt',
32671             '⌫': 'Backspace',
32672             '⌦': 'Delete'
32673         };
32674
32675     for (var i = 0; i < code.length; i++) {
32676         if (code[i] in replacements) {
32677             result += replacements[code[i]] + '+';
32678         } else {
32679             result += code[i];
32680         }
32681     }
32682
32683     return result;
32684 };
32685 iD.ui.Commit = function(context) {
32686     var dispatch = d3.dispatch('cancel', 'save');
32687
32688     function commit(selection) {
32689         var changes = context.history().changes(),
32690             summary = context.history().difference().summary();
32691
32692         function zoomToEntity(change) {
32693             var entity = change.entity;
32694             if (change.changeType !== 'deleted' &&
32695                 context.graph().entity(entity.id).geometry(context.graph()) !== 'vertex') {
32696                 context.map().zoomTo(entity);
32697                 context.surface().selectAll(
32698                     iD.util.entityOrMemberSelector([entity.id], context.graph()))
32699                     .classed('hover', true);
32700             }
32701         }
32702
32703         var header = selection.append('div')
32704             .attr('class', 'header fillL');
32705
32706         header.append('h3')
32707             .text(t('commit.title'));
32708
32709         var body = selection.append('div')
32710             .attr('class', 'body');
32711
32712
32713         // Comment Section
32714         var commentSection = body.append('div')
32715             .attr('class', 'modal-section form-field commit-form');
32716
32717         commentSection.append('label')
32718             .attr('class', 'form-label')
32719             .text(t('commit.message_label'));
32720
32721         var commentField = commentSection.append('textarea')
32722             .attr('placeholder', t('commit.description_placeholder'))
32723             .attr('maxlength', 255)
32724             .property('value', context.storage('comment') || '')
32725             .on('input.save', enableDisableSaveButton)
32726             .on('change.save', enableDisableSaveButton)
32727             .on('blur.save', function() {
32728                 context.storage('comment', this.value);
32729             });
32730
32731         function enableDisableSaveButton() {
32732             d3.selectAll('.save-section .save-button')
32733                 .attr('disabled', (this.value.length ? null : true));
32734         }
32735
32736         commentField.node().select();
32737
32738         context.connection().userChangesets(function (err, changesets) {
32739             if (err) return;
32740
32741             var comments = [];
32742
32743             for (var i = 0; i < changesets.length; i++) {
32744                 if (changesets[i].tags.comment) {
32745                     comments.push({
32746                         title: changesets[i].tags.comment,
32747                         value: changesets[i].tags.comment
32748                     });
32749                 }
32750             }
32751
32752             commentField.call(d3.combobox().data(comments));
32753         });
32754
32755         var changeSetInfo = commentSection.append('div')
32756             .attr('class', 'changeset-info');
32757
32758         changeSetInfo.append('a')
32759           .attr('target', '_blank')
32760           .attr('tabindex', -1)
32761           .call(iD.svg.Icon('#icon-out-link', 'inline'))
32762           .attr('href', t('commit.about_changeset_comments_link'))
32763           .append('span')
32764           .text(t('commit.about_changeset_comments'));
32765
32766         // Warnings
32767         var warnings = body.selectAll('div.warning-section')
32768             .data([context.history().validate(changes)])
32769             .enter()
32770             .append('div')
32771             .attr('class', 'modal-section warning-section fillL2')
32772             .style('display', function(d) { return _.isEmpty(d) ? 'none' : null; })
32773             .style('background', '#ffb');
32774
32775         warnings.append('h3')
32776             .text(t('commit.warnings'));
32777
32778         var warningLi = warnings.append('ul')
32779             .attr('class', 'changeset-list')
32780             .selectAll('li')
32781             .data(function(d) { return d; })
32782             .enter()
32783             .append('li')
32784             .style()
32785             .on('mouseover', mouseover)
32786             .on('mouseout', mouseout)
32787             .on('click', warningClick);
32788
32789         warningLi
32790             .call(iD.svg.Icon('#icon-alert', 'pre-text'));
32791
32792         warningLi
32793             .append('strong').text(function(d) {
32794                 return d.message;
32795             });
32796
32797         warningLi.filter(function(d) { return d.tooltip; })
32798             .call(bootstrap.tooltip()
32799                 .title(function(d) { return d.tooltip; })
32800                 .placement('top')
32801             );
32802
32803
32804         // Upload Explanation
32805         var saveSection = body.append('div')
32806             .attr('class','modal-section save-section fillL cf');
32807
32808         var prose = saveSection.append('p')
32809             .attr('class', 'commit-info')
32810             .html(t('commit.upload_explanation'));
32811
32812         context.connection().userDetails(function(err, user) {
32813             if (err) return;
32814
32815             var userLink = d3.select(document.createElement('div'));
32816
32817             if (user.image_url) {
32818                 userLink.append('img')
32819                     .attr('src', user.image_url)
32820                     .attr('class', 'icon pre-text user-icon');
32821             }
32822
32823             userLink.append('a')
32824                 .attr('class','user-info')
32825                 .text(user.display_name)
32826                 .attr('href', context.connection().userURL(user.display_name))
32827                 .attr('tabindex', -1)
32828                 .attr('target', '_blank');
32829
32830             prose.html(t('commit.upload_explanation_with_user', {user: userLink.html()}));
32831         });
32832
32833
32834         // Buttons
32835         var buttonSection = saveSection.append('div')
32836             .attr('class','buttons fillL cf');
32837
32838         var saveButton = buttonSection.append('button')
32839             .attr('class', 'action col5 button save-button')
32840             .attr('disabled', function() {
32841                 var n = d3.select('.commit-form textarea').node();
32842                 return (n && n.value.length) ? null : true;
32843             })
32844             .on('click.save', function() {
32845                 dispatch.save({
32846                     comment: commentField.node().value
32847                 });
32848             });
32849
32850         saveButton.append('span')
32851             .attr('class', 'label')
32852             .text(t('commit.save'));
32853
32854         var cancelButton = buttonSection.append('button')
32855             .attr('class', 'action col5 button cancel-button')
32856             .on('click.cancel', function() { dispatch.cancel(); });
32857
32858         cancelButton.append('span')
32859             .attr('class', 'label')
32860             .text(t('commit.cancel'));
32861
32862
32863         // Changes
32864         var changeSection = body.selectAll('div.commit-section')
32865             .data([0])
32866             .enter()
32867             .append('div')
32868             .attr('class', 'commit-section modal-section fillL2');
32869
32870         changeSection.append('h3')
32871             .text(t('commit.changes', {count: summary.length}));
32872
32873         var li = changeSection.append('ul')
32874             .attr('class', 'changeset-list')
32875             .selectAll('li')
32876             .data(summary)
32877             .enter()
32878             .append('li')
32879             .on('mouseover', mouseover)
32880             .on('mouseout', mouseout)
32881             .on('click', zoomToEntity);
32882
32883         li.each(function(d) {
32884             d3.select(this)
32885                 .call(iD.svg.Icon('#icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
32886         });
32887
32888         li.append('span')
32889             .attr('class', 'change-type')
32890             .text(function(d) {
32891                 return t('commit.' + d.changeType) + ' ';
32892             });
32893
32894         li.append('strong')
32895             .attr('class', 'entity-type')
32896             .text(function(d) {
32897                 return context.presets().match(d.entity, d.graph).name();
32898             });
32899
32900         li.append('span')
32901             .attr('class', 'entity-name')
32902             .text(function(d) {
32903                 var name = iD.util.displayName(d.entity) || '',
32904                     string = '';
32905                 if (name !== '') string += ':';
32906                 return string += ' ' + name;
32907             });
32908
32909         li.style('opacity', 0)
32910             .transition()
32911             .style('opacity', 1);
32912
32913         li.style('opacity', 0)
32914             .transition()
32915             .style('opacity', 1);
32916
32917         function mouseover(d) {
32918             if (d.entity) {
32919                 context.surface().selectAll(
32920                     iD.util.entityOrMemberSelector([d.entity.id], context.graph())
32921                 ).classed('hover', true);
32922             }
32923         }
32924
32925         function mouseout() {
32926             context.surface().selectAll('.hover')
32927                 .classed('hover', false);
32928         }
32929
32930         function warningClick(d) {
32931             if (d.entity) {
32932                 context.map().zoomTo(d.entity);
32933                 context.enter(
32934                     iD.modes.Select(context, [d.entity.id])
32935                         .suppressMenu(true));
32936             }
32937         }
32938     }
32939
32940     return d3.rebind(commit, dispatch, 'on');
32941 };
32942 iD.ui.confirm = function(selection) {
32943     var modal = iD.ui.modal(selection);
32944
32945     modal.select('.modal')
32946         .classed('modal-alert', true);
32947
32948     var section = modal.select('.content');
32949
32950     section.append('div')
32951         .attr('class', 'modal-section header');
32952
32953     section.append('div')
32954         .attr('class', 'modal-section message-text');
32955
32956     var buttons = section.append('div')
32957         .attr('class', 'modal-section buttons cf');
32958
32959     modal.okButton = function() {
32960         buttons
32961             .append('button')
32962             .attr('class', 'action col4')
32963             .on('click.confirm', function() {
32964                 modal.remove();
32965             })
32966             .text(t('confirm.okay'));
32967
32968         return modal;
32969     };
32970
32971     return modal;
32972 };
32973 iD.ui.Conflicts = function(context) {
32974     var dispatch = d3.dispatch('download', 'cancel', 'save'),
32975         list;
32976
32977     function conflicts(selection) {
32978         var header = selection
32979             .append('div')
32980             .attr('class', 'header fillL');
32981
32982         header
32983             .append('button')
32984             .attr('class', 'fr')
32985             .on('click', function() { dispatch.cancel(); })
32986             .call(iD.svg.Icon('#icon-close'));
32987
32988         header
32989             .append('h3')
32990             .text(t('save.conflict.header'));
32991
32992         var body = selection
32993             .append('div')
32994             .attr('class', 'body fillL');
32995
32996         body
32997             .append('div')
32998             .attr('class', 'conflicts-help')
32999             .text(t('save.conflict.help'))
33000             .append('a')
33001             .attr('class', 'conflicts-download')
33002             .text(t('save.conflict.download_changes'))
33003             .on('click.download', function() { dispatch.download(); });
33004
33005         body
33006             .append('div')
33007             .attr('class', 'conflict-container fillL3')
33008             .call(showConflict, 0);
33009
33010         body
33011             .append('div')
33012             .attr('class', 'conflicts-done')
33013             .attr('opacity', 0)
33014             .style('display', 'none')
33015             .text(t('save.conflict.done'));
33016
33017         var buttons = body
33018             .append('div')
33019             .attr('class','buttons col12 joined conflicts-buttons');
33020
33021         buttons
33022             .append('button')
33023             .attr('disabled', list.length > 1)
33024             .attr('class', 'action conflicts-button col6')
33025             .text(t('save.title'))
33026             .on('click.try_again', function() { dispatch.save(); });
33027
33028         buttons
33029             .append('button')
33030             .attr('class', 'secondary-action conflicts-button col6')
33031             .text(t('confirm.cancel'))
33032             .on('click.cancel', function() { dispatch.cancel(); });
33033     }
33034
33035
33036     function showConflict(selection, index) {
33037         if (index < 0 || index >= list.length) return;
33038
33039         var parent = d3.select(selection.node().parentNode);
33040
33041         // enable save button if this is the last conflict being reviewed..
33042         if (index === list.length - 1) {
33043             window.setTimeout(function() {
33044                 parent.select('.conflicts-button')
33045                     .attr('disabled', null);
33046
33047                 parent.select('.conflicts-done')
33048                     .transition()
33049                     .attr('opacity', 1)
33050                     .style('display', 'block');
33051             }, 250);
33052         }
33053
33054         var item = selection
33055             .selectAll('.conflict')
33056             .data([list[index]]);
33057
33058         var enter = item.enter()
33059             .append('div')
33060             .attr('class', 'conflict');
33061
33062         enter
33063             .append('h4')
33064             .attr('class', 'conflict-count')
33065             .text(t('save.conflict.count', { num: index + 1, total: list.length }));
33066
33067         enter
33068             .append('a')
33069             .attr('class', 'conflict-description')
33070             .attr('href', '#')
33071             .text(function(d) { return d.name; })
33072             .on('click', function(d) {
33073                 zoomToEntity(d.id);
33074                 d3.event.preventDefault();
33075             });
33076
33077         var details = enter
33078             .append('div')
33079             .attr('class', 'conflict-detail-container');
33080
33081         details
33082             .append('ul')
33083             .attr('class', 'conflict-detail-list')
33084             .selectAll('li')
33085             .data(function(d) { return d.details || []; })
33086             .enter()
33087             .append('li')
33088             .attr('class', 'conflict-detail-item')
33089             .html(function(d) { return d; });
33090
33091         details
33092             .append('div')
33093             .attr('class', 'conflict-choices')
33094             .call(addChoices);
33095
33096         details
33097             .append('div')
33098             .attr('class', 'conflict-nav-buttons joined cf')
33099             .selectAll('button')
33100             .data(['previous', 'next'])
33101             .enter()
33102             .append('button')
33103             .text(function(d) { return t('save.conflict.' + d); })
33104             .attr('class', 'conflict-nav-button action col6')
33105             .attr('disabled', function(d, i) {
33106                 return (i === 0 && index === 0) ||
33107                     (i === 1 && index === list.length - 1) || null;
33108             })
33109             .on('click', function(d, i) {
33110                 var container = parent.select('.conflict-container'),
33111                 sign = (i === 0 ? -1 : 1);
33112
33113                 container
33114                     .selectAll('.conflict')
33115                     .remove();
33116
33117                 container
33118                     .call(showConflict, index + sign);
33119
33120                 d3.event.preventDefault();
33121             });
33122
33123         item.exit()
33124             .remove();
33125
33126     }
33127
33128     function addChoices(selection) {
33129         var choices = selection
33130             .append('ul')
33131             .attr('class', 'layer-list')
33132             .selectAll('li')
33133             .data(function(d) { return d.choices || []; });
33134
33135         var enter = choices.enter()
33136             .append('li')
33137             .attr('class', 'layer');
33138
33139         var label = enter
33140             .append('label');
33141
33142         label
33143             .append('input')
33144             .attr('type', 'radio')
33145             .attr('name', function(d) { return d.id; })
33146             .on('change', function(d, i) {
33147                 var ul = this.parentNode.parentNode.parentNode;
33148                 ul.__data__.chosen = i;
33149                 choose(ul, d);
33150             });
33151
33152         label
33153             .append('span')
33154             .text(function(d) { return d.text; });
33155
33156         choices
33157             .each(function(d, i) {
33158                 var ul = this.parentNode;
33159                 if (ul.__data__.chosen === i) choose(ul, d);
33160             });
33161     }
33162
33163     function choose(ul, datum) {
33164         if (d3.event) d3.event.preventDefault();
33165
33166         d3.select(ul)
33167             .selectAll('li')
33168             .classed('active', function(d) { return d === datum; })
33169             .selectAll('input')
33170             .property('checked', function(d) { return d === datum; });
33171
33172         var extent = iD.geo.Extent(),
33173             entity;
33174
33175         entity = context.graph().hasEntity(datum.id);
33176         if (entity) extent._extend(entity.extent(context.graph()));
33177
33178         datum.action();
33179
33180         entity = context.graph().hasEntity(datum.id);
33181         if (entity) extent._extend(entity.extent(context.graph()));
33182
33183         zoomToEntity(datum.id, extent);
33184     }
33185
33186     function zoomToEntity(id, extent) {
33187         context.surface().selectAll('.hover')
33188             .classed('hover', false);
33189
33190         var entity = context.graph().hasEntity(id);
33191         if (entity) {
33192             if (extent) {
33193                 context.map().trimmedExtent(extent);
33194             } else {
33195                 context.map().zoomTo(entity);
33196             }
33197             context.surface().selectAll(
33198                 iD.util.entityOrMemberSelector([entity.id], context.graph()))
33199                 .classed('hover', true);
33200         }
33201     }
33202
33203
33204     // The conflict list should be an array of objects like:
33205     // {
33206     //     id: id,
33207     //     name: entityName(local),
33208     //     details: merge.conflicts(),
33209     //     chosen: 1,
33210     //     choices: [
33211     //         choice(id, keepMine, forceLocal),
33212     //         choice(id, keepTheirs, forceRemote)
33213     //     ]
33214     // }
33215     conflicts.list = function(_) {
33216         if (!arguments.length) return list;
33217         list = _;
33218         return conflicts;
33219     };
33220
33221     return d3.rebind(conflicts, dispatch, 'on');
33222 };
33223 iD.ui.Contributors = function(context) {
33224     var debouncedUpdate = _.debounce(function() { update(); }, 1000),
33225         limit = 4,
33226         hidden = false,
33227         wrap = d3.select(null);
33228
33229     function update() {
33230         var users = {},
33231             entities = context.intersects(context.map().extent());
33232
33233         entities.forEach(function(entity) {
33234             if (entity && entity.user) users[entity.user] = true;
33235         });
33236
33237         var u = Object.keys(users),
33238             subset = u.slice(0, u.length > limit ? limit - 1 : limit);
33239
33240         wrap.html('')
33241             .call(iD.svg.Icon('#icon-nearby', 'pre-text light'));
33242
33243         var userList = d3.select(document.createElement('span'));
33244
33245         userList.selectAll()
33246             .data(subset)
33247             .enter()
33248             .append('a')
33249             .attr('class', 'user-link')
33250             .attr('href', function(d) { return context.connection().userURL(d); })
33251             .attr('target', '_blank')
33252             .attr('tabindex', -1)
33253             .text(String);
33254
33255         if (u.length > limit) {
33256             var count = d3.select(document.createElement('span'));
33257
33258             count.append('a')
33259                 .attr('target', '_blank')
33260                 .attr('tabindex', -1)
33261                 .attr('href', function() {
33262                     return context.connection().changesetsURL(context.map().center(), context.map().zoom());
33263                 })
33264                 .text(u.length - limit + 1);
33265
33266             wrap.append('span')
33267                 .html(t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
33268
33269         } else {
33270             wrap.append('span')
33271                 .html(t('contributors.list', { users: userList.html() }));
33272         }
33273
33274         if (!u.length) {
33275             hidden = true;
33276             wrap
33277                 .transition()
33278                 .style('opacity', 0);
33279
33280         } else if (hidden) {
33281             wrap
33282                 .transition()
33283                 .style('opacity', 1);
33284         }
33285     }
33286
33287     return function(selection) {
33288         wrap = selection;
33289         update();
33290
33291         context.connection().on('loaded.contributors', debouncedUpdate);
33292         context.map().on('move.contributors', debouncedUpdate);
33293     };
33294 };
33295 iD.ui.Disclosure = function() {
33296     var dispatch = d3.dispatch('toggled'),
33297         title,
33298         expanded = false,
33299         content = function () {};
33300
33301     var disclosure = function(selection) {
33302         var $link = selection.selectAll('.hide-toggle')
33303             .data([0]);
33304
33305         $link.enter().append('a')
33306             .attr('href', '#')
33307             .attr('class', 'hide-toggle');
33308
33309         $link.text(title)
33310             .on('click', toggle)
33311             .classed('expanded', expanded);
33312
33313         var $body = selection.selectAll('div')
33314             .data([0]);
33315
33316         $body.enter().append('div');
33317
33318         $body.classed('hide', !expanded)
33319             .call(content);
33320
33321         function toggle() {
33322             expanded = !expanded;
33323             $link.classed('expanded', expanded);
33324             $body.call(iD.ui.Toggle(expanded));
33325             dispatch.toggled(expanded);
33326         }
33327     };
33328
33329     disclosure.title = function(_) {
33330         if (!arguments.length) return title;
33331         title = _;
33332         return disclosure;
33333     };
33334
33335     disclosure.expanded = function(_) {
33336         if (!arguments.length) return expanded;
33337         expanded = _;
33338         return disclosure;
33339     };
33340
33341     disclosure.content = function(_) {
33342         if (!arguments.length) return content;
33343         content = _;
33344         return disclosure;
33345     };
33346
33347     return d3.rebind(disclosure, dispatch, 'on');
33348 };
33349 iD.ui.EntityEditor = function(context) {
33350     var dispatch = d3.dispatch('choose'),
33351         state = 'select',
33352         coalesceChanges = false,
33353         modified = false,
33354         base,
33355         id,
33356         preset,
33357         reference;
33358
33359     var presetEditor = iD.ui.preset(context)
33360         .on('change', changeTags);
33361     var rawTagEditor = iD.ui.RawTagEditor(context)
33362         .on('change', changeTags);
33363
33364     function entityEditor(selection) {
33365         var entity = context.entity(id),
33366             tags = _.clone(entity.tags);
33367
33368         var $header = selection.selectAll('.header')
33369             .data([0]);
33370
33371         // Enter
33372         var $enter = $header.enter().append('div')
33373             .attr('class', 'header fillL cf');
33374
33375         $enter.append('button')
33376             .attr('class', 'fl preset-reset preset-choose')
33377             .append('span')
33378             .html('&#9668;');
33379
33380         $enter.append('button')
33381             .attr('class', 'fr preset-close')
33382             .call(iD.svg.Icon(modified ? '#icon-apply' : '#icon-close'));
33383
33384         $enter.append('h3');
33385
33386         // Update
33387         $header.select('h3')
33388             .text(t('inspector.edit'));
33389
33390         $header.select('.preset-close')
33391             .on('click', function() {
33392                 context.enter(iD.modes.Browse(context));
33393             });
33394
33395         var $body = selection.selectAll('.inspector-body')
33396             .data([0]);
33397
33398         // Enter
33399         $enter = $body.enter().append('div')
33400             .attr('class', 'inspector-body');
33401
33402         $enter.append('div')
33403             .attr('class', 'preset-list-item inspector-inner')
33404             .append('div')
33405             .attr('class', 'preset-list-button-wrap')
33406             .append('button')
33407             .attr('class', 'preset-list-button preset-reset')
33408             .call(bootstrap.tooltip()
33409                 .title(t('inspector.back_tooltip'))
33410                 .placement('bottom'))
33411             .append('div')
33412             .attr('class', 'label');
33413
33414         $body.select('.preset-list-button-wrap')
33415             .call(reference.button);
33416
33417         $body.select('.preset-list-item')
33418             .call(reference.body);
33419
33420         $enter.append('div')
33421             .attr('class', 'inspector-border inspector-preset');
33422
33423         $enter.append('div')
33424             .attr('class', 'inspector-border raw-tag-editor inspector-inner');
33425
33426         $enter.append('div')
33427             .attr('class', 'inspector-border raw-member-editor inspector-inner');
33428
33429         $enter.append('div')
33430             .attr('class', 'raw-membership-editor inspector-inner');
33431
33432         selection.selectAll('.preset-reset')
33433             .on('click', function() {
33434                 dispatch.choose(preset);
33435             });
33436
33437         // Update
33438         $body.select('.preset-list-item button')
33439             .call(iD.ui.PresetIcon()
33440                 .geometry(context.geometry(id))
33441                 .preset(preset));
33442
33443         $body.select('.preset-list-item .label')
33444             .text(preset.name());
33445
33446         $body.select('.inspector-preset')
33447             .call(presetEditor
33448                 .preset(preset)
33449                 .entityID(id)
33450                 .tags(tags)
33451                 .state(state));
33452
33453         $body.select('.raw-tag-editor')
33454             .call(rawTagEditor
33455                 .preset(preset)
33456                 .entityID(id)
33457                 .tags(tags)
33458                 .state(state));
33459
33460         if (entity.type === 'relation') {
33461             $body.select('.raw-member-editor')
33462                 .style('display', 'block')
33463                 .call(iD.ui.RawMemberEditor(context)
33464                     .entityID(id));
33465         } else {
33466             $body.select('.raw-member-editor')
33467                 .style('display', 'none');
33468         }
33469
33470         $body.select('.raw-membership-editor')
33471             .call(iD.ui.RawMembershipEditor(context)
33472                 .entityID(id));
33473
33474         function historyChanged() {
33475             if (state === 'hide') return;
33476
33477             var entity = context.hasEntity(id),
33478                 graph = context.graph();
33479             if (!entity) return;
33480
33481             entityEditor.preset(context.presets().match(entity, graph));
33482             entityEditor.modified(base !== graph);
33483             entityEditor(selection);
33484         }
33485
33486         context.history()
33487             .on('change.entity-editor', historyChanged);
33488     }
33489
33490     function clean(o) {
33491
33492         function cleanVal(k, v) {
33493             function keepSpaces(k) {
33494                 var whitelist = ['opening_hours', 'service_times', 'collection_times',
33495                     'operating_times', 'smoking_hours', 'happy_hours'];
33496                 return _.any(whitelist, function(s) { return k.indexOf(s) !== -1; });
33497             }
33498
33499             var blacklist = ['description', 'note', 'fixme'];
33500             if (_.any(blacklist, function(s) { return k.indexOf(s) !== -1; })) return v;
33501
33502             var cleaned = v.split(';')
33503                 .map(function(s) { return s.trim(); })
33504                 .join(keepSpaces(k) ? '; ' : ';');
33505
33506             // The code below is not intended to validate websites and emails.
33507             // It is only intended to prevent obvious copy-paste errors. (#2323)
33508
33509             // clean website- and email-like tags
33510             if (k.indexOf('website') !== -1 ||
33511                 k.indexOf('email') !== -1 ||
33512                 cleaned.indexOf('http') === 0) {
33513                 cleaned = cleaned
33514                     .replace(/[\u200B-\u200F\uFEFF]/g, '');  // strip LRM and other zero width chars
33515
33516             }
33517
33518             return cleaned;
33519         }
33520
33521         var out = {}, k, v;
33522         for (k in o) {
33523             if (k && (v = o[k]) !== undefined) {
33524                 out[k] = cleanVal(k, v);
33525             }
33526         }
33527         return out;
33528     }
33529
33530     // Tag changes that fire on input can all get coalesced into a single
33531     // history operation when the user leaves the field.  #2342
33532     function changeTags(changed, onInput) {
33533         var entity = context.entity(id),
33534             annotation = t('operations.change_tags.annotation'),
33535             tags = clean(_.extend({}, entity.tags, changed));
33536
33537         if (!_.isEqual(entity.tags, tags)) {
33538             if (coalesceChanges) {
33539                 context.overwrite(iD.actions.ChangeTags(id, tags), annotation);
33540             } else {
33541                 context.perform(iD.actions.ChangeTags(id, tags), annotation);
33542                 coalesceChanges = !!onInput;
33543             }
33544         }
33545     }
33546
33547     entityEditor.modified = function(_) {
33548         if (!arguments.length) return modified;
33549         modified = _;
33550         d3.selectAll('button.preset-close use')
33551             .attr('xlink:href', (modified ? '#icon-apply' : '#icon-close'));
33552     };
33553
33554     entityEditor.state = function(_) {
33555         if (!arguments.length) return state;
33556         state = _;
33557         return entityEditor;
33558     };
33559
33560     entityEditor.entityID = function(_) {
33561         if (!arguments.length) return id;
33562         id = _;
33563         base = context.graph();
33564         entityEditor.preset(context.presets().match(context.entity(id), base));
33565         entityEditor.modified(false);
33566         coalesceChanges = false;
33567         return entityEditor;
33568     };
33569
33570     entityEditor.preset = function(_) {
33571         if (!arguments.length) return preset;
33572         if (_ !== preset) {
33573             preset = _;
33574             reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context)
33575                 .showing(false);
33576         }
33577         return entityEditor;
33578     };
33579
33580     return d3.rebind(entityEditor, dispatch, 'on');
33581 };
33582 iD.ui.FeatureInfo = function(context) {
33583     function update(selection) {
33584         var features = context.features(),
33585             stats = features.stats(),
33586             count = 0,
33587             hiddenList = _.compact(_.map(features.hidden(), function(k) {
33588                 if (stats[k]) {
33589                     count += stats[k];
33590                     return String(stats[k]) + ' ' + t('feature.' + k + '.description');
33591                 }
33592             }));
33593
33594         selection.html('');
33595
33596         if (hiddenList.length) {
33597             var tooltip = bootstrap.tooltip()
33598                     .placement('top')
33599                     .html(true)
33600                     .title(function() {
33601                         return iD.ui.tooltipHtml(hiddenList.join('<br/>'));
33602                     });
33603
33604             var warning = selection.append('a')
33605                 .attr('href', '#')
33606                 .attr('tabindex', -1)
33607                 .html(t('feature_info.hidden_warning', { count: count }))
33608                 .call(tooltip)
33609                 .on('click', function() {
33610                     tooltip.hide(warning);
33611                     // open map data panel?
33612                     d3.event.preventDefault();
33613                 });
33614         }
33615
33616         selection
33617             .classed('hide', !hiddenList.length);
33618     }
33619
33620     return function(selection) {
33621         update(selection);
33622
33623         context.features().on('change.feature_info', function() {
33624             update(selection);
33625         });
33626     };
33627 };
33628 iD.ui.FeatureList = function(context) {
33629     var geocodeResults;
33630
33631     function featureList(selection) {
33632         var header = selection.append('div')
33633             .attr('class', 'header fillL cf');
33634
33635         header.append('h3')
33636             .text(t('inspector.feature_list'));
33637
33638         function keypress() {
33639             var q = search.property('value'),
33640                 items = list.selectAll('.feature-list-item');
33641             if (d3.event.keyCode === 13 && q.length && items.size()) {
33642                 click(items.datum());
33643             }
33644         }
33645
33646         function inputevent() {
33647             geocodeResults = undefined;
33648             drawList();
33649         }
33650
33651         var searchWrap = selection.append('div')
33652             .attr('class', 'search-header');
33653
33654         var search = searchWrap.append('input')
33655             .attr('placeholder', t('inspector.search'))
33656             .attr('type', 'search')
33657             .on('keypress', keypress)
33658             .on('input', inputevent);
33659
33660         searchWrap
33661             .call(iD.svg.Icon('#icon-search', 'pre-text'));
33662
33663         var listWrap = selection.append('div')
33664             .attr('class', 'inspector-body');
33665
33666         var list = listWrap.append('div')
33667             .attr('class', 'feature-list cf');
33668
33669         context
33670             .on('exit.feature-list', clearSearch);
33671         context.map()
33672             .on('drawn.feature-list', mapDrawn);
33673
33674         function clearSearch() {
33675             search.property('value', '');
33676             drawList();
33677         }
33678
33679         function mapDrawn(e) {
33680             if (e.full) {
33681                 drawList();
33682             }
33683         }
33684
33685         function features() {
33686             var entities = {},
33687                 result = [],
33688                 graph = context.graph(),
33689                 q = search.property('value').toLowerCase();
33690
33691             if (!q) return result;
33692
33693             var idMatch = q.match(/^([nwr])([0-9]+)$/);
33694
33695             if (idMatch) {
33696                 result.push({
33697                     id: idMatch[0],
33698                     geometry: idMatch[1] === 'n' ? 'point' : idMatch[1] === 'w' ? 'line' : 'relation',
33699                     type: idMatch[1] === 'n' ? t('inspector.node') : idMatch[1] === 'w' ? t('inspector.way') : t('inspector.relation'),
33700                     name: idMatch[2]
33701                 });
33702             }
33703
33704             var locationMatch = sexagesimal.pair(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
33705
33706             if (locationMatch) {
33707                 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
33708                 result.push({
33709                     id: -1,
33710                     geometry: 'point',
33711                     type: t('inspector.location'),
33712                     name: loc[0].toFixed(6) + ', ' + loc[1].toFixed(6),
33713                     location: loc
33714                 });
33715             }
33716
33717             function addEntity(entity) {
33718                 if (entity.id in entities || result.length > 200)
33719                     return;
33720
33721                 entities[entity.id] = true;
33722
33723                 var name = iD.util.displayName(entity) || '';
33724                 if (name.toLowerCase().indexOf(q) >= 0) {
33725                     result.push({
33726                         id: entity.id,
33727                         entity: entity,
33728                         geometry: context.geometry(entity.id),
33729                         type: context.presets().match(entity, graph).name(),
33730                         name: name
33731                     });
33732                 }
33733
33734                 graph.parentRelations(entity).forEach(function(parent) {
33735                     addEntity(parent);
33736                 });
33737             }
33738
33739             var visible = context.surface().selectAll('.point, .line, .area')[0];
33740             for (var i = 0; i < visible.length && result.length <= 200; i++) {
33741                 addEntity(visible[i].__data__);
33742             }
33743
33744             (geocodeResults || []).forEach(function(d) {
33745                 // https://github.com/openstreetmap/iD/issues/1890
33746                 if (d.osm_type && d.osm_id) {
33747                     result.push({
33748                         id: iD.Entity.id.fromOSM(d.osm_type, d.osm_id),
33749                         geometry: d.osm_type === 'relation' ? 'relation' : d.osm_type === 'way' ? 'line' : 'point',
33750                         type: d.type !== 'yes' ? (d.type.charAt(0).toUpperCase() + d.type.slice(1)).replace('_', ' ')
33751                                                : (d.class.charAt(0).toUpperCase() + d.class.slice(1)).replace('_', ' '),
33752                         name: d.display_name,
33753                         extent: new iD.geo.Extent(
33754                             [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
33755                             [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
33756                     });
33757                 }
33758             });
33759
33760             return result;
33761         }
33762
33763         function drawList() {
33764             var value = search.property('value'),
33765                 results = features();
33766
33767             list.classed('filtered', value.length);
33768
33769             var noResultsWorldwide = geocodeResults && geocodeResults.length === 0;
33770
33771             var resultsIndicator = list.selectAll('.no-results-item')
33772                 .data([0])
33773                 .enter().append('button')
33774                 .property('disabled', true)
33775                 .attr('class', 'no-results-item')
33776                 .call(iD.svg.Icon('#icon-alert', 'pre-text'));
33777
33778             resultsIndicator.append('span')
33779                 .attr('class', 'entity-name');
33780
33781             list.selectAll('.no-results-item .entity-name')
33782                 .text(noResultsWorldwide ? t('geocoder.no_results_worldwide') : t('geocoder.no_results_visible'));
33783
33784             list.selectAll('.geocode-item')
33785                 .data([0])
33786                 .enter().append('button')
33787                 .attr('class', 'geocode-item')
33788                 .on('click', geocode)
33789                 .append('div')
33790                 .attr('class', 'label')
33791                 .append('span')
33792                 .attr('class', 'entity-name')
33793                 .text(t('geocoder.search'));
33794
33795             list.selectAll('.no-results-item')
33796                 .style('display', (value.length && !results.length) ? 'block' : 'none');
33797
33798             list.selectAll('.geocode-item')
33799                 .style('display', (value && geocodeResults === undefined) ? 'block' : 'none');
33800
33801             list.selectAll('.feature-list-item')
33802                 .data([-1])
33803                 .remove();
33804
33805             var items = list.selectAll('.feature-list-item')
33806                 .data(results, function(d) { return d.id; });
33807
33808             var enter = items.enter()
33809                 .insert('button', '.geocode-item')
33810                 .attr('class', 'feature-list-item')
33811                 .on('mouseover', mouseover)
33812                 .on('mouseout', mouseout)
33813                 .on('click', click);
33814
33815             var label = enter
33816                 .append('div')
33817                 .attr('class', 'label');
33818
33819             label.each(function(d) {
33820                 d3.select(this)
33821                     .call(iD.svg.Icon('#icon-' + d.geometry, 'pre-text'));
33822             });
33823
33824             label.append('span')
33825                 .attr('class', 'entity-type')
33826                 .text(function(d) { return d.type; });
33827
33828             label.append('span')
33829                 .attr('class', 'entity-name')
33830                 .text(function(d) { return d.name; });
33831
33832             enter.style('opacity', 0)
33833                 .transition()
33834                 .style('opacity', 1);
33835
33836             items.order();
33837
33838             items.exit()
33839                 .remove();
33840         }
33841
33842         function mouseover(d) {
33843             if (d.id === -1) return;
33844
33845             context.surface().selectAll(iD.util.entityOrMemberSelector([d.id], context.graph()))
33846                 .classed('hover', true);
33847         }
33848
33849         function mouseout() {
33850             context.surface().selectAll('.hover')
33851                 .classed('hover', false);
33852         }
33853
33854         function click(d) {
33855             d3.event.preventDefault();
33856             if (d.location) {
33857                 context.map().centerZoom([d.location[1], d.location[0]], 20);
33858             }
33859             else if (d.entity) {
33860                 if (d.entity.type === 'node') {
33861                     context.map().center(d.entity.loc);
33862                 } else if (d.entity.type === 'way') {
33863                     var center = context.projection(context.map().center()),
33864                         edge = iD.geo.chooseEdge(context.childNodes(d.entity), center, context.projection);
33865                     context.map().center(edge.loc);
33866                 }
33867                 context.enter(iD.modes.Select(context, [d.entity.id]).suppressMenu(true));
33868             } else {
33869                 context.zoomToEntity(d.id);
33870             }
33871         }
33872
33873         function geocode() {
33874             var searchVal = encodeURIComponent(search.property('value'));
33875             d3.json('https://nominatim.openstreetmap.org/search/' + searchVal + '?limit=10&format=json', function(err, resp) {
33876                 geocodeResults = resp || [];
33877                 drawList();
33878             });
33879         }
33880     }
33881
33882     return featureList;
33883 };
33884 iD.ui.flash = function(selection) {
33885     var modal = iD.ui.modal(selection);
33886
33887     modal.select('.modal').classed('modal-flash', true);
33888
33889     modal.select('.content')
33890         .classed('modal-section', true)
33891         .append('div')
33892         .attr('class', 'description');
33893
33894     modal.on('click.flash', function() { modal.remove(); });
33895
33896     setTimeout(function() {
33897         modal.remove();
33898         return true;
33899     }, 1500);
33900
33901     return modal;
33902 };
33903 iD.ui.FullScreen = function(context) {
33904     var element = context.container().node(),
33905         keybinding = d3.keybinding('full-screen');
33906         // button;
33907
33908     function getFullScreenFn() {
33909         if (element.requestFullscreen) {
33910             return element.requestFullscreen;
33911         } else if (element.msRequestFullscreen) {
33912             return  element.msRequestFullscreen;
33913         } else if (element.mozRequestFullScreen) {
33914             return  element.mozRequestFullScreen;
33915         } else if (element.webkitRequestFullscreen) {
33916             return element.webkitRequestFullscreen;
33917         }
33918     }
33919
33920     function getExitFullScreenFn() {
33921         if (document.exitFullscreen) {
33922             return document.exitFullscreen;
33923         } else if (document.msExitFullscreen) {
33924             return  document.msExitFullscreen;
33925         } else if (document.mozCancelFullScreen) {
33926             return  document.mozCancelFullScreen;
33927         } else if (document.webkitExitFullscreen) {
33928             return document.webkitExitFullscreen;
33929         }
33930     }
33931
33932     function isFullScreen() {
33933         return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement ||
33934             document.msFullscreenElement;
33935     }
33936
33937     function isSupported() {
33938         return !!getFullScreenFn();
33939     }
33940
33941     function fullScreen() {
33942         d3.event.preventDefault();
33943         if (!isFullScreen()) {
33944             // button.classed('active', true);
33945             getFullScreenFn().apply(element);
33946         } else {
33947             // button.classed('active', false);
33948             getExitFullScreenFn().apply(document);
33949         }
33950     }
33951
33952     return function() { // selection) {
33953         if (!isSupported())
33954             return;
33955
33956         // var tooltip = bootstrap.tooltip()
33957         //     .placement('left');
33958
33959         // button = selection.append('button')
33960         //     .attr('title', t('full_screen'))
33961         //     .attr('tabindex', -1)
33962         //     .on('click', fullScreen)
33963         //     .call(tooltip);
33964
33965         // button.append('span')
33966         //     .attr('class', 'icon full-screen');
33967
33968         keybinding
33969             .on('f11', fullScreen)
33970             .on(iD.ui.cmd('⌘⇧F'), fullScreen);
33971
33972         d3.select(document)
33973             .call(keybinding);
33974     };
33975 };
33976 iD.ui.Geolocate = function(context) {
33977     var geoOptions = { enableHighAccuracy: false, timeout: 6000 /* 6sec */ },
33978         locating = iD.ui.Loading(context).message(t('geolocate.locating')).blocking(true),
33979         timeoutId;
33980
33981     function click() {
33982         context.enter(iD.modes.Browse(context));
33983         context.container().call(locating);
33984         navigator.geolocation.getCurrentPosition(success, error, geoOptions);
33985
33986         // This timeout ensures that we still call finish() even if
33987         // the user declines to share their location in Firefox
33988         timeoutId = setTimeout(finish, 10000 /* 10sec */ );
33989     }
33990
33991     function success(position) {
33992         var map = context.map(),
33993             extent = iD.geo.Extent([position.coords.longitude, position.coords.latitude])
33994                 .padByMeters(position.coords.accuracy);
33995
33996         map.centerZoom(extent.center(), Math.min(20, map.extentZoom(extent)));
33997         finish();
33998     }
33999
34000     function error() {
34001         finish();
34002     }
34003
34004     function finish() {
34005         locating.close();  // unblock ui
34006         if (timeoutId) { clearTimeout(timeoutId); }
34007         timeoutId = undefined;
34008     }
34009
34010     return function(selection) {
34011         if (!navigator.geolocation) return;
34012
34013         selection.append('button')
34014             .attr('tabindex', -1)
34015             .attr('title', t('geolocate.title'))
34016             .on('click', click)
34017             .call(iD.svg.Icon('#icon-geolocate', 'light'))
34018             .call(bootstrap.tooltip()
34019                 .placement('left'));
34020     };
34021 };
34022 iD.ui.Help = function(context) {
34023     var key = 'H';
34024
34025     var docKeys = [
34026         'help.help',
34027         'help.editing_saving',
34028         'help.roads',
34029         'help.gps',
34030         'help.imagery',
34031         'help.addresses',
34032         'help.inspector',
34033         'help.buildings',
34034         'help.relations'];
34035
34036     var docs = docKeys.map(function(key) {
34037         var text = t(key);
34038         return {
34039             title: text.split('\n')[0].replace('#', '').trim(),
34040             html: marked(text.split('\n').slice(1).join('\n'))
34041         };
34042     });
34043
34044     function help(selection) {
34045
34046         function hide() {
34047             setVisible(false);
34048         }
34049
34050         function toggle() {
34051             if (d3.event) d3.event.preventDefault();
34052             tooltip.hide(button);
34053             setVisible(!button.classed('active'));
34054         }
34055
34056         function setVisible(show) {
34057             if (show !== shown) {
34058                 button.classed('active', show);
34059                 shown = show;
34060
34061                 if (show) {
34062                     selection.on('mousedown.help-inside', function() {
34063                         return d3.event.stopPropagation();
34064                     });
34065                     pane.style('display', 'block')
34066                         .style('right', '-500px')
34067                         .transition()
34068                         .duration(200)
34069                         .style('right', '0px');
34070                 } else {
34071                     pane.style('right', '0px')
34072                         .transition()
34073                         .duration(200)
34074                         .style('right', '-500px')
34075                         .each('end', function() {
34076                             d3.select(this).style('display', 'none');
34077                         });
34078                     selection.on('mousedown.help-inside', null);
34079                 }
34080             }
34081         }
34082
34083         function clickHelp(d, i) {
34084             pane.property('scrollTop', 0);
34085             doctitle.html(d.title);
34086             body.html(d.html);
34087             body.selectAll('a')
34088                 .attr('target', '_blank');
34089             menuItems.classed('selected', function(m) {
34090                 return m.title === d.title;
34091             });
34092
34093             nav.html('');
34094
34095             if (i > 0) {
34096                 var prevLink = nav.append('a')
34097                     .attr('class', 'previous')
34098                     .on('click', function() {
34099                         clickHelp(docs[i - 1], i - 1);
34100                     });
34101                 prevLink.append('span').html('&#9668; ' + docs[i - 1].title);
34102             }
34103             if (i < docs.length - 1) {
34104                 var nextLink = nav.append('a')
34105                     .attr('class', 'next')
34106                     .on('click', function() {
34107                         clickHelp(docs[i + 1], i + 1);
34108                     });
34109                 nextLink.append('span').html(docs[i + 1].title + ' &#9658;');
34110             }
34111         }
34112
34113         function clickWalkthrough() {
34114             d3.select(document.body).call(iD.ui.intro(context));
34115             setVisible(false);
34116         }
34117
34118
34119         var pane = selection.append('div')
34120                 .attr('class', 'help-wrap map-overlay fillL col5 content hide'),
34121             tooltip = bootstrap.tooltip()
34122                 .placement('left')
34123                 .html(true)
34124                 .title(iD.ui.tooltipHtml(t('help.title'), key)),
34125             button = selection.append('button')
34126                 .attr('tabindex', -1)
34127                 .on('click', toggle)
34128                 .call(iD.svg.Icon('#icon-help', 'light'))
34129                 .call(tooltip),
34130             shown = false;
34131
34132
34133         var toc = pane.append('ul')
34134             .attr('class', 'toc');
34135
34136         var menuItems = toc.selectAll('li')
34137             .data(docs)
34138             .enter()
34139             .append('li')
34140             .append('a')
34141             .html(function(d) { return d.title; })
34142             .on('click', clickHelp);
34143
34144         toc.append('li')
34145             .attr('class','walkthrough')
34146             .append('a')
34147             .text(t('splash.walkthrough'))
34148             .on('click', clickWalkthrough);
34149
34150         var content = pane.append('div')
34151             .attr('class', 'left-content');
34152
34153         var doctitle = content.append('h2')
34154             .text(t('help.title'));
34155
34156         var body = content.append('div')
34157             .attr('class', 'body');
34158
34159         var nav = content.append('div')
34160             .attr('class', 'nav');
34161
34162         clickHelp(docs[0], 0);
34163
34164         var keybinding = d3.keybinding('help')
34165             .on(key, toggle)
34166             .on('B', hide)
34167             .on('F', hide);
34168
34169         d3.select(document)
34170             .call(keybinding);
34171
34172         context.surface().on('mousedown.help-outside', hide);
34173         context.container().on('mousedown.help-outside', hide);
34174     }
34175
34176     return help;
34177 };
34178 iD.ui.Info = function(context) {
34179     var key = iD.ui.cmd('⌘I'),
34180         imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
34181         hidden = true;
34182
34183     function info(selection) {
34184         function radiansToMeters(r) {
34185             // using WGS84 authalic radius (6371007.1809 m)
34186             return r * 6371007.1809;
34187         }
34188
34189         function steradiansToSqmeters(r) {
34190             // http://gis.stackexchange.com/a/124857/40446
34191             return r / 12.56637 * 510065621724000;
34192         }
34193
34194         function toLineString(feature) {
34195             if (feature.type === 'LineString') return feature;
34196
34197             var result = { type: 'LineString', coordinates: [] };
34198             if (feature.type === 'Polygon') {
34199                 result.coordinates = feature.coordinates[0];
34200             } else if (feature.type === 'MultiPolygon') {
34201                 result.coordinates = feature.coordinates[0][0];
34202             }
34203
34204             return result;
34205         }
34206
34207         function displayLength(m) {
34208             var d = m * (imperial ? 3.28084 : 1),
34209                 p, unit;
34210
34211             if (imperial) {
34212                 if (d >= 5280) {
34213                     d /= 5280;
34214                     unit = 'mi';
34215                 } else {
34216                     unit = 'ft';
34217                 }
34218             } else {
34219                 if (d >= 1000) {
34220                     d /= 1000;
34221                     unit = 'km';
34222                 } else {
34223                     unit = 'm';
34224                 }
34225             }
34226
34227             // drop unnecessary precision
34228             p = d > 1000 ? 0 : d > 100 ? 1 : 2;
34229
34230             return String(d.toFixed(p)) + ' ' + unit;
34231         }
34232
34233         function displayArea(m2) {
34234             var d = m2 * (imperial ? 10.7639111056 : 1),
34235                 d1, d2, p1, p2, unit1, unit2;
34236
34237             if (imperial) {
34238                 if (d >= 6969600) {     // > 0.25mi² show mi²
34239                     d1 = d / 27878400;
34240                     unit1 = 'mi²';
34241                 } else {
34242                     d1 = d;
34243                     unit1 = 'ft²';
34244                 }
34245
34246                 if (d > 4356 && d < 43560000) {   // 0.1 - 1000 acres
34247                     d2 = d / 43560;
34248                     unit2 = 'ac';
34249                 }
34250
34251             } else {
34252                 if (d >= 250000) {    // > 0.25km² show km²
34253                     d1 = d / 1000000;
34254                     unit1 = 'km²';
34255                 } else {
34256                     d1 = d;
34257                     unit1 = 'm²';
34258                 }
34259
34260                 if (d > 1000 && d < 10000000) {   // 0.1 - 1000 hectares
34261                     d2 = d / 10000;
34262                     unit2 = 'ha';
34263                 }
34264             }
34265
34266             // drop unnecessary precision
34267             p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2;
34268             p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2;
34269
34270             return String(d1.toFixed(p1)) + ' ' + unit1 +
34271                 (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : '');
34272         }
34273
34274
34275         function redraw() {
34276             if (hidden) return;
34277
34278             var resolver = context.graph(),
34279                 selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }),
34280                 singular = selected.length === 1 ? selected[0] : null,
34281                 extent = iD.geo.Extent(),
34282                 entity;
34283
34284             wrap.html('');
34285             wrap.append('h4')
34286                 .attr('class', 'infobox-heading fillD')
34287                 .text(singular || t('infobox.selected', { n: selected.length }));
34288
34289             if (!selected.length) return;
34290
34291             var center;
34292             for (var i = 0; i < selected.length; i++) {
34293                 entity = context.entity(selected[i]);
34294                 extent._extend(entity.extent(resolver));
34295             }
34296             center = extent.center();
34297
34298
34299             var list = wrap.append('ul');
34300
34301             // multiple wrap, just display extent center..
34302             if (!singular) {
34303                 list.append('li')
34304                     .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
34305                 return;
34306             }
34307
34308             // single wrap, display details..
34309             if (!entity) return;
34310             var geometry = entity.geometry(resolver);
34311
34312             if (geometry === 'line' || geometry === 'area') {
34313                 var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()),
34314                     feature = entity.asGeoJSON(resolver),
34315                     length = radiansToMeters(d3.geo.length(toLineString(feature))),
34316                     lengthLabel = t('infobox.' + (closed ? 'perimeter' : 'length')),
34317                     centroid = d3.geo.centroid(feature);
34318
34319                 list.append('li')
34320                     .text(t('infobox.geometry') + ': ' +
34321                         (closed ? t('infobox.closed') + ' ' : '') + t('geometry.' + geometry) );
34322
34323                 if (closed) {
34324                     var area = steradiansToSqmeters(entity.area(resolver));
34325                     list.append('li')
34326                         .text(t('infobox.area') + ': ' + displayArea(area));
34327                 }
34328
34329                 list.append('li')
34330                     .text(lengthLabel + ': ' + displayLength(length));
34331
34332                 list.append('li')
34333                     .text(t('infobox.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5));
34334
34335
34336                 var toggle  = imperial ? 'imperial' : 'metric';
34337                 wrap.append('a')
34338                     .text(t('infobox.' + toggle))
34339                     .attr('href', '#')
34340                     .attr('class', 'button')
34341                     .on('click', function() {
34342                         d3.event.preventDefault();
34343                         imperial = !imperial;
34344                         redraw();
34345                     });
34346
34347             } else {
34348                 var centerLabel = t('infobox.' + (entity.type === 'node' ? 'location' : 'center'));
34349
34350                 list.append('li')
34351                     .text(t('infobox.geometry') + ': ' + t('geometry.' + geometry));
34352
34353                 list.append('li')
34354                     .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
34355             }
34356         }
34357
34358
34359         function toggle() {
34360             if (d3.event) d3.event.preventDefault();
34361
34362             hidden = !hidden;
34363
34364             if (hidden) {
34365                 wrap
34366                     .style('display', 'block')
34367                     .style('opacity', 1)
34368                     .transition()
34369                     .duration(200)
34370                     .style('opacity', 0)
34371                     .each('end', function() {
34372                         d3.select(this).style('display', 'none');
34373                     });
34374             } else {
34375                 wrap
34376                     .style('display', 'block')
34377                     .style('opacity', 0)
34378                     .transition()
34379                     .duration(200)
34380                     .style('opacity', 1);
34381
34382                 redraw();
34383             }
34384         }
34385
34386
34387         var wrap = selection.selectAll('.infobox')
34388             .data([0]);
34389
34390         wrap.enter()
34391             .append('div')
34392             .attr('class', 'infobox fillD2')
34393             .style('display', (hidden ? 'none' : 'block'));
34394
34395         context.map()
34396             .on('drawn.info', redraw);
34397
34398         redraw();
34399
34400         var keybinding = d3.keybinding('info')
34401             .on(key, toggle);
34402
34403         d3.select(document)
34404             .call(keybinding);
34405     }
34406
34407     return info;
34408 };
34409 iD.ui.Inspector = function(context) {
34410     var presetList = iD.ui.PresetList(context),
34411         entityEditor = iD.ui.EntityEditor(context),
34412         state = 'select',
34413         entityID,
34414         newFeature = false;
34415
34416     function inspector(selection) {
34417         presetList
34418             .entityID(entityID)
34419             .autofocus(newFeature)
34420             .on('choose', setPreset);
34421
34422         entityEditor
34423             .state(state)
34424             .entityID(entityID)
34425             .on('choose', showList);
34426
34427         var $wrap = selection.selectAll('.panewrap')
34428             .data([0]);
34429
34430         var $enter = $wrap.enter().append('div')
34431             .attr('class', 'panewrap');
34432
34433         $enter.append('div')
34434             .attr('class', 'preset-list-pane pane');
34435
34436         $enter.append('div')
34437             .attr('class', 'entity-editor-pane pane');
34438
34439         var $presetPane = $wrap.select('.preset-list-pane');
34440         var $editorPane = $wrap.select('.entity-editor-pane');
34441
34442         var graph = context.graph(),
34443             entity = context.entity(entityID),
34444             showEditor = state === 'hover' ||
34445                 entity.isUsed(graph) ||
34446                 entity.isHighwayIntersection(graph);
34447
34448         if (showEditor) {
34449             $wrap.style('right', '0%');
34450             $editorPane.call(entityEditor);
34451         } else {
34452             $wrap.style('right', '-100%');
34453             $presetPane.call(presetList);
34454         }
34455
34456         var $footer = selection.selectAll('.footer')
34457             .data([0]);
34458
34459         $footer.enter().append('div')
34460             .attr('class', 'footer');
34461
34462         selection.select('.footer')
34463             .call(iD.ui.ViewOnOSM(context)
34464                 .entityID(entityID));
34465
34466         function showList(preset) {
34467             $wrap.transition()
34468                 .styleTween('right', function() { return d3.interpolate('0%', '-100%'); });
34469
34470             $presetPane.call(presetList
34471                 .preset(preset)
34472                 .autofocus(true));
34473         }
34474
34475         function setPreset(preset) {
34476             $wrap.transition()
34477                 .styleTween('right', function() { return d3.interpolate('-100%', '0%'); });
34478
34479             $editorPane.call(entityEditor
34480                 .preset(preset));
34481         }
34482     }
34483
34484     inspector.state = function(_) {
34485         if (!arguments.length) return state;
34486         state = _;
34487         entityEditor.state(state);
34488         return inspector;
34489     };
34490
34491     inspector.entityID = function(_) {
34492         if (!arguments.length) return entityID;
34493         entityID = _;
34494         return inspector;
34495     };
34496
34497     inspector.newFeature = function(_) {
34498         if (!arguments.length) return newFeature;
34499         newFeature = _;
34500         return inspector;
34501     };
34502
34503     return inspector;
34504 };
34505 iD.ui.intro = function(context) {
34506     var step;
34507
34508     function intro(selection) {
34509
34510         function localizedName(id) {
34511             var features = {
34512                 n2140018997: 'city_hall',
34513                 n367813436: 'fire_department',
34514                 w203988286: 'memory_isle_park',
34515                 w203972937: 'riverwalk_trail',
34516                 w203972938: 'riverwalk_trail',
34517                 w203972940: 'riverwalk_trail',
34518                 w41785752: 'w_michigan_ave',
34519                 w134150789: 'w_michigan_ave',
34520                 w134150795: 'w_michigan_ave',
34521                 w134150800: 'w_michigan_ave',
34522                 w134150811: 'w_michigan_ave',
34523                 w134150802: 'e_michigan_ave',
34524                 w134150836: 'e_michigan_ave',
34525                 w41074896: 'e_michigan_ave',
34526                 w17965834: 'spring_st',
34527                 w203986457: 'scidmore_park',
34528                 w203049587: 'petting_zoo',
34529                 w17967397: 'n_andrews_st',
34530                 w17967315: 's_andrews_st',
34531                 w17967326: 'n_constantine_st',
34532                 w17966400: 's_constantine_st',
34533                 w170848823: 'rocky_river',
34534                 w170848824: 'rocky_river',
34535                 w170848331: 'rocky_river',
34536                 w17967752: 'railroad_dr',
34537                 w17965998: 'conrail_rr',
34538                 w134150845: 'conrail_rr',
34539                 w170989131: 'st_joseph_river',
34540                 w143497377: 'n_main_st',
34541                 w134150801: 's_main_st',
34542                 w134150830: 's_main_st',
34543                 w17966462: 's_main_st',
34544                 w17967734: 'water_st',
34545                 w17964996: 'foster_st',
34546                 w170848330: 'portage_river',
34547                 w17965351: 'flower_st',
34548                 w17965502: 'elm_st',
34549                 w17965402: 'walnut_st',
34550                 w17964793: 'morris_ave',
34551                 w17967444: 'east_st',
34552                 w17966984: 'portage_ave'
34553             };
34554             return features[id] && t('intro.graph.' + features[id]);
34555         }
34556
34557         context.enter(iD.modes.Browse(context));
34558
34559         // Save current map state
34560         var history = context.history().toJSON(),
34561             hash = window.location.hash,
34562             center = context.map().center(),
34563             zoom = context.map().zoom(),
34564             background = context.background().baseLayerSource(),
34565             opacity = d3.selectAll('#map .layer-background').style('opacity'),
34566             loadedTiles = context.connection().loadedTiles(),
34567             baseEntities = context.history().graph().base().entities,
34568             introGraph, name;
34569
34570         // Block saving
34571         context.inIntro(true);
34572
34573         // Load semi-real data used in intro
34574         context.connection().toggle(false).flush();
34575         context.history().reset();
34576
34577         introGraph = JSON.parse(iD.introGraph);
34578         for (var key in introGraph) {
34579             introGraph[key] = iD.Entity(introGraph[key]);
34580             name = localizedName(key);
34581             if (name) {
34582                 introGraph[key].tags.name = name;
34583             }
34584         }
34585         context.history().merge(d3.values(iD.Graph().load(introGraph).entities));
34586         context.background().bing();
34587
34588         d3.selectAll('#map .layer-background').style('opacity', 1);
34589
34590         var curtain = d3.curtain();
34591         selection.call(curtain);
34592
34593         function reveal(box, text, options) {
34594             options = options || {};
34595             if (text) curtain.reveal(box, text, options.tooltipClass, options.duration);
34596             else curtain.reveal(box, '', '', options.duration);
34597         }
34598
34599         var steps = ['navigation', 'point', 'area', 'line', 'startEditing'].map(function(step, i) {
34600             var s = iD.ui.intro[step](context, reveal)
34601                 .on('done', function() {
34602                     entered.filter(function(d) {
34603                         return d.title === s.title;
34604                     }).classed('finished', true);
34605                     enter(steps[i + 1]);
34606                 });
34607             return s;
34608         });
34609
34610         steps[steps.length - 1].on('startEditing', function() {
34611             curtain.remove();
34612             navwrap.remove();
34613             d3.selectAll('#map .layer-background').style('opacity', opacity);
34614             context.connection().toggle(true).flush().loadedTiles(loadedTiles);
34615             context.history().reset().merge(d3.values(baseEntities));
34616             context.background().baseLayerSource(background);
34617             if (history) context.history().fromJSON(history, false);
34618             context.map().centerZoom(center, zoom);
34619             window.location.replace(hash);
34620             context.inIntro(false);
34621         });
34622
34623         var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
34624
34625         var buttonwrap = navwrap.append('div')
34626             .attr('class', 'joined')
34627             .selectAll('button.step');
34628
34629         var entered = buttonwrap
34630             .data(steps)
34631             .enter()
34632             .append('button')
34633             .attr('class', 'step')
34634             .on('click', enter);
34635
34636         entered
34637             .call(iD.svg.Icon('#icon-apply', 'pre-text'));
34638
34639         entered
34640             .append('label')
34641             .text(function(d) { return t(d.title); });
34642
34643         enter(steps[0]);
34644
34645         function enter (newStep) {
34646             if (step) { step.exit(); }
34647
34648             context.enter(iD.modes.Browse(context));
34649
34650             step = newStep;
34651             step.enter();
34652
34653             entered.classed('active', function(d) {
34654                 return d.title === step.title;
34655             });
34656         }
34657
34658     }
34659     return intro;
34660 };
34661
34662 iD.ui.intro.pointBox = function(point, context) {
34663     var rect = context.surfaceRect();
34664     point = context.projection(point);
34665     return {
34666         left: point[0] + rect.left - 30,
34667         top: point[1] + rect.top - 50,
34668         width: 60,
34669         height: 70
34670     };
34671 };
34672
34673 iD.ui.intro.pad = function(box, padding, context) {
34674     if (box instanceof Array) {
34675         var rect = context.surfaceRect();
34676         box = context.projection(box);
34677         box = {
34678             left: box[0] + rect.left,
34679             top: box[1] + rect.top
34680         };
34681     }
34682     return {
34683         left: box.left - padding,
34684         top: box.top - padding,
34685         width: (box.width || 0) + 2 * padding,
34686         height: (box.width || 0) + 2 * padding
34687     };
34688 };
34689
34690 iD.ui.intro.icon = function(name, svgklass) {
34691     return '<svg class="icon ' + (svgklass || '') + '">' +
34692         '<use xlink:href="' + name + '"></use></svg>';
34693 };
34694 iD.ui.Lasso = function(context) {
34695     var group, polygon;
34696
34697     lasso.coordinates = [];
34698
34699     function lasso(selection) {
34700
34701         context.container().classed('lasso', true);
34702
34703         group = selection.append('g')
34704             .attr('class', 'lasso hide');
34705
34706         polygon = group.append('path')
34707             .attr('class', 'lasso-path');
34708
34709         group.call(iD.ui.Toggle(true));
34710
34711     }
34712
34713     function draw() {
34714         if (polygon) {
34715             polygon.data([lasso.coordinates])
34716                 .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; });
34717         }
34718     }
34719
34720     lasso.extent = function () {
34721         return lasso.coordinates.reduce(function(extent, point) {
34722             return extent.extend(iD.geo.Extent(point));
34723         }, iD.geo.Extent());
34724     };
34725
34726     lasso.p = function(_) {
34727         if (!arguments.length) return lasso;
34728         lasso.coordinates.push(_);
34729         draw();
34730         return lasso;
34731     };
34732
34733     lasso.close = function() {
34734         if (group) {
34735             group.call(iD.ui.Toggle(false, function() {
34736                 d3.select(this).remove();
34737             }));
34738         }
34739         context.container().classed('lasso', false);
34740     };
34741
34742     return lasso;
34743 };
34744 iD.ui.Loading = function(context) {
34745     var message = '',
34746         blocking = false,
34747         modal;
34748
34749     var loading = function(selection) {
34750         modal = iD.ui.modal(selection, blocking);
34751
34752         var loadertext = modal.select('.content')
34753             .classed('loading-modal', true)
34754             .append('div')
34755             .attr('class', 'modal-section fillL');
34756
34757         loadertext.append('img')
34758             .attr('class', 'loader')
34759             .attr('src', context.imagePath('loader-white.gif'));
34760
34761         loadertext.append('h3')
34762             .text(message);
34763
34764         modal.select('button.close')
34765             .attr('class', 'hide');
34766
34767         return loading;
34768     };
34769
34770     loading.message = function(_) {
34771         if (!arguments.length) return message;
34772         message = _;
34773         return loading;
34774     };
34775
34776     loading.blocking = function(_) {
34777         if (!arguments.length) return blocking;
34778         blocking = _;
34779         return loading;
34780     };
34781
34782     loading.close = function() {
34783         modal.remove();
34784     };
34785
34786     return loading;
34787 };
34788 iD.ui.MapData = function(context) {
34789     var key = 'F',
34790         features = context.features().keys(),
34791         layers = context.layers(),
34792         fills = ['wireframe', 'partial', 'full'],
34793         fillDefault = context.storage('area-fill') || 'partial',
34794         fillSelected = fillDefault;
34795
34796
34797     function map_data(selection) {
34798
34799         function showsFeature(d) {
34800             return context.features().enabled(d);
34801         }
34802
34803         function autoHiddenFeature(d) {
34804             return context.features().autoHidden(d);
34805         }
34806
34807         function clickFeature(d) {
34808             context.features().toggle(d);
34809             update();
34810         }
34811
34812         function showsFill(d) {
34813             return fillSelected === d;
34814         }
34815
34816         function setFill(d) {
34817             _.each(fills, function(opt) {
34818                 context.surface().classed('fill-' + opt, Boolean(opt === d));
34819             });
34820
34821             fillSelected = d;
34822             if (d !== 'wireframe') {
34823                 fillDefault = d;
34824                 context.storage('area-fill', d);
34825             }
34826             update();
34827         }
34828
34829         function toggleLayer(which) {
34830             var layer = layers.layer(which);
34831             if (layer) {
34832                 layer.enabled(!layer.enabled());
34833                 update();
34834             }
34835         }
34836
34837         function clickGpx() {
34838             toggleLayer('gpx');
34839         }
34840
34841         function clickMapillaryImages() {
34842             toggleLayer('mapillary-images');
34843         }
34844
34845         function clickMapillarySigns() {
34846             toggleLayer('mapillary-signs');
34847         }
34848
34849
34850         function drawMapillaryItems(selection) {
34851             var mapillaryImages = layers.layer('mapillary-images'),
34852                 mapillarySigns = layers.layer('mapillary-signs'),
34853                 supportsMapillaryImages = mapillaryImages && mapillaryImages.supported(),
34854                 supportsMapillarySigns = mapillarySigns && mapillarySigns.supported(),
34855                 showsMapillaryImages = supportsMapillaryImages && mapillaryImages.enabled(),
34856                 showsMapillarySigns = supportsMapillarySigns && mapillarySigns.enabled();
34857
34858             var mapillaryList = selection
34859                 .selectAll('.layer-list-mapillary')
34860                 .data([0]);
34861
34862             // Enter
34863             mapillaryList
34864                 .enter()
34865                 .append('ul')
34866                 .attr('class', 'layer-list layer-list-mapillary');
34867
34868             var mapillaryImageLayerItem = mapillaryList
34869                 .selectAll('.list-item-mapillary-images')
34870                 .data(supportsMapillaryImages ? [0] : []);
34871
34872             var enterImages = mapillaryImageLayerItem.enter()
34873                 .append('li')
34874                 .attr('class', 'list-item-mapillary-images');
34875
34876             var labelImages = enterImages.append('label')
34877                 .call(bootstrap.tooltip()
34878                     .title(t('mapillary_images.tooltip'))
34879                     .placement('top'));
34880
34881             labelImages.append('input')
34882                 .attr('type', 'checkbox')
34883                 .on('change', clickMapillaryImages);
34884
34885             labelImages.append('span')
34886                 .text(t('mapillary_images.title'));
34887
34888
34889             var mapillarySignLayerItem = mapillaryList
34890                 .selectAll('.list-item-mapillary-signs')
34891                 .data(supportsMapillarySigns ? [0] : []);
34892
34893             var enterSigns = mapillarySignLayerItem.enter()
34894                 .append('li')
34895                 .attr('class', 'list-item-mapillary-signs');
34896
34897             var labelSigns = enterSigns.append('label')
34898                 .call(bootstrap.tooltip()
34899                     .title(t('mapillary_signs.tooltip'))
34900                     .placement('top'));
34901
34902             labelSigns.append('input')
34903                 .attr('type', 'checkbox')
34904                 .on('change', clickMapillarySigns);
34905
34906             labelSigns.append('span')
34907                 .text(t('mapillary_signs.title'));
34908
34909             // Update
34910             mapillaryImageLayerItem
34911                 .classed('active', showsMapillaryImages)
34912                 .selectAll('input')
34913                 .property('checked', showsMapillaryImages);
34914
34915             mapillarySignLayerItem
34916                 .classed('active', showsMapillarySigns)
34917                 .selectAll('input')
34918                 .property('checked', showsMapillarySigns);
34919
34920             // Exit
34921             mapillaryImageLayerItem.exit()
34922                 .remove();
34923             mapillarySignLayerItem.exit()
34924                 .remove();
34925         }
34926
34927
34928         function drawGpxItem(selection) {
34929             var gpx = layers.layer('gpx'),
34930                 hasGpx = gpx && gpx.hasGpx(),
34931                 showsGpx = hasGpx && gpx.enabled();
34932
34933             var gpxLayerItem = selection
34934                 .selectAll('.layer-list-gpx')
34935                 .data(gpx ? [0] : []);
34936
34937             // Enter
34938             var enter = gpxLayerItem.enter()
34939                 .append('ul')
34940                 .attr('class', 'layer-list layer-list-gpx')
34941                 .append('li')
34942                 .classed('list-item-gpx', true);
34943
34944             enter.append('button')
34945                 .attr('class', 'list-item-gpx-extent')
34946                 .call(bootstrap.tooltip()
34947                     .title(t('gpx.zoom'))
34948                     .placement('left'))
34949                 .on('click', function() {
34950                     d3.event.preventDefault();
34951                     d3.event.stopPropagation();
34952                     gpx.fitZoom();
34953                 })
34954                 .call(iD.svg.Icon('#icon-search'));
34955
34956             enter.append('button')
34957                 .attr('class', 'list-item-gpx-browse')
34958                 .call(bootstrap.tooltip()
34959                     .title(t('gpx.browse'))
34960                     .placement('left'))
34961                 .on('click', function() {
34962                     d3.select(document.createElement('input'))
34963                         .attr('type', 'file')
34964                         .on('change', function() {
34965                             gpx.files(d3.event.target.files);
34966                         })
34967                         .node().click();
34968                 })
34969                 .call(iD.svg.Icon('#icon-geolocate'));
34970
34971             var labelGpx = enter.append('label')
34972                 .call(bootstrap.tooltip()
34973                     .title(t('gpx.drag_drop'))
34974                     .placement('top'));
34975
34976             labelGpx.append('input')
34977                 .attr('type', 'checkbox')
34978                 .on('change', clickGpx);
34979
34980             labelGpx.append('span')
34981                 .text(t('gpx.local_layer'));
34982
34983             // Update
34984             gpxLayerItem
34985                 .classed('active', showsGpx)
34986                 .selectAll('input')
34987                 .property('disabled', !hasGpx)
34988                 .property('checked', showsGpx);
34989
34990             // Exit
34991             gpxLayerItem.exit()
34992                 .remove();
34993         }
34994
34995
34996         function drawList(selection, data, type, name, change, active) {
34997             var items = selection.selectAll('li')
34998                 .data(data);
34999
35000             // Enter
35001             var enter = items.enter()
35002                 .append('li')
35003                 .attr('class', 'layer')
35004                 .call(bootstrap.tooltip()
35005                     .html(true)
35006                     .title(function(d) {
35007                         var tip = t(name + '.' + d + '.tooltip'),
35008                             key = (d === 'wireframe' ? 'W' : null);
35009
35010                         if (name === 'feature' && autoHiddenFeature(d)) {
35011                             tip += '<div>' + t('map_data.autohidden') + '</div>';
35012                         }
35013                         return iD.ui.tooltipHtml(tip, key);
35014                     })
35015                     .placement('top')
35016                 );
35017
35018             var label = enter.append('label');
35019
35020             label.append('input')
35021                 .attr('type', type)
35022                 .attr('name', name)
35023                 .on('change', change);
35024
35025             label.append('span')
35026                 .text(function(d) { return t(name + '.' + d + '.description'); });
35027
35028             // Update
35029             items
35030                 .classed('active', active)
35031                 .selectAll('input')
35032                 .property('checked', active)
35033                 .property('indeterminate', function(d) {
35034                     return (name === 'feature' && autoHiddenFeature(d));
35035                 });
35036
35037             // Exit
35038             items.exit()
35039                 .remove();
35040         }
35041
35042
35043         function update() {
35044             dataLayerContainer.call(drawMapillaryItems);
35045             dataLayerContainer.call(drawGpxItem);
35046
35047             fillList.call(drawList, fills, 'radio', 'area_fill', setFill, showsFill);
35048
35049             featureList.call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature);
35050         }
35051
35052         function hidePanel() {
35053             setVisible(false);
35054         }
35055
35056         function togglePanel() {
35057             if (d3.event) d3.event.preventDefault();
35058             tooltip.hide(button);
35059             setVisible(!button.classed('active'));
35060         }
35061
35062         function toggleWireframe() {
35063             if (d3.event) {
35064                 d3.event.preventDefault();
35065                 d3.event.stopPropagation();
35066             }
35067             setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe'));
35068             context.map().pan([0,0]);  // trigger a redraw
35069         }
35070
35071         function setVisible(show) {
35072             if (show !== shown) {
35073                 button.classed('active', show);
35074                 shown = show;
35075
35076                 if (show) {
35077                     update();
35078                     selection.on('mousedown.map_data-inside', function() {
35079                         return d3.event.stopPropagation();
35080                     });
35081                     content.style('display', 'block')
35082                         .style('right', '-300px')
35083                         .transition()
35084                         .duration(200)
35085                         .style('right', '0px');
35086                 } else {
35087                     content.style('display', 'block')
35088                         .style('right', '0px')
35089                         .transition()
35090                         .duration(200)
35091                         .style('right', '-300px')
35092                         .each('end', function() {
35093                             d3.select(this).style('display', 'none');
35094                         });
35095                     selection.on('mousedown.map_data-inside', null);
35096                 }
35097             }
35098         }
35099
35100
35101         var content = selection.append('div')
35102                 .attr('class', 'fillL map-overlay col3 content hide'),
35103             tooltip = bootstrap.tooltip()
35104                 .placement('left')
35105                 .html(true)
35106                 .title(iD.ui.tooltipHtml(t('map_data.description'), key)),
35107             button = selection.append('button')
35108                 .attr('tabindex', -1)
35109                 .on('click', togglePanel)
35110                 .call(iD.svg.Icon('#icon-data', 'light'))
35111                 .call(tooltip),
35112             shown = false;
35113
35114         content.append('h4')
35115             .text(t('map_data.title'));
35116
35117
35118         // data layers
35119         content.append('a')
35120             .text(t('map_data.data_layers'))
35121             .attr('href', '#')
35122             .classed('hide-toggle', true)
35123             .classed('expanded', true)
35124             .on('click', function() {
35125                 var exp = d3.select(this).classed('expanded');
35126                 dataLayerContainer.style('display', exp ? 'none' : 'block');
35127                 d3.select(this).classed('expanded', !exp);
35128                 d3.event.preventDefault();
35129             });
35130
35131         var dataLayerContainer = content.append('div')
35132             .attr('class', 'data-data-layers')
35133             .style('display', 'block');
35134
35135
35136         // area fills
35137         content.append('a')
35138             .text(t('map_data.fill_area'))
35139             .attr('href', '#')
35140             .classed('hide-toggle', true)
35141             .classed('expanded', false)
35142             .on('click', function() {
35143                 var exp = d3.select(this).classed('expanded');
35144                 fillContainer.style('display', exp ? 'none' : 'block');
35145                 d3.select(this).classed('expanded', !exp);
35146                 d3.event.preventDefault();
35147             });
35148
35149         var fillContainer = content.append('div')
35150             .attr('class', 'data-area-fills')
35151             .style('display', 'none');
35152
35153         var fillList = fillContainer.append('ul')
35154             .attr('class', 'layer-list layer-fill-list');
35155
35156
35157         // feature filters
35158         content.append('a')
35159             .text(t('map_data.map_features'))
35160             .attr('href', '#')
35161             .classed('hide-toggle', true)
35162             .classed('expanded', false)
35163             .on('click', function() {
35164                 var exp = d3.select(this).classed('expanded');
35165                 featureContainer.style('display', exp ? 'none' : 'block');
35166                 d3.select(this).classed('expanded', !exp);
35167                 d3.event.preventDefault();
35168             });
35169
35170         var featureContainer = content.append('div')
35171             .attr('class', 'data-feature-filters')
35172             .style('display', 'none');
35173
35174         var featureList = featureContainer.append('ul')
35175             .attr('class', 'layer-list layer-feature-list');
35176
35177
35178         context.features()
35179             .on('change.map_data-update', update);
35180
35181         setFill(fillDefault);
35182
35183         var keybinding = d3.keybinding('features')
35184             .on(key, togglePanel)
35185             .on('W', toggleWireframe)
35186             .on('B', hidePanel)
35187             .on('H', hidePanel);
35188
35189         d3.select(document)
35190             .call(keybinding);
35191
35192         context.surface().on('mousedown.map_data-outside', hidePanel);
35193         context.container().on('mousedown.map_data-outside', hidePanel);
35194     }
35195
35196     return map_data;
35197 };
35198 iD.ui.MapInMap = function(context) {
35199     var key = '/';
35200
35201     function map_in_map(selection) {
35202         var backgroundLayer = iD.TileLayer(),
35203             overlayLayers = {},
35204             projection = iD.geo.RawMercator(),
35205             gpxLayer = iD.svg.Gpx(projection, context).showLabels(false),
35206             zoom = d3.behavior.zoom()
35207                 .scaleExtent([ztok(0.5), ztok(24)])
35208                 .on('zoom', zoomPan),
35209             transformed = false,
35210             panning = false,
35211             hidden = true,
35212             zDiff = 6,    // by default, minimap renders at (main zoom - 6)
35213             tStart, tLast, tCurr, kLast, kCurr, tiles, svg, timeoutId;
35214
35215         function ztok(z) { return 256 * Math.pow(2, z); }
35216         function ktoz(k) { return Math.log(k) / Math.LN2 - 8; }
35217
35218
35219         function startMouse() {
35220             context.surface().on('mouseup.map-in-map-outside', endMouse);
35221             context.container().on('mouseup.map-in-map-outside', endMouse);
35222
35223             tStart = tLast = tCurr = projection.translate();
35224             panning = true;
35225         }
35226
35227
35228         function zoomPan() {
35229             var e = d3.event.sourceEvent,
35230                 t = d3.event.translate,
35231                 k = d3.event.scale,
35232                 zMain = ktoz(context.projection.scale() * 2 * Math.PI),
35233                 zMini = ktoz(k);
35234
35235             // restrict minimap zoom to < (main zoom - 3)
35236             if (zMini > zMain - 3) {
35237                 zMini = zMain - 3;
35238                 zoom.scale(kCurr).translate(tCurr);  // restore last good values
35239                 return;
35240             }
35241
35242             tCurr = t;
35243             kCurr = k;
35244             zDiff = zMain - zMini;
35245
35246             var scale = kCurr / kLast,
35247                 tX = (tCurr[0] / scale - tLast[0]) * scale,
35248                 tY = (tCurr[1] / scale - tLast[1]) * scale;
35249
35250             iD.util.setTransform(tiles, tX, tY, scale);
35251             iD.util.setTransform(svg, 0, 0, scale);
35252             transformed = true;
35253
35254             queueRedraw();
35255
35256             e.preventDefault();
35257             e.stopPropagation();
35258         }
35259
35260
35261         function endMouse() {
35262             context.surface().on('mouseup.map-in-map-outside', null);
35263             context.container().on('mouseup.map-in-map-outside', null);
35264
35265             updateProjection();
35266             panning = false;
35267
35268             if (tCurr[0] !== tStart[0] && tCurr[1] !== tStart[1]) {
35269                 var dMini = wrap.dimensions(),
35270                     cMini = [ dMini[0] / 2, dMini[1] / 2 ];
35271
35272                 context.map().center(projection.invert(cMini));
35273             }
35274         }
35275
35276
35277         function updateProjection() {
35278             var loc = context.map().center(),
35279                 dMini = wrap.dimensions(),
35280                 cMini = [ dMini[0] / 2, dMini[1] / 2 ],
35281                 tMain = context.projection.translate(),
35282                 kMain = context.projection.scale(),
35283                 zMain = ktoz(kMain * 2 * Math.PI),
35284                 zMini = Math.max(zMain - zDiff, 0.5),
35285                 kMini = ztok(zMini);
35286
35287             projection
35288                 .translate(tMain)
35289                 .scale(kMini / (2 * Math.PI));
35290
35291             var s = projection(loc),
35292                 mouse = panning ? [ tCurr[0] - tStart[0], tCurr[1] - tStart[1] ] : [0, 0],
35293                 tMini = [
35294                     cMini[0] - s[0] + tMain[0] + mouse[0],
35295                     cMini[1] - s[1] + tMain[1] + mouse[1]
35296                 ];
35297
35298             projection
35299                 .translate(tMini)
35300                 .clipExtent([[0, 0], dMini]);
35301
35302             zoom
35303                 .center(cMini)
35304                 .translate(tMini)
35305                 .scale(kMini);
35306
35307             tLast = tCurr = tMini;
35308             kLast = kCurr = kMini;
35309
35310             if (transformed) {
35311                 iD.util.setTransform(tiles, 0, 0);
35312                 iD.util.setTransform(svg, 0, 0);
35313                 transformed = false;
35314             }
35315         }
35316
35317
35318         function redraw() {
35319             if (hidden) return;
35320
35321             updateProjection();
35322
35323             var dMini = wrap.dimensions(),
35324                 zMini = ktoz(projection.scale() * 2 * Math.PI);
35325
35326             // setup tile container
35327             tiles = wrap
35328                 .selectAll('.map-in-map-tiles')
35329                 .data([0]);
35330
35331             tiles
35332                 .enter()
35333                 .append('div')
35334                 .attr('class', 'map-in-map-tiles');
35335
35336             // redraw background
35337             backgroundLayer
35338                 .source(context.background().baseLayerSource())
35339                 .projection(projection)
35340                 .dimensions(dMini);
35341
35342             var background = tiles
35343                 .selectAll('.map-in-map-background')
35344                 .data([0]);
35345
35346             background.enter()
35347                 .append('div')
35348                 .attr('class', 'map-in-map-background');
35349
35350             background
35351                 .call(backgroundLayer);
35352
35353
35354             // redraw overlay
35355             var overlaySources = context.background().overlayLayerSources();
35356             var activeOverlayLayers = [];
35357             for (var i = 0; i < overlaySources.length; i++) {
35358                 if (overlaySources[i].validZoom(zMini)) {
35359                     if (!overlayLayers[i]) overlayLayers[i] = iD.TileLayer();
35360                     activeOverlayLayers.push(overlayLayers[i]
35361                         .source(overlaySources[i])
35362                         .projection(projection)
35363                         .dimensions(dMini));
35364                 }
35365             }
35366
35367             var overlay = tiles
35368                 .selectAll('.map-in-map-overlay')
35369                 .data([0]);
35370
35371             overlay.enter()
35372                 .append('div')
35373                 .attr('class', 'map-in-map-overlay');
35374
35375             var overlays = overlay
35376                 .selectAll('div')
35377                 .data(activeOverlayLayers, function(d) { return d.source().name(); });
35378
35379             overlays.enter().append('div');
35380             overlays.each(function(layer) {
35381                 d3.select(this).call(layer);
35382             });
35383
35384             overlays.exit()
35385                 .remove();
35386
35387
35388             var gpx = tiles
35389                 .selectAll('.map-in-map-gpx')
35390                 .data(gpxLayer.enabled() ? [0] : []);
35391
35392             gpx.enter()
35393                 .append('svg')
35394                 .attr('class', 'map-in-map-gpx');
35395
35396             gpx.exit()
35397                 .remove();
35398
35399             gpx.call(gpxLayer);
35400
35401
35402             // redraw bounding box
35403             if (!panning) {
35404                 var getPath = d3.geo.path().projection(projection),
35405                     bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
35406
35407                 svg = wrap.selectAll('.map-in-map-svg')
35408                     .data([0]);
35409
35410                 svg.enter()
35411                     .append('svg')
35412                     .attr('class', 'map-in-map-svg');
35413
35414                 var path = svg.selectAll('.map-in-map-bbox')
35415                     .data([bbox]);
35416
35417                 path.enter()
35418                     .append('path')
35419                     .attr('class', 'map-in-map-bbox');
35420
35421                 path
35422                     .attr('d', getPath)
35423                     .classed('thick', function(d) { return getPath.area(d) < 30; });
35424             }
35425         }
35426
35427
35428         function queueRedraw() {
35429             clearTimeout(timeoutId);
35430             timeoutId = setTimeout(function() { redraw(); }, 300);
35431         }
35432
35433
35434         function toggle() {
35435             if (d3.event) d3.event.preventDefault();
35436
35437             hidden = !hidden;
35438
35439             var label = d3.select('.minimap-toggle');
35440             label.classed('active', !hidden)
35441                 .select('input').property('checked', !hidden);
35442
35443             if (hidden) {
35444                 wrap
35445                     .style('display', 'block')
35446                     .style('opacity', 1)
35447                     .transition()
35448                     .duration(200)
35449                     .style('opacity', 0)
35450                     .each('end', function() {
35451                         d3.select(this).style('display', 'none');
35452                     });
35453             } else {
35454                 wrap
35455                     .style('display', 'block')
35456                     .style('opacity', 0)
35457                     .transition()
35458                     .duration(200)
35459                     .style('opacity', 1);
35460
35461                 redraw();
35462             }
35463         }
35464
35465         iD.ui.MapInMap.toggle = toggle;
35466
35467         var wrap = selection.selectAll('.map-in-map')
35468             .data([0]);
35469
35470         wrap.enter()
35471             .append('div')
35472             .attr('class', 'map-in-map')
35473             .style('display', (hidden ? 'none' : 'block'))
35474             .on('mousedown.map-in-map', startMouse)
35475             .on('mouseup.map-in-map', endMouse)
35476             .call(zoom)
35477             .on('dblclick.zoom', null);
35478
35479         context.map()
35480             .on('drawn.map-in-map', function(drawn) {
35481                 if (drawn.full === true) redraw();
35482             });
35483
35484         redraw();
35485
35486         var keybinding = d3.keybinding('map-in-map')
35487             .on(key, toggle);
35488
35489         d3.select(document)
35490             .call(keybinding);
35491     }
35492
35493     return map_in_map;
35494 };
35495 iD.ui.modal = function(selection, blocking) {
35496
35497     var previous = selection.select('div.modal');
35498     var animate = previous.empty();
35499
35500     previous.transition()
35501         .duration(200)
35502         .style('opacity', 0)
35503         .remove();
35504
35505     var shaded = selection
35506         .append('div')
35507         .attr('class', 'shaded')
35508         .style('opacity', 0);
35509
35510     shaded.close = function() {
35511         shaded
35512             .transition()
35513             .duration(200)
35514             .style('opacity',0)
35515             .remove();
35516         modal
35517             .transition()
35518             .duration(200)
35519             .style('top','0px');
35520         keybinding.off();
35521     };
35522
35523     var keybinding = d3.keybinding('modal')
35524         .on('⌫', shaded.close)
35525         .on('⎋', shaded.close);
35526
35527     d3.select(document).call(keybinding);
35528
35529     var modal = shaded.append('div')
35530         .attr('class', 'modal fillL col6');
35531
35532         shaded.on('click.remove-modal', function() {
35533             if (d3.event.target === this && !blocking) shaded.close();
35534         });
35535
35536     modal.append('button')
35537         .attr('class', 'close')
35538         .on('click', function() {
35539             if (!blocking) shaded.close();
35540         })
35541         .append('div')
35542             .attr('class','icon close');
35543
35544     modal.append('div')
35545         .attr('class', 'content');
35546
35547     if (animate) {
35548         shaded.transition().style('opacity', 1);
35549     } else {
35550         shaded.style('opacity', 1);
35551     }
35552
35553     return shaded;
35554 };
35555 iD.ui.Modes = function(context) {
35556     var modes = [
35557         iD.modes.AddPoint(context),
35558         iD.modes.AddLine(context),
35559         iD.modes.AddArea(context)];
35560
35561     function editable() {
35562         return context.editable() && context.mode().id !== 'save';
35563     }
35564
35565     return function(selection) {
35566         var buttons = selection.selectAll('button.add-button')
35567             .data(modes);
35568
35569        buttons.enter().append('button')
35570            .attr('tabindex', -1)
35571            .attr('class', function(mode) { return mode.id + ' add-button col4'; })
35572            .on('click.mode-buttons', function(mode) {
35573                if (mode.id === context.mode().id) {
35574                    context.enter(iD.modes.Browse(context));
35575                } else {
35576                    context.enter(mode);
35577                }
35578            })
35579            .call(bootstrap.tooltip()
35580                .placement('bottom')
35581                .html(true)
35582                .title(function(mode) {
35583                    return iD.ui.tooltipHtml(mode.description, mode.key);
35584                }));
35585
35586         context.map()
35587             .on('move.modes', _.debounce(update, 500));
35588
35589         context
35590             .on('enter.modes', update);
35591
35592         buttons.each(function(d) {
35593             d3.select(this)
35594                 .call(iD.svg.Icon('#icon-' + d.button, 'pre-text'));
35595         });
35596
35597         buttons.append('span')
35598             .attr('class', 'label')
35599             .text(function(mode) { return mode.title; });
35600
35601         context.on('enter.editor', function(entered) {
35602             buttons.classed('active', function(mode) { return entered.button === mode.button; });
35603             context.container()
35604                 .classed('mode-' + entered.id, true);
35605         });
35606
35607         context.on('exit.editor', function(exited) {
35608             context.container()
35609                 .classed('mode-' + exited.id, false);
35610         });
35611
35612         var keybinding = d3.keybinding('mode-buttons');
35613
35614         modes.forEach(function(m) {
35615             keybinding.on(m.key, function() { if (editable()) context.enter(m); });
35616         });
35617
35618         d3.select(document)
35619             .call(keybinding);
35620
35621         function update() {
35622             buttons.property('disabled', !editable());
35623         }
35624     };
35625 };
35626 iD.ui.Notice = function(context) {
35627     return function(selection) {
35628         var div = selection.append('div')
35629             .attr('class', 'notice');
35630
35631         var button = div.append('button')
35632             .attr('class', 'zoom-to notice')
35633             .on('click', function() { context.map().zoom(context.minEditableZoom()); });
35634
35635         button
35636             .call(iD.svg.Icon('#icon-plus', 'pre-text'))
35637             .append('span')
35638             .attr('class', 'label')
35639             .text(t('zoom_in_edit'));
35640
35641         function disableTooHigh() {
35642             div.style('display', context.editable() ? 'none' : 'block');
35643         }
35644
35645         context.map()
35646             .on('move.notice', _.debounce(disableTooHigh, 500));
35647
35648         disableTooHigh();
35649     };
35650 };
35651 iD.ui.PresetIcon = function() {
35652     var preset, geometry;
35653
35654     function presetIcon(selection) {
35655         selection.each(render);
35656     }
35657
35658     function render() {
35659         var selection = d3.select(this),
35660             p = preset.apply(this, arguments),
35661             geom = geometry.apply(this, arguments),
35662             icon = p.icon || (geom === 'line' ? 'other-line' : 'marker-stroked'),
35663             maki = iD.data.featureIcons.hasOwnProperty(icon + '-24');
35664
35665         if (icon === 'dentist') maki = true;  // workaround for dentist icon missing in `maki-sprite.json`
35666
35667         function tag_classes(p) {
35668             var s = '';
35669             for (var i in p.tags) {
35670                 s += ' tag-' + i;
35671                 if (p.tags[i] !== '*') {
35672                     s += ' tag-' + i + '-' + p.tags[i];
35673                 }
35674             }
35675             return s;
35676         }
35677
35678         var $fill = selection.selectAll('.preset-icon-fill')
35679             .data([0]);
35680
35681         $fill.enter().append('div');
35682
35683         $fill.attr('class', function() {
35684             return 'preset-icon-fill preset-icon-fill-' + geom + tag_classes(p);
35685         });
35686
35687         var $frame = selection.selectAll('.preset-icon-frame')
35688             .data([0]);
35689
35690         $frame.enter()
35691             .append('div')
35692             .call(iD.svg.Icon('#preset-icon-frame'));
35693
35694         $frame.attr('class', function() {
35695             return 'preset-icon-frame ' + (geom === 'area' ? '' : 'hide');
35696         });
35697
35698
35699         var $icon = selection.selectAll('.preset-icon')
35700             .data([0]);
35701
35702         $icon.enter()
35703             .append('div')
35704             .attr('class', 'preset-icon')
35705             .call(iD.svg.Icon(''));
35706
35707         $icon
35708             .attr('class', 'preset-icon preset-icon-' + (maki ? '32' : (geom === 'area' ? '44' : '60')));
35709
35710         $icon.selectAll('svg')
35711             .attr('class', function() {
35712                 return 'icon ' + icon + tag_classes(p);
35713             });
35714
35715         $icon.selectAll('use')       // workaround: maki parking-24 broken?
35716             .attr('href', '#' + icon + (maki ? ( icon === 'parking' ? '-18' : '-24') : ''));
35717     }
35718
35719     presetIcon.preset = function(_) {
35720         if (!arguments.length) return preset;
35721         preset = d3.functor(_);
35722         return presetIcon;
35723     };
35724
35725     presetIcon.geometry = function(_) {
35726         if (!arguments.length) return geometry;
35727         geometry = d3.functor(_);
35728         return presetIcon;
35729     };
35730
35731     return presetIcon;
35732 };
35733 iD.ui.preset = function(context) {
35734     var event = d3.dispatch('change'),
35735         state,
35736         fields,
35737         preset,
35738         tags,
35739         id;
35740
35741     function UIField(field, entity, show) {
35742         field = _.clone(field);
35743
35744         field.input = iD.ui.preset[field.type](field, context)
35745             .on('change', event.change);
35746
35747         if (field.input.entity) field.input.entity(entity);
35748
35749         field.keys = field.keys || [field.key];
35750
35751         field.show = show;
35752
35753         field.shown = function() {
35754             return field.id === 'name' || field.show || _.any(field.keys, function(key) { return !!tags[key]; });
35755         };
35756
35757         field.modified = function() {
35758             var original = context.graph().base().entities[entity.id];
35759             return _.any(field.keys, function(key) {
35760                 return original ? tags[key] !== original.tags[key] : tags[key];
35761             });
35762         };
35763
35764         field.revert = function() {
35765             var original = context.graph().base().entities[entity.id],
35766                 t = {};
35767             field.keys.forEach(function(key) {
35768                 t[key] = original ? original.tags[key] : undefined;
35769             });
35770             return t;
35771         };
35772
35773         field.present = function() {
35774             return _.any(field.keys, function(key) {
35775                 return tags[key];
35776             });
35777         };
35778
35779         field.remove = function() {
35780             var t = {};
35781             field.keys.forEach(function(key) {
35782                 t[key] = undefined;
35783             });
35784             return t;
35785         };
35786
35787         return field;
35788     }
35789
35790     function fieldKey(field) {
35791         return field.id;
35792     }
35793
35794     function presets(selection) {
35795         selection.call(iD.ui.Disclosure()
35796             .title(t('inspector.all_fields'))
35797             .expanded(context.storage('preset_fields.expanded') !== 'false')
35798             .on('toggled', toggled)
35799             .content(content));
35800
35801         function toggled(expanded) {
35802             context.storage('preset_fields.expanded', expanded);
35803         }
35804     }
35805
35806     function content(selection) {
35807         if (!fields) {
35808             var entity = context.entity(id),
35809                 geometry = context.geometry(id);
35810
35811             fields = [UIField(context.presets().field('name'), entity)];
35812
35813             preset.fields.forEach(function(field) {
35814                 if (field.matchGeometry(geometry)) {
35815                     fields.push(UIField(field, entity, true));
35816                 }
35817             });
35818
35819             if (entity.isHighwayIntersection(context.graph())) {
35820                 fields.push(UIField(context.presets().field('restrictions'), entity, true));
35821             }
35822
35823             context.presets().universal().forEach(function(field) {
35824                 if (preset.fields.indexOf(field) < 0) {
35825                     fields.push(UIField(field, entity));
35826                 }
35827             });
35828         }
35829
35830         var shown = fields.filter(function(field) { return field.shown(); }),
35831             notShown = fields.filter(function(field) { return !field.shown(); });
35832
35833         var $form = selection.selectAll('.preset-form')
35834             .data([0]);
35835
35836         $form.enter().append('div')
35837             .attr('class', 'preset-form inspector-inner fillL3');
35838
35839         var $fields = $form.selectAll('.form-field')
35840             .data(shown, fieldKey);
35841
35842         // Enter
35843
35844         var $enter = $fields.enter()
35845             .append('div')
35846             .attr('class', function(field) {
35847                 return 'form-field form-field-' + field.id;
35848             });
35849
35850         var $label = $enter.append('label')
35851             .attr('class', 'form-label')
35852             .attr('for', function(field) { return 'preset-input-' + field.id; })
35853             .text(function(field) { return field.label(); });
35854
35855         var wrap = $label.append('div')
35856             .attr('class', 'form-label-button-wrap');
35857
35858         wrap.append('button')
35859             .attr('class', 'remove-icon')
35860             .call(iD.svg.Icon('#operation-delete'));
35861
35862         wrap.append('button')
35863             .attr('class', 'modified-icon')
35864             .attr('tabindex', -1)
35865             .call(iD.svg.Icon('#icon-undo'));
35866
35867         // Update
35868
35869         $fields.select('.form-label-button-wrap .remove-icon')
35870             .on('click', remove);
35871
35872         $fields.select('.modified-icon')
35873             .on('click', revert);
35874
35875         $fields
35876             .order()
35877             .classed('modified', function(field) {
35878                 return field.modified();
35879             })
35880             .classed('present', function(field) {
35881                 return field.present();
35882             })
35883             .each(function(field) {
35884                 var reference = iD.ui.TagReference(field.reference || {key: field.key}, context);
35885
35886                 if (state === 'hover') {
35887                     reference.showing(false);
35888                 }
35889
35890                 d3.select(this)
35891                     .call(field.input)
35892                     .selectAll('input')
35893                     .on('keydown', function() {
35894                         if (d3.event.keyCode === 13) {  // enter
35895                             context.enter(iD.modes.Browse(context));
35896                         }
35897                     })
35898                     .call(reference.body)
35899                     .select('.form-label-button-wrap')
35900                     .call(reference.button);
35901
35902                 field.input.tags(tags);
35903             });
35904
35905         $fields.exit()
35906             .remove();
35907
35908         notShown = notShown.map(function(field) {
35909             return {
35910                 title: field.label(),
35911                 value: field.label(),
35912                 field: field
35913             };
35914         });
35915
35916         var $more = selection.selectAll('.more-fields')
35917             .data((notShown.length > 0) ? [0] : []);
35918
35919         $more.enter().append('div')
35920             .attr('class', 'more-fields')
35921             .append('label')
35922                 .text(t('inspector.add_fields'));
35923
35924         var $input = $more.selectAll('.value')
35925             .data([0]);
35926
35927         $input.enter().append('input')
35928             .attr('class', 'value')
35929             .attr('type', 'text');
35930
35931         $input.value('')
35932             .attr('placeholder', function() {
35933                 var placeholder = [];
35934                 for (var field in notShown) {
35935                     placeholder.push(notShown[field].title);
35936                 }
35937                 return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : '');
35938             })
35939             .call(d3.combobox().data(notShown)
35940                 .minItems(1)
35941                 .on('accept', show));
35942
35943         $more.exit()
35944             .remove();
35945
35946         $input.exit()
35947             .remove();
35948
35949         function show(field) {
35950             field = field.field;
35951             field.show = true;
35952             content(selection);
35953             field.input.focus();
35954         }
35955
35956         function revert(field) {
35957             d3.event.stopPropagation();
35958             d3.event.preventDefault();
35959             event.change(field.revert());
35960         }
35961
35962         function remove(field) {
35963             d3.event.stopPropagation();
35964             d3.event.preventDefault();
35965             event.change(field.remove());
35966         }
35967     }
35968
35969     presets.preset = function(_) {
35970         if (!arguments.length) return preset;
35971         if (preset && preset.id === _.id) return presets;
35972         preset = _;
35973         fields = null;
35974         return presets;
35975     };
35976
35977     presets.state = function(_) {
35978         if (!arguments.length) return state;
35979         state = _;
35980         return presets;
35981     };
35982
35983     presets.tags = function(_) {
35984         if (!arguments.length) return tags;
35985         tags = _;
35986         // Don't reset fields here.
35987         return presets;
35988     };
35989
35990     presets.entityID = function(_) {
35991         if (!arguments.length) return id;
35992         if (id === _) return presets;
35993         id = _;
35994         fields = null;
35995         return presets;
35996     };
35997
35998     return d3.rebind(presets, event, 'on');
35999 };
36000 iD.ui.PresetList = function(context) {
36001     var event = d3.dispatch('choose'),
36002         id,
36003         currentPreset,
36004         autofocus = false;
36005
36006     function presetList(selection) {
36007         var geometry = context.geometry(id),
36008             presets = context.presets().matchGeometry(geometry);
36009
36010         selection.html('');
36011
36012         var messagewrap = selection.append('div')
36013             .attr('class', 'header fillL cf');
36014
36015         var message = messagewrap.append('h3')
36016             .text(t('inspector.choose'));
36017
36018         if (context.entity(id).isUsed(context.graph())) {
36019             messagewrap.append('button')
36020                 .attr('class', 'preset-choose')
36021                 .on('click', function() { event.choose(currentPreset); })
36022                 .append('span')
36023                 .html('&#9658;');
36024         } else {
36025             messagewrap.append('button')
36026                 .attr('class', 'close')
36027                 .on('click', function() {
36028                     context.enter(iD.modes.Browse(context));
36029                 })
36030                 .call(iD.svg.Icon('#icon-close'));
36031         }
36032
36033         function keydown() {
36034             // hack to let delete shortcut work when search is autofocused
36035             if (search.property('value').length === 0 &&
36036                 (d3.event.keyCode === d3.keybinding.keyCodes['⌫'] ||
36037                  d3.event.keyCode === d3.keybinding.keyCodes['⌦'])) {
36038                 d3.event.preventDefault();
36039                 d3.event.stopPropagation();
36040                 iD.operations.Delete([id], context)();
36041             } else if (search.property('value').length === 0 &&
36042                 (d3.event.ctrlKey || d3.event.metaKey) &&
36043                 d3.event.keyCode === d3.keybinding.keyCodes.z) {
36044                 d3.event.preventDefault();
36045                 d3.event.stopPropagation();
36046                 context.undo();
36047             } else if (!d3.event.ctrlKey && !d3.event.metaKey) {
36048                 d3.select(this).on('keydown', null);
36049             }
36050         }
36051
36052         function keypress() {
36053             // enter
36054             var value = search.property('value');
36055             if (d3.event.keyCode === 13 && value.length) {
36056                 list.selectAll('.preset-list-item:first-child').datum().choose();
36057             }
36058         }
36059
36060         function inputevent() {
36061             var value = search.property('value');
36062             list.classed('filtered', value.length);
36063             if (value.length) {
36064                 var results = presets.search(value, geometry);
36065                 message.text(t('inspector.results', {
36066                     n: results.collection.length,
36067                     search: value
36068                 }));
36069                 list.call(drawList, results);
36070             } else {
36071                 list.call(drawList, context.presets().defaults(geometry, 36));
36072                 message.text(t('inspector.choose'));
36073             }
36074         }
36075
36076         var searchWrap = selection.append('div')
36077             .attr('class', 'search-header');
36078
36079         var search = searchWrap.append('input')
36080             .attr('class', 'preset-search-input')
36081             .attr('placeholder', t('inspector.search'))
36082             .attr('type', 'search')
36083             .on('keydown', keydown)
36084             .on('keypress', keypress)
36085             .on('input', inputevent);
36086
36087         searchWrap
36088             .call(iD.svg.Icon('#icon-search', 'pre-text'));
36089
36090         if (autofocus) {
36091             search.node().focus();
36092         }
36093
36094         var listWrap = selection.append('div')
36095             .attr('class', 'inspector-body');
36096
36097         var list = listWrap.append('div')
36098             .attr('class', 'preset-list fillL cf')
36099             .call(drawList, context.presets().defaults(geometry, 36));
36100     }
36101
36102     function drawList(list, presets) {
36103         var collection = presets.collection.map(function(preset) {
36104             return preset.members ? CategoryItem(preset) : PresetItem(preset);
36105         });
36106
36107         var items = list.selectAll('.preset-list-item')
36108             .data(collection, function(d) { return d.preset.id; });
36109
36110         items.enter().append('div')
36111             .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
36112             .classed('current', function(item) { return item.preset === currentPreset; })
36113             .each(function(item) {
36114                 d3.select(this).call(item);
36115             })
36116             .style('opacity', 0)
36117             .transition()
36118             .style('opacity', 1);
36119
36120         items.order();
36121
36122         items.exit()
36123             .remove();
36124     }
36125
36126     function CategoryItem(preset) {
36127         var box, sublist, shown = false;
36128
36129         function item(selection) {
36130             var wrap = selection.append('div')
36131                 .attr('class', 'preset-list-button-wrap category col12');
36132
36133             wrap.append('button')
36134                 .attr('class', 'preset-list-button')
36135                 .classed('expanded', false)
36136                 .call(iD.ui.PresetIcon()
36137                     .geometry(context.geometry(id))
36138                     .preset(preset))
36139                 .on('click', function() {
36140                     var isExpanded = d3.select(this).classed('expanded');
36141                     var triangle = isExpanded ? '▶ ' :  '▼ ';
36142                     d3.select(this).classed('expanded', !isExpanded);
36143                     d3.select(this).selectAll('.label').text(triangle + preset.name());
36144                     item.choose();
36145                 })
36146                 .append('div')
36147                 .attr('class', 'label')
36148                 .text(function() {
36149                   return '▶ ' + preset.name();
36150                 });
36151
36152             box = selection.append('div')
36153                 .attr('class', 'subgrid col12')
36154                 .style('max-height', '0px')
36155                 .style('opacity', 0);
36156
36157             box.append('div')
36158                 .attr('class', 'arrow');
36159
36160             sublist = box.append('div')
36161                 .attr('class', 'preset-list fillL3 cf fl');
36162         }
36163
36164         item.choose = function() {
36165             if (!box || !sublist) return;
36166
36167             if (shown) {
36168                 shown = false;
36169                 box.transition()
36170                     .duration(200)
36171                     .style('opacity', '0')
36172                     .style('max-height', '0px')
36173                     .style('padding-bottom', '0px');
36174             } else {
36175                 shown = true;
36176                 sublist.call(drawList, preset.members);
36177                 box.transition()
36178                     .duration(200)
36179                     .style('opacity', '1')
36180                     .style('max-height', 200 + preset.members.collection.length * 80 + 'px')
36181                     .style('padding-bottom', '20px');
36182             }
36183         };
36184
36185         item.preset = preset;
36186
36187         return item;
36188     }
36189
36190     function PresetItem(preset) {
36191         function item(selection) {
36192             var wrap = selection.append('div')
36193                 .attr('class', 'preset-list-button-wrap col12');
36194
36195             wrap.append('button')
36196                 .attr('class', 'preset-list-button')
36197                 .call(iD.ui.PresetIcon()
36198                     .geometry(context.geometry(id))
36199                     .preset(preset))
36200                 .on('click', item.choose)
36201                 .append('div')
36202                 .attr('class', 'label')
36203                 .text(preset.name());
36204
36205             wrap.call(item.reference.button);
36206             selection.call(item.reference.body);
36207         }
36208
36209         item.choose = function() {
36210             context.presets().choose(preset);
36211
36212             context.perform(
36213                 iD.actions.ChangePreset(id, currentPreset, preset),
36214                 t('operations.change_tags.annotation'));
36215
36216             event.choose(preset);
36217         };
36218
36219         item.help = function() {
36220             d3.event.stopPropagation();
36221             item.reference.toggle();
36222         };
36223
36224         item.preset = preset;
36225         item.reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context);
36226
36227         return item;
36228     }
36229
36230     presetList.autofocus = function(_) {
36231         if (!arguments.length) return autofocus;
36232         autofocus = _;
36233         return presetList;
36234     };
36235
36236     presetList.entityID = function(_) {
36237         if (!arguments.length) return id;
36238         id = _;
36239         presetList.preset(context.presets().match(context.entity(id), context.graph()));
36240         return presetList;
36241     };
36242
36243     presetList.preset = function(_) {
36244         if (!arguments.length) return currentPreset;
36245         currentPreset = _;
36246         return presetList;
36247     };
36248
36249     return d3.rebind(presetList, event, 'on');
36250 };
36251 iD.ui.RadialMenu = function(context, operations) {
36252     var menu,
36253         center = [0, 0],
36254         tooltip;
36255
36256     var radialMenu = function(selection) {
36257         if (!operations.length)
36258             return;
36259
36260         selection.node().parentNode.focus();
36261
36262         function click(operation) {
36263             d3.event.stopPropagation();
36264             if (operation.disabled())
36265                 return;
36266             operation();
36267             radialMenu.close();
36268         }
36269
36270         menu = selection.append('g')
36271             .attr('class', 'radial-menu')
36272             .attr('transform', 'translate(' + center + ')')
36273             .attr('opacity', 0);
36274
36275         menu.transition()
36276             .attr('opacity', 1);
36277
36278         var r = 50,
36279             a = Math.PI / 4,
36280             a0 = -Math.PI / 4,
36281             a1 = a0 + (operations.length - 1) * a;
36282
36283         menu.append('path')
36284             .attr('class', 'radial-menu-background')
36285             .attr('d', 'M' + r * Math.sin(a0) + ',' +
36286                              r * Math.cos(a0) +
36287                       ' A' + r + ',' + r + ' 0 ' + (operations.length > 5 ? '1' : '0') + ',0 ' +
36288                              (r * Math.sin(a1) + 1e-3) + ',' +
36289                              (r * Math.cos(a1) + 1e-3)) // Force positive-length path (#1305)
36290             .attr('stroke-width', 50)
36291             .attr('stroke-linecap', 'round');
36292
36293         var button = menu.selectAll()
36294             .data(operations)
36295             .enter()
36296             .append('g')
36297             .attr('class', function(d) { return 'radial-menu-item radial-menu-item-' + d.id; })
36298             .classed('disabled', function(d) { return d.disabled(); })
36299             .attr('transform', function(d, i) {
36300                 return 'translate(' + iD.geo.roundCoords([
36301                         r * Math.sin(a0 + i * a),
36302                         r * Math.cos(a0 + i * a)]).join(',') + ')';
36303             });
36304
36305         button.append('circle')
36306             .attr('r', 15)
36307             .on('click', click)
36308             .on('mousedown', mousedown)
36309             .on('mouseover', mouseover)
36310             .on('mouseout', mouseout);
36311
36312         button.append('use')
36313             .attr('transform', 'translate(-10,-10)')
36314             .attr('width', '20')
36315             .attr('height', '20')
36316             .attr('xlink:href', function(d) { return '#operation-' + d.id; });
36317
36318         tooltip = d3.select(document.body)
36319             .append('div')
36320             .attr('class', 'tooltip-inner radial-menu-tooltip');
36321
36322         function mousedown() {
36323             d3.event.stopPropagation(); // https://github.com/openstreetmap/iD/issues/1869
36324         }
36325
36326         function mouseover(d, i) {
36327             var rect = context.surfaceRect(),
36328                 angle = a0 + i * a,
36329                 top = rect.top + (r + 25) * Math.cos(angle) + center[1] + 'px',
36330                 left = rect.left + (r + 25) * Math.sin(angle) + center[0] + 'px',
36331                 bottom = rect.height - (r + 25) * Math.cos(angle) - center[1] + 'px',
36332                 right = rect.width - (r + 25) * Math.sin(angle) - center[0] + 'px';
36333
36334             tooltip
36335                 .style('top', null)
36336                 .style('left', null)
36337                 .style('bottom', null)
36338                 .style('right', null)
36339                 .style('display', 'block')
36340                 .html(iD.ui.tooltipHtml(d.tooltip(), d.keys[0]));
36341
36342             if (i === 0) {
36343                 tooltip
36344                     .style('right', right)
36345                     .style('top', top);
36346             } else if (i >= 4) {
36347                 tooltip
36348                     .style('left', left)
36349                     .style('bottom', bottom);
36350             } else {
36351                 tooltip
36352                     .style('left', left)
36353                     .style('top', top);
36354             }
36355         }
36356
36357         function mouseout() {
36358             tooltip.style('display', 'none');
36359         }
36360     };
36361
36362     radialMenu.close = function() {
36363         if (menu) {
36364             menu
36365                 .style('pointer-events', 'none')
36366                 .transition()
36367                 .attr('opacity', 0)
36368                 .remove();
36369         }
36370
36371         if (tooltip) {
36372             tooltip.remove();
36373         }
36374     };
36375
36376     radialMenu.center = function(_) {
36377         if (!arguments.length) return center;
36378         center = _;
36379         return radialMenu;
36380     };
36381
36382     return radialMenu;
36383 };
36384 iD.ui.RawMemberEditor = function(context) {
36385     var id;
36386
36387     function selectMember(d) {
36388         d3.event.preventDefault();
36389         context.enter(iD.modes.Select(context, [d.id]));
36390     }
36391
36392     function changeRole(d) {
36393         var role = d3.select(this).property('value');
36394         context.perform(
36395             iD.actions.ChangeMember(d.relation.id, _.extend({}, d.id, {role: role}), d.index),
36396             t('operations.change_role.annotation'));
36397     }
36398
36399     function deleteMember(d) {
36400         context.perform(
36401             iD.actions.DeleteMember(d.relation.id, d.index),
36402             t('operations.delete_member.annotation'));
36403
36404         if (!context.hasEntity(d.relation.id)) {
36405             context.enter(iD.modes.Browse(context));
36406         }
36407     }
36408
36409     function rawMemberEditor(selection) {
36410         var entity = context.entity(id),
36411             memberships = [];
36412
36413         entity.members.forEach(function(member, index) {
36414             memberships.push({
36415                 index: index,
36416                 id: member.id,
36417                 role: member.role,
36418                 relation: entity,
36419                 member: context.hasEntity(member.id)
36420             });
36421         });
36422
36423         selection.call(iD.ui.Disclosure()
36424             .title(t('inspector.all_members') + ' (' + memberships.length + ')')
36425             .expanded(true)
36426             .on('toggled', toggled)
36427             .content(content));
36428
36429         function toggled(expanded) {
36430             if (expanded) {
36431                 selection.node().parentNode.scrollTop += 200;
36432             }
36433         }
36434
36435         function content($wrap) {
36436             var $list = $wrap.selectAll('.member-list')
36437                 .data([0]);
36438
36439             $list.enter().append('ul')
36440                 .attr('class', 'member-list');
36441
36442             var $items = $list.selectAll('li')
36443                 .data(memberships, function(d) {
36444                     return iD.Entity.key(d.relation) + ',' + d.index + ',' +
36445                         (d.member ? iD.Entity.key(d.member) : 'incomplete');
36446                 });
36447
36448             var $enter = $items.enter().append('li')
36449                 .attr('class', 'member-row form-field')
36450                 .classed('member-incomplete', function(d) { return !d.member; });
36451
36452             $enter.each(function(d) {
36453                 if (d.member) {
36454                     var $label = d3.select(this).append('label')
36455                         .attr('class', 'form-label')
36456                         .append('a')
36457                         .attr('href', '#')
36458                         .on('click', selectMember);
36459
36460                     $label.append('span')
36461                         .attr('class', 'member-entity-type')
36462                         .text(function(d) { return context.presets().match(d.member, context.graph()).name(); });
36463
36464                     $label.append('span')
36465                         .attr('class', 'member-entity-name')
36466                         .text(function(d) { return iD.util.displayName(d.member); });
36467
36468                 } else {
36469                     d3.select(this).append('label')
36470                         .attr('class', 'form-label')
36471                         .text(t('inspector.incomplete'));
36472                 }
36473             });
36474
36475             $enter.append('input')
36476                 .attr('class', 'member-role')
36477                 .property('type', 'text')
36478                 .attr('maxlength', 255)
36479                 .attr('placeholder', t('inspector.role'))
36480                 .property('value', function(d) { return d.role; })
36481                 .on('change', changeRole);
36482
36483             $enter.append('button')
36484                 .attr('tabindex', -1)
36485                 .attr('class', 'remove button-input-action member-delete minor')
36486                 .on('click', deleteMember)
36487                 .call(iD.svg.Icon('#operation-delete'));
36488
36489             $items.exit()
36490                 .remove();
36491         }
36492     }
36493
36494     rawMemberEditor.entityID = function(_) {
36495         if (!arguments.length) return id;
36496         id = _;
36497         return rawMemberEditor;
36498     };
36499
36500     return rawMemberEditor;
36501 };
36502 iD.ui.RawMembershipEditor = function(context) {
36503     var id, showBlank;
36504
36505     function selectRelation(d) {
36506         d3.event.preventDefault();
36507         context.enter(iD.modes.Select(context, [d.relation.id]));
36508     }
36509
36510     function changeRole(d) {
36511         var role = d3.select(this).property('value');
36512         context.perform(
36513             iD.actions.ChangeMember(d.relation.id, _.extend({}, d.member, {role: role}), d.index),
36514             t('operations.change_role.annotation'));
36515     }
36516
36517     function addMembership(d, role) {
36518         showBlank = false;
36519
36520         if (d.relation) {
36521             context.perform(
36522                 iD.actions.AddMember(d.relation.id, {id: id, type: context.entity(id).type, role: role}),
36523                 t('operations.add_member.annotation'));
36524
36525         } else {
36526             var relation = iD.Relation();
36527
36528             context.perform(
36529                 iD.actions.AddEntity(relation),
36530                 iD.actions.AddMember(relation.id, {id: id, type: context.entity(id).type, role: role}),
36531                 t('operations.add.annotation.relation'));
36532
36533             context.enter(iD.modes.Select(context, [relation.id]));
36534         }
36535     }
36536
36537     function deleteMembership(d) {
36538         context.perform(
36539             iD.actions.DeleteMember(d.relation.id, d.index),
36540             t('operations.delete_member.annotation'));
36541     }
36542
36543     function relations(q) {
36544         var newRelation = {
36545                 relation: null,
36546                 value: t('inspector.new_relation')
36547             },
36548             result = [],
36549             graph = context.graph();
36550
36551         context.intersects(context.extent()).forEach(function(entity) {
36552             if (entity.type !== 'relation' || entity.id === id)
36553                 return;
36554
36555             var presetName = context.presets().match(entity, graph).name(),
36556                 entityName = iD.util.displayName(entity) || '';
36557
36558             var value = presetName + ' ' + entityName;
36559             if (q && value.toLowerCase().indexOf(q.toLowerCase()) === -1)
36560                 return;
36561
36562             result.push({
36563                 relation: entity,
36564                 value: value
36565             });
36566         });
36567
36568         result.sort(function(a, b) {
36569             return iD.Relation.creationOrder(a.relation, b.relation);
36570         });
36571         result.unshift(newRelation);
36572
36573         return result;
36574     }
36575
36576     function rawMembershipEditor(selection) {
36577         var entity = context.entity(id),
36578             memberships = [];
36579
36580         context.graph().parentRelations(entity).forEach(function(relation) {
36581             relation.members.forEach(function(member, index) {
36582                 if (member.id === entity.id) {
36583                     memberships.push({relation: relation, member: member, index: index});
36584                 }
36585             });
36586         });
36587
36588         selection.call(iD.ui.Disclosure()
36589             .title(t('inspector.all_relations') + ' (' + memberships.length + ')')
36590             .expanded(true)
36591             .on('toggled', toggled)
36592             .content(content));
36593
36594         function toggled(expanded) {
36595             if (expanded) {
36596                 selection.node().parentNode.scrollTop += 200;
36597             }
36598         }
36599
36600         function content($wrap) {
36601             var $list = $wrap.selectAll('.member-list')
36602                 .data([0]);
36603
36604             $list.enter().append('ul')
36605                 .attr('class', 'member-list');
36606
36607             var $items = $list.selectAll('li.member-row-normal')
36608                 .data(memberships, function(d) { return iD.Entity.key(d.relation) + ',' + d.index; });
36609
36610             var $enter = $items.enter().append('li')
36611                 .attr('class', 'member-row member-row-normal form-field');
36612
36613             var $label = $enter.append('label')
36614                 .attr('class', 'form-label')
36615                 .append('a')
36616                 .attr('href', '#')
36617                 .on('click', selectRelation);
36618
36619             $label.append('span')
36620                 .attr('class', 'member-entity-type')
36621                 .text(function(d) { return context.presets().match(d.relation, context.graph()).name(); });
36622
36623             $label.append('span')
36624                 .attr('class', 'member-entity-name')
36625                 .text(function(d) { return iD.util.displayName(d.relation); });
36626
36627             $enter.append('input')
36628                 .attr('class', 'member-role')
36629                 .property('type', 'text')
36630                 .attr('maxlength', 255)
36631                 .attr('placeholder', t('inspector.role'))
36632                 .property('value', function(d) { return d.member.role; })
36633                 .on('change', changeRole);
36634
36635             $enter.append('button')
36636                 .attr('tabindex', -1)
36637                 .attr('class', 'remove button-input-action member-delete minor')
36638                 .on('click', deleteMembership)
36639                 .call(iD.svg.Icon('#operation-delete'));
36640
36641             $items.exit()
36642                 .remove();
36643
36644             if (showBlank) {
36645                 var $new = $list.selectAll('.member-row-new')
36646                     .data([0]);
36647
36648                 $enter = $new.enter().append('li')
36649                     .attr('class', 'member-row member-row-new form-field');
36650
36651                 $enter.append('input')
36652                     .attr('type', 'text')
36653                     .attr('class', 'member-entity-input')
36654                     .call(d3.combobox()
36655                         .minItems(1)
36656                         .fetcher(function(value, callback) {
36657                             callback(relations(value));
36658                         })
36659                         .on('accept', function(d) {
36660                             addMembership(d, $new.select('.member-role').property('value'));
36661                         }));
36662
36663                 $enter.append('input')
36664                     .attr('class', 'member-role')
36665                     .property('type', 'text')
36666                     .attr('maxlength', 255)
36667                     .attr('placeholder', t('inspector.role'))
36668                     .on('change', changeRole);
36669
36670                 $enter.append('button')
36671                     .attr('tabindex', -1)
36672                     .attr('class', 'remove button-input-action member-delete minor')
36673                     .on('click', deleteMembership)
36674                     .call(iD.svg.Icon('#operation-delete'));
36675
36676             } else {
36677                 $list.selectAll('.member-row-new')
36678                     .remove();
36679             }
36680
36681             var $add = $wrap.selectAll('.add-relation')
36682                 .data([0]);
36683
36684             $add.enter()
36685                 .append('button')
36686                 .attr('class', 'add-relation')
36687                 .call(iD.svg.Icon('#icon-plus', 'light'));
36688
36689             $wrap.selectAll('.add-relation')
36690                 .on('click', function() {
36691                     showBlank = true;
36692                     content($wrap);
36693                     $list.selectAll('.member-entity-input').node().focus();
36694                 });
36695         }
36696     }
36697
36698     rawMembershipEditor.entityID = function(_) {
36699         if (!arguments.length) return id;
36700         id = _;
36701         return rawMembershipEditor;
36702     };
36703
36704     return rawMembershipEditor;
36705 };
36706 iD.ui.RawTagEditor = function(context) {
36707     var event = d3.dispatch('change'),
36708         showBlank = false,
36709         state,
36710         preset,
36711         tags,
36712         id;
36713
36714     function rawTagEditor(selection) {
36715         var count = Object.keys(tags).filter(function(d) { return d; }).length;
36716
36717         selection.call(iD.ui.Disclosure()
36718             .title(t('inspector.all_tags') + ' (' + count + ')')
36719             .expanded(context.storage('raw_tag_editor.expanded') === 'true' || preset.isFallback())
36720             .on('toggled', toggled)
36721             .content(content));
36722
36723         function toggled(expanded) {
36724             context.storage('raw_tag_editor.expanded', expanded);
36725             if (expanded) {
36726                 selection.node().parentNode.scrollTop += 200;
36727             }
36728         }
36729     }
36730
36731     function content($wrap) {
36732         var entries = d3.entries(tags);
36733
36734         if (!entries.length || showBlank) {
36735             showBlank = false;
36736             entries.push({key: '', value: ''});
36737         }
36738
36739         var $list = $wrap.selectAll('.tag-list')
36740             .data([0]);
36741
36742         $list.enter().append('ul')
36743             .attr('class', 'tag-list');
36744
36745         var $newTag = $wrap.selectAll('.add-tag')
36746             .data([0]);
36747
36748         $newTag.enter()
36749             .append('button')
36750             .attr('class', 'add-tag')
36751             .call(iD.svg.Icon('#icon-plus', 'light'));
36752
36753         $newTag.on('click', addTag);
36754
36755         var $items = $list.selectAll('li')
36756             .data(entries, function(d) { return d.key; });
36757
36758         // Enter
36759
36760         var $enter = $items.enter().append('li')
36761             .attr('class', 'tag-row cf');
36762
36763         $enter.append('div')
36764             .attr('class', 'key-wrap')
36765             .append('input')
36766             .property('type', 'text')
36767             .attr('class', 'key')
36768             .attr('maxlength', 255);
36769
36770         $enter.append('div')
36771             .attr('class', 'input-wrap-position')
36772             .append('input')
36773             .property('type', 'text')
36774             .attr('class', 'value')
36775             .attr('maxlength', 255);
36776
36777         $enter.append('button')
36778             .attr('tabindex', -1)
36779             .attr('class', 'remove minor')
36780             .call(iD.svg.Icon('#operation-delete'));
36781
36782         if (context.taginfo()) {
36783             $enter.each(bindTypeahead);
36784         }
36785
36786         // Update
36787
36788         $items.order();
36789
36790         $items.each(function(tag) {
36791             var isRelation = (context.entity(id).type === 'relation'),
36792                 reference;
36793             if (isRelation && tag.key === 'type')
36794                 reference = iD.ui.TagReference({rtype: tag.value}, context);
36795             else
36796                 reference = iD.ui.TagReference({key: tag.key, value: tag.value}, context);
36797
36798             if (state === 'hover') {
36799                 reference.showing(false);
36800             }
36801
36802             d3.select(this)
36803                 .call(reference.button)
36804                 .call(reference.body);
36805         });
36806
36807         $items.select('input.key')
36808             .value(function(d) { return d.key; })
36809             .on('blur', keyChange)
36810             .on('change', keyChange);
36811
36812         $items.select('input.value')
36813             .value(function(d) { return d.value; })
36814             .on('blur', valueChange)
36815             .on('change', valueChange)
36816             .on('keydown.push-more', pushMore);
36817
36818         $items.select('button.remove')
36819             .on('click', removeTag);
36820
36821         $items.exit()
36822             .each(unbind)
36823             .remove();
36824
36825         function pushMore() {
36826             if (d3.event.keyCode === 9 && !d3.event.shiftKey &&
36827                 $list.selectAll('li:last-child input.value').node() === this) {
36828                 addTag();
36829             }
36830         }
36831
36832         function bindTypeahead() {
36833             var row = d3.select(this),
36834                 key = row.selectAll('input.key'),
36835                 value = row.selectAll('input.value');
36836
36837             function sort(value, data) {
36838                 var sameletter = [],
36839                     other = [];
36840                 for (var i = 0; i < data.length; i++) {
36841                     if (data[i].value.substring(0, value.length) === value) {
36842                         sameletter.push(data[i]);
36843                     } else {
36844                         other.push(data[i]);
36845                     }
36846                 }
36847                 return sameletter.concat(other);
36848             }
36849
36850             key.call(d3.combobox()
36851                 .fetcher(function(value, callback) {
36852                     context.taginfo().keys({
36853                         debounce: true,
36854                         geometry: context.geometry(id),
36855                         query: value
36856                     }, function(err, data) {
36857                         if (!err) callback(sort(value, data));
36858                     });
36859                 }));
36860
36861             value.call(d3.combobox()
36862                 .fetcher(function(value, callback) {
36863                     context.taginfo().values({
36864                         debounce: true,
36865                         key: key.value(),
36866                         geometry: context.geometry(id),
36867                         query: value
36868                     }, function(err, data) {
36869                         if (!err) callback(sort(value, data));
36870                     });
36871                 }));
36872         }
36873
36874         function unbind() {
36875             var row = d3.select(this);
36876
36877             row.selectAll('input.key')
36878                 .call(d3.combobox.off);
36879
36880             row.selectAll('input.value')
36881                 .call(d3.combobox.off);
36882         }
36883
36884         function keyChange(d) {
36885             var kOld = d.key,
36886                 kNew = this.value.trim(),
36887                 tag = {};
36888
36889             if (kNew && kNew !== kOld) {
36890                 var match = kNew.match(/^(.*?)(?:_(\d+))?$/),
36891                     base = match[1],
36892                     suffix = +(match[2] || 1);
36893                 while (tags[kNew]) {  // rename key if already in use
36894                     kNew = base + '_' + suffix++;
36895                 }
36896             }
36897             tag[kOld] = undefined;
36898             tag[kNew] = d.value;
36899             d.key = kNew; // Maintain DOM identity through the subsequent update.
36900             this.value = kNew;
36901             event.change(tag);
36902         }
36903
36904         function valueChange(d) {
36905             var tag = {};
36906             tag[d.key] = this.value;
36907             event.change(tag);
36908         }
36909
36910         function removeTag(d) {
36911             var tag = {};
36912             tag[d.key] = undefined;
36913             event.change(tag);
36914             d3.select(this.parentNode).remove();
36915         }
36916
36917         function addTag() {
36918             // Wrapped in a setTimeout in case it's being called from a blur
36919             // handler. Without the setTimeout, the call to `content` would
36920             // wipe out the pending value change.
36921             setTimeout(function() {
36922                 showBlank = true;
36923                 content($wrap);
36924                 $list.selectAll('li:last-child input.key').node().focus();
36925             }, 0);
36926         }
36927     }
36928
36929     rawTagEditor.state = function(_) {
36930         if (!arguments.length) return state;
36931         state = _;
36932         return rawTagEditor;
36933     };
36934
36935     rawTagEditor.preset = function(_) {
36936         if (!arguments.length) return preset;
36937         preset = _;
36938         return rawTagEditor;
36939     };
36940
36941     rawTagEditor.tags = function(_) {
36942         if (!arguments.length) return tags;
36943         tags = _;
36944         return rawTagEditor;
36945     };
36946
36947     rawTagEditor.entityID = function(_) {
36948         if (!arguments.length) return id;
36949         id = _;
36950         return rawTagEditor;
36951     };
36952
36953     return d3.rebind(rawTagEditor, event, 'on');
36954 };
36955 iD.ui.Restore = function(context) {
36956     return function(selection) {
36957         if (!context.history().lock() || !context.history().restorableChanges())
36958             return;
36959
36960         var modal = iD.ui.modal(selection);
36961
36962         modal.select('.modal')
36963             .attr('class', 'modal fillL col6');
36964
36965         var introModal = modal.select('.content');
36966
36967         introModal.attr('class','cf');
36968
36969         introModal.append('div')
36970             .attr('class', 'modal-section')
36971             .append('h3')
36972             .text(t('restore.heading'));
36973
36974         introModal.append('div')
36975             .attr('class','modal-section')
36976             .append('p')
36977             .text(t('restore.description'));
36978
36979         var buttonWrap = introModal.append('div')
36980             .attr('class', 'modal-actions cf');
36981
36982         var restore = buttonWrap.append('button')
36983             .attr('class', 'restore col6')
36984             .text(t('restore.restore'))
36985             .on('click', function() {
36986                 context.history().restore();
36987                 modal.remove();
36988             });
36989
36990         buttonWrap.append('button')
36991             .attr('class', 'reset col6')
36992             .text(t('restore.reset'))
36993             .on('click', function() {
36994                 context.history().clearSaved();
36995                 modal.remove();
36996             });
36997
36998         restore.node().focus();
36999     };
37000 };
37001 iD.ui.Save = function(context) {
37002     var history = context.history(),
37003         key = iD.ui.cmd('⌘S');
37004
37005     function saving() {
37006         return context.mode().id === 'save';
37007     }
37008
37009     function save() {
37010         d3.event.preventDefault();
37011         if (!context.inIntro() && !saving() && history.hasChanges()) {
37012             context.enter(iD.modes.Save(context));
37013         }
37014     }
37015
37016     return function(selection) {
37017         var tooltip = bootstrap.tooltip()
37018             .placement('bottom')
37019             .html(true)
37020             .title(iD.ui.tooltipHtml(t('save.no_changes'), key));
37021
37022         var button = selection.append('button')
37023             .attr('class', 'save col12 disabled')
37024             .attr('tabindex', -1)
37025             .on('click', save)
37026             .call(tooltip);
37027
37028         button.append('span')
37029             .attr('class', 'label')
37030             .text(t('save.title'));
37031
37032         button.append('span')
37033             .attr('class', 'count')
37034             .text('0');
37035
37036         var keybinding = d3.keybinding('undo-redo')
37037             .on(key, save, true);
37038
37039         d3.select(document)
37040             .call(keybinding);
37041
37042         var numChanges = 0;
37043
37044         context.history().on('change.save', function() {
37045             var _ = history.difference().summary().length;
37046             if (_ === numChanges)
37047                 return;
37048             numChanges = _;
37049
37050             tooltip.title(iD.ui.tooltipHtml(t(numChanges > 0 ?
37051                     'save.help' : 'save.no_changes'), key));
37052
37053             button
37054                 .classed('disabled', numChanges === 0)
37055                 .classed('has-count', numChanges > 0);
37056
37057             button.select('span.count')
37058                 .text(numChanges);
37059         });
37060
37061         context.on('enter.save', function() {
37062             button.property('disabled', saving());
37063             if (saving()) button.call(tooltip.hide);
37064         });
37065     };
37066 };
37067 iD.ui.Scale = function(context) {
37068     var projection = context.projection,
37069         maxLength = 180,
37070         tickHeight = 8;
37071
37072     function scaleDefs(loc1, loc2) {
37073         var lat = (loc2[1] + loc1[1]) / 2,
37074             imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
37075             conversion = (imperial ? 3.28084 : 1),
37076             dist = iD.geo.lonToMeters(loc2[0] - loc1[0], lat) * conversion,
37077             scale = { dist: 0, px: 0, text: '' },
37078             buckets, i, val, dLon;
37079
37080         if (imperial) {
37081             buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
37082         } else {
37083             buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
37084         }
37085
37086         // determine a user-friendly endpoint for the scale
37087         for (i = 0; i < buckets.length; i++) {
37088             val = buckets[i];
37089             if (dist >= val) {
37090                 scale.dist = Math.floor(dist / val) * val;
37091                 break;
37092             }
37093         }
37094
37095         dLon = iD.geo.metersToLon(scale.dist / conversion, lat);
37096         scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
37097
37098         if (imperial) {
37099             if (scale.dist >= 5280) {
37100                 scale.dist /= 5280;
37101                 scale.text = String(scale.dist) + ' mi';
37102             } else {
37103                 scale.text = String(scale.dist) + ' ft';
37104             }
37105         } else {
37106             if (scale.dist >= 1000) {
37107                 scale.dist /= 1000;
37108                 scale.text = String(scale.dist) + ' km';
37109             } else {
37110                 scale.text = String(scale.dist) + ' m';
37111             }
37112         }
37113
37114         return scale;
37115     }
37116
37117     function update(selection) {
37118         // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
37119         var dims = context.map().dimensions(),
37120             loc1 = projection.invert([0, dims[1]]),
37121             loc2 = projection.invert([maxLength, dims[1]]),
37122             scale = scaleDefs(loc1, loc2);
37123
37124         selection.select('#scalepath')
37125             .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
37126
37127         selection.select('#scaletext')
37128             .attr('x', scale.px + 8)
37129             .attr('y', tickHeight)
37130             .text(scale.text);
37131     }
37132
37133     return function(selection) {
37134         var g = selection.append('svg')
37135             .attr('id', 'scale')
37136             .append('g')
37137             .attr('transform', 'translate(10,11)');
37138
37139         g.append('path').attr('id', 'scalepath');
37140         g.append('text').attr('id', 'scaletext');
37141
37142         update(selection);
37143
37144         context.map().on('move.scale', function() {
37145             update(selection);
37146         });
37147     };
37148 };
37149 iD.ui.SelectionList = function(context, selectedIDs) {
37150
37151     function selectEntity(entity) {
37152         context.enter(iD.modes.Select(context, [entity.id]).suppressMenu(true));
37153     }
37154
37155
37156     function selectionList(selection) {
37157         selection.classed('selection-list-pane', true);
37158
37159         var header = selection.append('div')
37160             .attr('class', 'header fillL cf');
37161
37162         header.append('h3')
37163             .text(t('inspector.multiselect'));
37164
37165         var listWrap = selection.append('div')
37166             .attr('class', 'inspector-body');
37167
37168         var list = listWrap.append('div')
37169             .attr('class', 'feature-list cf');
37170
37171         context.history().on('change.selection-list', drawList);
37172         drawList();
37173
37174         function drawList() {
37175             var entities = selectedIDs
37176                 .map(function(id) { return context.hasEntity(id); })
37177                 .filter(function(entity) { return entity; });
37178
37179             var items = list.selectAll('.feature-list-item')
37180                 .data(entities, iD.Entity.key);
37181
37182             var enter = items.enter().append('button')
37183                 .attr('class', 'feature-list-item')
37184                 .on('click', selectEntity);
37185
37186             // Enter
37187             var label = enter.append('div')
37188                 .attr('class', 'label')
37189                 .call(iD.svg.Icon('', 'pre-text'));
37190
37191             label.append('span')
37192                 .attr('class', 'entity-type');
37193
37194             label.append('span')
37195                 .attr('class', 'entity-name');
37196
37197             // Update
37198             items.selectAll('use')
37199                 .attr('href', function() {
37200                     var entity = this.parentNode.parentNode.__data__;
37201                     return '#icon-' + context.geometry(entity.id);
37202                 });
37203
37204             items.selectAll('.entity-type')
37205                 .text(function(entity) { return context.presets().match(entity, context.graph()).name(); });
37206
37207             items.selectAll('.entity-name')
37208                 .text(function(entity) { return iD.util.displayName(entity); });
37209
37210             // Exit
37211             items.exit()
37212                 .remove();
37213         }
37214     }
37215
37216     return selectionList;
37217
37218 };
37219 iD.ui.Sidebar = function(context) {
37220     var inspector = iD.ui.Inspector(context),
37221         current;
37222
37223     function sidebar(selection) {
37224         var featureListWrap = selection.append('div')
37225             .attr('class', 'feature-list-pane')
37226             .call(iD.ui.FeatureList(context));
37227
37228         selection.call(iD.ui.Notice(context));
37229
37230         var inspectorWrap = selection.append('div')
37231             .attr('class', 'inspector-hidden inspector-wrap fr');
37232
37233         function hover(id) {
37234             if (!current && context.hasEntity(id)) {
37235                 featureListWrap.classed('inspector-hidden', true);
37236                 inspectorWrap.classed('inspector-hidden', false)
37237                     .classed('inspector-hover', true);
37238
37239                 if (inspector.entityID() !== id || inspector.state() !== 'hover') {
37240                     inspector
37241                         .state('hover')
37242                         .entityID(id);
37243
37244                     inspectorWrap.call(inspector);
37245                 }
37246             } else if (!current) {
37247                 featureListWrap.classed('inspector-hidden', false);
37248                 inspectorWrap.classed('inspector-hidden', true);
37249                 inspector.state('hide');
37250             }
37251         }
37252
37253         sidebar.hover = _.throttle(hover, 200);
37254
37255         sidebar.select = function(id, newFeature) {
37256             if (!current && id) {
37257                 featureListWrap.classed('inspector-hidden', true);
37258                 inspectorWrap.classed('inspector-hidden', false)
37259                     .classed('inspector-hover', false);
37260
37261                 if (inspector.entityID() !== id || inspector.state() !== 'select') {
37262                     inspector
37263                         .state('select')
37264                         .entityID(id)
37265                         .newFeature(newFeature);
37266
37267                     inspectorWrap.call(inspector);
37268                 }
37269             } else if (!current) {
37270                 featureListWrap.classed('inspector-hidden', false);
37271                 inspectorWrap.classed('inspector-hidden', true);
37272                 inspector.state('hide');
37273             }
37274         };
37275
37276         sidebar.show = function(component) {
37277             featureListWrap.classed('inspector-hidden', true);
37278             inspectorWrap.classed('inspector-hidden', true);
37279             if (current) current.remove();
37280             current = selection.append('div')
37281                 .attr('class', 'sidebar-component')
37282                 .call(component);
37283         };
37284
37285         sidebar.hide = function() {
37286             featureListWrap.classed('inspector-hidden', false);
37287             inspectorWrap.classed('inspector-hidden', true);
37288             if (current) current.remove();
37289             current = null;
37290         };
37291     }
37292
37293     sidebar.hover = function() {};
37294     sidebar.hover.cancel = function() {};
37295     sidebar.select = function() {};
37296     sidebar.show = function() {};
37297     sidebar.hide = function() {};
37298
37299     return sidebar;
37300 };
37301 iD.ui.SourceSwitch = function(context) {
37302     var keys;
37303
37304     function click() {
37305         d3.event.preventDefault();
37306
37307         if (context.history().hasChanges() &&
37308             !window.confirm(t('source_switch.lose_changes'))) return;
37309
37310         var live = d3.select(this)
37311             .classed('live');
37312
37313         context.connection()
37314             .switch(live ? keys[1] : keys[0]);
37315
37316         context.enter(iD.modes.Browse(context));
37317         context.flush();
37318
37319         d3.select(this)
37320             .text(live ? t('source_switch.dev') : t('source_switch.live'))
37321             .classed('live', !live);
37322     }
37323
37324     var sourceSwitch = function(selection) {
37325         selection.append('a')
37326             .attr('href', '#')
37327             .text(t('source_switch.live'))
37328             .classed('live', true)
37329             .attr('tabindex', -1)
37330             .on('click', click);
37331     };
37332
37333     sourceSwitch.keys = function(_) {
37334         if (!arguments.length) return keys;
37335         keys = _;
37336         return sourceSwitch;
37337     };
37338
37339     return sourceSwitch;
37340 };
37341 iD.ui.Spinner = function(context) {
37342     var connection = context.connection();
37343
37344     return function(selection) {
37345         var img = selection.append('img')
37346             .attr('src', context.imagePath('loader-black.gif'))
37347             .style('opacity', 0);
37348
37349         connection.on('loading.spinner', function() {
37350             img.transition()
37351                 .style('opacity', 1);
37352         });
37353
37354         connection.on('loaded.spinner', function() {
37355             img.transition()
37356                 .style('opacity', 0);
37357         });
37358     };
37359 };
37360 iD.ui.Splash = function(context) {
37361     return function(selection) {
37362         if (context.storage('sawSplash'))
37363              return;
37364
37365         context.storage('sawSplash', true);
37366
37367         var modal = iD.ui.modal(selection);
37368
37369         modal.select('.modal')
37370             .attr('class', 'modal-splash modal col6');
37371
37372         var introModal = modal.select('.content')
37373             .append('div')
37374             .attr('class', 'fillL');
37375
37376         introModal.append('div')
37377             .attr('class','modal-section cf')
37378             .append('h3').text(t('splash.welcome'));
37379
37380         introModal.append('div')
37381             .attr('class','modal-section')
37382             .append('p')
37383             .html(t('splash.text', {
37384                 version: iD.version,
37385                 website: '<a href="http://ideditor.com/">ideditor.com</a>',
37386                 github: '<a href="https://github.com/openstreetmap/iD">github.com</a>'
37387             }));
37388
37389         var buttons = introModal.append('div').attr('class', 'modal-actions cf');
37390
37391         buttons.append('button')
37392             .attr('class', 'col6 walkthrough')
37393             .text(t('splash.walkthrough'))
37394             .on('click', function() {
37395                 d3.select(document.body).call(iD.ui.intro(context));
37396                 modal.close();
37397             });
37398
37399         buttons.append('button')
37400             .attr('class', 'col6 start')
37401             .text(t('splash.start'))
37402             .on('click', modal.close);
37403
37404         modal.select('button.close').attr('class','hide');
37405
37406     };
37407 };
37408 iD.ui.Status = function(context) {
37409     var connection = context.connection(),
37410         errCount = 0;
37411
37412     return function(selection) {
37413
37414         function update() {
37415
37416             connection.status(function(err, apiStatus) {
37417
37418                 selection.html('');
37419
37420                 if (err && errCount++ < 2) return;
37421
37422                 if (err) {
37423                     selection.text(t('status.error'));
37424
37425                 } else if (apiStatus === 'readonly') {
37426                     selection.text(t('status.readonly'));
37427
37428                 } else if (apiStatus === 'offline') {
37429                     selection.text(t('status.offline'));
37430                 }
37431
37432                 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
37433                 if (!err) errCount = 0;
37434
37435             });
37436         }
37437
37438         connection.on('auth', function() { update(selection); });
37439         window.setInterval(update, 90000);
37440         update(selection);
37441     };
37442 };
37443 iD.ui.Success = function(context) {
37444     var dispatch = d3.dispatch('cancel'),
37445         changeset;
37446
37447     function success(selection) {
37448         var message = (changeset.comment || t('success.edited_osm')).substring(0, 130) +
37449             ' ' + context.connection().changesetURL(changeset.id);
37450
37451         var header = selection.append('div')
37452             .attr('class', 'header fillL');
37453
37454         header.append('button')
37455             .attr('class', 'fr')
37456             .on('click', function() { dispatch.cancel(); })
37457             .call(iD.svg.Icon('#icon-close'));
37458
37459         header.append('h3')
37460             .text(t('success.just_edited'));
37461
37462         var body = selection.append('div')
37463             .attr('class', 'body save-success fillL');
37464
37465         body.append('p')
37466             .html(t('success.help_html'));
37467
37468         body.append('a')
37469             .attr('class', 'details')
37470             .attr('target', '_blank')
37471             .attr('tabindex', -1)
37472             .call(iD.svg.Icon('#icon-out-link', 'inline'))
37473             .attr('href', t('success.help_link_url'))
37474             .append('span')
37475             .text(t('success.help_link_text'));
37476
37477         var changesetURL = context.connection().changesetURL(changeset.id);
37478
37479         body.append('a')
37480             .attr('class', 'button col12 osm')
37481             .attr('target', '_blank')
37482             .attr('href', changesetURL)
37483             .text(t('success.view_on_osm'));
37484
37485         var sharing = {
37486             facebook: 'https://facebook.com/sharer/sharer.php?u=' + encodeURIComponent(changesetURL),
37487             twitter: 'https://twitter.com/intent/tweet?source=webclient&text=' + encodeURIComponent(message),
37488             google: 'https://plus.google.com/share?url=' + encodeURIComponent(changesetURL)
37489         };
37490
37491         body.selectAll('.button.social')
37492             .data(d3.entries(sharing))
37493             .enter()
37494             .append('a')
37495             .attr('class', 'button social col4')
37496             .attr('target', '_blank')
37497             .attr('href', function(d) { return d.value; })
37498             .call(bootstrap.tooltip()
37499                 .title(function(d) { return t('success.' + d.key); })
37500                 .placement('bottom'))
37501             .each(function(d) { d3.select(this).call(iD.svg.Icon('#logo-' + d.key, 'social')); });
37502     }
37503
37504     success.changeset = function(_) {
37505         if (!arguments.length) return changeset;
37506         changeset = _;
37507         return success;
37508     };
37509
37510     return d3.rebind(success, dispatch, 'on');
37511 };
37512 iD.ui.TagReference = function(tag, context) {
37513     var tagReference = {},
37514         button,
37515         body,
37516         loaded,
37517         showing;
37518
37519     function findLocal(data) {
37520         var locale = iD.detect().locale.toLowerCase(),
37521             localized;
37522
37523         localized = _.find(data, function(d) {
37524             return d.lang.toLowerCase() === locale;
37525         });
37526         if (localized) return localized;
37527
37528         // try the non-regional version of a language, like
37529         // 'en' if the language is 'en-US'
37530         if (locale.indexOf('-') !== -1) {
37531             var first = locale.split('-')[0];
37532             localized = _.find(data, function(d) {
37533                 return d.lang.toLowerCase() === first;
37534             });
37535             if (localized) return localized;
37536         }
37537
37538         // finally fall back to english
37539         return _.find(data, function(d) {
37540             return d.lang.toLowerCase() === 'en';
37541         });
37542     }
37543
37544     function load(param) {
37545         button.classed('tag-reference-loading', true);
37546
37547         context.taginfo().docs(param, function show(err, data) {
37548             var docs;
37549             if (!err && data) {
37550                 docs = findLocal(data);
37551             }
37552
37553             body.html('');
37554
37555             if (!docs || !docs.description) {
37556                 if (param.hasOwnProperty('value')) {
37557                     load(_.omit(param, 'value'));   // retry with key only
37558                 } else {
37559                     body.append('p').text(t('inspector.no_documentation_key'));
37560                     done();
37561                 }
37562                 return;
37563             }
37564
37565             if (docs.image && docs.image.thumb_url_prefix) {
37566                 body
37567                     .append('img')
37568                     .attr('class', 'wiki-image')
37569                     .attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix)
37570                     .on('load', function() { done(); })
37571                     .on('error', function() { d3.select(this).remove(); done(); });
37572             } else {
37573                 done();
37574             }
37575
37576             body
37577                 .append('p')
37578                 .text(docs.description);
37579
37580             body
37581                 .append('a')
37582                 .attr('target', '_blank')
37583                 .attr('href', 'https://wiki.openstreetmap.org/wiki/' + docs.title)
37584                 .call(iD.svg.Icon('#icon-out-link', 'inline'))
37585                 .append('span')
37586                 .text(t('inspector.reference'));
37587         });
37588     }
37589
37590     function done() {
37591         loaded = true;
37592
37593         button.classed('tag-reference-loading', false);
37594
37595         body.transition()
37596             .duration(200)
37597             .style('max-height', '200px')
37598             .style('opacity', '1');
37599
37600         showing = true;
37601     }
37602
37603     function hide(selection) {
37604         selection = selection || body.transition().duration(200);
37605
37606         selection
37607             .style('max-height', '0px')
37608             .style('opacity', '0');
37609
37610         showing = false;
37611     }
37612
37613     tagReference.button = function(selection) {
37614         button = selection.selectAll('.tag-reference-button')
37615             .data([0]);
37616
37617         button.enter()
37618             .append('button')
37619             .attr('class', 'tag-reference-button')
37620             .attr('tabindex', -1)
37621             .call(iD.svg.Icon('#icon-inspect'));
37622
37623         button.on('click', function () {
37624             d3.event.stopPropagation();
37625             d3.event.preventDefault();
37626             if (showing) {
37627                 hide();
37628             } else if (loaded) {
37629                 done();
37630             } else {
37631                 if (context.taginfo()) {
37632                     load(tag);
37633                 }
37634             }
37635         });
37636     };
37637
37638     tagReference.body = function(selection) {
37639         body = selection.selectAll('.tag-reference-body')
37640             .data([0]);
37641
37642         body.enter().append('div')
37643             .attr('class', 'tag-reference-body cf')
37644             .style('max-height', '0')
37645             .style('opacity', '0');
37646
37647         if (showing === false) {
37648             hide(body);
37649         }
37650     };
37651
37652     tagReference.showing = function(_) {
37653         if (!arguments.length) return showing;
37654         showing = _;
37655         return tagReference;
37656     };
37657
37658     return tagReference;
37659 };
37660 // toggles the visibility of ui elements, using a combination of the
37661 // hide class, which sets display=none, and a d3 transition for opacity.
37662 // this will cause blinking when called repeatedly, so check that the
37663 // value actually changes between calls.
37664 iD.ui.Toggle = function(show, callback) {
37665     return function(selection) {
37666         selection
37667             .style('opacity', show ? 0 : 1)
37668             .classed('hide', false)
37669             .transition()
37670             .style('opacity', show ? 1 : 0)
37671             .each('end', function() {
37672                 d3.select(this)
37673                     .classed('hide', !show)
37674                     .style('opacity', null);
37675                 if (callback) callback.apply(this);
37676             });
37677     };
37678 };
37679 iD.ui.UndoRedo = function(context) {
37680     var commands = [{
37681         id: 'undo',
37682         cmd: iD.ui.cmd('⌘Z'),
37683         action: function() { if (!(context.inIntro() || saving())) context.undo(); },
37684         annotation: function() { return context.history().undoAnnotation(); }
37685     }, {
37686         id: 'redo',
37687         cmd: iD.ui.cmd('⌘⇧Z'),
37688         action: function() {if (!(context.inIntro() || saving())) context.redo(); },
37689         annotation: function() { return context.history().redoAnnotation(); }
37690     }];
37691
37692     function saving() {
37693         return context.mode().id === 'save';
37694     }
37695
37696     return function(selection) {
37697         var tooltip = bootstrap.tooltip()
37698             .placement('bottom')
37699             .html(true)
37700             .title(function (d) {
37701                 return iD.ui.tooltipHtml(d.annotation() ?
37702                     t(d.id + '.tooltip', {action: d.annotation()}) :
37703                     t(d.id + '.nothing'), d.cmd);
37704             });
37705
37706         var buttons = selection.selectAll('button')
37707             .data(commands)
37708             .enter().append('button')
37709             .attr('class', 'col6 disabled')
37710             .on('click', function(d) { return d.action(); })
37711             .call(tooltip);
37712
37713         buttons.each(function(d) {
37714             d3.select(this)
37715                 .call(iD.svg.Icon('#icon-' + d.id));
37716         });
37717
37718         var keybinding = d3.keybinding('undo')
37719             .on(commands[0].cmd, function() { d3.event.preventDefault(); commands[0].action(); })
37720             .on(commands[1].cmd, function() { d3.event.preventDefault(); commands[1].action(); });
37721
37722         d3.select(document)
37723             .call(keybinding);
37724
37725         context.history()
37726             .on('change.undo_redo', update);
37727
37728         context
37729             .on('enter.undo_redo', update);
37730
37731         function update() {
37732             buttons
37733                 .property('disabled', saving())
37734                 .classed('disabled', function(d) { return !d.annotation(); })
37735                 .each(function() {
37736                     var selection = d3.select(this);
37737                     if (selection.property('tooltipVisible')) {
37738                         selection.call(tooltip.show);
37739                     }
37740                 });
37741         }
37742     };
37743 };
37744 iD.ui.ViewOnOSM = function(context) {
37745     var id;
37746
37747     function viewOnOSM(selection) {
37748         var entity = context.entity(id);
37749
37750         selection.style('display', entity.isNew() ? 'none' : null);
37751
37752         var $link = selection.selectAll('.view-on-osm')
37753             .data([0]);
37754
37755         $link.enter()
37756             .append('a')
37757             .attr('class', 'view-on-osm')
37758             .attr('target', '_blank')
37759             .call(iD.svg.Icon('#icon-out-link', 'inline'))
37760             .append('span')
37761             .text(t('inspector.view_on_osm'));
37762
37763         $link
37764             .attr('href', context.connection().entityURL(entity));
37765     }
37766
37767     viewOnOSM.entityID = function(_) {
37768         if (!arguments.length) return id;
37769         id = _;
37770         return viewOnOSM;
37771     };
37772
37773     return viewOnOSM;
37774 };
37775 iD.ui.Zoom = function(context) {
37776     var zooms = [{
37777         id: 'zoom-in',
37778         icon: 'plus',
37779         title: t('zoom.in'),
37780         action: context.zoomIn,
37781         key: '+'
37782     }, {
37783         id: 'zoom-out',
37784         icon: 'minus',
37785         title: t('zoom.out'),
37786         action: context.zoomOut,
37787         key: '-'
37788     }];
37789
37790     function zoomIn() {
37791         d3.event.preventDefault();
37792         if (!context.inIntro()) context.zoomIn();
37793     }
37794
37795     function zoomOut() {
37796         d3.event.preventDefault();
37797         if (!context.inIntro()) context.zoomOut();
37798     }
37799
37800     function zoomInFurther() {
37801         d3.event.preventDefault();
37802         if (!context.inIntro()) context.zoomInFurther();
37803     }
37804
37805     function zoomOutFurther() {
37806         d3.event.preventDefault();
37807         if (!context.inIntro()) context.zoomOutFurther();
37808     }
37809
37810
37811     return function(selection) {
37812         var button = selection.selectAll('button')
37813             .data(zooms)
37814             .enter().append('button')
37815             .attr('tabindex', -1)
37816             .attr('class', function(d) { return d.id; })
37817             .on('click.editor', function(d) { d.action(); })
37818             .call(bootstrap.tooltip()
37819                 .placement('left')
37820                 .html(true)
37821                 .title(function(d) {
37822                     return iD.ui.tooltipHtml(d.title, d.key);
37823                 }));
37824
37825         button.each(function(d) {
37826             d3.select(this)
37827                 .call(iD.svg.Icon('#icon-' + d.icon, 'light'));
37828         });
37829
37830         var keybinding = d3.keybinding('zoom');
37831
37832         _.each(['=','ffequals','plus','ffplus'], function(key) {
37833             keybinding.on(key, zoomIn);
37834             keybinding.on('⇧' + key, zoomIn);
37835             keybinding.on(iD.ui.cmd('⌘' + key), zoomInFurther);
37836             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomInFurther);
37837         });
37838         _.each(['-','ffminus','_','dash'], function(key) {
37839             keybinding.on(key, zoomOut);
37840             keybinding.on('⇧' + key, zoomOut);
37841             keybinding.on(iD.ui.cmd('⌘' + key), zoomOutFurther);
37842             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomOutFurther);
37843         });
37844
37845         d3.select(document)
37846             .call(keybinding);
37847     };
37848 };
37849 iD.ui.preset.access = function(field) {
37850     var dispatch = d3.dispatch('change'),
37851         items;
37852
37853     function access(selection) {
37854         var wrap = selection.selectAll('.preset-input-wrap')
37855             .data([0]);
37856
37857         wrap.enter().append('div')
37858             .attr('class', 'cf preset-input-wrap')
37859             .append('ul');
37860
37861         items = wrap.select('ul').selectAll('li')
37862             .data(field.keys);
37863
37864         // Enter
37865
37866         var enter = items.enter().append('li')
37867             .attr('class', function(d) { return 'cf preset-access-' + d; });
37868
37869         enter.append('span')
37870             .attr('class', 'col6 label preset-label-access')
37871             .attr('for', function(d) { return 'preset-input-access-' + d; })
37872             .text(function(d) { return field.t('types.' + d); });
37873
37874         enter.append('div')
37875             .attr('class', 'col6 preset-input-access-wrap')
37876             .append('input')
37877             .attr('type', 'text')
37878             .attr('class', 'preset-input-access')
37879             .attr('id', function(d) { return 'preset-input-access-' + d; })
37880             .each(function(d) {
37881                 d3.select(this)
37882                     .call(d3.combobox()
37883                         .data(access.options(d)));
37884             });
37885
37886         // Update
37887
37888         wrap.selectAll('.preset-input-access')
37889             .on('change', change)
37890             .on('blur', change);
37891     }
37892
37893     function change(d) {
37894         var tag = {};
37895         tag[d] = d3.select(this).value() || undefined;
37896         dispatch.change(tag);
37897     }
37898
37899     access.options = function(type) {
37900         var options = ['no', 'permissive', 'private', 'destination'];
37901
37902         if (type !== 'access') {
37903             options.unshift('yes');
37904             options.push('designated');
37905
37906             if (type === 'bicycle') {
37907                 options.push('dismount');
37908             }
37909         }
37910
37911         return options.map(function(option) {
37912             return {
37913                 title: field.t('options.' + option + '.description'),
37914                 value: option
37915             };
37916         });
37917     };
37918
37919     var placeholders = {
37920         footway: {
37921             foot: 'designated',
37922             motor_vehicle: 'no'
37923         },
37924         steps: {
37925             foot: 'yes',
37926             motor_vehicle: 'no',
37927             bicycle: 'no',
37928             horse: 'no'
37929         },
37930         pedestrian: {
37931             foot: 'yes',
37932             motor_vehicle: 'no'
37933         },
37934         cycleway: {
37935             motor_vehicle: 'no',
37936             bicycle: 'designated'
37937         },
37938         bridleway: {
37939             motor_vehicle: 'no',
37940             horse: 'designated'
37941         },
37942         path: {
37943             foot: 'yes',
37944             motor_vehicle: 'no',
37945             bicycle: 'yes',
37946             horse: 'yes'
37947         },
37948         motorway: {
37949             foot: 'no',
37950             motor_vehicle: 'yes',
37951             bicycle: 'no',
37952             horse: 'no'
37953         },
37954         trunk: {
37955             motor_vehicle: 'yes'
37956         },
37957         primary: {
37958             foot: 'yes',
37959             motor_vehicle: 'yes',
37960             bicycle: 'yes',
37961             horse: 'yes'
37962         },
37963         secondary: {
37964             foot: 'yes',
37965             motor_vehicle: 'yes',
37966             bicycle: 'yes',
37967             horse: 'yes'
37968         },
37969         tertiary: {
37970             foot: 'yes',
37971             motor_vehicle: 'yes',
37972             bicycle: 'yes',
37973             horse: 'yes'
37974         },
37975         residential: {
37976             foot: 'yes',
37977             motor_vehicle: 'yes',
37978             bicycle: 'yes',
37979             horse: 'yes'
37980         },
37981         unclassified: {
37982             foot: 'yes',
37983             motor_vehicle: 'yes',
37984             bicycle: 'yes',
37985             horse: 'yes'
37986         },
37987         service: {
37988             foot: 'yes',
37989             motor_vehicle: 'yes',
37990             bicycle: 'yes',
37991             horse: 'yes'
37992         },
37993         motorway_link: {
37994             foot: 'no',
37995             motor_vehicle: 'yes',
37996             bicycle: 'no',
37997             horse: 'no'
37998         },
37999         trunk_link: {
38000             motor_vehicle: 'yes'
38001         },
38002         primary_link: {
38003             foot: 'yes',
38004             motor_vehicle: 'yes',
38005             bicycle: 'yes',
38006             horse: 'yes'
38007         },
38008         secondary_link: {
38009             foot: 'yes',
38010             motor_vehicle: 'yes',
38011             bicycle: 'yes',
38012             horse: 'yes'
38013         },
38014         tertiary_link: {
38015             foot: 'yes',
38016             motor_vehicle: 'yes',
38017             bicycle: 'yes',
38018             horse: 'yes'
38019         }
38020     };
38021
38022     access.tags = function(tags) {
38023         items.selectAll('.preset-input-access')
38024             .value(function(d) { return tags[d] || ''; })
38025             .attr('placeholder', function() {
38026                 return tags.access ? tags.access : field.placeholder();
38027             });
38028
38029         // items.selectAll('#preset-input-access-access')
38030         //     .attr('placeholder', 'yes');
38031
38032         _.forEach(placeholders[tags.highway], function(v, k) {
38033             items.selectAll('#preset-input-access-' + k)
38034                 .attr('placeholder', function() { return (tags.access || v); });
38035         });
38036     };
38037
38038     access.focus = function() {
38039         items.selectAll('.preset-input-access')
38040             .node().focus();
38041     };
38042
38043     return d3.rebind(access, dispatch, 'on');
38044 };
38045 iD.ui.preset.address = function(field, context) {
38046     var dispatch = d3.dispatch('init', 'change'),
38047         wrap,
38048         entity,
38049         isInitialized;
38050
38051     var widths = {
38052         housenumber: 1/3,
38053         street: 2/3,
38054         city: 2/3,
38055         state: 1/4,
38056         postcode: 1/3
38057     };
38058
38059     function getStreets() {
38060         var extent = entity.extent(context.graph()),
38061             l = extent.center(),
38062             box = iD.geo.Extent(l).padByMeters(200);
38063
38064         return context.intersects(box)
38065             .filter(isAddressable)
38066             .map(function(d) {
38067                 var loc = context.projection([
38068                     (extent[0][0] + extent[1][0]) / 2,
38069                     (extent[0][1] + extent[1][1]) / 2]),
38070                     choice = iD.geo.chooseEdge(context.childNodes(d), loc, context.projection);
38071                 return {
38072                     title: d.tags.name,
38073                     value: d.tags.name,
38074                     dist: choice.distance
38075                 };
38076             }).sort(function(a, b) {
38077                 return a.dist - b.dist;
38078             });
38079
38080         function isAddressable(d) {
38081             return d.tags.highway && d.tags.name && d.type === 'way';
38082         }
38083     }
38084
38085     function getCities() {
38086         var extent = entity.extent(context.graph()),
38087             l = extent.center(),
38088             box = iD.geo.Extent(l).padByMeters(200);
38089
38090         return context.intersects(box)
38091             .filter(isAddressable)
38092             .map(function(d) {
38093                 return {
38094                     title: d.tags['addr:city'] || d.tags.name,
38095                     value: d.tags['addr:city'] || d.tags.name,
38096                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
38097                 };
38098             }).sort(function(a, b) {
38099                 return a.dist - b.dist;
38100             });
38101
38102         function isAddressable(d) {
38103             if (d.tags.name &&
38104                 (d.tags.admin_level === '8' || d.tags.border_type === 'city'))
38105                 return true;
38106
38107             if (d.tags.place && d.tags.name && (
38108                     d.tags.place === 'city' ||
38109                     d.tags.place === 'town' ||
38110                     d.tags.place === 'village'))
38111                 return true;
38112
38113             if (d.tags['addr:city']) return true;
38114
38115             return false;
38116         }
38117     }
38118
38119     function getPostCodes() {
38120         var extent = entity.extent(context.graph()),
38121             l = extent.center(),
38122             box = iD.geo.Extent(l).padByMeters(200);
38123
38124         return context.intersects(box)
38125             .filter(isAddressable)
38126             .map(function(d) {
38127                 return {
38128                     title: d.tags['addr:postcode'],
38129                     value: d.tags['addr:postcode'],
38130                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
38131                 };
38132             }).sort(function(a, b) {
38133                 return a.dist - b.dist;
38134             });
38135
38136         function isAddressable(d) {
38137             return d.tags['addr:postcode'];
38138         }
38139     }
38140
38141     function address(selection) {
38142         isInitialized = false;
38143
38144         wrap = selection.selectAll('.preset-input-wrap')
38145             .data([0]);
38146
38147         // Enter
38148
38149         wrap.enter()
38150             .append('div')
38151             .attr('class', 'preset-input-wrap');
38152
38153         var center = entity.extent(context.graph()).center(),
38154             addressFormat;
38155
38156         iD.services.nominatim().countryCode(center, function (err, countryCode) {
38157             addressFormat = _.find(iD.data.addressFormats, function (a) {
38158                 return a && a.countryCodes && _.contains(a.countryCodes, countryCode);
38159             }) || _.first(iD.data.addressFormats);
38160
38161             function row(r) {
38162                 // Normalize widths.
38163                 var total = _.reduce(r, function(sum, field) {
38164                     return sum + (widths[field] || 0.5);
38165                 }, 0);
38166
38167                 return r.map(function (field) {
38168                     return {
38169                         id: field,
38170                         width: (widths[field] || 0.5) / total
38171                     };
38172                 });
38173             }
38174
38175             wrap.selectAll('div')
38176                 .data(addressFormat.format)
38177                 .enter()
38178                 .append('div')
38179                 .attr('class', 'addr-row')
38180                 .selectAll('input')
38181                 .data(row)
38182                 .enter()
38183                 .append('input')
38184                 .property('type', 'text')
38185                 .attr('placeholder', function (d) { return field.t('placeholders.' + d.id); })
38186                 .attr('class', function (d) { return 'addr-' + d.id; })
38187                 .style('width', function (d) { return d.width * 100 + '%'; });
38188
38189             // Update
38190
38191             wrap.selectAll('.addr-street')
38192                 .call(d3.combobox()
38193                     .fetcher(function(value, callback) {
38194                         callback(getStreets());
38195                     }));
38196
38197             wrap.selectAll('.addr-city')
38198                 .call(d3.combobox()
38199                     .fetcher(function(value, callback) {
38200                         callback(getCities());
38201                     }));
38202
38203             wrap.selectAll('.addr-postcode')
38204                 .call(d3.combobox()
38205                     .fetcher(function(value, callback) {
38206                         callback(getPostCodes());
38207                     }));
38208
38209             wrap.selectAll('input')
38210                 .on('blur', change())
38211                 .on('change', change());
38212
38213             wrap.selectAll('input:not(.combobox-input)')
38214                 .on('input', change(true));
38215
38216             dispatch.init();
38217             isInitialized = true;
38218         });
38219     }
38220
38221     function change(onInput) {
38222         return function() {
38223             var tags = {};
38224
38225             wrap.selectAll('input')
38226                 .each(function (field) {
38227                     tags['addr:' + field.id] = this.value || undefined;
38228                 });
38229
38230             dispatch.change(tags, onInput);
38231         };
38232     }
38233
38234     function updateTags(tags) {
38235         wrap.selectAll('input')
38236             .value(function (field) {
38237                 return tags['addr:' + field.id] || '';
38238             });
38239     }
38240
38241     address.entity = function(_) {
38242         if (!arguments.length) return entity;
38243         entity = _;
38244         return address;
38245     };
38246
38247     address.tags = function(tags) {
38248         if (isInitialized) {
38249             updateTags(tags);
38250         } else {
38251             dispatch.on('init', function () {
38252                 updateTags(tags);
38253             });
38254         }
38255     };
38256
38257     address.focus = function() {
38258         var node = wrap.selectAll('input').node();
38259         if (node) node.focus();
38260     };
38261
38262     return d3.rebind(address, dispatch, 'on');
38263 };
38264 iD.ui.preset.check =
38265 iD.ui.preset.defaultcheck = function(field) {
38266     var dispatch = d3.dispatch('change'),
38267         options = field.strings && field.strings.options,
38268         values = [],
38269         texts = [],
38270         entity, value, box, text, label;
38271
38272     if (options) {
38273         for (var k in options) {
38274             values.push(k === 'undefined' ? undefined : k);
38275             texts.push(field.t('options.' + k, { 'default': options[k] }));
38276         }
38277     } else {
38278         values = [undefined, 'yes'];
38279         texts = [t('inspector.unknown'), t('inspector.check.yes')];
38280         if (field.type === 'check') {
38281             values.push('no');
38282             texts.push(t('inspector.check.no'));
38283         }
38284     }
38285
38286     var check = function(selection) {
38287         // hack: pretend oneway field is a oneway_yes field
38288         // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
38289         if (field.id === 'oneway') {
38290             for (var key in entity.tags) {
38291                 if (key in iD.oneWayTags && (entity.tags[key] in iD.oneWayTags[key])) {
38292                     texts[0] = t('presets.fields.oneway_yes.options.undefined');
38293                     break;
38294                 }
38295             }
38296         }
38297
38298         selection.classed('checkselect', 'true');
38299
38300         label = selection.selectAll('.preset-input-wrap')
38301             .data([0]);
38302
38303         var enter = label.enter().append('label')
38304             .attr('class', 'preset-input-wrap');
38305
38306         enter.append('input')
38307             .property('indeterminate', field.type === 'check')
38308             .attr('type', 'checkbox')
38309             .attr('id', 'preset-input-' + field.id);
38310
38311         enter.append('span')
38312             .text(texts[0])
38313             .attr('class', 'value');
38314
38315         box = label.select('input')
38316             .on('click', function() {
38317                 var t = {};
38318                 t[field.key] = values[(values.indexOf(value) + 1) % values.length];
38319                 dispatch.change(t);
38320                 d3.event.stopPropagation();
38321             });
38322
38323         text = label.select('span.value');
38324     };
38325
38326     check.entity = function(_) {
38327         if (!arguments.length) return entity;
38328         entity = _;
38329         return check;
38330     };
38331
38332     check.tags = function(tags) {
38333         value = tags[field.key];
38334         box.property('indeterminate', field.type === 'check' && !value);
38335         box.property('checked', value === 'yes');
38336         text.text(texts[values.indexOf(value)]);
38337         label.classed('set', !!value);
38338     };
38339
38340     check.focus = function() {
38341         box.node().focus();
38342     };
38343
38344     return d3.rebind(check, dispatch, 'on');
38345 };
38346 iD.ui.preset.combo =
38347 iD.ui.preset.typeCombo = function(field, context) {
38348     var dispatch = d3.dispatch('change'),
38349         optstrings = field.strings && field.strings.options,
38350         optarray = field.options,
38351         snake_case = (field.snake_case || (field.snake_case === undefined)),
38352         strings = {},
38353         input;
38354
38355     function snake(s) {
38356         return s.replace(/\s+/g, '_');
38357     }
38358
38359     function unsnake(s) {
38360         return s.replace(/_+/g, ' ');
38361     }
38362
38363     function clean(s) {
38364         return s.split(';')
38365             .map(function(s) { return s.trim(); })
38366             .join(';');
38367     }
38368
38369     function optString() {
38370         return _.find(_.keys(strings), function(k) {
38371                 return strings[k] === input.value();
38372             });
38373     }
38374
38375     function combo(selection) {
38376         var combobox = d3.combobox();
38377
38378         input = selection.selectAll('input')
38379             .data([0]);
38380
38381         var enter = input.enter()
38382             .append('input')
38383             .attr('type', 'text')
38384             .attr('id', 'preset-input-' + field.id);
38385
38386         if (optstrings) { enter.attr('readonly', 'readonly'); }
38387
38388         input
38389             .call(combobox)
38390             .on('change', change)
38391             .on('blur', change)
38392             .each(function() {
38393                 if (optstrings) {
38394                     _.each(optstrings, function(v, k) {
38395                         strings[k] = field.t('options.' + k, { 'default': v });
38396                     });
38397                     stringsLoaded();
38398                 } else if (optarray) {
38399                     _.each(optarray, function(k) {
38400                         strings[k] = (snake_case ? unsnake(k) : k);
38401                     });
38402                     stringsLoaded();
38403                 } else if (context.taginfo()) {
38404                     context.taginfo().values({key: field.key}, function(err, data) {
38405                         if (!err) {
38406                             _.each(_.pluck(data, 'value'), function(k) {
38407                                 strings[k] = (snake_case ? unsnake(k) : k);
38408                             });
38409                             stringsLoaded();
38410                         }
38411                     });
38412                 }
38413             });
38414
38415         function stringsLoaded() {
38416             var keys = _.keys(strings),
38417                 strs = [],
38418                 placeholders;
38419
38420             combobox.data(keys.map(function(k) {
38421                 var s = strings[k],
38422                     o = {};
38423                 o.title = o.value = s;
38424                 if (s.length < 20) { strs.push(s); }
38425                 return o;
38426             }));
38427
38428             placeholders = strs.length > 1 ? strs : keys;
38429             input.attr('placeholder', field.placeholder() ||
38430                 (placeholders.slice(0, 3).join(', ') + '...'));
38431         }
38432     }
38433
38434     function change() {
38435         var value = optString() || clean(input.value());
38436
38437         if (snake_case) {
38438             value = snake(value);
38439         }
38440         if (field.type === 'typeCombo' && !value) {
38441             value = 'yes';
38442         }
38443
38444         var t = {};
38445         t[field.key] = value || undefined;
38446         dispatch.change(t);
38447     }
38448
38449     combo.tags = function(tags) {
38450         var key = tags[field.key],
38451             optstring = optString(),
38452             value = strings[key] || key || '';
38453
38454         if (field.type === 'typeCombo' && value.toLowerCase() === 'yes') {
38455             value = '';
38456         }
38457         if (!optstring && snake_case) {
38458             value = unsnake(value);
38459         }
38460         input.value(value);
38461     };
38462
38463     combo.focus = function() {
38464         input.node().focus();
38465     };
38466
38467     return d3.rebind(combo, dispatch, 'on');
38468 };
38469 iD.ui.preset.cycleway = function(field) {
38470     var dispatch = d3.dispatch('change'),
38471         items;
38472
38473     function cycleway(selection) {
38474         var wrap = selection.selectAll('.preset-input-wrap')
38475             .data([0]);
38476
38477         wrap.enter().append('div')
38478             .attr('class', 'cf preset-input-wrap')
38479             .append('ul');
38480
38481         items = wrap.select('ul').selectAll('li')
38482             .data(field.keys);
38483
38484         // Enter
38485
38486         var enter = items.enter().append('li')
38487             .attr('class', function(d) { return 'cf preset-cycleway-' + d; });
38488
38489         enter.append('span')
38490             .attr('class', 'col6 label preset-label-cycleway')
38491             .attr('for', function(d) { return 'preset-input-cycleway-' + d; })
38492             .text(function(d) { return field.t('types.' + d); });
38493
38494         enter.append('div')
38495             .attr('class', 'col6 preset-input-cycleway-wrap')
38496             .append('input')
38497             .attr('type', 'text')
38498             .attr('class', 'preset-input-cycleway')
38499             .attr('id', function(d) { return 'preset-input-cycleway-' + d; })
38500             .each(function(d) {
38501                 d3.select(this)
38502                     .call(d3.combobox()
38503                         .data(cycleway.options(d)));
38504             });
38505
38506         // Update
38507
38508         wrap.selectAll('.preset-input-cycleway')
38509             .on('change', change)
38510             .on('blur', change);
38511     }
38512
38513     function change() {
38514         var inputs = d3.selectAll('.preset-input-cycleway')[0],
38515             left = d3.select(inputs[0]).value(),
38516             right = d3.select(inputs[1]).value(),
38517             tag = {};
38518         if (left === 'none' || left === '') { left = undefined; }
38519         if (right === 'none' || right === '') { right = undefined; }
38520
38521         // Always set both left and right as changing one can affect the other
38522         tag = {
38523             cycleway: undefined,
38524             'cycleway:left': left,
38525             'cycleway:right': right
38526         };
38527
38528         // If the left and right tags match, use the cycleway tag to tag both
38529         // sides the same way
38530         if (left === right) {
38531             tag = {
38532                 cycleway: left,
38533                 'cycleway:left': undefined,
38534                 'cycleway:right': undefined
38535             };
38536         }
38537
38538         dispatch.change(tag);
38539     }
38540
38541     cycleway.options = function() {
38542         return d3.keys(field.strings.options).map(function(option) {
38543             return {
38544                 title: field.t('options.' + option + '.description'),
38545                 value: option
38546             };
38547         });
38548     };
38549
38550     cycleway.tags = function(tags) {
38551         items.selectAll('.preset-input-cycleway')
38552             .value(function(d) {
38553                 // If cycleway is set, always return that
38554                 if (tags.cycleway) {
38555                     return tags.cycleway;
38556                 }
38557                 return tags[d] || '';
38558             })
38559             .attr('placeholder', field.placeholder());
38560     };
38561
38562     cycleway.focus = function() {
38563         items.selectAll('.preset-input-cycleway')
38564             .node().focus();
38565     };
38566
38567     return d3.rebind(cycleway, dispatch, 'on');
38568 };
38569 iD.ui.preset.text =
38570 iD.ui.preset.number =
38571 iD.ui.preset.tel =
38572 iD.ui.preset.email =
38573 iD.ui.preset.url = function(field) {
38574
38575     var dispatch = d3.dispatch('change'),
38576         input;
38577
38578     function i(selection) {
38579         input = selection.selectAll('input')
38580             .data([0]);
38581
38582         input.enter().append('input')
38583             .attr('type', field.type)
38584             .attr('id', 'preset-input-' + field.id)
38585             .attr('placeholder', field.placeholder() || t('inspector.unknown'));
38586
38587         input
38588             .on('input', change(true))
38589             .on('blur', change())
38590             .on('change', change());
38591
38592         if (field.type === 'number') {
38593             input.attr('type', 'text');
38594
38595             var spinControl = selection.selectAll('.spin-control')
38596                 .data([0]);
38597
38598             var enter = spinControl.enter().append('div')
38599                 .attr('class', 'spin-control');
38600
38601             enter.append('button')
38602                 .datum(1)
38603                 .attr('class', 'increment');
38604
38605             enter.append('button')
38606                 .datum(-1)
38607                 .attr('class', 'decrement');
38608
38609             spinControl.selectAll('button')
38610                 .on('click', function(d) {
38611                     d3.event.preventDefault();
38612                     var num = parseInt(input.node().value || 0, 10);
38613                     if (!isNaN(num)) input.node().value = num + d;
38614                     change()();
38615                 });
38616         }
38617     }
38618
38619     function change(onInput) {
38620         return function() {
38621             var t = {};
38622             t[field.key] = input.value() || undefined;
38623             dispatch.change(t, onInput);
38624         };
38625     }
38626
38627     i.tags = function(tags) {
38628         input.value(tags[field.key] || '');
38629     };
38630
38631     i.focus = function() {
38632         input.node().focus();
38633     };
38634
38635     return d3.rebind(i, dispatch, 'on');
38636 };
38637 iD.ui.preset.localized = function(field, context) {
38638     var dispatch = d3.dispatch('change', 'input'),
38639         wikipedia = iD.services.wikipedia(),
38640         input, localizedInputs, wikiTitles,
38641         entity;
38642
38643     function i(selection) {
38644         input = selection.selectAll('.localized-main')
38645             .data([0]);
38646
38647         input.enter().append('input')
38648             .attr('type', 'text')
38649             .attr('id', 'preset-input-' + field.id)
38650             .attr('class', 'localized-main')
38651             .attr('placeholder', field.placeholder());
38652
38653         if (field.id === 'name') {
38654             var preset = context.presets().match(entity, context.graph());
38655             input.call(d3.combobox().fetcher(
38656                 iD.util.SuggestNames(preset, iD.data.suggestions)
38657             ));
38658         }
38659
38660         input
38661             .on('input', change(true))
38662             .on('blur', change())
38663             .on('change', change());
38664
38665         var translateButton = selection.selectAll('.localized-add')
38666             .data([0]);
38667
38668         translateButton.enter()
38669             .append('button')
38670             .attr('class', 'button-input-action localized-add minor')
38671             .call(iD.svg.Icon('#icon-plus'))
38672             .call(bootstrap.tooltip()
38673                 .title(t('translate.translate'))
38674                 .placement('left'));
38675
38676         translateButton
38677             .on('click', addNew);
38678
38679         localizedInputs = selection.selectAll('.localized-wrap')
38680             .data([0]);
38681
38682         localizedInputs.enter().append('div')
38683             .attr('class', 'localized-wrap');
38684     }
38685
38686     function addNew() {
38687         d3.event.preventDefault();
38688         var data = localizedInputs.selectAll('div.entry').data();
38689         var defaultLang = iD.detect().locale.toLowerCase().split('-')[0];
38690         var langExists = _.find(data, function(datum) { return datum.lang === defaultLang;});
38691         var isLangEn = defaultLang.indexOf('en') > -1;
38692         if (isLangEn || langExists) {
38693           defaultLang = '';
38694         }
38695         data.push({ lang: defaultLang, value: '' });
38696         localizedInputs.call(render, data);
38697     }
38698
38699     function change(onInput) {
38700         return function() {
38701             var t = {};
38702             t[field.key] = d3.select(this).value() || undefined;
38703             dispatch.change(t, onInput);
38704         };
38705     }
38706
38707     function key(lang) { return field.key + ':' + lang; }
38708
38709     function changeLang(d) {
38710         var lang = d3.select(this).value(),
38711             t = {},
38712             language = _.find(iD.data.wikipedia, function(d) {
38713                 return d[0].toLowerCase() === lang.toLowerCase() ||
38714                     d[1].toLowerCase() === lang.toLowerCase();
38715             });
38716
38717         if (language) lang = language[2];
38718
38719         if (d.lang && d.lang !== lang) {
38720             t[key(d.lang)] = undefined;
38721         }
38722
38723         var value = d3.select(this.parentNode)
38724             .selectAll('.localized-value')
38725             .value();
38726
38727         if (lang && value) {
38728             t[key(lang)] = value;
38729         } else if (lang && wikiTitles && wikiTitles[d.lang]) {
38730             t[key(lang)] = wikiTitles[d.lang];
38731         }
38732
38733         d.lang = lang;
38734         dispatch.change(t);
38735     }
38736
38737     function changeValue(d) {
38738         if (!d.lang) return;
38739         var t = {};
38740         t[key(d.lang)] = d3.select(this).value() || undefined;
38741         dispatch.change(t);
38742     }
38743
38744     function fetcher(value, cb) {
38745         var v = value.toLowerCase();
38746
38747         cb(iD.data.wikipedia.filter(function(d) {
38748             return d[0].toLowerCase().indexOf(v) >= 0 ||
38749             d[1].toLowerCase().indexOf(v) >= 0 ||
38750             d[2].toLowerCase().indexOf(v) >= 0;
38751         }).map(function(d) {
38752             return { value: d[1] };
38753         }));
38754     }
38755
38756     function render(selection, data) {
38757         var wraps = selection.selectAll('div.entry').
38758             data(data, function(d) { return d.lang; });
38759
38760         var innerWrap = wraps.enter()
38761             .insert('div', ':first-child');
38762
38763         innerWrap.attr('class', 'entry')
38764             .each(function() {
38765                 var wrap = d3.select(this);
38766                 var langcombo = d3.combobox().fetcher(fetcher);
38767
38768                 var label = wrap.append('label')
38769                     .attr('class','form-label')
38770                     .text(t('translate.localized_translation_label'))
38771                     .attr('for','localized-lang');
38772
38773                 label.append('button')
38774                     .attr('class', 'minor remove')
38775                     .on('click', function(d){
38776                         d3.event.preventDefault();
38777                         var t = {};
38778                         t[key(d.lang)] = undefined;
38779                         dispatch.change(t);
38780                         d3.select(this.parentNode.parentNode)
38781                             .style('top','0')
38782                             .style('max-height','240px')
38783                             .transition()
38784                             .style('opacity', '0')
38785                             .style('max-height','0px')
38786                             .remove();
38787                     })
38788                     .call(iD.svg.Icon('#operation-delete'));
38789
38790                 wrap.append('input')
38791                     .attr('class', 'localized-lang')
38792                     .attr('type', 'text')
38793                     .attr('placeholder',t('translate.localized_translation_language'))
38794                     .on('blur', changeLang)
38795                     .on('change', changeLang)
38796                     .call(langcombo);
38797
38798                 wrap.append('input')
38799                     .on('blur', changeValue)
38800                     .on('change', changeValue)
38801                     .attr('type', 'text')
38802                     .attr('placeholder', t('translate.localized_translation_name'))
38803                     .attr('class', 'localized-value');
38804             });
38805
38806         innerWrap
38807             .style('margin-top', '0px')
38808             .style('max-height', '0px')
38809             .style('opacity', '0')
38810             .transition()
38811             .duration(200)
38812             .style('margin-top', '10px')
38813             .style('max-height', '240px')
38814             .style('opacity', '1')
38815             .each('end', function() {
38816                 d3.select(this)
38817                     .style('max-height', '')
38818                     .style('overflow', 'visible');
38819             });
38820
38821         wraps.exit()
38822             .transition()
38823             .duration(200)
38824             .style('max-height','0px')
38825             .style('opacity', '0')
38826             .style('top','-10px')
38827             .remove();
38828
38829         var entry = selection.selectAll('.entry');
38830
38831         entry.select('.localized-lang')
38832             .value(function(d) {
38833                 var lang = _.find(iD.data.wikipedia, function(lang) { return lang[2] === d.lang; });
38834                 return lang ? lang[1] : d.lang;
38835             });
38836
38837         entry.select('.localized-value')
38838             .value(function(d) { return d.value; });
38839     }
38840
38841     i.tags = function(tags) {
38842         // Fetch translations from wikipedia
38843         if (tags.wikipedia && !wikiTitles) {
38844             wikiTitles = {};
38845             var wm = tags.wikipedia.match(/([^:]+):(.+)/);
38846             if (wm && wm[0] && wm[1]) {
38847                 wikipedia.translations(wm[1], wm[2], function(d) {
38848                     wikiTitles = d;
38849                 });
38850             }
38851         }
38852
38853         input.value(tags[field.key] || '');
38854
38855         var postfixed = [], k, m;
38856         for (k in tags) {
38857             m = k.match(/^(.*):([a-zA-Z_-]+)$/);
38858             if (m && m[1] === field.key && m[2]) {
38859                 postfixed.push({ lang: m[2], value: tags[k] });
38860             }
38861         }
38862
38863         localizedInputs.call(render, postfixed.reverse());
38864     };
38865
38866     i.focus = function() {
38867         input.node().focus();
38868     };
38869
38870     i.entity = function(_) {
38871         entity = _;
38872     };
38873
38874     return d3.rebind(i, dispatch, 'on');
38875 };
38876 iD.ui.preset.maxspeed = function(field, context) {
38877     var dispatch = d3.dispatch('change'),
38878         entity,
38879         imperial,
38880         unitInput,
38881         combobox,
38882         input;
38883
38884     var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
38885         imperialValues = [20, 25, 30, 35, 40, 45, 50, 55, 65, 70];
38886
38887     function maxspeed(selection) {
38888         combobox = d3.combobox();
38889         var unitCombobox = d3.combobox().data(['km/h', 'mph'].map(comboValues));
38890
38891         input = selection.selectAll('#preset-input-' + field.id)
38892             .data([0]);
38893
38894         input.enter().append('input')
38895             .attr('type', 'text')
38896             .attr('id', 'preset-input-' + field.id)
38897             .attr('placeholder', field.placeholder());
38898
38899         input
38900             .call(combobox)
38901             .on('change', change)
38902             .on('blur', change);
38903
38904         var childNodes = context.graph().childNodes(context.entity(entity.id)),
38905             loc = childNodes[~~(childNodes.length/2)].loc;
38906
38907         imperial = _.any(iD.data.imperial.features, function(f) {
38908             return _.any(f.geometry.coordinates, function(d) {
38909                 return iD.geo.pointInPolygon(loc, d[0]);
38910             });
38911         });
38912
38913         unitInput = selection.selectAll('input.maxspeed-unit')
38914             .data([0]);
38915
38916         unitInput.enter().append('input')
38917             .attr('type', 'text')
38918             .attr('class', 'maxspeed-unit');
38919
38920         unitInput
38921             .on('blur', changeUnits)
38922             .on('change', changeUnits)
38923             .call(unitCombobox);
38924
38925         function changeUnits() {
38926             imperial = unitInput.value() === 'mph';
38927             unitInput.value(imperial ? 'mph' : 'km/h');
38928             setSuggestions();
38929             change();
38930         }
38931
38932     }
38933
38934     function setSuggestions() {
38935         combobox.data((imperial ? imperialValues : metricValues).map(comboValues));
38936         unitInput.value(imperial ? 'mph' : 'km/h');
38937     }
38938
38939     function comboValues(d) {
38940         return {
38941             value: d.toString(),
38942             title: d.toString()
38943         };
38944     }
38945
38946     function change() {
38947         var tag = {},
38948             value = input.value();
38949
38950         if (!value) {
38951             tag[field.key] = undefined;
38952         } else if (isNaN(value) || !imperial) {
38953             tag[field.key] = value;
38954         } else {
38955             tag[field.key] = value + ' mph';
38956         }
38957
38958         dispatch.change(tag);
38959     }
38960
38961     maxspeed.tags = function(tags) {
38962         var value = tags[field.key];
38963
38964         if (value && value.indexOf('mph') >= 0) {
38965             value = parseInt(value, 10);
38966             imperial = true;
38967         } else if (value) {
38968             imperial = false;
38969         }
38970
38971         setSuggestions();
38972
38973         input.value(value || '');
38974     };
38975
38976     maxspeed.focus = function() {
38977         input.node().focus();
38978     };
38979
38980     maxspeed.entity = function(_) {
38981         entity = _;
38982     };
38983
38984     return d3.rebind(maxspeed, dispatch, 'on');
38985 };
38986 iD.ui.preset.radio = function(field) {
38987     var dispatch = d3.dispatch('change'),
38988         labels, radios, placeholder;
38989
38990     function radio(selection) {
38991         selection.classed('preset-radio', true);
38992
38993         var wrap = selection.selectAll('.preset-input-wrap')
38994             .data([0]);
38995
38996         var buttonWrap = wrap.enter().append('div')
38997             .attr('class', 'preset-input-wrap toggle-list');
38998
38999         buttonWrap.append('span')
39000             .attr('class', 'placeholder');
39001
39002         placeholder = selection.selectAll('.placeholder');
39003
39004         labels = wrap.selectAll('label')
39005             .data(field.options || field.keys);
39006
39007         var enter = labels.enter().append('label');
39008
39009         enter.append('input')
39010             .attr('type', 'radio')
39011             .attr('name', field.id)
39012             .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
39013             .attr('checked', false);
39014
39015         enter.append('span')
39016             .text(function(d) { return field.t('options.' + d, { 'default': d }); });
39017
39018         radios = labels.selectAll('input')
39019             .on('change', change);
39020     }
39021
39022     function change() {
39023         var t = {};
39024         if (field.key) t[field.key] = undefined;
39025         radios.each(function(d) {
39026             var active = d3.select(this).property('checked');
39027             if (field.key) {
39028                 if (active) t[field.key] = d;
39029             } else {
39030                 t[d] = active ? 'yes' : undefined;
39031             }
39032         });
39033         dispatch.change(t);
39034     }
39035
39036     radio.tags = function(tags) {
39037         function checked(d) {
39038             if (field.key) {
39039                 return tags[field.key] === d;
39040             } else {
39041                 return !!(tags[d] && tags[d] !== 'no');
39042             }
39043         }
39044
39045         labels.classed('active', checked);
39046         radios.property('checked', checked);
39047         var selection = radios.filter(function() { return this.checked; });
39048         if (selection.empty()) {
39049             placeholder.text(t('inspector.none'));
39050         } else {
39051             placeholder.text(selection.attr('value'));
39052         }
39053     };
39054
39055     radio.focus = function() {
39056         radios.node().focus();
39057     };
39058
39059     return d3.rebind(radio, dispatch, 'on');
39060 };
39061 iD.ui.preset.restrictions = function(field, context) {
39062     var dispatch = d3.dispatch('change'),
39063         hover = iD.behavior.Hover(context),
39064         vertexID,
39065         fromNodeID;
39066
39067
39068     function restrictions(selection) {
39069         // if form field is hidden or has detached from dom, clean up.
39070         if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
39071             selection.call(restrictions.off);
39072             return;
39073         }
39074
39075         var wrap = selection.selectAll('.preset-input-wrap')
39076             .data([0]);
39077
39078         var enter = wrap.enter()
39079             .append('div')
39080             .attr('class', 'preset-input-wrap');
39081
39082         enter
39083             .append('div')
39084             .attr('class', 'restriction-help');
39085
39086
39087         var intersection = iD.geo.Intersection(context.graph(), vertexID),
39088             graph = intersection.graph,
39089             vertex = graph.entity(vertexID),
39090             filter = d3.functor(true),
39091             extent = iD.geo.Extent(),
39092             projection = iD.geo.RawMercator();
39093
39094         var d = wrap.dimensions(),
39095             c = [d[0] / 2, d[1] / 2],
39096             z = 24;
39097
39098         projection
39099             .scale(256 * Math.pow(2, z) / (2 * Math.PI));
39100
39101         var s = projection(vertex.loc);
39102
39103         projection
39104             .translate([c[0] - s[0], c[1] - s[1]])
39105             .clipExtent([[0, 0], d]);
39106
39107         var drawLayers = iD.svg.Layers(projection, context).only('osm').dimensions(d),
39108             drawVertices = iD.svg.Vertices(projection, context),
39109             drawLines = iD.svg.Lines(projection, context),
39110             drawTurns = iD.svg.Turns(projection, context);
39111
39112         enter
39113             .call(drawLayers)
39114             .selectAll('.surface')
39115             .call(hover);
39116
39117
39118         var surface = wrap.selectAll('.surface');
39119
39120         surface
39121             .dimensions(d)
39122             .call(drawVertices, graph, [vertex], filter, extent, z)
39123             .call(drawLines, graph, intersection.ways, filter)
39124             .call(drawTurns, graph, intersection.turns(fromNodeID));
39125
39126         surface
39127             .on('click.restrictions', click)
39128             .on('mouseover.restrictions', mouseover)
39129             .on('mouseout.restrictions', mouseout);
39130
39131         surface
39132             .selectAll('.selected')
39133             .classed('selected', false);
39134
39135         if (fromNodeID) {
39136             surface
39137                 .selectAll('.' + intersection.highways[fromNodeID].id)
39138                 .classed('selected', true);
39139         }
39140
39141         mouseout();
39142
39143         context.history()
39144             .on('change.restrictions', render);
39145
39146         d3.select(window)
39147             .on('resize.restrictions', function() {
39148                 wrap.dimensions(null);
39149                 render();
39150             });
39151
39152         function click() {
39153             var datum = d3.event.target.__data__;
39154             if (datum instanceof iD.Entity) {
39155                 fromNodeID = intersection.adjacentNodeId(datum.id);
39156                 render();
39157             } else if (datum instanceof iD.geo.Turn) {
39158                 if (datum.restriction) {
39159                     context.perform(
39160                         iD.actions.UnrestrictTurn(datum, projection),
39161                         t('operations.restriction.annotation.delete'));
39162                 } else {
39163                     context.perform(
39164                         iD.actions.RestrictTurn(datum, projection),
39165                         t('operations.restriction.annotation.create'));
39166                 }
39167             }
39168         }
39169
39170         function mouseover() {
39171             var datum = d3.event.target.__data__;
39172             if (datum instanceof iD.geo.Turn) {
39173                 var graph = context.graph(),
39174                     presets = context.presets(),
39175                     preset;
39176
39177                 if (datum.restriction) {
39178                     preset = presets.match(graph.entity(datum.restriction), graph);
39179                 } else {
39180                     preset = presets.item('type/restriction/' +
39181                         iD.geo.inferRestriction(
39182                             graph,
39183                             datum.from,
39184                             datum.via,
39185                             datum.to,
39186                             projection));
39187                 }
39188
39189                 wrap.selectAll('.restriction-help')
39190                     .text(t('operations.restriction.help.' +
39191                         (datum.restriction ? 'toggle_off' : 'toggle_on'),
39192                         {restriction: preset.name()}));
39193             }
39194         }
39195
39196         function mouseout() {
39197             wrap.selectAll('.restriction-help')
39198                 .text(t('operations.restriction.help.' +
39199                     (fromNodeID ? 'toggle' : 'select')));
39200         }
39201
39202         function render() {
39203             if (context.hasEntity(vertexID)) {
39204                 restrictions(selection);
39205             }
39206         }
39207     }
39208
39209     restrictions.entity = function(_) {
39210         if (!vertexID || vertexID !== _.id) {
39211             fromNodeID = null;
39212             vertexID = _.id;
39213         }
39214     };
39215
39216     restrictions.tags = function() {};
39217     restrictions.focus = function() {};
39218
39219     restrictions.off = function(selection) {
39220         selection.selectAll('.surface')
39221             .call(hover.off)
39222             .on('click.restrictions', null)
39223             .on('mouseover.restrictions', null)
39224             .on('mouseout.restrictions', null);
39225
39226         context.history()
39227             .on('change.restrictions', null);
39228
39229         d3.select(window)
39230             .on('resize.restrictions', null);
39231     };
39232
39233     return d3.rebind(restrictions, dispatch, 'on');
39234 };
39235 iD.ui.preset.textarea = function(field) {
39236     var dispatch = d3.dispatch('change'),
39237         input;
39238
39239     function i(selection) {
39240         input = selection.selectAll('textarea')
39241             .data([0]);
39242
39243         input.enter().append('textarea')
39244             .attr('id', 'preset-input-' + field.id)
39245             .attr('placeholder', field.placeholder() || t('inspector.unknown'))
39246             .attr('maxlength', 255);
39247
39248         input
39249             .on('input', change(true))
39250             .on('blur', change())
39251             .on('change', change());
39252     }
39253
39254     function change(onInput) {
39255         return function() {
39256             var t = {};
39257             t[field.key] = input.value() || undefined;
39258             dispatch.change(t, onInput);
39259         };
39260     }
39261
39262     i.tags = function(tags) {
39263         input.value(tags[field.key] || '');
39264     };
39265
39266     i.focus = function() {
39267         input.node().focus();
39268     };
39269
39270     return d3.rebind(i, dispatch, 'on');
39271 };
39272 iD.ui.preset.wikipedia = function(field, context) {
39273     var dispatch = d3.dispatch('change'),
39274         wikipedia = iD.services.wikipedia(),
39275         link, entity, lang, title;
39276
39277     function i(selection) {
39278         var langcombo = d3.combobox()
39279             .fetcher(function(value, cb) {
39280                 var v = value.toLowerCase();
39281
39282                 cb(iD.data.wikipedia.filter(function(d) {
39283                     return d[0].toLowerCase().indexOf(v) >= 0 ||
39284                         d[1].toLowerCase().indexOf(v) >= 0 ||
39285                         d[2].toLowerCase().indexOf(v) >= 0;
39286                 }).map(function(d) {
39287                     return { value: d[1] };
39288                 }));
39289             });
39290
39291         var titlecombo = d3.combobox()
39292             .fetcher(function(value, cb) {
39293
39294                 if (!value) value = context.entity(entity.id).tags.name || '';
39295                 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
39296
39297                 searchfn(language()[2], value, function(query, data) {
39298                     cb(data.map(function(d) {
39299                         return { value: d };
39300                     }));
39301                 });
39302             });
39303
39304         lang = selection.selectAll('input.wiki-lang')
39305             .data([0]);
39306
39307         lang.enter().append('input')
39308             .attr('type', 'text')
39309             .attr('class', 'wiki-lang')
39310             .value('English');
39311
39312         lang
39313             .call(langcombo)
39314             .on('blur', changeLang)
39315             .on('change', changeLang);
39316
39317         title = selection.selectAll('input.wiki-title')
39318             .data([0]);
39319
39320         title.enter().append('input')
39321             .attr('type', 'text')
39322             .attr('class', 'wiki-title')
39323             .attr('id', 'preset-input-' + field.id);
39324
39325         title
39326             .call(titlecombo)
39327             .on('blur', change)
39328             .on('change', change);
39329
39330         link = selection.selectAll('a.wiki-link')
39331             .data([0]);
39332
39333         link.enter().append('a')
39334             .attr('class', 'wiki-link button-input-action minor')
39335             .attr('target', '_blank')
39336             .call(iD.svg.Icon('#icon-out-link', 'inline'));
39337     }
39338
39339     function language() {
39340         var value = lang.value().toLowerCase();
39341         var locale = iD.detect().locale.toLowerCase();
39342         var localeLanguage;
39343         return _.find(iD.data.wikipedia, function(d) {
39344             if (d[2] === locale) localeLanguage = d;
39345             return d[0].toLowerCase() === value ||
39346                 d[1].toLowerCase() === value ||
39347                 d[2] === value;
39348         }) || localeLanguage || ['English', 'English', 'en'];
39349     }
39350
39351     function changeLang() {
39352         lang.value(language()[1]);
39353         change();
39354     }
39355
39356     function change() {
39357         var value = title.value(),
39358             m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/),
39359             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
39360             anchor;
39361
39362         if (l) {
39363             // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
39364             value = decodeURIComponent(m[2]).replace(/_/g, ' ');
39365             if (m[3]) {
39366                 try {
39367                     // Best-effort `anchordecode:` implementation
39368                     anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
39369                 } catch (e) {
39370                     anchor = decodeURIComponent(m[3]);
39371                 }
39372                 value += '#' + anchor.replace(/_/g, ' ');
39373             }
39374             value = value.slice(0, 1).toUpperCase() + value.slice(1);
39375             lang.value(l[1]);
39376             title.value(value);
39377         }
39378
39379         var t = {};
39380         t[field.key] = value ? language()[2] + ':' + value : undefined;
39381         dispatch.change(t);
39382     }
39383
39384     i.tags = function(tags) {
39385         var value = tags[field.key] || '',
39386             m = value.match(/([^:]+):([^#]+)(?:#(.+))?/),
39387             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
39388             anchor = m && m[3];
39389
39390         // value in correct format
39391         if (l) {
39392             lang.value(l[1]);
39393             title.value(m[2] + (anchor ? ('#' + anchor) : ''));
39394             if (anchor) {
39395                 try {
39396                     // Best-effort `anchorencode:` implementation
39397                     anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
39398                 } catch (e) {
39399                     anchor = anchor.replace(/ /g, '_');
39400                 }
39401             }
39402             link.attr('href', 'https://' + m[1] + '.wikipedia.org/wiki/' +
39403                       m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : ''));
39404
39405         // unrecognized value format
39406         } else {
39407             title.value(value);
39408             link.attr('href', 'https://en.wikipedia.org/wiki/Special:Search?search=' + value);
39409         }
39410     };
39411
39412     i.entity = function(_) {
39413         entity = _;
39414     };
39415
39416     i.focus = function() {
39417         title.node().focus();
39418     };
39419
39420     return d3.rebind(i, dispatch, 'on');
39421 };
39422 iD.ui.intro.area = function(context, reveal) {
39423     var event = d3.dispatch('done'),
39424         timeout;
39425
39426     var step = {
39427         title: 'intro.areas.title'
39428     };
39429
39430     step.enter = function() {
39431         var playground = [-85.63552, 41.94159],
39432             corner = [-85.63565411045074, 41.9417715536927];
39433         context.map().centerZoom(playground, 19);
39434         reveal('button.add-area',
39435             t('intro.areas.add', { button: iD.ui.intro.icon('#icon-area', 'pre-text') }),
39436             { tooltipClass: 'intro-areas-add' });
39437
39438         context.on('enter.intro', addArea);
39439
39440         function addArea(mode) {
39441             if (mode.id !== 'add-area') return;
39442             context.on('enter.intro', drawArea);
39443
39444             var padding = 120 * Math.pow(2, context.map().zoom() - 19);
39445             var pointBox = iD.ui.intro.pad(corner, padding, context);
39446             reveal(pointBox, t('intro.areas.corner'));
39447
39448             context.map().on('move.intro', function() {
39449                 padding = 120 * Math.pow(2, context.map().zoom() - 19);
39450                 pointBox = iD.ui.intro.pad(corner, padding, context);
39451                 reveal(pointBox, t('intro.areas.corner'), {duration: 0});
39452             });
39453         }
39454
39455         function drawArea(mode) {
39456             if (mode.id !== 'draw-area') return;
39457             context.on('enter.intro', enterSelect);
39458
39459             var padding = 150 * Math.pow(2, context.map().zoom() - 19);
39460             var pointBox = iD.ui.intro.pad(playground, padding, context);
39461             reveal(pointBox, t('intro.areas.place'));
39462
39463             context.map().on('move.intro', function() {
39464                 padding = 150 * Math.pow(2, context.map().zoom() - 19);
39465                 pointBox = iD.ui.intro.pad(playground, padding, context);
39466                 reveal(pointBox, t('intro.areas.place'), {duration: 0});
39467             });
39468         }
39469
39470         function enterSelect(mode) {
39471             if (mode.id !== 'select') return;
39472             context.map().on('move.intro', null);
39473             context.on('enter.intro', null);
39474
39475             timeout = setTimeout(function() {
39476                 reveal('.preset-search-input',
39477                     t('intro.areas.search',
39478                     { name: context.presets().item('leisure/playground').name() }));
39479                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
39480             }, 500);
39481         }
39482
39483         function keySearch() {
39484             var first = d3.select('.preset-list-item:first-child');
39485             if (first.classed('preset-leisure-playground')) {
39486                 reveal(first.select('.preset-list-button').node(), t('intro.areas.choose'));
39487                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
39488                 d3.select('.preset-search-input').on('keyup.intro', null);
39489             }
39490         }
39491
39492         function selectedPreset() {
39493             reveal('.pane',
39494                 t('intro.areas.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39495             context.on('exit.intro', event.done);
39496         }
39497     };
39498
39499     step.exit = function() {
39500         window.clearTimeout(timeout);
39501         context.on('enter.intro', null);
39502         context.on('exit.intro', null);
39503         context.history().on('change.intro', null);
39504         context.map().on('move.intro', null);
39505         d3.select('.preset-search-input').on('keyup.intro', null);
39506     };
39507
39508     return d3.rebind(step, event, 'on');
39509 };
39510 iD.ui.intro.line = function(context, reveal) {
39511     var event = d3.dispatch('done'),
39512         timeouts = [];
39513
39514     var step = {
39515         title: 'intro.lines.title'
39516     };
39517
39518     function timeout(f, t) {
39519         timeouts.push(window.setTimeout(f, t));
39520     }
39521
39522     function eventCancel() {
39523         d3.event.stopPropagation();
39524         d3.event.preventDefault();
39525     }
39526
39527     step.enter = function() {
39528         var centroid = [-85.62830, 41.95699];
39529         var midpoint = [-85.62975395449628, 41.95787501510204];
39530         var start = [-85.6297754121684, 41.95805253325314];
39531         var intersection = [-85.62974496187628, 41.95742515554585];
39532
39533         context.map().centerZoom(start, 18);
39534         reveal('button.add-line',
39535             t('intro.lines.add', { button: iD.ui.intro.icon('#icon-line', 'pre-text') }),
39536             { tooltipClass: 'intro-lines-add' });
39537
39538         context.on('enter.intro', addLine);
39539
39540         function addLine(mode) {
39541             if (mode.id !== 'add-line') return;
39542             context.on('enter.intro', drawLine);
39543
39544             var padding = 150 * Math.pow(2, context.map().zoom() - 18);
39545             var pointBox = iD.ui.intro.pad(start, padding, context);
39546             reveal(pointBox, t('intro.lines.start'));
39547
39548             context.map().on('move.intro', function() {
39549                 padding = 150 * Math.pow(2, context.map().zoom() - 18);
39550                 pointBox = iD.ui.intro.pad(start, padding, context);
39551                 reveal(pointBox, t('intro.lines.start'), {duration: 0});
39552             });
39553         }
39554
39555         function drawLine(mode) {
39556             if (mode.id !== 'draw-line') return;
39557             context.history().on('change.intro', addIntersection);
39558             context.on('enter.intro', retry);
39559
39560             var padding = 300 * Math.pow(2, context.map().zoom() - 19);
39561             var pointBox = iD.ui.intro.pad(midpoint, padding, context);
39562             reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}));
39563
39564             context.map().on('move.intro', function() {
39565                 padding = 300 * Math.pow(2, context.map().zoom() - 19);
39566                 pointBox = iD.ui.intro.pad(midpoint, padding, context);
39567                 reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}), {duration: 0});
39568             });
39569         }
39570
39571         // ended line before creating intersection
39572         function retry(mode) {
39573             if (mode.id !== 'select') return;
39574             var pointBox = iD.ui.intro.pad(intersection, 30, context),
39575                 ids = mode.selectedIDs();
39576             reveal(pointBox, t('intro.lines.restart', {name: t('intro.graph.flower_st')}));
39577             d3.select(window).on('mousedown.intro', eventCancel, true);
39578
39579             timeout(function() {
39580                 context.replace(iD.actions.DeleteMultiple(ids));
39581                 step.exit();
39582                 step.enter();
39583             }, 3000);
39584         }
39585
39586         function addIntersection(changes) {
39587             if ( _.any(changes.created(), function(d) {
39588                 return d.type === 'node' && context.graph().parentWays(d).length > 1;
39589             })) {
39590                 context.history().on('change.intro', null);
39591                 context.on('enter.intro', enterSelect);
39592
39593                 var padding = 900 * Math.pow(2, context.map().zoom() - 19);
39594                 var pointBox = iD.ui.intro.pad(centroid, padding, context);
39595                 reveal(pointBox, t('intro.lines.finish'));
39596
39597                 context.map().on('move.intro', function() {
39598                     padding = 900 * Math.pow(2, context.map().zoom() - 19);
39599                     pointBox = iD.ui.intro.pad(centroid, padding, context);
39600                     reveal(pointBox, t('intro.lines.finish'), {duration: 0});
39601                 });
39602             }
39603         }
39604
39605         function enterSelect(mode) {
39606             if (mode.id !== 'select') return;
39607             context.map().on('move.intro', null);
39608             context.on('enter.intro', null);
39609             d3.select('#curtain').style('pointer-events', 'all');
39610
39611             presetCategory();
39612         }
39613
39614         function presetCategory() {
39615             timeout(function() {
39616                 d3.select('#curtain').style('pointer-events', 'none');
39617                 var road = d3.select('.preset-category-road .preset-list-button');
39618                 reveal(road.node(), t('intro.lines.road'));
39619                 road.one('click.intro', roadCategory);
39620             }, 500);
39621         }
39622
39623         function roadCategory() {
39624             timeout(function() {
39625                 var grid = d3.select('.subgrid');
39626                 reveal(grid.node(), t('intro.lines.residential'));
39627                 grid.selectAll(':not(.preset-highway-residential) .preset-list-button')
39628                     .one('click.intro', retryPreset);
39629                 grid.selectAll('.preset-highway-residential .preset-list-button')
39630                     .one('click.intro', roadDetails);
39631             }, 500);
39632         }
39633
39634         // selected wrong road type
39635         function retryPreset() {
39636             timeout(function() {
39637                 var preset = d3.select('.entity-editor-pane .preset-list-button');
39638                 reveal(preset.node(), t('intro.lines.wrong_preset'));
39639                 preset.one('click.intro', presetCategory);
39640             }, 500);
39641         }
39642
39643         function roadDetails() {
39644             reveal('.pane',
39645                 t('intro.lines.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39646             context.on('exit.intro', event.done);
39647         }
39648
39649     };
39650
39651     step.exit = function() {
39652         d3.select(window).on('mousedown.intro', null, true);
39653         d3.select('#curtain').style('pointer-events', 'none');
39654         timeouts.forEach(window.clearTimeout);
39655         context.on('enter.intro', null);
39656         context.on('exit.intro', null);
39657         context.map().on('move.intro', null);
39658         context.history().on('change.intro', null);
39659     };
39660
39661     return d3.rebind(step, event, 'on');
39662 };
39663 iD.ui.intro.navigation = function(context, reveal) {
39664     var event = d3.dispatch('done'),
39665         timeouts = [];
39666
39667     var step = {
39668         title: 'intro.navigation.title'
39669     };
39670
39671     function set(f, t) {
39672         timeouts.push(window.setTimeout(f, t));
39673     }
39674
39675     function eventCancel() {
39676         d3.event.stopPropagation();
39677         d3.event.preventDefault();
39678     }
39679
39680     step.enter = function() {
39681         var rect = context.surfaceRect(),
39682             map = {
39683                 left: rect.left + 10,
39684                 top: rect.top + 70,
39685                 width: rect.width - 70,
39686                 height: rect.height - 170
39687             };
39688
39689         context.map().centerZoom([-85.63591, 41.94285], 19);
39690
39691         reveal(map, t('intro.navigation.drag'));
39692
39693         context.map().on('move.intro', _.debounce(function() {
39694             context.map().on('move.intro', null);
39695             townhall();
39696             context.on('enter.intro', inspectTownHall);
39697         }, 400));
39698
39699         function townhall() {
39700             var hall = [-85.63645945147184, 41.942986488012565];
39701
39702             var point = context.projection(hall);
39703             if (point[0] < 0 || point[0] > rect.width ||
39704                 point[1] < 0 || point[1] > rect.height) {
39705                 context.map().center(hall);
39706             }
39707
39708             var box = iD.ui.intro.pointBox(hall, context);
39709             reveal(box, t('intro.navigation.select'));
39710
39711             context.map().on('move.intro', function() {
39712                 var box = iD.ui.intro.pointBox(hall, context);
39713                 reveal(box, t('intro.navigation.select'), {duration: 0});
39714             });
39715         }
39716
39717         function inspectTownHall(mode) {
39718             if (mode.id !== 'select') return;
39719             context.on('enter.intro', null);
39720             context.map().on('move.intro', null);
39721             set(function() {
39722                 reveal('.entity-editor-pane',
39723                     t('intro.navigation.pane', { button: iD.ui.intro.icon('#icon-close', 'pre-text') }));
39724                 context.on('exit.intro', streetSearch);
39725             }, 700);
39726         }
39727
39728         function streetSearch() {
39729             context.on('exit.intro', null);
39730             reveal('.search-header input',
39731                 t('intro.navigation.search', { name: t('intro.graph.spring_st') }));
39732             d3.select('.search-header input').on('keyup.intro', searchResult);
39733         }
39734
39735         function searchResult() {
39736             var first = d3.select('.feature-list-item:nth-child(0n+2)'),  // skip No Results item
39737                 firstName = first.select('.entity-name'),
39738                 name = t('intro.graph.spring_st');
39739
39740             if (!firstName.empty() && firstName.text() === name) {
39741                 reveal(first.node(), t('intro.navigation.choose', { name: name }));
39742                 context.on('exit.intro', selectedStreet);
39743                 d3.select('.search-header input')
39744                     .on('keydown.intro', eventCancel, true)
39745                     .on('keyup.intro', null);
39746             }
39747         }
39748
39749         function selectedStreet() {
39750             var springSt = [-85.63585099140167, 41.942506848938926];
39751             context.map().center(springSt);
39752             context.on('exit.intro', event.done);
39753             set(function() {
39754                 reveal('.entity-editor-pane',
39755                     t('intro.navigation.chosen', {
39756                         name: t('intro.graph.spring_st'),
39757                         button: iD.ui.intro.icon('#icon-close', 'pre-text')
39758                     }));
39759             }, 400);
39760         }
39761     };
39762
39763     step.exit = function() {
39764         timeouts.forEach(window.clearTimeout);
39765         context.map().on('move.intro', null);
39766         context.on('enter.intro', null);
39767         context.on('exit.intro', null);
39768         d3.select('.search-header input')
39769             .on('keydown.intro', null)
39770             .on('keyup.intro', null);
39771     };
39772
39773     return d3.rebind(step, event, 'on');
39774 };
39775 iD.ui.intro.point = function(context, reveal) {
39776     var event = d3.dispatch('done'),
39777         timeouts = [];
39778
39779     var step = {
39780         title: 'intro.points.title'
39781     };
39782
39783     function setTimeout(f, t) {
39784         timeouts.push(window.setTimeout(f, t));
39785     }
39786
39787     function eventCancel() {
39788         d3.event.stopPropagation();
39789         d3.event.preventDefault();
39790     }
39791
39792     step.enter = function() {
39793         context.map().centerZoom([-85.63279, 41.94394], 19);
39794         reveal('button.add-point',
39795             t('intro.points.add', { button: iD.ui.intro.icon('#icon-point', 'pre-text') }),
39796             { tooltipClass: 'intro-points-add' });
39797
39798         var corner = [-85.632481,41.944094];
39799
39800         context.on('enter.intro', addPoint);
39801
39802         function addPoint(mode) {
39803             if (mode.id !== 'add-point') return;
39804             context.on('enter.intro', enterSelect);
39805
39806             var pointBox = iD.ui.intro.pad(corner, 150, context);
39807             reveal(pointBox, t('intro.points.place'));
39808
39809             context.map().on('move.intro', function() {
39810                 pointBox = iD.ui.intro.pad(corner, 150, context);
39811                 reveal(pointBox, t('intro.points.place'), {duration: 0});
39812             });
39813         }
39814
39815         function enterSelect(mode) {
39816             if (mode.id !== 'select') return;
39817             context.map().on('move.intro', null);
39818             context.on('enter.intro', null);
39819
39820             setTimeout(function() {
39821                 reveal('.preset-search-input',
39822                     t('intro.points.search', {name: context.presets().item('amenity/cafe').name()}));
39823                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
39824             }, 500);
39825         }
39826
39827         function keySearch() {
39828             var first = d3.select('.preset-list-item:first-child');
39829             if (first.classed('preset-amenity-cafe')) {
39830                 reveal(first.select('.preset-list-button').node(), t('intro.points.choose'));
39831                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
39832                 d3.select('.preset-search-input')
39833                     .on('keydown.intro', eventCancel, true)
39834                     .on('keyup.intro', null);
39835             }
39836         }
39837
39838         function selectedPreset() {
39839             setTimeout(function() {
39840                 reveal('.entity-editor-pane', t('intro.points.describe'), {tooltipClass: 'intro-points-describe'});
39841                 context.history().on('change.intro', closeEditor);
39842                 context.on('exit.intro', selectPoint);
39843             }, 400);
39844         }
39845
39846         function closeEditor() {
39847             d3.select('.preset-search-input').on('keydown.intro', null);
39848             context.history().on('change.intro', null);
39849             reveal('.entity-editor-pane',
39850                 t('intro.points.close', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39851         }
39852
39853         function selectPoint() {
39854             context.on('exit.intro', null);
39855             context.history().on('change.intro', null);
39856             context.on('enter.intro', enterReselect);
39857
39858             var pointBox = iD.ui.intro.pad(corner, 150, context);
39859             reveal(pointBox, t('intro.points.reselect'));
39860
39861             context.map().on('move.intro', function() {
39862                 pointBox = iD.ui.intro.pad(corner, 150, context);
39863                 reveal(pointBox, t('intro.points.reselect'), {duration: 0});
39864             });
39865         }
39866
39867         function enterReselect(mode) {
39868             if (mode.id !== 'select') return;
39869             context.map().on('move.intro', null);
39870             context.on('enter.intro', null);
39871
39872             setTimeout(function() {
39873                 reveal('.entity-editor-pane',
39874                     t('intro.points.fixname', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
39875                 context.on('exit.intro', deletePoint);
39876             }, 500);
39877         }
39878
39879         function deletePoint() {
39880             context.on('exit.intro', null);
39881             context.on('enter.intro', enterDelete);
39882
39883             var pointBox = iD.ui.intro.pad(corner, 150, context);
39884             reveal(pointBox, t('intro.points.reselect_delete'));
39885
39886             context.map().on('move.intro', function() {
39887                 pointBox = iD.ui.intro.pad(corner, 150, context);
39888                 reveal(pointBox, t('intro.points.reselect_delete'), {duration: 0});
39889             });
39890         }
39891
39892         function enterDelete(mode) {
39893             if (mode.id !== 'select') return;
39894             context.map().on('move.intro', null);
39895             context.on('enter.intro', null);
39896             context.on('exit.intro', deletePoint);
39897             context.map().on('move.intro', deletePoint);
39898             context.history().on('change.intro', deleted);
39899
39900             setTimeout(function() {
39901                 var node = d3.select('.radial-menu-item-delete').node();
39902                 var pointBox = iD.ui.intro.pad(node.getBoundingClientRect(), 50, context);
39903                 reveal(pointBox,
39904                     t('intro.points.delete', { button: iD.ui.intro.icon('#operation-delete', 'pre-text') }));
39905             }, 300);
39906         }
39907
39908         function deleted(changed) {
39909             if (changed.deleted().length) event.done();
39910         }
39911
39912     };
39913
39914     step.exit = function() {
39915         timeouts.forEach(window.clearTimeout);
39916         context.on('exit.intro', null);
39917         context.on('enter.intro', null);
39918         context.map().on('move.intro', null);
39919         context.history().on('change.intro', null);
39920         d3.select('.preset-search-input')
39921             .on('keyup.intro', null)
39922             .on('keydown.intro', null);
39923     };
39924
39925     return d3.rebind(step, event, 'on');
39926 };
39927 iD.ui.intro.startEditing = function(context, reveal) {
39928     var event = d3.dispatch('done', 'startEditing'),
39929         modal,
39930         timeouts = [];
39931
39932     var step = {
39933         title: 'intro.startediting.title'
39934     };
39935
39936     function timeout(f, t) {
39937         timeouts.push(window.setTimeout(f, t));
39938     }
39939
39940     step.enter = function() {
39941         reveal('.map-control.help-control',
39942             t('intro.startediting.help', { button: iD.ui.intro.icon('#icon-help', 'pre-text') }));
39943
39944         timeout(function() {
39945             reveal('#bar button.save', t('intro.startediting.save'));
39946         }, 5000);
39947
39948         timeout(function() {
39949             reveal('#surface');
39950         }, 10000);
39951
39952         timeout(function() {
39953             modal = iD.ui.modal(context.container());
39954
39955             modal.select('.modal')
39956                 .attr('class', 'modal-splash modal col6');
39957
39958             modal.selectAll('.close').remove();
39959
39960             var startbutton = modal.select('.content')
39961                 .attr('class', 'fillL')
39962                     .append('button')
39963                         .attr('class', 'modal-section huge-modal-button')
39964                         .on('click', function() {
39965                             modal.remove();
39966                         });
39967
39968                 startbutton.append('div')
39969                     .attr('class','illustration');
39970                 startbutton.append('h2')
39971                     .text(t('intro.startediting.start'));
39972
39973             event.startEditing();
39974         }, 10500);
39975     };
39976
39977     step.exit = function() {
39978         if (modal) modal.remove();
39979         timeouts.forEach(window.clearTimeout);
39980     };
39981
39982     return d3.rebind(step, event, 'on');
39983 };
39984 iD.presets = function() {
39985
39986     // an iD.presets.Collection with methods for
39987     // loading new data and returning defaults
39988
39989     var all = iD.presets.Collection([]),
39990         defaults = { area: all, line: all, point: all, vertex: all, relation: all },
39991         fields = {},
39992         universal = [],
39993         recent = iD.presets.Collection([]);
39994
39995     // Index of presets by (geometry, tag key).
39996     var index = {
39997         point: {},
39998         vertex: {},
39999         line: {},
40000         area: {},
40001         relation: {}
40002     };
40003
40004     all.match = function(entity, resolver) {
40005         var geometry = entity.geometry(resolver),
40006             geometryMatches = index[geometry],
40007             best = -1,
40008             match;
40009
40010         for (var k in entity.tags) {
40011             var keyMatches = geometryMatches[k];
40012             if (!keyMatches) continue;
40013
40014             for (var i = 0; i < keyMatches.length; i++) {
40015                 var score = keyMatches[i].matchScore(entity);
40016                 if (score > best) {
40017                     best = score;
40018                     match = keyMatches[i];
40019                 }
40020             }
40021         }
40022
40023         return match || all.item(geometry);
40024     };
40025
40026     // Because of the open nature of tagging, iD will never have a complete
40027     // list of tags used in OSM, so we want it to have logic like "assume
40028     // that a closed way with an amenity tag is an area, unless the amenity
40029     // is one of these specific types". This function computes a structure
40030     // that allows testing of such conditions, based on the presets designated
40031     // as as supporting (or not supporting) the area geometry.
40032     //
40033     // The returned object L is a whitelist/blacklist of tags. A closed way
40034     // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
40035     // (see `iD.Way#isArea()`). In other words, the keys of L form the whitelist,
40036     // and the subkeys form the blacklist.
40037     all.areaKeys = function() {
40038         var areaKeys = {},
40039             ignore = ['barrier', 'highway', 'footway', 'railway', 'type'],
40040             presets = _.reject(all.collection, 'suggestion');
40041
40042         // whitelist
40043         presets.forEach(function(d) {
40044             for (var key in d.tags) break;
40045             if (!key) return;
40046             if (ignore.indexOf(key) !== -1) return;
40047
40048             if (d.geometry.indexOf('area') !== -1) {
40049                 areaKeys[key] = areaKeys[key] || {};
40050             }
40051         });
40052
40053         // blacklist
40054         presets.forEach(function(d) {
40055             for (var key in d.tags) break;
40056             if (!key) return;
40057             if (ignore.indexOf(key) !== -1) return;
40058
40059             var value = d.tags[key];
40060             if (d.geometry.indexOf('area') === -1 &&
40061                 d.geometry.indexOf('line') !== -1 &&
40062                 key in areaKeys && value !== '*') {
40063                 areaKeys[key][value] = true;
40064             }
40065         });
40066
40067         return areaKeys;
40068     };
40069
40070     all.load = function(d) {
40071
40072         if (d.fields) {
40073             _.forEach(d.fields, function(d, id) {
40074                 fields[id] = iD.presets.Field(id, d);
40075                 if (d.universal) universal.push(fields[id]);
40076             });
40077         }
40078
40079         if (d.presets) {
40080             _.forEach(d.presets, function(d, id) {
40081                 all.collection.push(iD.presets.Preset(id, d, fields));
40082             });
40083         }
40084
40085         if (d.categories) {
40086             _.forEach(d.categories, function(d, id) {
40087                 all.collection.push(iD.presets.Category(id, d, all));
40088             });
40089         }
40090
40091         if (d.defaults) {
40092             var getItem = _.bind(all.item, all);
40093             defaults = {
40094                 area: iD.presets.Collection(d.defaults.area.map(getItem)),
40095                 line: iD.presets.Collection(d.defaults.line.map(getItem)),
40096                 point: iD.presets.Collection(d.defaults.point.map(getItem)),
40097                 vertex: iD.presets.Collection(d.defaults.vertex.map(getItem)),
40098                 relation: iD.presets.Collection(d.defaults.relation.map(getItem))
40099             };
40100         }
40101
40102         for (var i = 0; i < all.collection.length; i++) {
40103             var preset = all.collection[i],
40104                 geometry = preset.geometry;
40105
40106             for (var j = 0; j < geometry.length; j++) {
40107                 var g = index[geometry[j]];
40108                 for (var k in preset.tags) {
40109                     (g[k] = g[k] || []).push(preset);
40110                 }
40111             }
40112         }
40113
40114         return all;
40115     };
40116
40117     all.field = function(id) {
40118         return fields[id];
40119     };
40120
40121     all.universal = function() {
40122         return universal;
40123     };
40124
40125     all.defaults = function(geometry, n) {
40126         var rec = recent.matchGeometry(geometry).collection.slice(0, 4),
40127             def = _.uniq(rec.concat(defaults[geometry].collection)).slice(0, n - 1);
40128         return iD.presets.Collection(_.unique(rec.concat(def).concat(all.item(geometry))));
40129     };
40130
40131     all.choose = function(preset) {
40132         if (!preset.isFallback()) {
40133             recent = iD.presets.Collection(_.unique([preset].concat(recent.collection)));
40134         }
40135         return all;
40136     };
40137
40138     return all;
40139 };
40140 iD.presets.Category = function(id, category, all) {
40141     category = _.clone(category);
40142
40143     category.id = id;
40144
40145     category.members = iD.presets.Collection(category.members.map(function(id) {
40146         return all.item(id);
40147     }));
40148
40149     category.matchGeometry = function(geometry) {
40150         return category.geometry.indexOf(geometry) >= 0;
40151     };
40152
40153     category.matchScore = function() { return -1; };
40154
40155     category.name = function() {
40156         return t('presets.categories.' + id + '.name', {'default': id});
40157     };
40158
40159     category.terms = function() {
40160         return [];
40161     };
40162
40163     return category;
40164 };
40165 iD.presets.Collection = function(collection) {
40166
40167     var maxSearchResults = 50,
40168         maxSuggestionResults = 10;
40169
40170     var presets = {
40171
40172         collection: collection,
40173
40174         item: function(id) {
40175             return _.find(collection, function(d) {
40176                 return d.id === id;
40177             });
40178         },
40179
40180         matchGeometry: function(geometry) {
40181             return iD.presets.Collection(collection.filter(function(d) {
40182                 return d.matchGeometry(geometry);
40183             }));
40184         },
40185
40186         search: function(value, geometry) {
40187             if (!value) return this;
40188
40189             value = value.toLowerCase();
40190
40191             var searchable = _.filter(collection, function(a) {
40192                     return a.searchable !== false && a.suggestion !== true;
40193                 }),
40194                 suggestions = _.filter(collection, function(a) {
40195                     return a.suggestion === true;
40196                 });
40197
40198             function leading(a) {
40199                 var index = a.indexOf(value);
40200                 return index === 0 || a[index - 1] === ' ';
40201             }
40202
40203             // matches value to preset.name
40204             var leading_name = _.filter(searchable, function(a) {
40205                     return leading(a.name().toLowerCase());
40206                 }).sort(function(a, b) {
40207                     var i = a.name().toLowerCase().indexOf(value) - b.name().toLowerCase().indexOf(value);
40208                     if (i === 0) return a.name().length - b.name().length;
40209                     else return i;
40210                 });
40211
40212             // matches value to preset.terms values
40213             var leading_terms = _.filter(searchable, function(a) {
40214                     return _.any(a.terms() || [], leading);
40215                 });
40216
40217             // matches value to preset.tags values
40218             var leading_tag_values = _.filter(searchable, function(a) {
40219                     return _.any(_.without(_.values(a.tags || {}), '*'), leading);
40220                 });
40221
40222
40223             // finds close matches to value in preset.name
40224             var levenstein_name = searchable.map(function(a) {
40225                     return {
40226                         preset: a,
40227                         dist: iD.util.editDistance(value, a.name().toLowerCase())
40228                     };
40229                 }).filter(function(a) {
40230                     return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
40231                 }).sort(function(a, b) {
40232                     return a.dist - b.dist;
40233                 }).map(function(a) {
40234                     return a.preset;
40235                 });
40236
40237             // finds close matches to value in preset.terms
40238             var leventstein_terms = _.filter(searchable, function(a) {
40239                     return _.any(a.terms() || [], function(b) {
40240                         return iD.util.editDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
40241                     });
40242                 });
40243
40244             function suggestionName(name) {
40245                 var nameArray = name.split(' - ');
40246                 if (nameArray.length > 1) {
40247                     name = nameArray.slice(0, nameArray.length-1).join(' - ');
40248                 }
40249                 return name.toLowerCase();
40250             }
40251
40252             var leading_suggestions = _.filter(suggestions, function(a) {
40253                     return leading(suggestionName(a.name()));
40254                 }).sort(function(a, b) {
40255                     a = suggestionName(a.name());
40256                     b = suggestionName(b.name());
40257                     var i = a.indexOf(value) - b.indexOf(value);
40258                     if (i === 0) return a.length - b.length;
40259                     else return i;
40260                 });
40261
40262             var leven_suggestions = suggestions.map(function(a) {
40263                     return {
40264                         preset: a,
40265                         dist: iD.util.editDistance(value, suggestionName(a.name()))
40266                     };
40267                 }).filter(function(a) {
40268                     return a.dist + Math.min(value.length - suggestionName(a.preset.name()).length, 0) < 1;
40269                 }).sort(function(a, b) {
40270                     return a.dist - b.dist;
40271                 }).map(function(a) {
40272                     return a.preset;
40273                 });
40274
40275             var other = presets.item(geometry);
40276
40277             var results = leading_name.concat(
40278                             leading_terms,
40279                             leading_tag_values,
40280                             leading_suggestions.slice(0, maxSuggestionResults+5),
40281                             levenstein_name,
40282                             leventstein_terms,
40283                             leven_suggestions.slice(0, maxSuggestionResults)
40284                         ).slice(0, maxSearchResults-1);
40285
40286             return iD.presets.Collection(_.unique(
40287                     results.concat(other)
40288                 ));
40289         }
40290     };
40291
40292     return presets;
40293 };
40294 iD.presets.Field = function(id, field) {
40295     field = _.clone(field);
40296
40297     field.id = id;
40298
40299     field.matchGeometry = function(geometry) {
40300         return !field.geometry || field.geometry === geometry;
40301     };
40302
40303     field.t = function(scope, options) {
40304         return t('presets.fields.' + id + '.' + scope, options);
40305     };
40306
40307     field.label = function() {
40308         return field.t('label', {'default': id});
40309     };
40310
40311     var placeholder = field.placeholder;
40312     field.placeholder = function() {
40313         return field.t('placeholder', {'default': placeholder});
40314     };
40315
40316     return field;
40317 };
40318 iD.presets.Preset = function(id, preset, fields) {
40319     preset = _.clone(preset);
40320
40321     preset.id = id;
40322     preset.fields = (preset.fields || []).map(getFields);
40323     preset.geometry = (preset.geometry || []);
40324
40325     function getFields(f) {
40326         return fields[f];
40327     }
40328
40329     preset.matchGeometry = function(geometry) {
40330         return preset.geometry.indexOf(geometry) >= 0;
40331     };
40332
40333     var matchScore = preset.matchScore || 1;
40334     preset.matchScore = function(entity) {
40335         var tags = preset.tags,
40336             score = 0;
40337
40338         for (var t in tags) {
40339             if (entity.tags[t] === tags[t]) {
40340                 score += matchScore;
40341             } else if (tags[t] === '*' && t in entity.tags) {
40342                 score += matchScore / 2;
40343             } else {
40344                 return -1;
40345             }
40346         }
40347
40348         return score;
40349     };
40350
40351     preset.t = function(scope, options) {
40352         return t('presets.presets.' + id + '.' + scope, options);
40353     };
40354
40355     var name = preset.name;
40356     preset.name = function() {
40357         if (preset.suggestion) {
40358             id = id.split('/');
40359             id = id[0] + '/' + id[1];
40360             return name + ' - ' + t('presets.presets.' + id + '.name');
40361         }
40362         return preset.t('name', {'default': name});
40363     };
40364
40365     preset.terms = function() {
40366         return preset.t('terms', {'default': ''}).toLowerCase().trim().split(/\s*,+\s*/);
40367     };
40368
40369     preset.isFallback = function() {
40370         var tagCount = Object.keys(preset.tags).length;
40371         return tagCount === 0 || (tagCount === 1 && preset.tags.hasOwnProperty('area'));
40372     };
40373
40374     preset.reference = function(geometry) {
40375         var key = Object.keys(preset.tags)[0],
40376             value = preset.tags[key];
40377
40378         if (geometry === 'relation' && key === 'type') {
40379             return { rtype: value };
40380         } else if (value === '*') {
40381             return { key: key };
40382         } else {
40383             return { key: key, value: value };
40384         }
40385     };
40386
40387     var removeTags = preset.removeTags || preset.tags;
40388     preset.removeTags = function(tags, geometry) {
40389         tags = _.omit(tags, _.keys(removeTags));
40390
40391         for (var f in preset.fields) {
40392             var field = preset.fields[f];
40393             if (field.matchGeometry(geometry) && field.default === tags[field.key]) {
40394                 delete tags[field.key];
40395             }
40396         }
40397
40398         delete tags.area;
40399         return tags;
40400     };
40401
40402     var applyTags = preset.addTags || preset.tags;
40403     preset.applyTags = function(tags, geometry) {
40404         var k;
40405
40406         tags = _.clone(tags);
40407
40408         for (k in applyTags) {
40409             if (applyTags[k] === '*') {
40410                 tags[k] = 'yes';
40411             } else {
40412                 tags[k] = applyTags[k];
40413             }
40414         }
40415
40416         // Add area=yes if necessary.
40417         // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
40418         // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
40419         // 2. chosen preset doesn't have a key in areaKeys (`railway=station`)
40420         if (geometry === 'area') {
40421             var needsAreaTag = true;
40422             if (preset.geometry.indexOf('line') === -1) {
40423                 for (k in applyTags) {
40424                     if (k in iD.areaKeys) {
40425                         needsAreaTag = false;
40426                         break;
40427                     }
40428                 }
40429             }
40430             if (needsAreaTag) {
40431                 tags.area = 'yes';
40432             }
40433         }
40434
40435         for (var f in preset.fields) {
40436             var field = preset.fields[f];
40437             if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
40438                 tags[field.key] = field.default;
40439             }
40440         }
40441
40442         return tags;
40443     };
40444
40445     return preset;
40446 };
40447 iD.validations = {};
40448 iD.validations.DeprecatedTag = function() {
40449
40450     var validation = function(changes) {
40451         var warnings = [];
40452         for (var i = 0; i < changes.created.length; i++) {
40453             var change = changes.created[i],
40454                 deprecatedTags = change.deprecatedTags();
40455
40456             if (!_.isEmpty(deprecatedTags)) {
40457                 var tags = iD.util.tagText({ tags: deprecatedTags });
40458                 warnings.push({
40459                     id: 'deprecated_tags',
40460                     message: t('validations.deprecated_tags', { tags: tags }),
40461                     entity: change
40462                 });
40463             }
40464         }
40465         return warnings;
40466     };
40467
40468     return validation;
40469 };
40470 iD.validations.ManyDeletions = function() {
40471     var threshold = 100;
40472
40473     var validation = function(changes) {
40474         var warnings = [];
40475         if (changes.deleted.length > threshold) {
40476             warnings.push({
40477                 id: 'many_deletions',
40478                 message: t('validations.many_deletions', { n: changes.deleted.length })
40479             });
40480         }
40481         return warnings;
40482     };
40483
40484     return validation;
40485 };
40486 iD.validations.MissingTag = function() {
40487
40488     var validation = function(changes, graph) {
40489         var warnings = [];
40490         for (var i = 0; i < changes.created.length; i++) {
40491             var change = changes.created[i],
40492                 geometry = change.geometry(graph);
40493
40494             if ((geometry === 'point' || geometry === 'line' || geometry === 'area') && !change.isUsed(graph)) {
40495                 warnings.push({
40496                     id: 'missing_tag',
40497                     message: t('validations.untagged_' + geometry),
40498                     tooltip: t('validations.untagged_' + geometry + '_tooltip'),
40499                     entity: change
40500                 });
40501             }
40502         }
40503         return warnings;
40504     };
40505
40506     return validation;
40507 };
40508 iD.validations.TagSuggestsArea = function() {
40509
40510     // https://github.com/openstreetmap/josm/blob/mirror/src/org/
40511     // openstreetmap/josm/data/validation/tests/UnclosedWays.java#L80
40512     function tagSuggestsArea(tags) {
40513         if (_.isEmpty(tags)) return false;
40514
40515         var presence = ['landuse', 'amenities', 'tourism', 'shop'];
40516         for (var i = 0; i < presence.length; i++) {
40517             if (tags[presence[i]] !== undefined) {
40518                 return presence[i] + '=' + tags[presence[i]];
40519             }
40520         }
40521
40522         if (tags.building && tags.building === 'yes') return 'building=yes';
40523     }
40524
40525     var validation = function(changes, graph) {
40526         var warnings = [];
40527         for (var i = 0; i < changes.created.length; i++) {
40528             var change = changes.created[i],
40529                 geometry = change.geometry(graph),
40530                 suggestion = (geometry === 'line' ? tagSuggestsArea(change.tags) : undefined);
40531
40532             if (suggestion) {
40533                 warnings.push({
40534                     id: 'tag_suggests_area',
40535                     message: t('validations.tag_suggests_area', { tag: suggestion }),
40536                     entity: change
40537                 });
40538             }
40539         }
40540         return warnings;
40541     };
40542
40543     return validation;
40544 };
40545 })();
40546 window.locale = { _current: 'en' };
40547
40548 locale.current = function(_) {
40549     if (!arguments.length) return locale._current;
40550     if (locale[_] !== undefined) locale._current = _;
40551     else if (locale[_.split('-')[0]]) locale._current = _.split('-')[0];
40552     return locale;
40553 };
40554
40555 function t(s, o, loc) {
40556     loc = loc || locale._current;
40557
40558     var path = s.split(".").reverse(),
40559         rep = locale[loc];
40560
40561     while (rep !== undefined && path.length) rep = rep[path.pop()];
40562
40563     if (rep !== undefined) {
40564         if (o) for (var k in o) rep = rep.replace('{' + k + '}', o[k]);
40565         return rep;
40566     }
40567
40568     if (loc !== 'en') {
40569         return t(s, o, 'en');
40570     }
40571
40572     if (o && 'default' in o) {
40573         return o['default'];
40574     }
40575
40576     var missing = 'Missing ' + loc + ' translation: ' + s;
40577     if (typeof console !== "undefined") console.error(missing);
40578
40579     return missing;
40580 }
40581 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","locating":"Locating, please wait..."},"inspector":{"no_documentation_combination":"There is no documentation available for this tag combination","no_documentation_key":"There is no documentation available for this key","show_more":"Show More","view_on_osm":"View on openstreetmap.org","all_fields":"All fields","all_tags":"All tags","all_members":"All members","all_relations":"All relations","new_relation":"New relation...","role":"Role","choose":"Select feature type","results":"{n} results for {search}","reference":"View on OpenStreetMap Wiki","back_tooltip":"Change feature","remove":"Remove","search":"Search","multiselect":"Selected items","unknown":"Unknown","incomplete":"<not downloaded>","feature_list":"Search features","edit":"Edit feature","check":{"yes":"Yes","no":"No"},"none":"None","node":"Node","way":"Way","relation":"Relation","location":"Location","add_fields":"Add field:"},"background":{"title":"Background","description":"Background settings","percent_brightness":"{opacity}% brightness","none":"None","best_imagery":"Best known imagery source for this location","custom":"Custom","custom_button":"Edit custom background","custom_prompt":"Enter a tile URL template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme.","fix_misalignment":"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"},"traffic_roads":{"description":"Traffic Roads","tooltip":"Highways, Streets, etc."},"service_roads":{"description":"Service Roads","tooltip":"Service Roads, Parking Aisles, Tracks, etc."},"paths":{"description":"Paths","tooltip":"Sidewalks, Foot Paths, Cycle Paths, etc."},"buildings":{"description":"Buildings","tooltip":"Buildings, Shelters, Garages, etc."},"landuse":{"description":"Landuse Features","tooltip":"Forests, Farmland, Parks, Residential, Commercial, etc."},"boundaries":{"description":"Boundaries","tooltip":"Administrative Boundaries"},"water":{"description":"Water Features","tooltip":"Rivers, Lakes, Ponds, Basins, etc."},"rail":{"description":"Rail Features","tooltip":"Railways"},"power":{"description":"Power Features","tooltip":"Power Lines, Power Plants, Substations, etc."},"past_future":{"description":"Past/Future","tooltip":"Proposed, Construction, Abandoned, Demolished, etc."},"others":{"description":"Others","tooltip":"Everything Else"}},"area_fill":{"wireframe":{"description":"No Fill (Wireframe)","tooltip":"Enabling wireframe mode makes it easy to see the background imagery."},"partial":{"description":"Partial Fill","tooltip":"Areas are drawn with fill only around their inner edges. (Recommended for beginner mappers)"},"full":{"description":"Full Fill","tooltip":"Areas are drawn fully filled."}},"restore":{"heading":"You have unsaved changes","description":"Do you wish to restore unsaved changes from a previous editing session?","restore":"Restore","reset":"Reset"},"save":{"title":"Save","help":"Save changes to OpenStreetMap, making them visible to other users.","no_changes":"No changes to save.","error":"Errors occurred while trying to save","status_code":"Server returned status code {code}","unknown_error_details":"Please ensure you are connected to the internet.","uploading":"Uploading changes to OpenStreetMap.","unsaved_changes":"You have unsaved changes","conflict":{"header":"Resolve conflicting edits","count":"Conflict {num} of {total}","previous":"< Previous","next":"Next >","keep_local":"Keep mine","keep_remote":"Use theirs","restore":"Restore","delete":"Leave Deleted","download_changes":"Or download your changes.","done":"All conflicts resolved!","help":"Another user changed some of the same map features you changed.\nClick on each item below for more details about the conflict, and choose whether to keep\nyour changes or the other user's changes.\n"}},"merge_remote_changes":{"conflict":{"deleted":"This object has been deleted by {user}.","location":"This object was moved by both you and {user}.","nodelist":"Nodes were changed by both you and {user}.","memberlist":"Relation members were changed by both you and {user}.","tags":"You changed the <b>{tag}</b> tag to \"{local}\" and {user} changed it to \"{remote}\"."}},"success":{"edited_osm":"Edited OSM!","just_edited":"You just edited OpenStreetMap!","view_on_osm":"View on OSM","facebook":"Share on Facebook","twitter":"Share on Twitter","google":"Share on Google+","help_html":"Your changes should appear in the \"Standard\" layer in a few minutes. Other layers, and certain features, may take longer.","help_link_text":"Details","help_link_url":"https://wiki.openstreetmap.org/wiki/FAQ#I_have_just_made_some_changes_to_the_map._How_do_I_get_to_see_my_changes.3F"},"confirm":{"okay":"Okay","cancel":"Cancel"},"splash":{"welcome":"Welcome to the iD OpenStreetMap editor","text":"iD is a friendly but powerful tool for contributing to the world's best free world map. This is version {version}. For more information see {website} and report bugs at {github}.","walkthrough":"Start the Walkthrough","start":"Edit Now"},"source_switch":{"live":"live","lose_changes":"You have unsaved changes. Switching the map server will discard them. Are you sure you want to switch servers?","dev":"dev"},"tag_reference":{"description":"Description","on_wiki":"{tag} on wiki.osm.org","used_with":"used with {type}"},"validations":{"untagged_point":"Untagged point","untagged_line":"Untagged line","untagged_area":"Untagged area","many_deletions":"You're deleting {n} objects. Are you sure you want to do this? This will delete them from the map that everyone else sees on openstreetmap.org.","tag_suggests_area":"The tag {tag} suggests line should be area, but it is not an area","untagged_point_tooltip":"Select a feature type that describes what this point is.","untagged_line_tooltip":"Select a feature type that describes what this line is.","untagged_area_tooltip":"Select a feature type that describes what this area is.","deprecated_tags":"Deprecated tags: {tags}"},"zoom":{"in":"Zoom In","out":"Zoom Out"},"cannot_zoom":"Cannot zoom out further in current mode.","full_screen":"Toggle Full Screen","gpx":{"local_layer":"Local GPX file","drag_drop":"Drag and drop a .gpx file on the page, or click the button to the right to browse","zoom":"Zoom to GPX track","browse":"Browse for a .gpx file"},"mapillary_images":{"tooltip":"Street-level photos from Mapillary","title":"Photo Overlay (Mapillary)"},"mapillary_signs":{"tooltip":"Traffic signs from Mapillary","title":"Traffic Sign Overlay (Mapillary)"},"mapillary":{"view_on_mapillary":"View this image on Mapillary"},"help":{"title":"Help","help":"# Help\n\nThis is an editor for [OpenStreetMap](http://www.openstreetmap.org/), the\nfree and editable map of the world. You can use it to add and update\ndata in your area, making an open-source and open-data map of the world\nbetter for everyone.\n\nEdits that you make on this map will be visible to everyone who uses\nOpenStreetMap. In order to make an edit, you'll need to\n[log in](https://www.openstreetmap.org/login).\n\nThe [iD editor](http://ideditor.com/) is a collaborative project with [source\ncode available on GitHub](https://github.com/openstreetmap/iD).\n","editing_saving":"# Editing & Saving\n\nThis editor is designed to work primarily online, and you're accessing\nit through a website right now.\n\n### Selecting Features\n\nTo select a map feature, like a road or point of interest, click\non it on the map. This will highlight the selected feature, open a panel with\ndetails about it, and show a menu of things you can do with the feature.\n\nTo select multiple features, hold down the 'Shift' key. Then either click\non the features you want to select, or drag on the map to draw a rectangle.\nThis will draw a box and select all the points within it.\n\n### Saving Edits\n\nWhen you make changes like editing roads, buildings, and places, these are\nstored locally until you save them to the server. Don't worry if you make\na mistake - you can undo changes by clicking the undo button, and redo\nchanges by clicking the redo button.\n\nClick 'Save' to finish a group of edits - for instance, if you've completed\nan area of town and would like to start on a new area. You'll have a chance\nto review what you've done, and the editor supplies helpful suggestions\nand warnings if something doesn't seem right about the changes.\n\nIf everything looks good, you can enter a short comment explaining the change\nyou made, and click 'Save' again to post the changes\nto [OpenStreetMap.org](http://www.openstreetmap.org/), where they are visible\nto all other users and available for others to build and improve upon.\n\nIf you can't finish your edits in one sitting, you can leave the editor\nwindow and come back (on the same browser and computer), and the\neditor application will offer to restore your work.\n\n### Using the editor\n\nA list of available keyboard shortcuts can be found [here](http://wiki.openstreetmap.org/wiki/ID/Shortcuts).\n","roads":"# Roads\n\nYou can create, fix, and delete roads with this editor. Roads can be all\nkinds: paths, highways, trails, cycleways, and more - any often-crossed\nsegment should be mappable.\n\n### Selecting\n\nClick on a road to select it. An outline should become visible, along\nwith a small tools menu on the map and a sidebar showing more information\nabout the road.\n\n### Modifying\n\nOften you'll see roads that aren't aligned to the imagery behind them\nor to a GPS track. You can adjust these roads so they are in the correct\nplace.\n\nFirst click on the road you want to change. This will highlight it and show\ncontrol points along it that you can drag to better locations. If\nyou want to add new control points for more detail, double-click a part\nof the road without a node, and one will be added.\n\nIf the road connects to another road, but doesn't properly connect on\nthe map, you can drag one of its control points onto the other road in\norder to join them. Having roads connect is important for the map\nand essential for providing driving directions.\n\nYou can also click the 'Move' tool or press the `M` shortcut key to move the entire road at\none time, and then click again to save that movement.\n\n### Deleting\n\nIf a road is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the road could simply be newly built.\n\nYou can delete a road by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n\n### Creating\n\nFound somewhere there should be a road but there isn't? Click the 'Line'\nicon in the top-left of the editor or press the shortcut key `2` to start drawing\na line.\n\nClick on the start of the road on the map to start drawing. If the road\nbranches off from an existing road, start by clicking on the place where they connect.\n\nThen click on points along the road so that it follows the right path, according\nto satellite imagery or GPS. If the road you are drawing crosses another road, connect\nit by clicking on the intersection point. When you're done drawing, double-click\nor press 'Return' or 'Enter' on your keyboard.\n","gps":"# GPS\n\nCollected GPS traces are one valuable source of data for OpenStreetMap. This editor\nsupports local traces - `.gpx` files on your local computer. You can collect\nthis kind of GPS trace with a number of smartphone applications as well as\npersonal GPS hardware.\n\nFor information on how to perform a GPS survey, read\n[Mapping with a smartphone, GPS, or paper](http://learnosm.org/en/mobile-mapping/).\n\nTo use a GPX track for mapping, drag and drop the GPX file onto the map\neditor. If it's recognized, it will be added to the map as a bright purple\nline. Click on the 'Map Data' menu on the right side to enable,\ndisable, or zoom to this new GPX-powered layer.\n\nThe GPX track isn't directly uploaded to OpenStreetMap - the best way to\nuse it is to draw on the map, using it as a guide for the new features that\nyou add, and also to [upload it to OpenStreetMap](http://www.openstreetmap.org/trace/create)\nfor other users to use.\n","imagery":"# Imagery\n\nAerial imagery is an important resource for mapping. A combination of\nairplane flyovers, satellite views, and freely-compiled sources are available\nin the editor under the 'Background Settings' menu on the right.\n\nBy default a [Bing Maps](http://www.bing.com/maps/) satellite layer is\npresented in the editor, but as you pan and zoom the map to new geographical\nareas, new sources will become available. Some countries, like the United\nStates, France, and Denmark have very high-quality imagery available for some areas.\n\nImagery is sometimes offset from the map data because of a mistake on the\nimagery provider's side. If you see a lot of roads shifted from the background,\ndon't immediately move them all to match the background. Instead you can adjust\nthe imagery so that it matches the existing data by clicking 'Fix alignment' at\nthe bottom of the Background Settings UI.\n","addresses":"# Addresses\n\nAddresses are some of the most useful information for the map.\n\nAlthough addresses are often represented as parts of streets, in OpenStreetMap\nthey're recorded as attributes of buildings and places along streets.\n\nYou can add address information to places mapped as building outlines\nas well as those mapped as single points. The optimal source of address\ndata is from an on-the-ground survey or personal knowledge - as with any\nother feature, copying from commercial sources like Google Maps is strictly\nforbidden.\n","inspector":"# Using the Inspector\n\nThe inspector is the section on the left side of the page that allows you to\nedit the details of the selected feature.\n\n### Selecting a Feature Type\n\nAfter you add a point, line, or area, you can choose what type of feature it\nis, like whether it's a highway or residential road, supermarket or cafe.\nThe inspector will display buttons for common feature types, and you can\nfind others by typing what you're looking for in the search box.\n\nClick the 'i' in the bottom-right-hand corner of a feature type button to\nlearn more about it. Click a button to choose that type.\n\n### Using Forms and Editing Tags\n\nAfter you choose a feature type, or when you select a feature that already\nhas a type assigned, the inspector will display fields with details about\nthe feature like its name and address.\n\nBelow the fields you see, you can click the 'Add field' dropdown to add\nother details, like a Wikipedia link, wheelchair access, and more.\n\nAt the bottom of the inspector, click 'Additional tags' to add arbitrary\nother tags to the element. [Taginfo](http://taginfo.openstreetmap.org/) is a\ngreat resource for learn more about popular tag combinations.\n\nChanges you make in the inspector are automatically applied to the map.\nYou can undo them at any time by clicking the 'Undo' button.\n","buildings":"# Buildings\n\nOpenStreetMap is the world's largest database of buildings. You can create\nand improve this database.\n\n### Selecting\n\nYou can select a building by clicking on its border. This will highlight the\nbuilding and open a small tools menu and a sidebar showing more information\nabout the building.\n\n### Modifying\n\nSometimes buildings are incorrectly placed or have incorrect tags.\n\nTo move an entire building, select it, then click the 'Move' tool. Move your\nmouse to shift the building, and click when it's correctly placed.\n\nTo fix the specific shape of a building, click and drag the nodes that form\nits border into better places.\n\n### Creating\n\nOne of the main questions around adding buildings to the map is that\nOpenStreetMap records buildings both as shapes and points. The rule of thumb\nis to _map a building as a shape whenever possible_, and map companies, homes,\namenities, and other things that operate out of buildings as points placed\nwithin the building shape.\n\nStart drawing a building as a shape by clicking the 'Area' button in the top\nleft of the interface, and end it either by pressing 'Return' on your keyboard\nor clicking on the first node drawn to close the shape.\n\n### Deleting\n\nIf a building is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the building could simply be newly built.\n\nYou can delete a building by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n","relations":"# Relations\n\nA relation is a special type of feature in OpenStreetMap that groups together\nother features. For example, two common types of relations are *route relations*,\nwhich group together sections of road that belong to a specific freeway or\nhighway, and *multipolygons*, which group together several lines that define\na complex area (one with several pieces or holes in it like a donut).\n\nThe group of features in a relation are called *members*. In the sidebar, you can\nsee which relations a feature is a member of, and click on a relation there\nto select the it. When the relation is selected, you can see all of its\nmembers listed in the sidebar and highlighted on the map.\n\nFor the most part, iD will take care of maintaining relations automatically\nwhile you edit. The main thing you should be aware of is that if you delete a\nsection of road to redraw it more accurately, you should make sure that the\nnew section is a member of the same relations as the original.\n\n## Editing Relations\n\nIf you want to edit relations, here are the basics.\n\nTo add a feature to a relation, select the feature, click the \"+\" button in the\n\"All relations\" section of the sidebar, and select or type the name of the relation.\n\nTo create a new relation, select the first feature that should be a member,\nclick the \"+\" button in the \"All relations\" section, and select \"New relation...\".\n\nTo remove a feature from a relation, select the feature and click the trash\nbutton next to the relation you want to remove it from.\n\nYou can create multipolygons with holes using the \"Merge\" tool. Draw two areas (inner\nand outer), hold the Shift key and click on each of them to select them both, and then\nclick the \"Merge\" (+) button.\n"},"intro":{"graph":{"city_hall":"Three Rivers City Hall","fire_department":"Three Rivers Fire Department","memory_isle_park":"Memory Isle Park","riverwalk_trail":"Riverwalk Trail","w_michigan_ave":"West Michigan Avenue","e_michigan_ave":"East Michigan Avenue","spring_st":"Spring Street","scidmore_park":"Scidmore Park","petting_zoo":"Scidmore Park Petting Zoo","n_andrews_st":"North Andrews Street","s_andrews_st":"South Andrews Street","n_constantine_st":"North Constantine Street","s_constantine_st":"South Constantine Street","rocky_river":"Rocky River","railroad_dr":"Railroad Drive","conrail_rr":"Conrail Railroad","st_joseph_river":"Saint Joseph River","n_main_st":"North Main Street","s_main_st":"South Main Street","water_st":"Water Street","foster_st":"Foster Street","portage_river":"Portage River","flower_st":"Flower Street","elm_st":"Elm Street","walnut_st":"Walnut Street","morris_ave":"Morris Avenue","east_st":"East Street","portage_ave":"Portage Avenue"},"navigation":{"title":"Navigation","drag":"The main map area shows OpenStreetMap data on top of a background. You can navigate by dragging and scrolling, just like any web map. **Drag the map!**","select":"Map features are represented three ways: using points, lines or areas. All features can be selected by clicking on them. **Click on the point to select it.**","pane":"When a feature is selected, the feature editor is displayed. The header shows us the feature type and the main pane shows the feature's attributes, such as its name and address. **Close the feature editor by pressing the {button} button in the top right.**","search":"You can also search for features in the current view, or worldwide. **Search for '{name}'**","choose":"**Choose {name} from the list to select it.**","chosen":"Great! {name} is now selected. **Close the feature editor by pressing the {button} button.**"},"points":{"title":"Points","add":"Points can be used to represent features such as shops, restaurants, and monuments. They mark a specific location, and describe what's there. **Click the {button} Point button to add a new point.**","place":"The point can be placed by clicking on the map. **Click the map to place the new point on top of the building.**","search":"There are many different features that can be represented by points. The point you just added is a Cafe. **Search for '{name}'**","choose":"**Choose Cafe from the list.**","describe":"The point is now marked as a cafe. Using the feature editor, we can add more information about the feature. **Add a name**","close":"The feature editor will remember all of your changes automatically.  When you change a feature, the close button will change to a checkmark. **Click the {button} button to close the feature editor**","reselect":"Often points will already exist, but have mistakes or be incomplete. We can edit existing points. **Click to select the point you just created.**","fixname":"**Change the name, then click the {button} button to close the feature editor.**","reselect_delete":"All features on the map can be deleted. **Click to select the point you created.**","delete":"The menu around the point contains operations that can be performed on it, including delete. **Click on the {button} button to delete the point.**"},"areas":{"title":"Areas","add":"Areas are used to show the boundaries of features like lakes, buildings, and residential areas. They can be also be used for more detailed mapping of many features you might normally map as points. **Click the {button} Area button to add a new area.**","corner":"Areas are drawn by placing nodes that mark the boundary of the area. **Click to place a starting node on one of the corners of the playground.**","place":"Draw the area by placing more nodes. Finish the area by clicking on the starting node. **Draw an area for the playground.**","search":"**Search for '{name}'.**","choose":"**Choose Playground from the list.**","describe":"**Add a name, then click the {button} button to close the feature editor**"},"lines":{"title":"Lines","add":"Lines are used to represent features such as roads, railroads, and rivers. **Click the {button} Line button to add a new line.**","start":"**Start the line by clicking on the end of the road.**","intersect":"Click to add more nodes to the line. You can drag the map while drawing if necessary. Roads, and many other types of lines, are part of a larger network. It is important for these lines to be connected properly in order for routing applications to work. **Click on {name} to create an intersection connecting the two lines.**","finish":"Lines can be finished by clicking on the last node again. **Finish drawing the road.**","road":"**Select Road from the list**","residential":"There are different types of roads, the most common of which is Residential. **Choose the Residential road type**","describe":"**Name the road, then click the {button} button to close the feature editor.**","restart":"The road needs to intersect {name}.","wrong_preset":"You didn't select the Residential road type. **Click here to choose again**"},"startediting":{"title":"Start Editing","help":"You can replay this walkthrough or view more documentation by clicking the {button} Help button.","save":"Don't forget to regularly save your changes!","start":"Start mapping!"}},"presets":{"categories":{"category-building":{"name":"Building Features"},"category-golf":{"name":"Golf Features"},"category-landuse":{"name":"Land Use Features"},"category-path":{"name":"Path Features"},"category-rail":{"name":"Rail Features"},"category-restriction":{"name":"Restriction Features"},"category-road":{"name":"Road Features"},"category-route":{"name":"Route Features"},"category-water-area":{"name":"Water Features"},"category-water-line":{"name":"Water Features"}},"fields":{"access":{"label":"Allowed Access","placeholder":"Not Specified","types":{"access":"All","foot":"Foot","motor_vehicle":"Motor Vehicles","bicycle":"Bicycles","horse":"Horses"},"options":{"yes":{"title":"Allowed","description":"Access permitted by law; a right of way"},"no":{"title":"Prohibited","description":"Access not permitted to the general public"},"permissive":{"title":"Permissive","description":"Access permitted until such time as the owner revokes the permission"},"private":{"title":"Private","description":"Access permitted only with permission of the owner on an individual basis"},"designated":{"title":"Designated","description":"Access permitted according to signs or specific local laws"},"destination":{"title":"Destination","description":"Access permitted only to reach a destination"},"dismount":{"title":"Dismount","description":"Access permitted but rider must dismount"}}},"access_simple":{"label":"Allowed Access","placeholder":"yes"},"access_toilets":{"label":"Access"},"address":{"label":"Address","placeholders":{"housename":"Housename","housenumber":"123","conscriptionnumber":"123","street":"Street","city":"City","postcode":"Postcode","place":"Place","hamlet":"Hamlet","suburb":"Suburb","subdistrict":"Subdistrict","district":"District","province":"Province","state":"State","country":"Country"}},"admin_level":{"label":"Admin Level"},"aerialway":{"label":"Type"},"aerialway/access":{"label":"Access","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aerialway/bubble":{"label":"Bubble"},"aerialway/capacity":{"label":"Capacity (per hour)","placeholder":"500, 2500, 5000..."},"aerialway/duration":{"label":"Duration (minutes)","placeholder":"1, 2, 3..."},"aerialway/heating":{"label":"Heated"},"aerialway/occupancy":{"label":"Occupancy","placeholder":"2, 4, 8..."},"aerialway/summer/access":{"label":"Access (summer)","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aeroway":{"label":"Type"},"amenity":{"label":"Type"},"area/highway":{"label":"Type"},"artist":{"label":"Artist"},"artwork_type":{"label":"Type"},"atm":{"label":"ATM"},"backrest":{"label":"Backrest"},"barrier":{"label":"Type"},"bench":{"label":"Bench"},"bicycle_parking":{"label":"Type"},"boundary":{"label":"Type"},"brand":{"label":"Brand"},"building":{"label":"Building"},"building_area":{"label":"Building"},"capacity":{"label":"Capacity","placeholder":"50, 100, 200..."},"cardinal_direction":{"label":"Direction","options":{"N":"North","E":"East","S":"South","W":"West","NE":"Northeast","SE":"Southeast","SW":"Southwest","NW":"Northwest","NNE":"North-northeast","ENE":"East-northeast","ESE":"East-southeast","SSE":"South-southeast","SSW":"South-southwest","WSW":"West-southwest","WNW":"West-northwest","NNW":"North-northwest"}},"clock_direction":{"label":"Direction","options":{"clockwise":"Clockwise","anticlockwise":"Counterclockwise"}},"collection_times":{"label":"Collection Times"},"construction":{"label":"Type"},"content":{"label":"Contents"},"country":{"label":"Country"},"covered":{"label":"Covered"},"craft":{"label":"Type"},"crop":{"label":"Crop"},"crossing":{"label":"Type"},"cuisine":{"label":"Cuisine"},"cycleway":{"label":"Bike Lanes","placeholder":"none","types":{"cycleway:left":"Left side","cycleway:right":"Right side"},"options":{"none":{"title":"None","description":"No bike lane"},"lane":{"title":"Standard bike lane","description":"A bike lane separated from auto traffic by a painted line"},"shared_lane":{"title":"Shared bike lane","description":"A bike lane with no separation from auto traffic"},"track":{"title":"Bike track","description":"A bike lane separated from traffic by a physical barrier"},"share_busway":{"title":"Bike lane shared with bus","description":"A bike lane shared with a bus lane"},"opposite_lane":{"title":"Opposite bike lane","description":"A bike lane that travels in the opposite direction of traffic"},"opposite":{"title":"Contraflow bike lane","description":"A bike lane that travels in both directions on a one-way street"}}},"delivery":{"label":"Delivery"},"denomination":{"label":"Denomination"},"denotation":{"label":"Denotation"},"description":{"label":"Description"},"diaper":{"label":"Diaper Changing Available"},"dock":{"label":"Type"},"drive_through":{"label":"Drive-Through"},"electrified":{"label":"Electrification","placeholder":"Contact Line, Electrified Rail...","options":{"contact_line":"Contact Line","rail":"Electrified Rail","yes":"Yes (unspecified)","no":"No"}},"elevation":{"label":"Elevation"},"emergency":{"label":"Emergency"},"entrance":{"label":"Type"},"except":{"label":"Exceptions"},"fax":{"label":"Fax","placeholder":"+31 42 123 4567"},"fee":{"label":"Fee"},"fire_hydrant/type":{"label":"Type","options":{"pillar":"Pillar/Aboveground","underground":"Underground","wall":"Wall","pond":"Pond"}},"fixme":{"label":"Fix Me"},"fuel":{"label":"Fuel"},"fuel/biodiesel":{"label":"Sells Biodiesel"},"fuel/diesel":{"label":"Sells Diesel"},"fuel/e10":{"label":"Sells E10"},"fuel/e85":{"label":"Sells E85"},"fuel/lpg":{"label":"Sells Propane"},"fuel/octane_100":{"label":"Sells Racing Gasoline"},"fuel/octane_91":{"label":"Sells Regular Gasoline"},"fuel/octane_95":{"label":"Sells Midgrade Gasoline"},"fuel/octane_98":{"label":"Sells Premium Gasoline"},"gauge":{"label":"Gauge"},"gender":{"label":"Gender","placeholder":"Unknown","options":{"male":"Male","female":"Female","unisex":"Unisex"}},"generator/method":{"label":"Method"},"generator/source":{"label":"Source"},"generator/type":{"label":"Type"},"golf_hole":{"label":"Reference","placeholder":"Hole number (1-18)"},"handicap":{"label":"Handicap","placeholder":"1-18"},"handrail":{"label":"Handrail"},"highway":{"label":"Type"},"historic":{"label":"Type"},"hoops":{"label":"Hoops","placeholder":"1, 2, 4..."},"iata":{"label":"IATA"},"icao":{"label":"ICAO"},"incline":{"label":"Incline"},"incline_steps":{"label":"Incline","options":{"up":"Up","down":"Down"}},"information":{"label":"Type"},"internet_access":{"label":"Internet Access","options":{"yes":"Yes","no":"No","wlan":"Wifi","wired":"Wired","terminal":"Terminal"}},"lamp_type":{"label":"Type"},"landuse":{"label":"Type"},"lanes":{"label":"Lanes","placeholder":"1, 2, 3..."},"layer":{"label":"Layer"},"leaf_cycle":{"label":"Leaf Cycle","options":{"evergreen":"Evergreen","deciduous":"Deciduous","semi_evergreen":"Semi-Evergreen","semi_deciduous":"Semi-Deciduous","mixed":"Mixed"}},"leaf_cycle_singular":{"label":"Leaf Cycle","options":{"evergreen":"Evergreen","deciduous":"Deciduous","semi_evergreen":"Semi-Evergreen","semi_deciduous":"Semi-Deciduous"}},"leaf_type":{"label":"Leaf Type","options":{"broadleaved":"Broadleaved","needleleaved":"Needleleaved","mixed":"Mixed","leafless":"Leafless"}},"leaf_type_singular":{"label":"Leaf Type","options":{"broadleaved":"Broadleaved","needleleaved":"Needleleaved","leafless":"Leafless"}},"leisure":{"label":"Type"},"length":{"label":"Length (Meters)"},"level":{"label":"Level"},"levels":{"label":"Levels","placeholder":"2, 4, 6..."},"lit":{"label":"Lit"},"location":{"label":"Location"},"man_made":{"label":"Type"},"maxspeed":{"label":"Speed Limit","placeholder":"40, 50, 60..."},"maxstay":{"label":"Max Stay"},"mtb/scale":{"label":"Mountain Biking Difficulty","placeholder":"0, 1, 2, 3...","options":{"0":"0: Solid gravel/packed earth, no obstacles, wide curves","1":"1: Some loose surface, small obstacles, wide curves","2":"2: Much loose surface, large obstacles, easy hairpins","3":"3: Slippery surface, large obstacles, tight hairpins","4":"4: Loose surface or boulders, dangerous hairpins","5":"5: Maximum difficulty, boulder fields, landslides","6":"6: Not rideable except by the very best mountain bikers"}},"mtb/scale/imba":{"label":"IMBA Trail Difficulty","placeholder":"Easy, Medium, Difficult...","options":{"0":"Easiest (white circle)","1":"Easy (green circle)","2":"Medium (blue square)","3":"Difficult (black diamond)","4":"Extremely Difficult (double black diamond)"}},"mtb/scale/uphill":{"label":"Mountain Biking Uphill Difficulty","placeholder":"0, 1, 2, 3...","options":{"0":"0: Avg. incline <10%, gravel/packed earth, no obstacles","1":"1: Avg. incline <15%, gravel/packed earth, few small objects","2":"2: Avg. incline <20%, stable surface, fistsize rocks/roots","3":"3: Avg. incline <25%, variable surface, fistsize rocks/branches","4":"4: Avg. incline <30%, poor condition, big rocks/branches","5":"5: Very steep, bike generally needs to be pushed or carried"}},"name":{"label":"Name","placeholder":"Common name (if any)"},"natural":{"label":"Natural"},"network":{"label":"Network"},"note":{"label":"Note"},"office":{"label":"Type"},"oneway":{"label":"One Way","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"oneway_yes":{"label":"One Way","options":{"undefined":"Assumed to be Yes","yes":"Yes","no":"No"}},"opening_hours":{"label":"Hours"},"operator":{"label":"Operator"},"par":{"label":"Par","placeholder":"3, 4, 5..."},"park_ride":{"label":"Park and Ride"},"parking":{"label":"Type","options":{"surface":"Surface","multi-storey":"Multilevel","underground":"Underground","sheds":"Sheds","carports":"Carports","garage_boxes":"Garage Boxes","lane":"Roadside Lane"}},"phone":{"label":"Phone","placeholder":"+31 42 123 4567"},"piste/difficulty":{"label":"Difficulty","placeholder":"Easy, Intermediate, Advanced...","options":{"novice":"Novice (instructional)","easy":"Easy (green circle)","intermediate":"Intermediate (blue square)","advanced":"Advanced (black diamond)","expert":"Expert (double black diamond)","freeride":"Freeride (off-piste)","extreme":"Extreme (climbing equipment required)"}},"piste/grooming":{"label":"Grooming","options":{"classic":"Classic","mogul":"Mogul","backcountry":"Backcountry","classic+skating":"Classic and Skating","scooter":"Scooter/Snowmobile","skating":"Skating"}},"piste/type":{"label":"Type","options":{"downhill":"Downhill","nordic":"Nordic","skitour":"Skitour","sled":"Sled","hike":"Hike","sleigh":"Sleigh","ice_skate":"Ice Skate","snow_park":"Snow Park","playground":"Playground"}},"place":{"label":"Type"},"population":{"label":"Population"},"power":{"label":"Type"},"power_supply":{"label":"Power Supply"},"railway":{"label":"Type"},"recycling/cans":{"label":"Accepts Cans"},"recycling/clothes":{"label":"Accepts Clothes"},"recycling/glass":{"label":"Accepts Glass"},"recycling/glass_bottles":{"label":"Accepts Glass Bottles"},"recycling/paper":{"label":"Accepts Paper"},"recycling/plastic":{"label":"Accepts Plastic"},"recycling/type":{"label":"Recycling Type","options":{"container":"Container","centre":"Recycling Center"}},"ref":{"label":"Reference"},"relation":{"label":"Type"},"religion":{"label":"Religion"},"restriction":{"label":"Type"},"restrictions":{"label":"Turn Restrictions"},"route":{"label":"Type"},"route_master":{"label":"Type"},"sac_scale":{"label":"Hiking Difficulty","placeholder":"Mountain Hiking, Alpine Hiking...","options":{"hiking":"T1: Hiking","mountain_hiking":"T2: Mountain Hiking","demanding_mountain_hiking":"T3: Demanding Mountain Hiking","alpine_hiking":"T4: Alpine Hiking","demanding_alpine_hiking":"T5: Demanding Alpine Hiking","difficult_alpine_hiking":"T6: Difficult Alpine Hiking"}},"sanitary_dump_station":{"label":"Toilet Disposal"},"seasonal":{"label":"Seasonal"},"service":{"label":"Type"},"service/bicycle/chain_tool":{"label":"Chain Tool","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service/bicycle/pump":{"label":"Air Pump","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service_rail":{"label":"Service Type","options":{"spur":"Spur","yard":"Yard","siding":"Siding","crossover":"Crossover"}},"shelter":{"label":"Shelter"},"shelter_type":{"label":"Type"},"shop":{"label":"Type"},"site":{"label":"Type"},"sloped_curb":{"label":"Sloped Curb"},"smoking":{"label":"Smoking","placeholder":"No, Separated, Yes...","options":{"no":"No smoking anywhere","separated":"In smoking areas, not physically isolated","isolated":"In smoking areas, physically isolated","outside":"Allowed outside","yes":"Allowed everywhere","dedicated":"Dedicated to smokers (e.g. smokers' club)"}},"smoothness":{"label":"Smoothness","placeholder":"Thin Rollers, Wheels, Off-Road...","options":{"excellent":"Thin Rollers: rollerblade, skateboard","good":"Thin Wheels: racing bike","intermediate":"Wheels: city bike, wheelchair, scooter","bad":"Robust Wheels: trekking bike, car, rickshaw","very_bad":"High Clearance: light duty off-road vehicle","horrible":"Off-Road: heavy duty off-road vehicle","very_horrible":"Specialized off-road: tractor, ATV","impassable":"Impassable / No wheeled vehicle"}},"social_facility_for":{"label":"People served","placeholder":"Homeless, Disabled, Child, etc"},"source":{"label":"Source"},"sport":{"label":"Sport"},"sport_ice":{"label":"Sport"},"sport_racing":{"label":"Sport"},"structure":{"label":"Structure","placeholder":"Unknown","options":{"bridge":"Bridge","tunnel":"Tunnel","embankment":"Embankment","cutting":"Cutting","ford":"Ford"}},"studio_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/Unclassified Road","terms":""},"historic":{"name":"Historic Site","terms":""},"historic/archaeological_site":{"name":"Archaeological Site","terms":""},"historic/boundary_stone":{"name":"Boundary Stone","terms":""},"historic/castle":{"name":"Castle","terms":""},"historic/memorial":{"name":"Memorial","terms":""},"historic/monument":{"name":"Monument","terms":""},"historic/ruins":{"name":"Ruins","terms":""},"historic/wayside_cross":{"name":"Wayside Cross","terms":""},"historic/wayside_shrine":{"name":"Wayside Shrine","terms":""},"junction":{"name":"Junction","terms":""},"landuse":{"name":"Land Use","terms":""},"landuse/allotments":{"name":"Community Garden","terms":"allotment,garden"},"landuse/basin":{"name":"Basin","terms":""},"landuse/cemetery":{"name":"Cemetery","terms":""},"landuse/churchyard":{"name":"Churchyard","terms":""},"landuse/commercial":{"name":"Commercial Area","terms":""},"landuse/construction":{"name":"Construction","terms":""},"landuse/farm":{"name":"Farmland","terms":""},"landuse/farmland":{"name":"Farmland","terms":""},"landuse/farmyard":{"name":"Farmyard","terms":""},"landuse/forest":{"name":"Forest","terms":"tree"},"landuse/garages":{"name":"Garages","terms":""},"landuse/grass":{"name":"Grass","terms":""},"landuse/industrial":{"name":"Industrial Area","terms":""},"landuse/landfill":{"name":"Landfill","terms":"dump"},"landuse/meadow":{"name":"Meadow","terms":""},"landuse/military":{"name":"Military Area","terms":""},"landuse/orchard":{"name":"Orchard","terms":""},"landuse/plant_nursery":{"name":"Plant Nursery","terms":"vivero"},"landuse/quarry":{"name":"Quarry","terms":""},"landuse/residential":{"name":"Residential Area","terms":""},"landuse/retail":{"name":"Retail Area","terms":""},"landuse/vineyard":{"name":"Vineyard","terms":""},"leisure":{"name":"Leisure","terms":""},"leisure/adult_gaming_centre":{"name":"Adult Gaming Center","terms":"gambling,slot machine"},"leisure/bird_hide":{"name":"Bird Hide","terms":"machan,ornithology"},"leisure/bowling_alley":{"name":"Bowling Alley","terms":""},"leisure/common":{"name":"Common","terms":"open space"},"leisure/dog_park":{"name":"Dog Park","terms":""},"leisure/firepit":{"name":"Firepit","terms":"fireplace,campfire"},"leisure/garden":{"name":"Garden","terms":""},"leisure/golf_course":{"name":"Golf Course","terms":"links"},"leisure/ice_rink":{"name":"Ice Rink","terms":"hockey,skating,curling"},"leisure/marina":{"name":"Marina","terms":"boat"},"leisure/nature_reserve":{"name":"Nature Reserve","terms":"protected,wildlife"},"leisure/park":{"name":"Park","terms":"esplanade,estate,forest,garden,grass,green,grounds,lawn,lot,meadow,parkland,place,playground,plaza,pleasure garden,recreation area,square,tract,village green,woodland"},"leisure/picnic_table":{"name":"Picnic Table","terms":"bench"},"leisure/pitch":{"name":"Sport Pitch","terms":"field"},"leisure/pitch/american_football":{"name":"American Football Field","terms":""},"leisure/pitch/baseball":{"name":"Baseball Diamond","terms":""},"leisure/pitch/basketball":{"name":"Basketball Court","terms":""},"leisure/pitch/rugby_league":{"name":"Rugby League Field","terms":""},"leisure/pitch/rugby_union":{"name":"Rugby Union Field","terms":""},"leisure/pitch/skateboard":{"name":"Skate Park","terms":""},"leisure/pitch/soccer":{"name":"Soccer Field","terms":""},"leisure/pitch/tennis":{"name":"Tennis Court","terms":""},"leisure/pitch/volleyball":{"name":"Volleyball Court","terms":""},"leisure/playground":{"name":"Playground","terms":"jungle gym,play area"},"leisure/running_track":{"name":"Running Track","terms":""},"leisure/slipway":{"name":"Slipway","terms":"boat launch,boat ramp"},"leisure/sports_centre":{"name":"Sports Center / Gym","terms":"gym"},"leisure/sports_centre/swimming":{"name":"Swimming Pool Facility","terms":"dive,water"},"leisure/stadium":{"name":"Stadium","terms":""},"leisure/swimming_pool":{"name":"Swimming Pool","terms":"dive,water"},"leisure/track":{"name":"Racetrack (non-Motorsport)","terms":""},"leisure/water_park":{"name":"Water Park","terms":"swim,pool,dive"},"line":{"name":"Line","terms":""},"man_made":{"name":"Man Made","terms":""},"man_made/adit":{"name":"Adit","terms":"entrance,underground,mine,cave"},"man_made/breakwater":{"name":"Breakwater","terms":""},"man_made/chimney":{"name":"Chimney","terms":""},"man_made/cutline":{"name":"Cut line","terms":""},"man_made/embankment":{"name":"Embankment","terms":""},"man_made/flagpole":{"name":"Flagpole","terms":""},"man_made/gasometer":{"name":"Gasometer","terms":"gas holder"},"man_made/groyne":{"name":"Groyne","terms":""},"man_made/lighthouse":{"name":"Lighthouse","terms":""},"man_made/mast":{"name":"Radio Mast","terms":"broadcast tower,cell phone tower,cell tower,guyed tower,mobile phone tower,radio tower,television tower,transmission mast,transmission tower,tv tower"},"man_made/observation":{"name":"Observation Tower","terms":"lookout tower,fire tower"},"man_made/petroleum_well":{"name":"Oil Well","terms":"drilling rig,oil derrick,oil drill,oil horse,oil rig,oil pump,petroleum well,pumpjack"},"man_made/pier":{"name":"Pier","terms":"dock"},"man_made/pipeline":{"name":"Pipeline","terms":""},"man_made/silo":{"name":"Silo","terms":"grain,corn,wheat"},"man_made/storage_tank":{"name":"Storage Tank","terms":"water,oil,gas,petrol"},"man_made/surveillance":{"name":"Surveillance","terms":""},"man_made/survey_point":{"name":"Survey Point","terms":""},"man_made/tower":{"name":"Tower","terms":""},"man_made/wastewater_plant":{"name":"Wastewater Plant","terms":"sewage*,water treatment plant,reclamation plant"},"man_made/water_tower":{"name":"Water Tower","terms":""},"man_made/water_well":{"name":"Water Well","terms":""},"man_made/water_works":{"name":"Water Works","terms":""},"man_made/works":{"name":"Works","terms":"car assembly plant,aluminium processing plant,brewery,furniture manufacture factory,oil refinery,plastic recycling"},"military/airfield":{"name":"Airfield","terms":""},"military/barracks":{"name":"Barracks","terms":""},"military/bunker":{"name":"Bunker","terms":""},"military/checkpoint":{"name":"Checkpoint","terms":""},"military/danger_area":{"name":"Danger Area","terms":""},"military/naval_base":{"name":"Naval Base","terms":""},"military/obstacle_course":{"name":"Obstacle Course","terms":""},"military/range":{"name":"Military Range","terms":""},"military/training_area":{"name":"Training area","terms":""},"natural":{"name":"Natural","terms":""},"natural/bay":{"name":"Bay","terms":""},"natural/beach":{"name":"Beach","terms":""},"natural/cave_entrance":{"name":"Cave Entrance","terms":"cavern,hollow,grotto,shelter,cavity"},"natural/cliff":{"name":"Cliff","terms":""},"natural/coastline":{"name":"Coastline","terms":"shore"},"natural/fell":{"name":"Fell","terms":""},"natural/glacier":{"name":"Glacier","terms":""},"natural/grassland":{"name":"Grassland","terms":""},"natural/heath":{"name":"Heath","terms":""},"natural/peak":{"name":"Peak","terms":"acme,aiguille,alp,climax,crest,crown,hill,mount,mountain,pinnacle,summit,tip,top"},"natural/saddle":{"name":"Saddle","terms":"pass,mountain pass,top"},"natural/scree":{"name":"Scree","terms":"loose rocks"},"natural/scrub":{"name":"Scrub","terms":"bush,shrubs"},"natural/spring":{"name":"Spring","terms":""},"natural/tree":{"name":"Tree","terms":""},"natural/tree_row":{"name":"Tree row","terms":""},"natural/volcano":{"name":"Volcano","terms":"mountain,crater"},"natural/water":{"name":"Water","terms":""},"natural/water/lake":{"name":"Lake","terms":"lakelet,loch,mere"},"natural/water/pond":{"name":"Pond","terms":"lakelet,millpond,tarn,pool,mere"},"natural/water/reservoir":{"name":"Reservoir","terms":""},"natural/wetland":{"name":"Wetland","terms":""},"natural/wood":{"name":"Wood","terms":"tree"},"office":{"name":"Office","terms":""},"office/accountant":{"name":"Accountant","terms":""},"office/administrative":{"name":"Administrative Office","terms":""},"office/architect":{"name":"Architect","terms":""},"office/company":{"name":"Company Office","terms":""},"office/educational_institution":{"name":"Educational Institution","terms":""},"office/employment_agency":{"name":"Employment Agency","terms":"job"},"office/estate_agent":{"name":"Real Estate Office","terms":""},"office/financial":{"name":"Financial Office","terms":""},"office/government":{"name":"Government Office","terms":""},"office/insurance":{"name":"Insurance Office","terms":""},"office/it":{"name":"IT Office","terms":""},"office/lawyer":{"name":"Law Office","terms":""},"office/newspaper":{"name":"Newspaper","terms":""},"office/ngo":{"name":"NGO Office","terms":""},"office/physician":{"name":"Physician","terms":""},"office/political_party":{"name":"Political Party","terms":""},"office/research":{"name":"Research Office","terms":""},"office/telecommunication":{"name":"Telecom Office","terms":""},"office/therapist":{"name":"Therapist","terms":""},"office/travel_agent":{"name":"Travel Agency","terms":""},"piste":{"name":"Piste/Ski Trail","terms":"ski,sled,sleigh,snowboard,nordic,downhill,snowmobile"},"place":{"name":"Place","terms":""},"place/city":{"name":"City","terms":""},"place/farm":{"name":"Farm","terms":""},"place/hamlet":{"name":"Hamlet","terms":""},"place/island":{"name":"Island","terms":"archipelago,atoll,bar,cay,isle,islet,key,reef"},"place/isolated_dwelling":{"name":"Isolated Dwelling","terms":""},"place/locality":{"name":"Locality","terms":""},"place/neighbourhood":{"name":"Neighborhood","terms":"neighbourhood"},"place/suburb":{"name":"Borough","terms":"Boro,Quarter"},"place/town":{"name":"Town","terms":""},"place/village":{"name":"Village","terms":""},"point":{"name":"Point","terms":""},"power":{"name":"Power","terms":""},"power/generator":{"name":"Power Generator","terms":""},"power/line":{"name":"Power Line","terms":""},"power/minor_line":{"name":"Minor Power Line","terms":""},"power/pole":{"name":"Power Pole","terms":""},"power/sub_station":{"name":"Substation","terms":""},"power/substation":{"name":"Substation","terms":""},"power/tower":{"name":"High-Voltage Tower","terms":""},"power/transformer":{"name":"Transformer","terms":""},"public_transport/platform":{"name":"Platform","terms":""},"public_transport/stop_position":{"name":"Stop Position","terms":""},"railway":{"name":"Railway","terms":""},"railway/abandoned":{"name":"Abandoned Railway","terms":""},"railway/disused":{"name":"Disused Railway","terms":""},"railway/funicular":{"name":"Funicular","terms":"venicular,cliff railway,cable car,cable railway,funicular railway"},"railway/halt":{"name":"Railway Halt","terms":"break,interrupt,rest,wait,interruption"},"railway/level_crossing":{"name":"Railway Crossing","terms":"crossing,railroad crossing,level crossing,grade crossing,road through railroad,train crossing"},"railway/monorail":{"name":"Monorail","terms":""},"railway/narrow_gauge":{"name":"Narrow Gauge Rail","terms":"narrow gauge railway,narrow gauge railroad"},"railway/platform":{"name":"Railway Platform","terms":""},"railway/rail":{"name":"Rail","terms":""},"railway/station":{"name":"Railway Station","terms":"train station,station"},"railway/subway":{"name":"Subway","terms":""},"railway/subway_entrance":{"name":"Subway Entrance","terms":""},"railway/tram":{"name":"Tram","terms":"streetcar"},"relation":{"name":"Relation","terms":""},"roundabout":{"name":"Roundabout","terms":""},"route/ferry":{"name":"Ferry Route","terms":""},"shop":{"name":"Shop","terms":""},"shop/alcohol":{"name":"Liquor Store","terms":"alcohol,beer,booze,wine"},"shop/anime":{"name":"Anime Shop","terms":""},"shop/antiques":{"name":"Antiques Shop","terms":""},"shop/art":{"name":"Art Store","terms":"art*,exhibit*,gallery"},"shop/baby_goods":{"name":"Baby Goods Store","terms":""},"shop/bag":{"name":"Bag/Luggage Store","terms":"handbag,purse"},"shop/bakery":{"name":"Bakery","terms":""},"shop/bathroom_furnishing":{"name":"Bathroom Furnishing Store","terms":""},"shop/beauty":{"name":"Beauty Shop","terms":"nail spa,spa,salon,tanning"},"shop/bed":{"name":"Bedding/Mattress Store","terms":""},"shop/beverages":{"name":"Beverage Store","terms":""},"shop/bicycle":{"name":"Bicycle Shop","terms":"bike,repair"},"shop/bookmaker":{"name":"Bookmaker","terms":""},"shop/books":{"name":"Book Store","terms":""},"shop/boutique":{"name":"Boutique","terms":""},"shop/butcher":{"name":"Butcher","terms":"meat"},"shop/candles":{"name":"Candle Shop","terms":""},"shop/car":{"name":"Car Dealership","terms":"auto"},"shop/car_parts":{"name":"Car Parts Store","terms":"auto"},"shop/car_repair":{"name":"Car Repair Shop","terms":"auto"},"shop/carpet":{"name":"Carpet Store","terms":"rug"},"shop/cheese":{"name":"Cheese Store","terms":""},"shop/chemist":{"name":"Chemist","terms":""},"shop/chocolate":{"name":"Chocolate Store","terms":""},"shop/clothes":{"name":"Clothing Store","terms":""},"shop/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":"Wet Dock / Dry Dock","terms":"boat,ship,vessel,marine"},"waterway/drain":{"name":"Drain","terms":""},"waterway/fuel":{"name":"Marine Fuel Station","terms":"petrol,gas,diesel,boat"},"waterway/river":{"name":"River","terms":"beck,branch,brook,course,creek,estuary,rill,rivulet,run,runnel,stream,tributary,watercourse"},"waterway/riverbank":{"name":"Riverbank","terms":""},"waterway/sanitary_dump_station":{"name":"Marine Toilet Disposal","terms":"Boat,Watercraft,Sanitary,Dump Station,Pumpout,Pump out,Elsan,CDP,CTDP,Chemical Toilet"},"waterway/stream":{"name":"Stream","terms":"beck,branch,brook,burn,course,creek,current,drift,flood,flow,freshet,race,rill,rindle,rivulet,run,runnel,rush,spate,spritz,surge,tide,torrent,tributary,watercourse"},"waterway/water_point":{"name":"Marine Drinking Water","terms":""},"waterway/weir":{"name":"Weir","terms":""}}}},"suggestions":{"amenity":{"fuel":{"76":{"count":314},"Neste":{"count":189},"BP":{"count":2511},"Shell":{"count":8380},"Agip":{"count":2651},"Migrol":{"count":65},"Avia":{"count":897},"Texaco":{"count":680},"Total":{"count":2607},"Statoil":{"count":596},"Esso":{"count":3652},"Jet":{"count":441},"Avanti":{"count":90},"Sainsbury's":{"count":58},"OMV":{"count":701},"Aral":{"count":1339},"Tesco":{"count":197},"JET":{"count":180},"Morrisons":{"count":111},"United":{"count":91},"Canadian Tire":{"count":66},"Mobil":{"count":613},"Caltex":{"count":1001},"Sunoco":{"count":355},"Q8":{"count":1161},"ABC":{"count":79},"ARAL":{"count":375},"CEPSA":{"count":1018},"BFT":{"count":89},"Petron":{"count":878},"Intermarché":{"count":434},"Total Access":{"count":51},"Super U":{"count":124},"Auchan":{"count":53},"Elf":{"count":129},"Carrefour":{"count":205},"Station Service E. Leclerc":{"count":530},"Shell Express":{"count":131},"Hess":{"count":127},"Flying V":{"count":129},"bft":{"count":168},"Gulf":{"count":199},"PTT":{"count":191},"St1":{"count":100},"Teboil":{"count":115},"HEM":{"count":212},"GALP":{"count":626},"OK":{"count":163},"ÖMV":{"count":101},"Tinq":{"count":215},"OKQ8":{"count":186},"Repsol":{"count":424},"Westfalen":{"count":96},"Esso Express":{"count":98},"Raiffeisenbank":{"count":117},"Tamoil":{"count":866},"Engen":{"count":241},"Sasol":{"count":59},"Topaz":{"count":78},"LPG":{"count":174},"Coop":{"count":62},"Orlen":{"count":598},"Oilibya":{"count":68},"Tango":{"count":122},"Star":{"count":319},"Петрол":{"count":84},"Cepsa":{"count":96},"OIL!":{"count":63},"Ultramar":{"count":125},"Irving":{"count":87},"Lukoil":{"count":701},"Petro-Canada":{"count":489},"7-Eleven":{"count":488},"Agrola":{"count":69},"Husky":{"count":126},"Slovnaft":{"count":219},"Sheetz":{"count":134},"Mol":{"count":61},"Petronas":{"count":159},"Газпромнефть":{"count":748},"Лукойл":{"count":1477},"Elan":{"count":112},"Роснефть":{"count":638},"Turmöl":{"count":57},"Neste A24":{"count":55},"Marathon":{"count":189},"Valero":{"count":366},"Eni":{"count":236},"Chevron":{"count":954},"ТНК":{"count":520},"REPSOL":{"count":1603},"MOL":{"count":228},"Bliska":{"count":150},"Api":{"count":302},"Arco":{"count":179},"Pemex":{"count":423},"Exxon":{"count":506},"Coles Express":{"count":115},"Petrom":{"count":259},"PETRONOR":{"count":207},"Rompetrol":{"count":174},"Lotos":{"count":178},"ОМВ":{"count":60},"BR":{"count":129},"Copec":{"count":505},"Petrobras":{"count":270},"Liberty":{"count":55},"IP":{"count":871},"Erg":{"count":596},"Eneos":{"count":97},"Citgo":{"count":279},"Metano":{"count":208},"Сургутнефтегаз":{"count":61},"EKO":{"count":59},"Eko":{"count":58},"Indipend.":{"count":172},"IES":{"count":63},"TotalErg":{"count":89},"Cenex":{"count":115},"ПТК":{"count":82},"HP":{"count":79},"Phillips 66":{"count":216},"CARREFOUR":{"count":74},"ERG":{"count":76},"Speedway":{"count":148},"Benzina":{"count":96},"Татнефть":{"count":264},"Terpel":{"count":259},"WOG":{"count":189},"Seaoil":{"count":54},"АЗС":{"count":1077},"Kwik Trip":{"count":108},"Wawa":{"count":89},"Pertamina":{"count":186},"COSMO":{"count":64},"Z":{"count":76},"Indian Oil":{"count":183},"АГЗС":{"count":494},"INA":{"count":121},"JOMO":{"count":62},"Holiday":{"count":97},"YPF":{"count":70},"IDEMITSU":{"count":87},"ENEOS":{"count":736},"Bharat Petroleum":{"count":64},"CAMPSA":{"count":615},"Casey's General Store":{"count":190},"Башнефть":{"count":60},"Kangaroo":{"count":60},"コスモ石油 (COSMO)":{"count":136},"MEROIL":{"count":77},"1-2-3":{"count":71},"出光":{"count":228,"tags":{"name:en":"IDEMITSU"}},"НК Альянс":{"count":88},"Sinclair":{"count":100},"Conoco":{"count":189},"SPBU":{"count":54},"Макпетрол":{"count":109},"Circle K":{"count":166},"Posto Ipiranga":{"count":70},"Posto Shell":{"count":54},"Phoenix":{"count":144},"Ipiranga":{"count":119},"OKKO":{"count":85},"ОККО":{"count":119},"บางจาก":{"count":60},"QuikTrip":{"count":105},"Stewart's":{"count":63},"Posto BR":{"count":68},"ป ต ท":{"count":152},"ปตท":{"count":88},"ANP":{"count":80},"Kum & Go":{"count":80},"Petrolimex":{"count":55},"Sokimex":{"count":66},"Tela":{"count":154},"Posto":{"count":71},"H-E-B":{"count":182},"Укрнафта":{"count":58},"Татнефтепродукт":{"count":54},"Afriquia":{"count":88},"Murphy USA":{"count":67},"昭和シェル (Showa-shell)":{"count":94},"エネオス":{"count":53},"CNG":{"count":94}},"pub":{"Kings Arms":{"count":67},"The Ship":{"count":89},"The White Horse":{"count":204},"The White Hart":{"count":226},"Royal Oak":{"count":150},"The Red Lion":{"count":233},"The Kings Arms":{"count":58},"The Star":{"count":73},"The Anchor":{"count":64},"The Cross Keys":{"count":55},"The Wheatsheaf":{"count":117},"The Crown Inn":{"count":67},"The Kings Head":{"count":53},"The Castle":{"count":62},"The Railway":{"count":102},"The White Lion":{"count":118},"The Bell":{"count":121},"The Bull":{"count":68},"The Plough":{"count":179},"The George":{"count":110},"The Royal Oak":{"count":209},"The Fox":{"count":74},"Prince of Wales":{"count":77},"The Rising Sun":{"count":71},"The Prince of Wales":{"count":51},"The Crown":{"count":244},"The Chequers":{"count":66},"The Swan":{"count":152},"Rose and Crown":{"count":79},"The Victoria":{"count":67},"New Inn":{"count":90},"Royal Hotel":{"count":57},"Red Lion":{"count":207},"Cross Keys":{"count":61},"The Greyhound":{"count":96},"The Black Horse":{"count":94},"The New Inn":{"count":105},"Kings Head":{"count":59},"The Albion":{"count":51},"The Angel":{"count":52},"The Queens Head":{"count":52},"The Ship Inn":{"count":83},"Rose & Crown":{"count":51},"Queens Head":{"count":52},"Irish Pub":{"count":76}},"fast_food":{"Quick":{"count":484},"McDonald's":{"count":12376,"tags":{"cuisine":"burger"}},"Subway":{"count":5576,"tags":{"cuisine":"sandwich"}},"Burger King":{"count":3734,"tags":{"cuisine":"burger"}},"Ali Baba":{"count":61},"Hungry Jacks":{"count":173,"tags":{"cuisine":"burger"}},"Red Rooster":{"count":148},"KFC":{"count":3198,"tags":{"cuisine":"chicken"}},"Domino's Pizza":{"count":985,"tags":{"cuisine":"pizza"}},"Chowking":{"count":142},"Jollibee":{"count":396},"Hesburger":{"count":102},"肯德基":{"count":86},"Wendy's":{"count":1621,"tags":{"cuisine":"burger"}},"Tim Hortons":{"count":323},"Steers":{"count":151},"Hardee's":{"count":268,"tags":{"cuisine":"burger"}},"Arby's":{"count":782},"A&W":{"count":283},"Dairy Queen":{"count":791},"Hallo Pizza":{"count":76},"Fish & Chips":{"count":93},"Harvey's":{"count":90},"麥當勞":{"count":65},"Pizza Pizza":{"count":215},"Kotipizza":{"count":74},"Jack in the Box":{"count":546,"tags":{"cuisine":"burger"}},"Istanbul":{"count":56},"Kochlöffel":{"count":68},"Döner":{"count":228},"Telepizza":{"count":201},"Sibylla":{"count":61},"Carl's Jr.":{"count":298,"tags":{"cuisine":"burger"}},"Quiznos":{"count":266,"tags":{"cuisine":"sandwich"}},"Wimpy":{"count":141},"Sonic":{"count":566,"tags":{"cuisine":"burger"}},"Taco Bell":{"count":1423,"tags":{"cuisine":"mexican"}},"Pizza Nova":{"count":63},"Papa John's":{"count":304,"tags":{"cuisine":"pizza"}},"Nordsee":{"count":159},"Mr. Sub":{"count":103},"Макдоналдс":{"count":324,"tags":{"name:en":"McDonald's"}},"Asia Imbiss":{"count":111},"Chipotle":{"count":290,"tags":{"cuisine":"mexican"}},"マクドナルド":{"count":692,"tags":{"name:en":"McDonald's","cuisine":"burger"}},"In-N-Out Burger":{"count":65},"Jimmy John's":{"count":141},"Jamba Juice":{"count":68},"Робин Сдобин":{"count":82},"Baskin Robbins":{"count":74},"ケンタッキーフライドチキン":{"count":164,"tags":{"name:en":"KFC","cuisine":"chicken"}},"吉野家":{"count":191},"Taco Time":{"count":88},"松屋":{"count":281,"tags":{"name:en":"Matsuya"}},"Little Caesars":{"count":81},"El Pollo Loco":{"count":63},"Del Taco":{"count":141},"White Castle":{"count":80},"Boston Market":{"count":66},"Chick-fil-A":{"count":257,"tags":{"cuisine":"chicken"}},"Panda Express":{"count":238,"tags":{"cuisine":"chinese"}},"Whataburger":{"count":364},"Taco John's":{"count":78},"Теремок":{"count":68},"Culver's":{"count":425},"Five Guys":{"count":141},"Church's Chicken":{"count":95},"Popeye's":{"count":167,"tags":{"cuisine":"chicken"}},"Long John Silver's":{"count":93},"Pollo Campero":{"count":62},"Zaxby's":{"count":51},"すき家":{"count":276,"tags":{"name:en":"SUKIYA"}},"モスバーガー":{"count":257,"tags":{"name:en":"MOS BURGER"}},"Русский Аппетит":{"count":69},"なか卯":{"count":63}},"restaurant":{"Pizza Hut":{"count":1180,"tags":{"cuisine":"pizza"}},"Little Chef":{"count":64},"Adler":{"count":158},"Zur Krone":{"count":90},"Deutsches Haus":{"count":90},"Krone":{"count":171},"Akropolis":{"count":152},"Schützenhaus":{"count":124},"Kreuz":{"count":74},"Waldschänke":{"count":55},"La Piazza":{"count":69},"Lamm":{"count":66},"Zur Sonne":{"count":73},"Zur Linde":{"count":204},"Poseidon":{"count":110},"Shanghai":{"count":82},"Red Lobster":{"count":235},"Zum Löwen":{"count":84},"Swiss Chalet":{"count":107},"Olympia":{"count":74},"Wagamama":{"count":64},"Frankie & Benny's":{"count":66},"Hooters":{"count":103},"Sternen":{"count":78},"Hirschen":{"count":79},"Denny's":{"count":450},"Athen":{"count":68},"Sonne":{"count":126},"Hirsch":{"count":79},"Ratskeller":{"count":150},"La Cantina":{"count":56},"Gasthaus Krone":{"count":56},"El Greco":{"count":86},"Gasthof zur Post":{"count":79},"Nando's":{"count":246},"Löwen":{"count":112},"La Pataterie":{"count":51},"Bella Napoli":{"count":53},"Pizza Express":{"count":262},"Mandarin":{"count":65},"Hong Kong":{"count":83},"Zizzi":{"count":68},"Cracker Barrel":{"count":183},"Rhodos":{"count":81},"Lindenhof":{"count":79},"Milano":{"count":54},"Dolce Vita":{"count":77},"Kirchenwirt":{"count":81},"Kantine":{"count":52},"Ochsen":{"count":95},"Spur":{"count":62},"Mykonos":{"count":59},"Lotus":{"count":66},"Applebee's":{"count":531},"Flunch":{"count":72},"Zur Post":{"count":116},"China Town":{"count":76},"La Dolce Vita":{"count":73},"Waffle House":{"count":207},"Delphi":{"count":88},"Linde":{"count":103},"Outback Steakhouse":{"count":218},"Dionysos":{"count":69},"Kelsey's":{"count":57},"Boston Pizza":{"count":165},"Bella Italia":{"count":132},"Sizzler":{"count":53},"Grüner Baum":{"count":116},"Taj Mahal":{"count":104},"Rössli":{"count":68},"Wimpy":{"count":51},"Traube":{"count":65},"Adria":{"count":52},"Red Robin":{"count":185},"Roma":{"count":61},"San Marco":{"count":67},"Hellas":{"count":55},"La Perla":{"count":67},"Vips":{"count":53},"Panera Bread":{"count":218},"Da Vinci":{"count":54},"Hippopotamus":{"count":96},"Prezzo":{"count":75},"Courtepaille":{"count":106},"Hard Rock Cafe":{"count":70},"Panorama":{"count":61},"デニーズ":{"count":82},"Sportheim":{"count":65},"餃子の王将":{"count":57},"Bären":{"count":60},"Alte Post":{"count":60},"Pizzeria Roma":{"count":51},"China Garden":{"count":66},"Vapiano":{"count":82},"Mamma Mia":{"count":64},"Schwarzer Adler":{"count":57},"IHOP":{"count":317},"Chili's":{"count":328},"Asia":{"count":51},"Olive Garden":{"count":279},"TGI Friday's":{"count":159},"Friendly's":{"count":78},"Buffalo Grill":{"count":202},"Texas Roadhouse":{"count":110},"ガスト":{"count":230,"tags":{"name:en":"Gusto"}},"Sakura":{"count":75},"Mensa":{"count":99},"The Keg":{"count":53},"サイゼリヤ":{"count":93},"La Strada":{"count":52},"Village Inn":{"count":92},"Buffalo Wild Wings":{"count":176},"Peking":{"count":59},"Boston Market":{"count":61},"Round Table Pizza":{"count":53},"Jimmy John's":{"count":69},"California Pizza Kitchen":{"count":61},"Якитория":{"count":77},"Golden Corral":{"count":101},"Perkins":{"count":105},"Ruby Tuesday":{"count":162},"Shari's":{"count":65},"Bob Evans":{"count":129},"바다횟집 (Bada Fish Restaurant)":{"count":55},"Mang Inasal":{"count":84},"Евразия":{"count":102},"ジョナサン":{"count":59},"Longhorn Steakhouse":{"count":66}},"bank":{"Chase":{"count":721},"Commonwealth Bank":{"count":232},"Citibank":{"count":277},"HSBC":{"count":1102},"Barclays":{"count":965},"Westpac":{"count":208},"NAB":{"count":131},"ANZ":{"count":218},"Lloyds Bank":{"count":547},"Landbank":{"count":81},"Sparkasse":{"count":4555},"UCPB":{"count":92},"PNB":{"count":244},"Metrobank":{"count":269},"BDO":{"count":290},"Volksbank":{"count":2591},"BPI":{"count":415},"Postbank":{"count":443},"NatWest":{"count":628},"Raiffeisenbank":{"count":2119},"Yorkshire Bank":{"count":63},"ABSA":{"count":95},"Standard Bank":{"count":109},"FNB":{"count":97},"Deutsche Bank":{"count":855},"SEB":{"count":133},"Commerzbank":{"count":806},"Targobank":{"count":166},"ABN AMRO":{"count":130},"Handelsbanken":{"count":184},"Swedbank":{"count":223},"Kreissparkasse":{"count":600},"UniCredit Bank":{"count":408},"Monte dei Paschi di Siena":{"count":132},"Caja Rural":{"count":99},"Dresdner Bank":{"count":66},"Sparda-Bank":{"count":320},"VÚB":{"count":107},"Slovenská sporiteľňa":{"count":134},"Bank of Montreal":{"count":118},"KBC":{"count":203},"Royal Bank of Scotland":{"count":111},"TSB":{"count":80},"US Bank":{"count":256},"HypoVereinsbank":{"count":561},"Bank Austria":{"count":176},"ING":{"count":496},"Erste Bank":{"count":180},"CIBC":{"count":326},"Scotiabank":{"count":413},"Caisse d'Épargne":{"count":882},"Santander":{"count":1323},"Bank of Scotland":{"count":89},"TD Canada Trust":{"count":450},"BMO":{"count":169},"Danske Bank":{"count":131},"OTP":{"count":192},"Crédit Agricole":{"count":1239},"LCL":{"count":553},"VR-Bank":{"count":430},"ČSOB":{"count":160},"Česká spořitelna":{"count":212},"BNP":{"count":112},"Royal Bank":{"count":65},"Nationwide":{"count":209},"Halifax":{"count":225},"BAWAG PSK":{"count":102},"National Bank":{"count":84},"Nedbank":{"count":80},"First National Bank":{"count":85},"Nordea":{"count":319},"Rabobank":{"count":609},"Sparkasse KölnBonn":{"count":69},"Tatra banka":{"count":67},"Berliner Sparkasse":{"count":62},"Berliner Volksbank":{"count":77},"Wells Fargo":{"count":874},"Credit Suisse":{"count":71},"Société Générale":{"count":634},"Osuuspankki":{"count":75},"Sparkasse Aachen":{"count":56},"Hamburger Sparkasse":{"count":156},"Cassa di Risparmio del Veneto":{"count":68},"BNP Paribas":{"count":617},"Banque Populaire":{"count":433},"BNP Paribas Fortis":{"count":209},"Banco Popular":{"count":291},"Bancaja":{"count":55},"Banesto":{"count":208},"La Caixa":{"count":583},"Santander Consumer Bank":{"count":88},"BRD":{"count":191},"BCR":{"count":143},"Banca Transilvania":{"count":141},"BW-Bank":{"count":97},"Komerční banka":{"count":132},"Banco Pastor":{"count":64},"Stadtsparkasse":{"count":86},"Ulster Bank":{"count":86},"Sberbank":{"count":58},"CIC":{"count":427},"Bancpost":{"count":56},"Caja Madrid":{"count":115},"Maybank":{"count":94},"中国银行":{"count":85},"Unicredit Banca":{"count":243},"Crédit Mutuel":{"count":690},"BBVA":{"count":647},"Intesa San Paolo":{"count":69},"TD Bank":{"count":206},"Belfius":{"count":231},"Bank of America":{"count":924},"RBC":{"count":230},"Alpha Bank":{"count":123},"Сбербанк":{"count":4794},"Россельхозбанк":{"count":201},"Crédit du Nord":{"count":96},"BancoEstado":{"count":80},"Millennium Bank":{"count":414},"State Bank of India":{"count":151},"Беларусбанк":{"count":242},"ING Bank Śląski":{"count":67},"Caixa Geral de Depósitos":{"count":129},"Kreissparkasse Köln":{"count":65},"Banco BCI":{"count":51},"Banco de Chile":{"count":98},"ВТБ24":{"count":326},"UBS":{"count":134},"PKO BP":{"count":265},"Chinabank":{"count":55},"PSBank":{"count":59},"Union Bank":{"count":124},"China Bank":{"count":66},"RCBC":{"count":122},"Unicaja":{"count":83},"BBK":{"count":79},"Ibercaja":{"count":69},"RBS":{"count":143},"Commercial Bank of Ceylon PLC":{"count":79},"Bank of Ireland":{"count":109},"BNL":{"count":87},"Banco Santander":{"count":138},"Banco Itaú":{"count":111},"AIB":{"count":72},"BZ WBK":{"count":77},"Banco do Brasil":{"count":557},"Caixa Econômica Federal":{"count":184},"Fifth Third Bank":{"count":84},"Banca Popolare di Vicenza":{"count":81},"Wachovia":{"count":58},"OLB":{"count":53},"みずほ銀行":{"count":78},"BES":{"count":72},"ICICI Bank":{"count":91},"HDFC Bank":{"count":91},"La Banque Postale":{"count":67},"Pekao SA":{"count":56},"Oberbank":{"count":90},"Bradesco":{"count":295},"Oldenburgische Landesbank":{"count":56},"Bendigo Bank":{"count":93},"Argenta":{"count":86},"AXA":{"count":68},"Axis Bank":{"count":61},"Banco Nación":{"count":67},"GE Money Bank":{"count":72},"Альфа-Банк":{"count":185},"Белагропромбанк":{"count":70},"Caja Círculo":{"count":65},"Banco Galicia":{"count":51},"Eurobank":{"count":97},"Banca Intesa":{"count":62},"Canara Bank":{"count":92},"Cajamar":{"count":77},"Banamex":{"count":149},"Crédit Mutuel de Bretagne":{"count":335},"Davivienda":{"count":83},"Bank Spółdzielczy":{"count":159},"Credit Agricole":{"count":157},"Bankinter":{"count":59},"Banque Nationale":{"count":63},"Bank of the West":{"count":96},"Key Bank":{"count":155},"Western Union":{"count":88},"Citizens Bank":{"count":115},"ПриватБанк":{"count":513},"Security Bank":{"count":78},"Millenium":{"count":60},"Bankia":{"count":149},"三菱東京UFJ銀行":{"count":159},"Caixa":{"count":117},"Banco de Costa Rica":{"count":63},"SunTrust Bank":{"count":73},"Itaú":{"count":338},"PBZ":{"count":52},"中国工商银行":{"count":51},"Bancolombia":{"count":89},"Райффайзен Банк Аваль":{"count":64},"Bancomer":{"count":115},"Banorte":{"count":80},"Alior Bank":{"count":81},"BOC":{"count":51},"Банк Москвы":{"count":118},"ВТБ":{"count":59},"Getin Bank":{"count":55},"Caja Duero":{"count":57},"Regions Bank":{"count":62},"Росбанк":{"count":177},"Banco Estado":{"count":72},"BCI":{"count":68},"SunTrust":{"count":68},"PNC Bank":{"count":254},"신한은행":{"count":217,"tags":{"name:en":"Sinhan Bank"}},"우리은행":{"count":291,"tags":{"name:en":"Uri Bank"}},"국민은행":{"count":165,"tags":{"name:en":"Gungmin Bank"}},"중소기업은행":{"count":52,"tags":{"name:en":"Industrial Bank of Korea"}},"광주은행":{"count":51,"tags":{"name:en":"Gwangju Bank"}},"Газпромбанк":{"count":100},"M&T Bank":{"count":92},"Caja de Burgos":{"count":51},"Santander Totta":{"count":69},"УкрСиббанк":{"count":192},"Ощадбанк":{"count":364},"Уралсиб":{"count":85},"りそな銀行":{"count":225,"tags":{"name:en":"Mizuho Bank"}},"Ecobank":{"count":66},"Cajero Automatico Bancared":{"count":145},"Промсвязьбанк":{"count":93},"三井住友銀行":{"count":129},"Banco Provincia":{"count":67},"BB&T":{"count":147},"Возрождение":{"count":59},"Capital One":{"count":59},"横浜銀行":{"count":51},"Bank Mandiri":{"count":62},"Banco de la Nación":{"count":92},"Banco G&T Continental":{"count":62},"Peoples Bank":{"count":60},"工商银行":{"count":51},"Совкомбанк":{"count":55},"Provincial":{"count":56},"Banco de Desarrollo Banrural":{"count":73},"Banco Bradesco":{"count":65},"Bicentenario":{"count":182},"ლიბერთი ბანკი":{"count":54,"tags":{"name:en":"Liberty Bank"}},"Banesco":{"count":108},"Mercantil":{"count":75},"Bank BRI":{"count":53},"Del Tesoro":{"count":91},"하나은행":{"count":77},"CityCommerce Bank":{"count":71},"De Venezuela":{"count":117}},"car_rental":{"Europcar":{"count":291},"Budget":{"count":92},"Sixt":{"count":161},"Avis":{"count":282},"Hertz":{"count":293},"Enterprise":{"count":199},"stadtmobil CarSharing-Station":{"count":148}},"pharmacy":{"Rowlands Pharmacy":{"count":71},"Boots":{"count":840},"Marien-Apotheke":{"count":314},"Mercury Drug":{"count":426},"Löwen-Apotheke":{"count":356},"Superdrug":{"count":117},"Sonnen-Apotheke":{"count":311},"Rathaus-Apotheke":{"count":132},"Engel-Apotheke":{"count":123},"Hirsch-Apotheke":{"count":83},"Stern-Apotheke":{"count":67},"Lloyds Pharmacy":{"count":295},"Rosen-Apotheke":{"count":208},"Stadt-Apotheke":{"count":302},"Markt-Apotheke":{"count":164},"Аптека":{"count":1989},"Pharmasave":{"count":64},"Brunnen-Apotheke":{"count":53},"Shoppers Drug Mart":{"count":430},"Apotheke am Markt":{"count":60},"Alte Apotheke":{"count":88},"Neue Apotheke":{"count":109},"Gintarinė vaistinė":{"count":101},"Rats-Apotheke":{"count":84},"Adler Apotheke":{"count":313},"Pharmacie Centrale":{"count":64},"Walgreens":{"count":1619},"Rite Aid":{"count":745},"Apotheke":{"count":165},"Linden-Apotheke":{"count":211},"Bahnhof-Apotheke":{"count":66},"Burg-Apotheke":{"count":55},"Jean Coutu":{"count":62},"Pharmaprix":{"count":60},"Farmacias Ahumada":{"count":104},"Farmacia Comunale":{"count":113},"Farmacias Cruz Verde":{"count":86},"Cruz Verde":{"count":99},"Hubertus Apotheke":{"count":52},"CVS":{"count":1560},"Farmacias SalcoBrand":{"count":133},"Фармация":{"count":120},"Bären-Apotheke":{"count":74},"Clicks":{"count":113},"セイジョー":{"count":53},"マツモトキヨシ":{"count":115},"Dr. Max":{"count":51},"Вита":{"count":106},"Радуга":{"count":70},"サンドラッグ":{"count":61},"Apteka":{"count":366},"Первая помощь":{"count":74},"Ригла":{"count":113},"Имплозия":{"count":63},"Kinney Drugs":{"count":68},"Классика":{"count":67},"Ljekarna":{"count":53},"SalcoBrand":{"count":88},"Аптека 36,6":{"count":224},"Фармакор":{"count":75},"スギ薬局":{"count":84},"Аптечный пункт":{"count":148},"Невис":{"count":60},"トモズ (Tomod's)":{"count":83},"Eurovaistinė":{"count":65},"Farmacity":{"count":68},"аптека":{"count":96},"The Generics Pharmacy":{"count":95},"Farmatodo":{"count":123},"Duane Reade":{"count":61},"H-E-B":{"count":262},"Фармленд":{"count":82},"ドラッグてらしま (Drug Terashima)":{"count":96},"Арніка":{"count":125},"ავერსი (Aversi)":{"count":62},"Farmahorro":{"count":58}},"cafe":{"Starbucks":{"count":4238,"tags":{"cuisine":"coffee_shop"}},"Cafeteria":{"count":115},"Costa":{"count":618},"Caffè Nero":{"count":169},"Кафе":{"count":226},"Café Central":{"count":61},"Second Cup":{"count":193},"Dunkin Donuts":{"count":428,"tags":{"cuisine":"donut"}},"Espresso House":{"count":53},"Segafredo":{"count":69},"Coffee Time":{"count":94},"Cafe Coffee Day":{"count":120},"Eiscafe Venezia":{"count":180},"スターバックス":{"count":251,"tags":{"name:en":"Starbucks"}},"Шоколадница":{"count":145},"Pret A Manger":{"count":119},"Столовая":{"count":391},"Jamba Juice":{"count":53},"ドトール":{"count":164,"tags":{"name:en":"DOUTOR"}},"Tchibo":{"count":100},"Кофе Хауз":{"count":104},"Caribou Coffee":{"count":100},"Уют":{"count":51},"Шашлычная":{"count":58},"คาเฟ่ อเมซอน":{"count":62},"Traveler's Coffee":{"count":60},"カフェ・ド・クリエ":{"count":67,"tags":{"name:en":"Cafe de CRIE"}},"Cafe Amazon":{"count":65}}},"shop":{"supermarket":{"Budgens":{"count":88},"Morrisons":{"count":411},"Interspar":{"count":142},"Merkur":{"count":107},"Sainsbury's":{"count":547},"Lidl":{"count":7130},"Edeka":{"count":2293},"Coles":{"count":400},"Iceland":{"count":315},"Coop":{"count":2100},"Tesco":{"count":1297},"Woolworths":{"count":541},"Zielpunkt":{"count":239},"Nahkauf":{"count":170},"Billa":{"count":1432},"Kaufland":{"count":1004},"Plus":{"count":120},"ALDI":{"count":5172},"Checkers":{"count":128},"Tesco Metro":{"count":137},"NP":{"count":153},"Penny":{"count":1759},"Norma":{"count":1068},"Asda":{"count":225},"Netto":{"count":4379},"Rewe":{"count":2645},"Aldi Süd":{"count":594},"Real":{"count":246},"Tesco Express":{"count":406},"King Soopers":{"count":72},"Kiwi":{"count":167},"Pick n Pay":{"count":241},"ICA":{"count":192},"Tengelmann":{"count":188},"Carrefour":{"count":1640},"Waitrose":{"count":258},"Spar":{"count":2386},"Hofer":{"count":442},"M-Preis":{"count":76},"tegut":{"count":210},"Sainsbury's Local":{"count":118},"E-Center":{"count":66},"Aldi Nord":{"count":210},"nahkauf":{"count":84},"Meijer":{"count":76},"Safeway":{"count":410},"Costco":{"count":152},"Albert":{"count":185},"Jumbo":{"count":194},"Shoprite":{"count":244},"MPreis":{"count":54},"Penny Market":{"count":429},"Tesco Extra":{"count":123},"Albert Heijn":{"count":476},"IGA":{"count":363},"Super U":{"count":488},"Metro":{"count":260},"Neukauf":{"count":77},"Migros":{"count":459},"Marktkauf":{"count":121},"Delikatesy Centrum":{"count":59},"C1000":{"count":307},"Hoogvliet":{"count":53},"Food Basics":{"count":75},"Casino":{"count":264},"Penny Markt":{"count":466},"Giant":{"count":191},"COOP Jednota":{"count":73},"Rema 1000":{"count":368},"Kaufpark":{"count":96},"ALDI SÜD":{"count":113},"Simply Market":{"count":330},"Konzum":{"count":230},"Carrefour Express":{"count":353},"Eurospar":{"count":270},"Mercator":{"count":125},"Famila":{"count":130},"Hemköp":{"count":82},"real,-":{"count":81},"Markant":{"count":88},"Volg":{"count":135},"Leader Price":{"count":267},"Treff 3000":{"count":94},"SuperBrugsen":{"count":67},"Kaiser's":{"count":256},"K+K":{"count":106},"Unimarkt":{"count":86},"Carrefour City":{"count":126},"Sobeys":{"count":122},"S-Market":{"count":109},"Combi":{"count":55},"Denner":{"count":276},"Konsum":{"count":133},"Franprix":{"count":312},"Monoprix":{"count":198},"Diska":{"count":69},"PENNY":{"count":79},"Dia":{"count":835},"Giant Eagle":{"count":85},"NORMA":{"count":115},"AD Delhaize":{"count":63},"Auchan":{"count":152},"Mercadona":{"count":769},"Consum":{"count":130},"Carrefour Market":{"count":80},"Whole Foods":{"count":210,"tags":{"shop":"supermarket"}},"Pam":{"count":56},"sky":{"count":105},"Despar":{"count":146},"Eroski":{"count":208},"Costcutter":{"count":63},"Maxi":{"count":108},"Colruyt":{"count":180},"The Co-operative":{"count":64},"Intermarché":{"count":1210},"Delhaize":{"count":207},"CBA":{"count":176},"Shopi":{"count":53},"Walmart":{"count":644},"Kroger":{"count":317},"Albertsons":{"count":242},"Trader Joe's":{"count":235},"Feneberg":{"count":58},"denn's Biomarkt":{"count":52},"dm":{"count":114},"Kvickly":{"count":55},"Makro":{"count":140},"Dico":{"count":53},"Nah & Frisch":{"count":73},"Champion":{"count":59},"ICA Supermarket":{"count":51},"Fakta":{"count":235},"Магнит":{"count":1760},"Caprabo":{"count":103},"Famiglia Cooperativa":{"count":64},"Народная 7Я семьЯ":{"count":154},"Esselunga":{"count":85},"Maxima":{"count":102},"Petit Casino":{"count":111},"Wasgau":{"count":60},"Pingo Doce":{"count":253},"Match":{"count":140},"Profi":{"count":60},"Lider":{"count":65},"Unimarc":{"count":177},"The Co-operative Food":{"count":190},"Santa Isabel":{"count":128},"Седьмой континент":{"count":79},"HIT":{"count":59},"Rimi":{"count":106},"Conad":{"count":304},"Фуршет":{"count":76},"Willys":{"count":56},"Farmfoods":{"count":64},"U Express":{"count":51},"Фора":{"count":52},"Dunnes Stores":{"count":73},"Сільпо":{"count":125},"マルエツ":{"count":59},"Piggly Wiggly":{"count":57},"Crai":{"count":54},"El Árbol":{"count":73},"Centre Commercial E. Leclerc":{"count":549},"Foodland":{"count":100},"Super Brugsen":{"count":67},"Дикси":{"count":683},"Пятёрочка":{"count":1344},"Publix":{"count":339},"Føtex":{"count":66},"coop":{"count":73},"Fressnapf":{"count":69},"Coop Konsum":{"count":79},"Carrefour Contact":{"count":83},"No Frills":{"count":105},"Plodine":{"count":52},"ADEG":{"count":68},"Minipreço":{"count":111},"Biedronka":{"count":1335},"Eurospin":{"count":155},"Семья":{"count":62},"Gadis":{"count":53},"Евроопт":{"count":68},"Centra":{"count":51},"Квартал":{"count":82},"New World":{"count":69},"Countdown":{"count":95},"Reliance Fresh":{"count":61},"Stokrotka":{"count":98},"Coop Jednota":{"count":74},"Fred Meyer":{"count":64},"Irma":{"count":58},"Continente":{"count":75},"Price Chopper":{"count":99},"Game":{"count":52},"Soriana":{"count":93},"Alimerka":{"count":64},"Piotr i Paweł":{"count":53},"Перекресток":{"count":312},"Maxima X":{"count":117},"Карусель":{"count":55},"ALDI Nord":{"count":51},"Condis":{"count":67},"Sam's Club":{"count":138},"Копейка":{"count":87},"Géant Casino":{"count":54},"ASDA":{"count":180},"Intermarche":{"count":115},"Stop & Shop":{"count":66},"Food Lion":{"count":216},"Harris Teeter":{"count":92},"Foodworks":{"count":62},"Polo Market":{"count":86},"Лента":{"count":51},"西友 (SEIYU)":{"count":58},"H-E-B":{"count":293},"Атак":{"count":53},"Полушка":{"count":139},"Extra":{"count":82},"Lewiatan":{"count":94},"Sigma":{"count":51},"АТБ":{"count":322},"Społem":{"count":55},"Bodega Aurrera":{"count":82},"Tesco Lotus":{"count":77},"Мария-Ра":{"count":108},"Магнолия":{"count":72},"Магазин":{"count":120},"Монетка":{"count":174},"Hy-Vee":{"count":75},"Walmart Supercenter":{"count":133},"Hannaford":{"count":57},"Wegmans":{"count":83},"業務スーパー":{"count":61},"Norfa XL":{"count":55},"ヨークマート (YorkMart)":{"count":64},"Leclerc Drive":{"count":76}},"electronics":{"Media Markt":{"count":285},"Maplin":{"count":65},"Best Buy":{"count":345},"Future Shop":{"count":73},"Saturn":{"count":134},"Currys":{"count":80},"Radio Shack":{"count":269},"Euronics":{"count":115},"Expert":{"count":123},"Эльдорадо":{"count":184},"Darty":{"count":74},"М.Видео":{"count":89},"ヤマダ電機":{"count":51}},"convenience":{"Shell":{"count":255},"Spar":{"count":1119},"McColl's":{"count":100},"Tesco Express":{"count":426},"Sainsbury's Local":{"count":104},"Aral":{"count":56},"One Stop":{"count":146},"The Co-operative Food":{"count":115},"Londis":{"count":352},"7-Eleven":{"count":4440},"CBA":{"count":135},"Coop":{"count":678},"Sale":{"count":80},"Statoil":{"count":69},"Sheetz":{"count":54},"Konzum":{"count":173},"Siwa":{"count":216},"Mercator":{"count":57},"Esso":{"count":67},"COOP Jednota":{"count":181},"Mac's":{"count":152},"Alepa":{"count":62},"Hasty Market":{"count":54},"K-Market":{"count":54},"Costcutter":{"count":292},"Valintatalo":{"count":62},"Casino":{"count":90},"Franprix":{"count":61},"Circle K":{"count":289},"セブンイレブン":{"count":3011,"tags":{"name:en":"7-Eleven"}},"ローソン":{"count":1596,"tags":{"name:en":"LAWSON"}},"BP":{"count":163},"Tesco":{"count":55},"Petit Casino":{"count":233},"Volg":{"count":116},"Mace":{"count":115},"Mini Market":{"count":272},"Nisa Local":{"count":77},"Dorfladen":{"count":75},"Продукты":{"count":4285},"Mini Stop":{"count":228},"LAWSON":{"count":419},"デイリーヤマザキ":{"count":141},"Biedronka":{"count":83},"Надежда":{"count":56},"Mobil":{"count":66},"Nisa":{"count":51},"Premier":{"count":129},"ABC":{"count":152},"ミニストップ":{"count":316,"tags":{"name:en":"MINISTOP"}},"サンクス":{"count":560,"tags":{"name:en":"sunkus"}},"スリーエフ":{"count":88},"8 à Huit":{"count":61},"Tchibo":{"count":56},"Żabka":{"count":546},"Almacen":{"count":229},"Vival":{"count":194},"FamilyMart":{"count":529},"ファミリーマート":{"count":1608,"tags":{"name:en":"FamilyMart"}},"Carrefour City":{"count":57},"Sunkus":{"count":62},"Casey's General Store":{"count":95},"セブンイレブン(Seven-Eleven)":{"count":65},"Jednota":{"count":58},"Магазин":{"count":915},"Гастроном":{"count":152},"Centra":{"count":111},"Магнит":{"count":701},"サークルK":{"count":538,"tags":{"name:en":"Circle K"}},"Wawa":{"count":135},"Proxi":{"count":123},"Универсам":{"count":78},"Перекресток":{"count":51},"Groszek":{"count":65},"Select":{"count":62},"Večerka":{"count":51},"Potraviny":{"count":249},"Смак":{"count":78},"Эконом":{"count":55},"Березка":{"count":77},"Społem":{"count":93},"Carrefour Express":{"count":84},"Cumberland Farms":{"count":63},"Chevron":{"count":59},"Coop Jednota":{"count":66},"Tesco Lotus Express":{"count":67},"Kiosk":{"count":55},"Sklep spożywczy":{"count":130},"24 часа":{"count":58},"Минимаркет":{"count":102},"Oxxo":{"count":669},"Пятёрочка":{"count":398},"abc":{"count":74},"7/11":{"count":51},"Stewart's":{"count":255},"Продукти":{"count":171},"ローソンストア100 (LAWSON STORE 100)":{"count":85},"Дикси":{"count":119},"Радуга":{"count":86},"ローソンストア100":{"count":76},"เซเว่นอีเลฟเว่น":{"count":185},"Delikatesy Centrum":{"count":53},"Citgo":{"count":62},"Фортуна":{"count":51},"Kum & Go":{"count":59},"Мария-Ра":{"count":76},"Picard":{"count":57},"Four Square":{"count":52},"Визит":{"count":57},"Авоська":{"count":55},"Dollar General":{"count":127},"Studenac":{"count":76},"Central Convenience Store":{"count":55},"Монетка":{"count":62},"продукты":{"count":114},"Теремок":{"count":56},"Kwik Trip":{"count":69},"Кулинария":{"count":55},"全家":{"count":90},"Мечта":{"count":54},"Epicerie":{"count":102},"Кировский":{"count":67},"Food Mart":{"count":117},"Delikatesy":{"count":81},"ポプラ":{"count":54},"Lewiatan":{"count":135},"Продуктовый магазин":{"count":149},"Продуктовый":{"count":84},"セイコーマート (Seicomart)":{"count":72},"Виктория":{"count":70},"Весна":{"count":57},"Mini Market Non-Stop":{"count":60},"QuikTrip":{"count":75},"Копеечка":{"count":51},"Royal Farms":{"count":51},"Alfamart":{"count":103},"Indomaret":{"count":141},"магазин":{"count":171},"全家便利商店":{"count":156},"Boutique":{"count":59},"მარკეტი (Market)":{"count":144},"Stores":{"count":61}},"chemist":{"dm":{"count":939},"Müller":{"count":212},"Schlecker":{"count":187},"Etos":{"count":467},"Bipa":{"count":289},"Rossmann":{"count":1669},"DM Drogeriemarkt":{"count":55},"Ihr Platz":{"count":73},"Douglas":{"count":62},"Kruidvat":{"count":123}},"car_repair":{"Peugeot":{"count":83},"Kwik Fit":{"count":128},"ATU":{"count":261},"Midas":{"count":202},"Feu Vert":{"count":113},"Norauto":{"count":152},"Speedy":{"count":115},"Автозапчасти":{"count":212},"Renault":{"count":171},"Pit Stop":{"count":58},"Jiffy Lube":{"count":198},"Шиномонтаж":{"count":1157},"СТО":{"count":395},"O'Reilly Auto Parts":{"count":81},"Carglass":{"count":112},"шиномонтаж":{"count":62},"Citroen":{"count":51},"Euromaster":{"count":87},"Firestone":{"count":88},"Автосервис":{"count":361},"Advance Auto Parts":{"count":52},"Roady":{"count":56}},"furniture":{"IKEA":{"count":169},"Jysk":{"count":109},"Roller":{"count":78},"Dänisches Bettenlager":{"count":309},"Conforama":{"count":99},"Matratzen Concord":{"count":52},"Мебель":{"count":210},"But":{"count":63}},"doityourself":{"Hornbach":{"count":123},"B&Q":{"count":225},"Hubo":{"count":77},"Mr Bricolage":{"count":88},"Gamma":{"count":111},"OBI":{"count":422},"Lowes":{"count":1152},"Wickes":{"count":123},"Hagebau":{"count":59},"Max Bahr":{"count":79},"Castorama":{"count":153},"Rona":{"count":61},"Home Depot":{"count":865},"Toom Baumarkt":{"count":71},"Homebase":{"count":225},"Baumax":{"count":95},"Lagerhaus":{"count":79},"Bauhaus":{"count":186},"Canadian Tire":{"count":97},"Leroy Merlin":{"count":209},"Hellweg":{"count":58},"Brico":{"count":98},"Bricomarché":{"count":235},"Toom":{"count":67},"Hagebaumarkt":{"count":107},"Praktiker":{"count":122},"Menards":{"count":70},"Weldom":{"count":73},"Bunnings Warehouse":{"count":91},"Ace Hardware":{"count":147},"Home Hardware":{"count":72},"Хозтовары":{"count":86},"Стройматериалы":{"count":197},"Bricorama":{"count":60},"Point P":{"count":59}},"stationery":{"Staples":{"count":299},"McPaper":{"count":83},"Office Depot":{"count":98},"Канцтовары":{"count":63}},"car":{"Skoda":{"count":97},"BMW":{"count":149},"Citroen":{"count":277},"Renault":{"count":382},"Mercedes-Benz":{"count":235},"Volvo":{"count":96},"Ford":{"count":239},"Volkswagen":{"count":217},"Mazda":{"count":105},"Mitsubishi":{"count":73},"Fiat":{"count":93},"Автозапчасти":{"count":277},"Opel":{"count":165},"Audi":{"count":121},"Toyota":{"count":271},"Nissan":{"count":189},"Suzuki":{"count":75},"Honda":{"count":157},"Peugeot":{"count":308},"Шиномонтаж":{"count":259},"Hyundai":{"count":166},"Subaru":{"count":58},"Chevrolet":{"count":86},"Автомагазин":{"count":72}},"clothes":{"Matalan":{"count":90},"KiK":{"count":1219},"H&M":{"count":658},"Urban Outfitters":{"count":63},"Vögele":{"count":132},"Zeeman":{"count":121},"Takko":{"count":515},"Adler":{"count":55},"C&A":{"count":506},"Zara":{"count":217},"Vero Moda":{"count":95},"NKD":{"count":486},"Ernsting's family":{"count":312},"Winners":{"count":65},"River Island":{"count":59},"Next":{"count":176},"Gap":{"count":81},"Adidas":{"count":92},"Woolworths":{"count":117},"Mr Price":{"count":88},"Jet":{"count":61},"Pep":{"count":134},"Edgars":{"count":110},"Ackermans":{"count":91},"Truworths":{"count":65},"Ross":{"count":93},"Burton":{"count":51},"Dorothy Perkins":{"count":53},"Deichmann":{"count":61},"Lindex":{"count":73},"s.Oliver":{"count":56},"Cecil":{"count":51},"Dress Barn":{"count":52},"Old Navy":{"count":174},"Jack & Jones":{"count":52},"Pimkie":{"count":73},"Esprit":{"count":231},"Primark":{"count":92},"Bonita":{"count":155},"Mexx":{"count":67},"Gerry Weber":{"count":71},"Tally Weijl":{"count":70},"Mango":{"count":133},"TK Maxx":{"count":84},"Benetton":{"count":101},"Ulla Popken":{"count":61},"AWG":{"count":66},"Tommy Hilfiger":{"count":75},"New Yorker":{"count":180},"Orsay":{"count":73},"Jeans Fritz":{"count":51},"Charles Vögele":{"count":69},"New Look":{"count":126},"Lacoste":{"count":78},"Etam":{"count":53},"Kiabi":{"count":148},"Jack Wolfskin":{"count":60},"American Apparel":{"count":57},"Men's Wearhouse":{"count":54},"Intimissimi":{"count":52},"United Colors of Benetton":{"count":96},"Jules":{"count":63},"Second Hand":{"count":53},"AOKI":{"count":57},"Calzedonia":{"count":68},"洋服の青山":{"count":100},"Levi's":{"count":63},"Celio":{"count":74},"TJ Maxx":{"count":57},"Promod":{"count":82},"Street One":{"count":72},"ユニクロ":{"count":59},"Banana Republic":{"count":57},"Одежда":{"count":75},"Marshalls":{"count":56},"La Halle":{"count":62},"Peacocks":{"count":89},"しまむら":{"count":60}},"books":{"Bruna":{"count":58},"Waterstones":{"count":90},"Libro":{"count":57},"Barnes & Noble":{"count":267},"Weltbild":{"count":74},"Thalia":{"count":121},"Книги":{"count":112}},"department_store":{"Debenhams":{"count":67},"Canadian Tire":{"count":75},"Karstadt":{"count":64},"Walmart":{"count":517},"Kmart":{"count":143},"Target":{"count":574},"Galeria Kaufhof":{"count":61},"Marks & Spencer":{"count":66},"Big W":{"count":57},"Woolworth":{"count":78},"Универмаг":{"count":72},"Sears":{"count":235},"Walmart Supercenter":{"count":101},"Kohl's":{"count":153},"Macy's":{"count":147},"Sam's Club":{"count":54},"JCPenney":{"count":66}},"alcohol":{"Alko":{"count":145},"The Beer Store":{"count":150},"Systembolaget":{"count":210},"LCBO":{"count":239},"Ароматный мир":{"count":62},"Bargain Booze":{"count":62},"Nicolas":{"count":119},"BWS":{"count":70},"Botilleria":{"count":77},"SAQ":{"count":72},"Gall & Gall":{"count":512},"Живое пиво":{"count":70}},"bakery":{"Kamps":{"count":252},"Banette":{"count":52},"Bäckerei Schmidt":{"count":57},"Anker":{"count":73},"Hofpfisterei":{"count":111},"Greggs":{"count":276},"Oebel":{"count":57},"Boulangerie":{"count":266},"Stadtbäckerei":{"count":57},"Steinecke":{"count":145},"Ihle":{"count":76},"Goldilocks":{"count":59},"Dat Backhus":{"count":67},"K&U":{"count":61},"Der Beck":{"count":96},"Thürmann":{"count":54},"Backwerk":{"count":95},"Schäfer's":{"count":51},"Panaderia":{"count":168},"Goeken backen":{"count":51},"Stadtbäckerei Junge":{"count":51},"Boulangerie Patisserie":{"count":119},"Paul":{"count":81},"Хлеб":{"count":89},"Пекарня":{"count":52},"Кулиничи":{"count":51}},"sports":{"Sports Direct":{"count":57},"Decathlon":{"count":309},"Intersport":{"count":283},"Sports Authority":{"count":75},"Спортмастер":{"count":87},"Sport 2000":{"count":90},"Dick's Sporting Goods":{"count":77}},"variety_store":{"Tedi":{"count":157},"Dollarama":{"count":103},"Family Dollar":{"count":61},"Dollar Tree":{"count":110},"Dollar General":{"count":80}},"pet":{"Fressnapf":{"count":318},"PetSmart":{"count":177},"Das Futterhaus":{"count":69},"Pets at Home":{"count":62},"Petco":{"count":101},"Зоомагазин":{"count":100}},"shoes":{"Deichmann":{"count":622},"Reno":{"count":183},"Ecco":{"count":55},"Clarks":{"count":109},"La Halle aux Chaussures":{"count":69},"Brantano":{"count":71},"Geox":{"count":51},"Salamander":{"count":51},"Обувь":{"count":100},"Payless Shoe Source":{"count":67},"Famous Footwear":{"count":59},"Quick Schuh":{"count":72},"Shoe Zone":{"count":55},"Foot Locker":{"count":82},"Bata":{"count":101},"ЦентрОбувь":{"count":51}},"toys":{"La Grande Récré":{"count":56},"Toys R Us":{"count":151,"tags":{"shop":"toys"}},"Intertoys":{"count":57},"Детский мир":{"count":86},"Игрушки":{"count":58}},"travel_agency":{"Flight Centre":{"count":92},"Thomas Cook":{"count":119}},"jewelry":{"Bijou Brigitte":{"count":57},"Christ":{"count":57},"Swarovski":{"count":74}},"optician":{"Fielmann":{"count":232},"Apollo Optik":{"count":150},"Vision Express":{"count":58},"Оптика":{"count":182},"Optic 2000":{"count":98},"Alain Afflelou":{"count":73},"Specsavers":{"count":124},"Krys":{"count":77},"Atol":{"count":55}},"video":{"Blockbuster":{"count":184},"World of Video":{"count":64}},"mobile_phone":{"Билайн":{"count":128},"ソフトバンクショップ (SoftBank shop)":{"count":255},"Vodafone":{"count":355},"O2":{"count":208},"Carphone Warehouse":{"count":127},"Orange":{"count":246},"Verizon Wireless":{"count":125},"Sprint":{"count":109},"T-Mobile":{"count":175},"МТС":{"count":352},"Евросеть":{"count":506},"Bell":{"count":190},"The Phone House":{"count":83},"SFR":{"count":71},"Связной":{"count":439},"Мегафон":{"count":251},"AT&T":{"count":124},"ドコモショップ (docomo shop)":{"count":114},"au":{"count":65},"Movistar":{"count":77},"Bitė":{"count":72}},"hifi":{},"computer":{"PC World":{"count":55},"DNS":{"count":128}},"hairdresser":{"Klier":{"count":119},"Supercuts":{"count":106},"Hairkiller":{"count":51},"Great Clips":{"count":182},"Парикмахерская":{"count":510},"Стиль":{"count":51},"Franck Provost":{"count":70},"Салон красоты":{"count":70}},"hardware":{"1000 мелочей":{"count":61},"Хозтовары":{"count":151},"Стройматериалы":{"count":54}},"motorcycle":{"Yamaha":{"count":67},"Honda":{"count":69}}}},"addressFormats":[{"format":[["housenumber","street"],["city","postcode"]]},{"countryCodes":["gb"],"format":[["housename"],["housenumber","street"],["city","postcode"]]},{"countryCodes":["ie"],"format":[["housename"],["housenumber","street"],["city"],["postcode"]]},{"countryCodes":["ad","at","ba","be","ch","cz","de","dk","es","fi","gr","hr","is","it","li","nl","no","pl","pt","se","si","sk","sm","va"],"format":[["street","housenumber"],["postcode","city"]]},{"countryCodes":["fr","lu","mo"],"format":[["housenumber","street"],["postcode","city"]]},{"countryCodes":["br"],"format":[["street"],["housenumber","suburb"],["city","postcode"]]},{"countryCodes":["vn"],"format":[["housenumber","street"],["subdistrict"],["district"],["city"],["province","postcode"]]},{"countryCodes":["us"],"format":[["housenumber","street"],["city","state","postcode"]]},{"countryCodes":["ca"],"format":[["housenumber","street"],["city","province","postcode"]]}]};