]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
Update to iD v1.9.4
[rails.git] / vendor / assets / iD / iD.js
1 (function(exports) {
2
3   var bootstrap = (typeof exports.bootstrap === "object") ?
4     exports.bootstrap :
5     (exports.bootstrap = {});
6
7   bootstrap.tooltip = function() {
8
9     var tooltip = function(selection) {
10         selection.each(setup);
11       },
12       animation = d3.functor(false),
13       html = d3.functor(false),
14       title = function() {
15         var title = this.getAttribute("data-original-title");
16         if (title) {
17           return title;
18         } else {
19           title = this.getAttribute("title");
20           this.removeAttribute("title");
21           this.setAttribute("data-original-title", title);
22         }
23         return title;
24       },
25       over = "mouseenter.tooltip",
26       out = "mouseleave.tooltip",
27       placements = "top left bottom right".split(" "),
28       placement = d3.functor("top");
29
30     tooltip.title = function(_) {
31       if (arguments.length) {
32         title = d3.functor(_);
33         return tooltip;
34       } else {
35         return title;
36       }
37     };
38
39     tooltip.html = function(_) {
40       if (arguments.length) {
41         html = d3.functor(_);
42         return tooltip;
43       } else {
44         return html;
45       }
46     };
47
48     tooltip.placement = function(_) {
49       if (arguments.length) {
50         placement = d3.functor(_);
51         return tooltip;
52       } else {
53         return placement;
54       }
55     };
56
57     tooltip.show = function(selection) {
58       selection.each(show);
59     };
60
61     tooltip.hide = function(selection) {
62       selection.each(hide);
63     };
64
65     tooltip.toggle = function(selection) {
66       selection.each(toggle);
67     };
68
69     tooltip.destroy = function(selection) {
70       selection
71         .on(over, null)
72         .on(out, null)
73         .attr("title", function() {
74           return this.getAttribute("data-original-title") || this.getAttribute("title");
75         })
76         .attr("data-original-title", null)
77         .select(".tooltip")
78         .remove();
79     };
80
81     function setup() {
82       var root = d3.select(this),
83           animate = animation.apply(this, arguments),
84           tip = root.append("div")
85             .attr("class", "tooltip");
86
87       if (animate) {
88         tip.classed("fade", true);
89       }
90
91       // TODO "inside" checks?
92
93       tip.append("div")
94         .attr("class", "tooltip-arrow");
95       tip.append("div")
96         .attr("class", "tooltip-inner");
97
98       var place = placement.apply(this, arguments);
99       tip.classed(place, true);
100
101       root.on(over, show);
102       root.on(out, hide);
103     }
104
105     function show() {
106       var root = d3.select(this),
107           content = title.apply(this, arguments),
108           tip = root.select(".tooltip")
109             .classed("in", true),
110           markup = html.apply(this, arguments),
111           innercontent = tip.select(".tooltip-inner")[markup ? "html" : "text"](content),
112           place = placement.apply(this, arguments),
113           outer = getPosition(root.node()),
114           inner = getPosition(tip.node()),
115           pos;
116
117       switch (place) {
118         case "top":
119           pos = {x: outer.x + (outer.w - inner.w) / 2, y: outer.y - inner.h};
120           break;
121         case "right":
122           pos = {x: outer.x + outer.w, y: outer.y + (outer.h - inner.h) / 2};
123           break;
124         case "left":
125           pos = {x: outer.x - inner.w, y: outer.y + (outer.h - inner.h) / 2};
126           break;
127         case "bottom":
128           pos = {x: Math.max(0, outer.x + (outer.w - inner.w) / 2), y: outer.y + outer.h};
129           break;
130       }
131
132       tip.style(pos ?
133         {left: ~~pos.x + "px", top: ~~pos.y + "px"} :
134         {left: null, top: null});
135
136       this.tooltipVisible = true;
137     }
138
139     function hide() {
140       d3.select(this).select(".tooltip")
141         .classed("in", false);
142
143       this.tooltipVisible = false;
144     }
145
146     function toggle() {
147       if (this.tooltipVisible) {
148         hide.apply(this, arguments);
149       } else {
150         show.apply(this, arguments);
151       }
152     }
153
154     return tooltip;
155   };
156
157   function getPosition(node) {
158     var mode = d3.select(node).style('position');
159     if (mode === 'absolute' || mode === 'static') {
160       return {
161         x: node.offsetLeft,
162         y: node.offsetTop,
163         w: node.offsetWidth,
164         h: node.offsetHeight
165       };
166     } else {
167       return {
168         x: 0,
169         y: 0,
170         w: node.offsetWidth,
171         h: node.offsetHeight
172       };
173     }
174   }
175
176 })(this);
177 !function(){
178   var d3 = {version: "3.5.5"}; // semver
179 d3.ascending = d3_ascending;
180
181 function d3_ascending(a, b) {
182   return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
183 }
184 d3.descending = function(a, b) {
185   return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
186 };
187 d3.min = function(array, f) {
188   var i = -1,
189       n = array.length,
190       a,
191       b;
192   if (arguments.length === 1) {
193     while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; }
194     while (++i < n) if ((b = array[i]) != null && a > b) a = b;
195   } else {
196     while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = b; break; }
197     while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
198   }
199   return a;
200 };
201 d3.max = function(array, f) {
202   var i = -1,
203       n = array.length,
204       a,
205       b;
206   if (arguments.length === 1) {
207     while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; }
208     while (++i < n) if ((b = array[i]) != null && b > a) a = b;
209   } else {
210     while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = b; break; }
211     while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
212   }
213   return a;
214 };
215 d3.extent = function(array, f) {
216   var i = -1,
217       n = array.length,
218       a,
219       b,
220       c;
221   if (arguments.length === 1) {
222     while (++i < n) if ((b = array[i]) != null && b >= b) { a = c = b; break; }
223     while (++i < n) if ((b = array[i]) != null) {
224       if (a > b) a = b;
225       if (c < b) c = b;
226     }
227   } else {
228     while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = c = b; break; }
229     while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
230       if (a > b) a = b;
231       if (c < b) c = b;
232     }
233   }
234   return [a, c];
235 };
236 function d3_number(x) {
237   return x === null ? NaN : +x;
238 }
239
240 function d3_numeric(x) {
241   return !isNaN(x);
242 }
243
244 d3.sum = function(array, f) {
245   var s = 0,
246       n = array.length,
247       a,
248       i = -1;
249   if (arguments.length === 1) {
250     while (++i < n) if (d3_numeric(a = +array[i])) s += a; // zero and null are equivalent
251   } else {
252     while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a;
253   }
254   return s;
255 };
256
257 d3.mean = function(array, f) {
258   var s = 0,
259       n = array.length,
260       a,
261       i = -1,
262       j = n;
263   if (arguments.length === 1) {
264     while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j;
265   } else {
266     while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j;
267   }
268   if (j) return s / j;
269 };
270 // R-7 per <http://en.wikipedia.org/wiki/Quantile>
271 d3.quantile = function(values, p) {
272   var H = (values.length - 1) * p + 1,
273       h = Math.floor(H),
274       v = +values[h - 1],
275       e = H - h;
276   return e ? v + e * (values[h] - v) : v;
277 };
278
279 d3.median = function(array, f) {
280   var numbers = [],
281       n = array.length,
282       a,
283       i = -1;
284   if (arguments.length === 1) {
285     while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a);
286   } else {
287     while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a);
288   }
289   if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5);
290 };
291
292 d3.variance = function(array, f) {
293   var n = array.length,
294       m = 0,
295       a,
296       d,
297       s = 0,
298       i = -1,
299       j = 0;
300   if (arguments.length === 1) {
301     while (++i < n) {
302       if (d3_numeric(a = d3_number(array[i]))) {
303         d = a - m;
304         m += d / ++j;
305         s += d * (a - m);
306       }
307     }
308   } else {
309     while (++i < n) {
310       if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) {
311         d = a - m;
312         m += d / ++j;
313         s += d * (a - m);
314       }
315     }
316   }
317   if (j > 1) return s / (j - 1);
318 };
319
320 d3.deviation = function() {
321   var v = d3.variance.apply(this, arguments);
322   return v ? Math.sqrt(v) : v;
323 };
324
325 function d3_bisector(compare) {
326   return {
327     left: function(a, x, lo, hi) {
328       if (arguments.length < 3) lo = 0;
329       if (arguments.length < 4) hi = a.length;
330       while (lo < hi) {
331         var mid = lo + hi >>> 1;
332         if (compare(a[mid], x) < 0) lo = mid + 1;
333         else hi = mid;
334       }
335       return lo;
336     },
337     right: function(a, x, lo, hi) {
338       if (arguments.length < 3) lo = 0;
339       if (arguments.length < 4) hi = a.length;
340       while (lo < hi) {
341         var mid = lo + hi >>> 1;
342         if (compare(a[mid], x) > 0) hi = mid;
343         else lo = mid + 1;
344       }
345       return lo;
346     }
347   };
348 }
349
350 var d3_bisect = d3_bisector(d3_ascending);
351 d3.bisectLeft = d3_bisect.left;
352 d3.bisect = d3.bisectRight = d3_bisect.right;
353
354 d3.bisector = function(f) {
355   return d3_bisector(f.length === 1
356       ? function(d, x) { return d3_ascending(f(d), x); }
357       : f);
358 };
359 d3.shuffle = function(array, i0, i1) {
360   if ((m = arguments.length) < 3) { i1 = array.length; if (m < 2) i0 = 0; }
361   var m = i1 - i0, t, i;
362   while (m) {
363     i = Math.random() * m-- | 0;
364     t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t;
365   }
366   return array;
367 };
368 d3.permute = function(array, indexes) {
369   var i = indexes.length, permutes = new Array(i);
370   while (i--) permutes[i] = array[indexes[i]];
371   return permutes;
372 };
373 d3.pairs = function(array) {
374   var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
375   while (i < n) pairs[i] = [p0 = p1, p1 = array[++i]];
376   return pairs;
377 };
378
379 d3.zip = function() {
380   if (!(n = arguments.length)) return [];
381   for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m;) {
382     for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n;) {
383       zip[j] = arguments[j][i];
384     }
385   }
386   return zips;
387 };
388
389 function d3_zipLength(d) {
390   return d.length;
391 }
392
393 d3.transpose = function(matrix) {
394   return d3.zip.apply(d3, matrix);
395 };
396 d3.keys = function(map) {
397   var keys = [];
398   for (var key in map) keys.push(key);
399   return keys;
400 };
401 d3.values = function(map) {
402   var values = [];
403   for (var key in map) values.push(map[key]);
404   return values;
405 };
406 d3.entries = function(map) {
407   var entries = [];
408   for (var key in map) entries.push({key: key, value: map[key]});
409   return entries;
410 };
411 d3.merge = function(arrays) {
412   var n = arrays.length,
413       m,
414       i = -1,
415       j = 0,
416       merged,
417       array;
418
419   while (++i < n) j += arrays[i].length;
420   merged = new Array(j);
421
422   while (--n >= 0) {
423     array = arrays[n];
424     m = array.length;
425     while (--m >= 0) {
426       merged[--j] = array[m];
427     }
428   }
429
430   return merged;
431 };
432 var abs = Math.abs;
433
434 d3.range = function(start, stop, step) {
435   if (arguments.length < 3) {
436     step = 1;
437     if (arguments.length < 2) {
438       stop = start;
439       start = 0;
440     }
441   }
442   if ((stop - start) / step === Infinity) throw new Error("infinite range");
443   var range = [],
444        k = d3_range_integerScale(abs(step)),
445        i = -1,
446        j;
447   start *= k, stop *= k, step *= k;
448   if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k);
449   else while ((j = start + step * ++i) < stop) range.push(j / k);
450   return range;
451 };
452
453 function d3_range_integerScale(x) {
454   var k = 1;
455   while (x * k % 1) k *= 10;
456   return k;
457 }
458 function d3_class(ctor, properties) {
459   for (var key in properties) {
460     Object.defineProperty(ctor.prototype, key, {
461       value: properties[key],
462       enumerable: false
463     });
464   }
465 }
466
467 d3.map = function(object, f) {
468   var map = new d3_Map;
469   if (object instanceof d3_Map) {
470     object.forEach(function(key, value) { map.set(key, value); });
471   } else if (Array.isArray(object)) {
472     var i = -1,
473         n = object.length,
474         o;
475     if (arguments.length === 1) while (++i < n) map.set(i, object[i]);
476     else while (++i < n) map.set(f.call(object, o = object[i], i), o);
477   } else {
478     for (var key in object) map.set(key, object[key]);
479   }
480   return map;
481 };
482
483 function d3_Map() {
484   this._ = Object.create(null);
485 }
486
487 var d3_map_proto = "__proto__",
488     d3_map_zero = "\0";
489
490 d3_class(d3_Map, {
491   has: d3_map_has,
492   get: function(key) {
493     return this._[d3_map_escape(key)];
494   },
495   set: function(key, value) {
496     return this._[d3_map_escape(key)] = value;
497   },
498   remove: d3_map_remove,
499   keys: d3_map_keys,
500   values: function() {
501     var values = [];
502     for (var key in this._) values.push(this._[key]);
503     return values;
504   },
505   entries: function() {
506     var entries = [];
507     for (var key in this._) entries.push({key: d3_map_unescape(key), value: this._[key]});
508     return entries;
509   },
510   size: d3_map_size,
511   empty: d3_map_empty,
512   forEach: function(f) {
513     for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]);
514   }
515 });
516
517 function d3_map_escape(key) {
518   return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key;
519 }
520
521 function d3_map_unescape(key) {
522   return (key += "")[0] === d3_map_zero ? key.slice(1) : key;
523 }
524
525 function d3_map_has(key) {
526   return d3_map_escape(key) in this._;
527 }
528
529 function d3_map_remove(key) {
530   return (key = d3_map_escape(key)) in this._ && delete this._[key];
531 }
532
533 function d3_map_keys() {
534   var keys = [];
535   for (var key in this._) keys.push(d3_map_unescape(key));
536   return keys;
537 }
538
539 function d3_map_size() {
540   var size = 0;
541   for (var key in this._) ++size;
542   return size;
543 }
544
545 function d3_map_empty() {
546   for (var key in this._) return false;
547   return true;
548 }
549
550 d3.nest = function() {
551   var nest = {},
552       keys = [],
553       sortKeys = [],
554       sortValues,
555       rollup;
556
557   function map(mapType, array, depth) {
558     if (depth >= keys.length) return rollup
559         ? rollup.call(nest, array) : (sortValues
560         ? array.sort(sortValues)
561         : array);
562
563     var i = -1,
564         n = array.length,
565         key = keys[depth++],
566         keyValue,
567         object,
568         setter,
569         valuesByKey = new d3_Map,
570         values;
571
572     while (++i < n) {
573       if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
574         values.push(object);
575       } else {
576         valuesByKey.set(keyValue, [object]);
577       }
578     }
579
580     if (mapType) {
581       object = mapType();
582       setter = function(keyValue, values) {
583         object.set(keyValue, map(mapType, values, depth));
584       };
585     } else {
586       object = {};
587       setter = function(keyValue, values) {
588         object[keyValue] = map(mapType, values, depth);
589       };
590     }
591
592     valuesByKey.forEach(setter);
593     return object;
594   }
595
596   function entries(map, depth) {
597     if (depth >= keys.length) return map;
598
599     var array = [],
600         sortKey = sortKeys[depth++];
601
602     map.forEach(function(key, keyMap) {
603       array.push({key: key, values: entries(keyMap, depth)});
604     });
605
606     return sortKey
607         ? array.sort(function(a, b) { return sortKey(a.key, b.key); })
608         : array;
609   }
610
611   nest.map = function(array, mapType) {
612     return map(mapType, array, 0);
613   };
614
615   nest.entries = function(array) {
616     return entries(map(d3.map, array, 0), 0);
617   };
618
619   nest.key = function(d) {
620     keys.push(d);
621     return nest;
622   };
623
624   // Specifies the order for the most-recently specified key.
625   // Note: only applies to entries. Map keys are unordered!
626   nest.sortKeys = function(order) {
627     sortKeys[keys.length - 1] = order;
628     return nest;
629   };
630
631   // Specifies the order for leaf values.
632   // Applies to both maps and entries array.
633   nest.sortValues = function(order) {
634     sortValues = order;
635     return nest;
636   };
637
638   nest.rollup = function(f) {
639     rollup = f;
640     return nest;
641   };
642
643   return nest;
644 };
645
646 d3.set = function(array) {
647   var set = new d3_Set;
648   if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
649   return set;
650 };
651
652 function d3_Set() {
653   this._ = Object.create(null);
654 }
655
656 d3_class(d3_Set, {
657   has: d3_map_has,
658   add: function(key) {
659     this._[d3_map_escape(key += "")] = true;
660     return key;
661   },
662   remove: d3_map_remove,
663   values: d3_map_keys,
664   size: d3_map_size,
665   empty: d3_map_empty,
666   forEach: function(f) {
667     for (var key in this._) f.call(this, d3_map_unescape(key));
668   }
669 });
670 d3.behavior = {};
671 var d3_document = this.document;
672
673 function d3_documentElement(node) {
674   return node
675       && (node.ownerDocument // node is a Node
676       || node.document // node is a Window
677       || node).documentElement; // node is a Document
678 }
679
680 function d3_window(node) {
681   return node
682       && ((node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
683         || (node.document && node) // node is a Window
684         || node.defaultView); // node is a Document
685 }
686 // Copies a variable number of methods from source to target.
687 d3.rebind = function(target, source) {
688   var i = 1, n = arguments.length, method;
689   while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
690   return target;
691 };
692
693 // Method is assumed to be a standard D3 getter-setter:
694 // If passed with no arguments, gets the value.
695 // If passed with arguments, sets the value and returns the target.
696 function d3_rebind(target, source, method) {
697   return function() {
698     var value = method.apply(source, arguments);
699     return value === source ? target : value;
700   };
701 }
702 function d3_vendorSymbol(object, name) {
703   if (name in object) return name;
704   name = name.charAt(0).toUpperCase() + name.slice(1);
705   for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
706     var prefixName = d3_vendorPrefixes[i] + name;
707     if (prefixName in object) return prefixName;
708   }
709 }
710
711 var d3_vendorPrefixes = ["webkit", "ms", "moz", "Moz", "o", "O"];
712 var d3_arraySlice = [].slice,
713     d3_array = function(list) { return d3_arraySlice.call(list); }; // conversion for NodeLists
714 function d3_noop() {}
715
716 d3.dispatch = function() {
717   var dispatch = new d3_dispatch,
718       i = -1,
719       n = arguments.length;
720   while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
721   return dispatch;
722 };
723
724 function d3_dispatch() {}
725
726 d3_dispatch.prototype.on = function(type, listener) {
727   var i = type.indexOf("."),
728       name = "";
729
730   // Extract optional namespace, e.g., "click.foo"
731   if (i >= 0) {
732     name = type.slice(i + 1);
733     type = type.slice(0, i);
734   }
735
736   if (type) return arguments.length < 2
737       ? this[type].on(name)
738       : this[type].on(name, listener);
739
740   if (arguments.length === 2) {
741     if (listener == null) for (type in this) {
742       if (this.hasOwnProperty(type)) this[type].on(name, null);
743     }
744     return this;
745   }
746 };
747
748 function d3_dispatch_event(dispatch) {
749   var listeners = [],
750       listenerByName = new d3_Map;
751
752   function event() {
753     var z = listeners, // defensive reference
754         i = -1,
755         n = z.length,
756         l;
757     while (++i < n) if (l = z[i].on) l.apply(this, arguments);
758     return dispatch;
759   }
760
761   event.on = function(name, listener) {
762     var l = listenerByName.get(name),
763         i;
764
765     // return the current listener, if any
766     if (arguments.length < 2) return l && l.on;
767
768     // remove the old listener, if any (with copy-on-write)
769     if (l) {
770       l.on = null;
771       listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
772       listenerByName.remove(name);
773     }
774
775     // add the new listener, if any
776     if (listener) listeners.push(listenerByName.set(name, {on: listener}));
777
778     return dispatch;
779   };
780
781   return event;
782 }
783
784 d3.event = null;
785
786 function d3_eventPreventDefault() {
787   d3.event.preventDefault();
788 }
789
790 function d3_eventCancel() {
791   d3.event.preventDefault();
792   d3.event.stopPropagation();
793 }
794
795 function d3_eventSource() {
796   var e = d3.event, s;
797   while (s = e.sourceEvent) e = s;
798   return e;
799 }
800
801 // Like d3.dispatch, but for custom events abstracting native UI events. These
802 // events have a target component (such as a brush), a target element (such as
803 // the svg:g element containing the brush) and the standard arguments `d` (the
804 // target element's data) and `i` (the selection index of the target element).
805 function d3_eventDispatch(target) {
806   var dispatch = new d3_dispatch,
807       i = 0,
808       n = arguments.length;
809
810   while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
811
812   // Creates a dispatch context for the specified `thiz` (typically, the target
813   // DOM element that received the source event) and `argumentz` (typically, the
814   // data `d` and index `i` of the target element). The returned function can be
815   // used to dispatch an event to any registered listeners; the function takes a
816   // single argument as input, being the event to dispatch. The event must have
817   // a "type" attribute which corresponds to a type registered in the
818   // constructor. This context will automatically populate the "sourceEvent" and
819   // "target" attributes of the event, as well as setting the `d3.event` global
820   // for the duration of the notification.
821   dispatch.of = function(thiz, argumentz) {
822     return function(e1) {
823       try {
824         var e0 =
825         e1.sourceEvent = d3.event;
826         e1.target = target;
827         d3.event = e1;
828         dispatch[e1.type].apply(thiz, argumentz);
829       } finally {
830         d3.event = e0;
831       }
832     };
833   };
834
835   return dispatch;
836 }
837 d3.requote = function(s) {
838   return s.replace(d3_requote_re, "\\$&");
839 };
840
841 var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
842 var d3_subclass = {}.__proto__?
843
844 // Until ECMAScript supports array subclassing, prototype injection works well.
845 function(object, prototype) {
846   object.__proto__ = prototype;
847 }:
848
849 // And if your browser doesn't support __proto__, we'll use direct extension.
850 function(object, prototype) {
851   for (var property in prototype) object[property] = prototype[property];
852 };
853
854 function d3_selection(groups) {
855   d3_subclass(groups, d3_selectionPrototype);
856   return groups;
857 }
858
859 var d3_select = function(s, n) { return n.querySelector(s); },
860     d3_selectAll = function(s, n) { return n.querySelectorAll(s); },
861     d3_selectMatches = function(n, s) {
862       var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")];
863       d3_selectMatches = function(n, s) {
864         return d3_selectMatcher.call(n, s);
865       };
866       return d3_selectMatches(n, s);
867     };
868
869 // Prefer Sizzle, if available.
870 if (typeof Sizzle === "function") {
871   d3_select = function(s, n) { return Sizzle(s, n)[0] || null; };
872   d3_selectAll = Sizzle;
873   d3_selectMatches = Sizzle.matchesSelector;
874 }
875
876 d3.selection = function() {
877   return d3.select(d3_document.documentElement);
878 };
879
880 var d3_selectionPrototype = d3.selection.prototype = [];
881
882
883 d3_selectionPrototype.select = function(selector) {
884   var subgroups = [],
885       subgroup,
886       subnode,
887       group,
888       node;
889
890   selector = d3_selection_selector(selector);
891
892   for (var j = -1, m = this.length; ++j < m;) {
893     subgroups.push(subgroup = []);
894     subgroup.parentNode = (group = this[j]).parentNode;
895     for (var i = -1, n = group.length; ++i < n;) {
896       if (node = group[i]) {
897         subgroup.push(subnode = selector.call(node, node.__data__, i, j));
898         if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
899       } else {
900         subgroup.push(null);
901       }
902     }
903   }
904
905   return d3_selection(subgroups);
906 };
907
908 function d3_selection_selector(selector) {
909   return typeof selector === "function" ? selector : function() {
910     return d3_select(selector, this);
911   };
912 }
913
914 d3_selectionPrototype.selectAll = function(selector) {
915   var subgroups = [],
916       subgroup,
917       node;
918
919   selector = d3_selection_selectorAll(selector);
920
921   for (var j = -1, m = this.length; ++j < m;) {
922     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
923       if (node = group[i]) {
924         subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j)));
925         subgroup.parentNode = node;
926       }
927     }
928   }
929
930   return d3_selection(subgroups);
931 };
932
933 function d3_selection_selectorAll(selector) {
934   return typeof selector === "function" ? selector : function() {
935     return d3_selectAll(selector, this);
936   };
937 }
938 var d3_nsPrefix = {
939   svg: "http://www.w3.org/2000/svg",
940   xhtml: "http://www.w3.org/1999/xhtml",
941   xlink: "http://www.w3.org/1999/xlink",
942   xml: "http://www.w3.org/XML/1998/namespace",
943   xmlns: "http://www.w3.org/2000/xmlns/"
944 };
945
946 d3.ns = {
947   prefix: d3_nsPrefix,
948   qualify: function(name) {
949     var i = name.indexOf(":"),
950         prefix = name;
951     if (i >= 0) {
952       prefix = name.slice(0, i);
953       name = name.slice(i + 1);
954     }
955     return d3_nsPrefix.hasOwnProperty(prefix)
956         ? {space: d3_nsPrefix[prefix], local: name}
957         : name;
958   }
959 };
960
961 d3_selectionPrototype.attr = function(name, value) {
962   if (arguments.length < 2) {
963
964     // For attr(string), return the attribute value for the first node.
965     if (typeof name === "string") {
966       var node = this.node();
967       name = d3.ns.qualify(name);
968       return name.local
969           ? node.getAttributeNS(name.space, name.local)
970           : node.getAttribute(name);
971     }
972
973     // For attr(object), the object specifies the names and values of the
974     // attributes to set or remove. The values may be functions that are
975     // evaluated for each element.
976     for (value in name) this.each(d3_selection_attr(value, name[value]));
977     return this;
978   }
979
980   return this.each(d3_selection_attr(name, value));
981 };
982
983 function d3_selection_attr(name, value) {
984   name = d3.ns.qualify(name);
985
986   // For attr(string, null), remove the attribute with the specified name.
987   function attrNull() {
988     this.removeAttribute(name);
989   }
990   function attrNullNS() {
991     this.removeAttributeNS(name.space, name.local);
992   }
993
994   // For attr(string, string), set the attribute with the specified name.
995   function attrConstant() {
996     this.setAttribute(name, value);
997   }
998   function attrConstantNS() {
999     this.setAttributeNS(name.space, name.local, value);
1000   }
1001
1002   // For attr(string, function), evaluate the function for each element, and set
1003   // or remove the attribute as appropriate.
1004   function attrFunction() {
1005     var x = value.apply(this, arguments);
1006     if (x == null) this.removeAttribute(name);
1007     else this.setAttribute(name, x);
1008   }
1009   function attrFunctionNS() {
1010     var x = value.apply(this, arguments);
1011     if (x == null) this.removeAttributeNS(name.space, name.local);
1012     else this.setAttributeNS(name.space, name.local, x);
1013   }
1014
1015   return value == null
1016       ? (name.local ? attrNullNS : attrNull) : (typeof value === "function"
1017       ? (name.local ? attrFunctionNS : attrFunction)
1018       : (name.local ? attrConstantNS : attrConstant));
1019 }
1020 function d3_collapse(s) {
1021   return s.trim().replace(/\s+/g, " ");
1022 }
1023
1024 d3_selectionPrototype.classed = function(name, value) {
1025   if (arguments.length < 2) {
1026
1027     // For classed(string), return true only if the first node has the specified
1028     // class or classes. Note that even if the browser supports DOMTokenList, it
1029     // probably doesn't support it on SVG elements (which can be animated).
1030     if (typeof name === "string") {
1031       var node = this.node(),
1032           n = (name = d3_selection_classes(name)).length,
1033           i = -1;
1034       if (value = node.classList) {
1035         while (++i < n) if (!value.contains(name[i])) return false;
1036       } else {
1037         value = node.getAttribute("class");
1038         while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
1039       }
1040       return true;
1041     }
1042
1043     // For classed(object), the object specifies the names of classes to add or
1044     // remove. The values may be functions that are evaluated for each element.
1045     for (value in name) this.each(d3_selection_classed(value, name[value]));
1046     return this;
1047   }
1048
1049   // Otherwise, both a name and a value are specified, and are handled as below.
1050   return this.each(d3_selection_classed(name, value));
1051 };
1052
1053 function d3_selection_classedRe(name) {
1054   return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
1055 }
1056
1057 function d3_selection_classes(name) {
1058   return (name + "").trim().split(/^|\s+/);
1059 }
1060
1061 // Multiple class names are allowed (e.g., "foo bar").
1062 function d3_selection_classed(name, value) {
1063   name = d3_selection_classes(name).map(d3_selection_classedName);
1064   var n = name.length;
1065
1066   function classedConstant() {
1067     var i = -1;
1068     while (++i < n) name[i](this, value);
1069   }
1070
1071   // When the value is a function, the function is still evaluated only once per
1072   // element even if there are multiple class names.
1073   function classedFunction() {
1074     var i = -1, x = value.apply(this, arguments);
1075     while (++i < n) name[i](this, x);
1076   }
1077
1078   return typeof value === "function"
1079       ? classedFunction
1080       : classedConstant;
1081 }
1082
1083 function d3_selection_classedName(name) {
1084   var re = d3_selection_classedRe(name);
1085   return function(node, value) {
1086     if (c = node.classList) return value ? c.add(name) : c.remove(name);
1087     var c = node.getAttribute("class") || "";
1088     if (value) {
1089       re.lastIndex = 0;
1090       if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
1091     } else {
1092       node.setAttribute("class", d3_collapse(c.replace(re, " ")));
1093     }
1094   };
1095 }
1096
1097 d3_selectionPrototype.style = function(name, value, priority) {
1098   var n = arguments.length;
1099   if (n < 3) {
1100
1101     // For style(object) or style(object, string), the object specifies the
1102     // names and values of the attributes to set or remove. The values may be
1103     // functions that are evaluated for each element. The optional string
1104     // specifies the priority.
1105     if (typeof name !== "string") {
1106       if (n < 2) value = "";
1107       for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
1108       return this;
1109     }
1110
1111     // For style(string), return the computed style value for the first node.
1112     if (n < 2) {
1113       var node = this.node();
1114       return d3_window(node).getComputedStyle(node, null).getPropertyValue(name);
1115     }
1116
1117     // For style(string, string) or style(string, function), use the default
1118     // priority. The priority is ignored for style(string, null).
1119     priority = "";
1120   }
1121
1122   // Otherwise, a name, value and priority are specified, and handled as below.
1123   return this.each(d3_selection_style(name, value, priority));
1124 };
1125
1126 function d3_selection_style(name, value, priority) {
1127
1128   // For style(name, null) or style(name, null, priority), remove the style
1129   // property with the specified name. The priority is ignored.
1130   function styleNull() {
1131     this.style.removeProperty(name);
1132   }
1133
1134   // For style(name, string) or style(name, string, priority), set the style
1135   // property with the specified name, using the specified priority.
1136   function styleConstant() {
1137     this.style.setProperty(name, value, priority);
1138   }
1139
1140   // For style(name, function) or style(name, function, priority), evaluate the
1141   // function for each element, and set or remove the style property as
1142   // appropriate. When setting, use the specified priority.
1143   function styleFunction() {
1144     var x = value.apply(this, arguments);
1145     if (x == null) this.style.removeProperty(name);
1146     else this.style.setProperty(name, x, priority);
1147   }
1148
1149   return value == null
1150       ? styleNull : (typeof value === "function"
1151       ? styleFunction : styleConstant);
1152 }
1153
1154 d3_selectionPrototype.property = function(name, value) {
1155   if (arguments.length < 2) {
1156
1157     // For property(string), return the property value for the first node.
1158     if (typeof name === "string") return this.node()[name];
1159
1160     // For property(object), the object specifies the names and values of the
1161     // properties to set or remove. The values may be functions that are
1162     // evaluated for each element.
1163     for (value in name) this.each(d3_selection_property(value, name[value]));
1164     return this;
1165   }
1166
1167   // Otherwise, both a name and a value are specified, and are handled as below.
1168   return this.each(d3_selection_property(name, value));
1169 };
1170
1171 function d3_selection_property(name, value) {
1172
1173   // For property(name, null), remove the property with the specified name.
1174   function propertyNull() {
1175     delete this[name];
1176   }
1177
1178   // For property(name, string), set the property with the specified name.
1179   function propertyConstant() {
1180     this[name] = value;
1181   }
1182
1183   // For property(name, function), evaluate the function for each element, and
1184   // set or remove the property as appropriate.
1185   function propertyFunction() {
1186     var x = value.apply(this, arguments);
1187     if (x == null) delete this[name];
1188     else this[name] = x;
1189   }
1190
1191   return value == null
1192       ? propertyNull : (typeof value === "function"
1193       ? propertyFunction : propertyConstant);
1194 }
1195
1196 d3_selectionPrototype.text = function(value) {
1197   return arguments.length
1198       ? this.each(typeof value === "function"
1199       ? function() { var v = value.apply(this, arguments); this.textContent = v == null ? "" : v; } : value == null
1200       ? function() { if (this.textContent !== "") this.textContent = ""; }
1201       : function() { if (this.textContent !== value) this.textContent = value; })
1202       : this.node().textContent;
1203 };
1204
1205 d3_selectionPrototype.html = function(value) {
1206   return arguments.length
1207       ? this.each(typeof value === "function"
1208       ? function() { var v = value.apply(this, arguments); this.innerHTML = v == null ? "" : v; } : value == null
1209       ? function() { this.innerHTML = ""; }
1210       : function() { this.innerHTML = value; })
1211       : this.node().innerHTML;
1212 };
1213
1214 d3_selectionPrototype.append = function(name) {
1215   name = d3_selection_creator(name);
1216   return this.select(function() {
1217     return this.appendChild(name.apply(this, arguments));
1218   });
1219 };
1220
1221 function d3_selection_creator(name) {
1222
1223   function create() {
1224     var document = this.ownerDocument,
1225         namespace = this.namespaceURI;
1226     return namespace
1227         ? document.createElementNS(namespace, name)
1228         : document.createElement(name);
1229   }
1230
1231   function createNS() {
1232     return this.ownerDocument.createElementNS(name.space, name.local);
1233   }
1234
1235   return typeof name === "function" ? name
1236       : (name = d3.ns.qualify(name)).local ? createNS
1237       : create;
1238 }
1239
1240 d3_selectionPrototype.insert = function(name, before) {
1241   name = d3_selection_creator(name);
1242   before = d3_selection_selector(before);
1243   return this.select(function() {
1244     return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
1245   });
1246 };
1247
1248 // TODO remove(selector)?
1249 // TODO remove(node)?
1250 // TODO remove(function)?
1251 d3_selectionPrototype.remove = function() {
1252   return this.each(d3_selectionRemove);
1253 };
1254
1255 function d3_selectionRemove() {
1256   var parent = this.parentNode;
1257   if (parent) parent.removeChild(this);
1258 }
1259
1260 d3_selectionPrototype.data = function(value, key) {
1261   var i = -1,
1262       n = this.length,
1263       group,
1264       node;
1265
1266   // If no value is specified, return the first value.
1267   if (!arguments.length) {
1268     value = new Array(n = (group = this[0]).length);
1269     while (++i < n) {
1270       if (node = group[i]) {
1271         value[i] = node.__data__;
1272       }
1273     }
1274     return value;
1275   }
1276
1277   function bind(group, groupData) {
1278     var i,
1279         n = group.length,
1280         m = groupData.length,
1281         n0 = Math.min(n, m),
1282         updateNodes = new Array(m),
1283         enterNodes = new Array(m),
1284         exitNodes = new Array(n),
1285         node,
1286         nodeData;
1287
1288     if (key) {
1289       var nodeByKeyValue = new d3_Map,
1290           keyValues = new Array(n),
1291           keyValue;
1292
1293       for (i = -1; ++i < n;) {
1294         if (nodeByKeyValue.has(keyValue = key.call(node = group[i], node.__data__, i))) {
1295           exitNodes[i] = node; // duplicate selection key
1296         } else {
1297           nodeByKeyValue.set(keyValue, node);
1298         }
1299         keyValues[i] = keyValue;
1300       }
1301
1302       for (i = -1; ++i < m;) {
1303         if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) {
1304           enterNodes[i] = d3_selection_dataNode(nodeData);
1305         } else if (node !== true) { // no duplicate data key
1306           updateNodes[i] = node;
1307           node.__data__ = nodeData;
1308         }
1309         nodeByKeyValue.set(keyValue, true);
1310       }
1311
1312       for (i = -1; ++i < n;) {
1313         if (nodeByKeyValue.get(keyValues[i]) !== true) {
1314           exitNodes[i] = group[i];
1315         }
1316       }
1317     } else {
1318       for (i = -1; ++i < n0;) {
1319         node = group[i];
1320         nodeData = groupData[i];
1321         if (node) {
1322           node.__data__ = nodeData;
1323           updateNodes[i] = node;
1324         } else {
1325           enterNodes[i] = d3_selection_dataNode(nodeData);
1326         }
1327       }
1328       for (; i < m; ++i) {
1329         enterNodes[i] = d3_selection_dataNode(groupData[i]);
1330       }
1331       for (; i < n; ++i) {
1332         exitNodes[i] = group[i];
1333       }
1334     }
1335
1336     enterNodes.update
1337         = updateNodes;
1338
1339     enterNodes.parentNode
1340         = updateNodes.parentNode
1341         = exitNodes.parentNode
1342         = group.parentNode;
1343
1344     enter.push(enterNodes);
1345     update.push(updateNodes);
1346     exit.push(exitNodes);
1347   }
1348
1349   var enter = d3_selection_enter([]),
1350       update = d3_selection([]),
1351       exit = d3_selection([]);
1352
1353   if (typeof value === "function") {
1354     while (++i < n) {
1355       bind(group = this[i], value.call(group, group.parentNode.__data__, i));
1356     }
1357   } else {
1358     while (++i < n) {
1359       bind(group = this[i], value);
1360     }
1361   }
1362
1363   update.enter = function() { return enter; };
1364   update.exit = function() { return exit; };
1365   return update;
1366 };
1367
1368 function d3_selection_dataNode(data) {
1369   return {__data__: data};
1370 }
1371
1372 d3_selectionPrototype.datum = function(value) {
1373   return arguments.length
1374       ? this.property("__data__", value)
1375       : this.property("__data__");
1376 };
1377
1378 d3_selectionPrototype.filter = function(filter) {
1379   var subgroups = [],
1380       subgroup,
1381       group,
1382       node;
1383
1384   if (typeof filter !== "function") filter = d3_selection_filter(filter);
1385
1386   for (var j = 0, m = this.length; j < m; j++) {
1387     subgroups.push(subgroup = []);
1388     subgroup.parentNode = (group = this[j]).parentNode;
1389     for (var i = 0, n = group.length; i < n; i++) {
1390       if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
1391         subgroup.push(node);
1392       }
1393     }
1394   }
1395
1396   return d3_selection(subgroups);
1397 };
1398
1399 function d3_selection_filter(selector) {
1400   return function() {
1401     return d3_selectMatches(this, selector);
1402   };
1403 }
1404
1405 d3_selectionPrototype.order = function() {
1406   for (var j = -1, m = this.length; ++j < m;) {
1407     for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
1408       if (node = group[i]) {
1409         if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
1410         next = node;
1411       }
1412     }
1413   }
1414   return this;
1415 };
1416
1417 d3_selectionPrototype.sort = function(comparator) {
1418   comparator = d3_selection_sortComparator.apply(this, arguments);
1419   for (var j = -1, m = this.length; ++j < m;) this[j].sort(comparator);
1420   return this.order();
1421 };
1422
1423 function d3_selection_sortComparator(comparator) {
1424   if (!arguments.length) comparator = d3_ascending;
1425   return function(a, b) {
1426     return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
1427   };
1428 }
1429
1430 d3_selectionPrototype.each = function(callback) {
1431   return d3_selection_each(this, function(node, i, j) {
1432     callback.call(node, node.__data__, i, j);
1433   });
1434 };
1435
1436 function d3_selection_each(groups, callback) {
1437   for (var j = 0, m = groups.length; j < m; j++) {
1438     for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
1439       if (node = group[i]) callback(node, i, j);
1440     }
1441   }
1442   return groups;
1443 }
1444
1445 d3_selectionPrototype.call = function(callback) {
1446   var args = d3_array(arguments);
1447   callback.apply(args[0] = this, args);
1448   return this;
1449 };
1450
1451 d3_selectionPrototype.empty = function() {
1452   return !this.node();
1453 };
1454
1455 d3_selectionPrototype.node = function() {
1456   for (var j = 0, m = this.length; j < m; j++) {
1457     for (var group = this[j], i = 0, n = group.length; i < n; i++) {
1458       var node = group[i];
1459       if (node) return node;
1460     }
1461   }
1462   return null;
1463 };
1464
1465 d3_selectionPrototype.size = function() {
1466   var n = 0;
1467   d3_selection_each(this, function() { ++n; });
1468   return n;
1469 };
1470
1471 function d3_selection_enter(selection) {
1472   d3_subclass(selection, d3_selection_enterPrototype);
1473   return selection;
1474 }
1475
1476 var d3_selection_enterPrototype = [];
1477
1478 d3.selection.enter = d3_selection_enter;
1479 d3.selection.enter.prototype = d3_selection_enterPrototype;
1480
1481 d3_selection_enterPrototype.append = d3_selectionPrototype.append;
1482 d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
1483 d3_selection_enterPrototype.node = d3_selectionPrototype.node;
1484 d3_selection_enterPrototype.call = d3_selectionPrototype.call;
1485 d3_selection_enterPrototype.size = d3_selectionPrototype.size;
1486
1487
1488 d3_selection_enterPrototype.select = function(selector) {
1489   var subgroups = [],
1490       subgroup,
1491       subnode,
1492       upgroup,
1493       group,
1494       node;
1495
1496   for (var j = -1, m = this.length; ++j < m;) {
1497     upgroup = (group = this[j]).update;
1498     subgroups.push(subgroup = []);
1499     subgroup.parentNode = group.parentNode;
1500     for (var i = -1, n = group.length; ++i < n;) {
1501       if (node = group[i]) {
1502         subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j));
1503         subnode.__data__ = node.__data__;
1504       } else {
1505         subgroup.push(null);
1506       }
1507     }
1508   }
1509
1510   return d3_selection(subgroups);
1511 };
1512
1513 d3_selection_enterPrototype.insert = function(name, before) {
1514   if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
1515   return d3_selectionPrototype.insert.call(this, name, before);
1516 };
1517
1518 function d3_selection_enterInsertBefore(enter) {
1519   var i0, j0;
1520   return function(d, i, j) {
1521     var group = enter[j].update,
1522         n = group.length,
1523         node;
1524     if (j != j0) j0 = j, i0 = 0;
1525     if (i >= i0) i0 = i + 1;
1526     while (!(node = group[i0]) && ++i0 < n);
1527     return node;
1528   };
1529 }
1530
1531 // TODO fast singleton implementation?
1532 d3.select = function(node) {
1533   var group;
1534   if (typeof node === "string") {
1535     group = [d3_select(node, d3_document)];
1536     group.parentNode = d3_document.documentElement;
1537   } else {
1538     group = [node];
1539     group.parentNode = d3_documentElement(node);
1540   }
1541   return d3_selection([group]);
1542 };
1543
1544 d3.selectAll = function(nodes) {
1545   var group;
1546   if (typeof nodes === "string") {
1547     group = d3_array(d3_selectAll(nodes, d3_document));
1548     group.parentNode = d3_document.documentElement;
1549   } else {
1550     group = nodes;
1551     group.parentNode = null;
1552   }
1553   return d3_selection([group]);
1554 };
1555
1556 d3_selectionPrototype.on = function(type, listener, capture) {
1557   var n = arguments.length;
1558   if (n < 3) {
1559
1560     // For on(object) or on(object, boolean), the object specifies the event
1561     // types and listeners to add or remove. The optional boolean specifies
1562     // whether the listener captures events.
1563     if (typeof type !== "string") {
1564       if (n < 2) listener = false;
1565       for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
1566       return this;
1567     }
1568
1569     // For on(string), return the listener for the first node.
1570     if (n < 2) return (n = this.node()["__on" + type]) && n._;
1571
1572     // For on(string, function), use the default capture.
1573     capture = false;
1574   }
1575
1576   // Otherwise, a type, listener and capture are specified, and handled as below.
1577   return this.each(d3_selection_on(type, listener, capture));
1578 };
1579
1580 function d3_selection_on(type, listener, capture) {
1581   var name = "__on" + type,
1582       i = type.indexOf("."),
1583       wrap = d3_selection_onListener;
1584
1585   if (i > 0) type = type.slice(0, i);
1586   var filter = d3_selection_onFilters.get(type);
1587   if (filter) type = filter, wrap = d3_selection_onFilter;
1588
1589   function onRemove() {
1590     var l = this[name];
1591     if (l) {
1592       this.removeEventListener(type, l, l.$);
1593       delete this[name];
1594     }
1595   }
1596
1597   function onAdd() {
1598     var l = wrap(listener, d3_array(arguments));
1599     if (typeof Raven !== 'undefined') l = Raven.wrap(l);
1600     onRemove.call(this);
1601     this.addEventListener(type, this[name] = l, l.$ = capture);
1602     l._ = listener;
1603   }
1604
1605   function removeAll() {
1606     var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"),
1607         match;
1608     for (var name in this) {
1609       if (match = name.match(re)) {
1610         var l = this[name];
1611         this.removeEventListener(match[1], l, l.$);
1612         delete this[name];
1613       }
1614     }
1615   }
1616
1617   return i
1618       ? listener ? onAdd : onRemove
1619       : listener ? d3_noop : removeAll;
1620 }
1621
1622 var d3_selection_onFilters = d3.map({
1623   mouseenter: "mouseover",
1624   mouseleave: "mouseout"
1625 });
1626
1627 if (d3_document) {
1628   d3_selection_onFilters.forEach(function(k) {
1629     if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
1630   });
1631 }
1632
1633 function d3_selection_onListener(listener, argumentz) {
1634   return function(e) {
1635     var o = d3.event; // Events can be reentrant (e.g., focus).
1636     d3.event = e;
1637     argumentz[0] = this.__data__;
1638     try {
1639       listener.apply(this, argumentz);
1640     } finally {
1641       d3.event = o;
1642     }
1643   };
1644 }
1645
1646 function d3_selection_onFilter(listener, argumentz) {
1647   var l = d3_selection_onListener(listener, argumentz);
1648   return function(e) {
1649     var target = this, related = e.relatedTarget;
1650     if (!related || (related !== target && !(related.compareDocumentPosition(target) & 8))) {
1651       l.call(target, e);
1652     }
1653   };
1654 }
1655
1656 var d3_event_dragSelect,
1657     d3_event_dragId = 0;
1658
1659 function d3_event_dragSuppress(node) {
1660   var name = ".dragsuppress-" + ++d3_event_dragId,
1661       click = "click" + name,
1662       w = d3.select(d3_window(node))
1663           .on("touchmove" + name, d3_eventPreventDefault)
1664           .on("dragstart" + name, d3_eventPreventDefault)
1665           .on("selectstart" + name, d3_eventPreventDefault);
1666
1667   if (d3_event_dragSelect == null) {
1668     d3_event_dragSelect = "onselectstart" in node ? false
1669         : d3_vendorSymbol(node.style, "userSelect");
1670   }
1671
1672   if (d3_event_dragSelect) {
1673     var style = d3_documentElement(node).style,
1674         select = style[d3_event_dragSelect];
1675     style[d3_event_dragSelect] = "none";
1676   }
1677
1678   return function(suppressClick) {
1679     w.on(name, null);
1680     if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
1681     if (suppressClick) { // suppress the next click, but only if it’s immediate
1682       var off = function() { w.on(click, null); };
1683       w.on(click, function() { d3_eventCancel(); off(); }, true);
1684       setTimeout(off, 0);
1685     }
1686   };
1687 }
1688
1689 d3.mouse = function(container) {
1690   return d3_mousePoint(container, d3_eventSource());
1691 };
1692
1693 // https://bugs.webkit.org/show_bug.cgi?id=44083
1694 var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0;
1695
1696 function d3_mousePoint(container, e) {
1697   if (e.changedTouches) e = e.changedTouches[0];
1698   var svg = container.ownerSVGElement || container;
1699   if (svg.createSVGPoint) {
1700     var point = svg.createSVGPoint();
1701     if (d3_mouse_bug44083 < 0) {
1702       var window = d3_window(container);
1703       if (window.scrollX || window.scrollY) {
1704         svg = d3.select("body").append("svg").style({
1705           position: "absolute",
1706           top: 0,
1707           left: 0,
1708           margin: 0,
1709           padding: 0,
1710           border: "none"
1711         }, "important");
1712         var ctm = svg[0][0].getScreenCTM();
1713         d3_mouse_bug44083 = !(ctm.f || ctm.e);
1714         svg.remove();
1715       }
1716     }
1717     if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY;
1718     else point.x = e.clientX, point.y = e.clientY;
1719     point = point.matrixTransform(container.getScreenCTM().inverse());
1720     return [point.x, point.y];
1721   }
1722   var rect = container.getBoundingClientRect();
1723   return [e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop];
1724 };
1725
1726 d3.touches = function(container, touches) {
1727   if (arguments.length < 2) touches = d3_eventSource().touches;
1728   return touches ? d3_array(touches).map(function(touch) {
1729     var point = d3_mousePoint(container, touch);
1730     point.identifier = touch.identifier;
1731     return point;
1732   }) : [];
1733 };
1734 var ε = 1e-6,
1735     ε2 = ε * ε,
1736     π = Math.PI,
1737     τ = 2 * π,
1738     τε = τ - ε,
1739     halfπ = π / 2,
1740     d3_radians = π / 180,
1741     d3_degrees = 180 / π;
1742
1743 function d3_sgn(x) {
1744   return x > 0 ? 1 : x < 0 ? -1 : 0;
1745 }
1746
1747 // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
1748 // the 3D cross product in a quadrant I Cartesian coordinate system (+x is
1749 // right, +y is up). Returns a positive value if ABC is counter-clockwise,
1750 // negative if clockwise, and zero if the points are collinear.
1751 function d3_cross2d(a, b, c) {
1752   return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
1753 }
1754
1755 function d3_acos(x) {
1756   return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
1757 }
1758
1759 function d3_asin(x) {
1760   return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
1761 }
1762
1763 function d3_sinh(x) {
1764   return ((x = Math.exp(x)) - 1 / x) / 2;
1765 }
1766
1767 function d3_cosh(x) {
1768   return ((x = Math.exp(x)) + 1 / x) / 2;
1769 }
1770
1771 function d3_tanh(x) {
1772   return ((x = Math.exp(2 * x)) - 1) / (x + 1);
1773 }
1774
1775 function d3_haversin(x) {
1776   return (x = Math.sin(x / 2)) * x;
1777 }
1778
1779 var ρ = Math.SQRT2,
1780     ρ2 = 2,
1781     ρ4 = 4;
1782
1783 // p0 = [ux0, uy0, w0]
1784 // p1 = [ux1, uy1, w1]
1785 d3.interpolateZoom = function(p0, p1) {
1786   var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],
1787       ux1 = p1[0], uy1 = p1[1], w1 = p1[2];
1788
1789   var dx = ux1 - ux0,
1790       dy = uy1 - uy0,
1791       d2 = dx * dx + dy * dy,
1792       d1 = Math.sqrt(d2),
1793       b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1),
1794       b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1),
1795       r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
1796       r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1),
1797       dr = r1 - r0,
1798       S = (dr || Math.log(w1 / w0)) / ρ;
1799
1800   function interpolate(t) {
1801     var s = t * S;
1802     if (dr) {
1803       // General case.
1804       var coshr0 = d3_cosh(r0),
1805           u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0));
1806       return [
1807         ux0 + u * dx,
1808         uy0 + u * dy,
1809         w0 * coshr0 / d3_cosh(ρ * s + r0)
1810       ];
1811     }
1812     // Special case for u0 ~= u1.
1813     return [
1814       ux0 + t * dx,
1815       uy0 + t * dy,
1816       w0 * Math.exp(ρ * s)
1817     ];
1818   }
1819
1820   interpolate.duration = S * 1000;
1821
1822   return interpolate;
1823 };
1824
1825 d3.behavior.zoom = function() {
1826   var view = {x: 0, y: 0, k: 1},
1827       translate0, // translate when we started zooming (to avoid drift)
1828       center0, // implicit desired position of translate0 after zooming
1829       center, // explicit desired position of translate0 after zooming
1830       size = [960, 500], // viewport size; required for zoom interpolation
1831       scaleExtent = d3_behavior_zoomInfinity,
1832       duration = 250,
1833       zooming = 0,
1834       mousedown = "mousedown.zoom",
1835       mousemove = "mousemove.zoom",
1836       mouseup = "mouseup.zoom",
1837       mousewheelTimer,
1838       touchstart = "touchstart.zoom",
1839       touchtime, // time of last touchstart (to detect double-tap)
1840       event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"),
1841       x0,
1842       x1,
1843       y0,
1844       y1;
1845
1846   // Lazily determine the DOM’s support for Wheel events.
1847   // https://developer.mozilla.org/en-US/docs/Mozilla_event_reference/wheel
1848   if (!d3_behavior_zoomWheel) {
1849     d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); }, "wheel")
1850         : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { return d3.event.wheelDelta; }, "mousewheel")
1851         : (d3_behavior_zoomDelta = function() { return -d3.event.detail; }, "MozMousePixelScroll");
1852   }
1853
1854   function zoom(g) {
1855     g   .on(mousedown, mousedowned)
1856         .on(d3_behavior_zoomWheel + ".zoom", mousewheeled)
1857         .on("dblclick.zoom", dblclicked)
1858         .on(touchstart, touchstarted);
1859   }
1860
1861   zoom.event = function(g) {
1862     g.each(function() {
1863       var dispatch = event.of(this, arguments),
1864           view1 = view;
1865       if (d3_transitionInheritId) {
1866         d3.select(this).transition()
1867             .each("start.zoom", function() {
1868               view = this.__chart__ || {x: 0, y: 0, k: 1}; // pre-transition state
1869               zoomstarted(dispatch);
1870             })
1871             .tween("zoom:zoom", function() {
1872               var dx = size[0],
1873                   dy = size[1],
1874                   cx = center0 ? center0[0] : dx / 2,
1875                   cy = center0 ? center0[1] : dy / 2,
1876                   i = d3.interpolateZoom(
1877                     [(cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k],
1878                     [(cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k]
1879                   );
1880               return function(t) {
1881                 var l = i(t), k = dx / l[2];
1882                 this.__chart__ = view = {x: cx - l[0] * k, y: cy - l[1] * k, k: k};
1883                 zoomed(dispatch);
1884               };
1885             })
1886             .each("interrupt.zoom", function() {
1887               zoomended(dispatch);
1888             })
1889             .each("end.zoom", function() {
1890               zoomended(dispatch);
1891             });
1892       } else {
1893         this.__chart__ = view;
1894         zoomstarted(dispatch);
1895         zoomed(dispatch);
1896         zoomended(dispatch);
1897       }
1898     });
1899   }
1900
1901   zoom.translate = function(_) {
1902     if (!arguments.length) return [view.x, view.y];
1903     view = {x: +_[0], y: +_[1], k: view.k}; // copy-on-write
1904     rescale();
1905     return zoom;
1906   };
1907
1908   zoom.scale = function(_) {
1909     if (!arguments.length) return view.k;
1910     view = {x: view.x, y: view.y, k: +_}; // copy-on-write
1911     rescale();
1912     return zoom;
1913   };
1914
1915   zoom.scaleExtent = function(_) {
1916     if (!arguments.length) return scaleExtent;
1917     scaleExtent = _ == null ? d3_behavior_zoomInfinity : [+_[0], +_[1]];
1918     return zoom;
1919   };
1920
1921   zoom.center = function(_) {
1922     if (!arguments.length) return center;
1923     center = _ && [+_[0], +_[1]];
1924     return zoom;
1925   };
1926
1927   zoom.size = function(_) {
1928     if (!arguments.length) return size;
1929     size = _ && [+_[0], +_[1]];
1930     return zoom;
1931   };
1932
1933   zoom.duration = function(_) {
1934     if (!arguments.length) return duration;
1935     duration = +_; // TODO function based on interpolateZoom distance?
1936     return zoom;
1937   };
1938
1939   zoom.x = function(z) {
1940     if (!arguments.length) return x1;
1941     x1 = z;
1942     x0 = z.copy();
1943     view = {x: 0, y: 0, k: 1}; // copy-on-write
1944     return zoom;
1945   };
1946
1947   zoom.y = function(z) {
1948     if (!arguments.length) return y1;
1949     y1 = z;
1950     y0 = z.copy();
1951     view = {x: 0, y: 0, k: 1}; // copy-on-write
1952     return zoom;
1953   };
1954
1955   function location(p) {
1956     return [(p[0] - view.x) / view.k, (p[1] - view.y) / view.k];
1957   }
1958
1959   function point(l) {
1960     return [l[0] * view.k + view.x, l[1] * view.k + view.y];
1961   }
1962
1963   function scaleTo(s) {
1964     view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
1965   }
1966
1967   function translateTo(p, l) {
1968     l = point(l);
1969     view.x += p[0] - l[0];
1970     view.y += p[1] - l[1];
1971   }
1972
1973   function zoomTo(that, p, l, k) {
1974     that.__chart__ = {x: view.x, y: view.y, k: view.k};
1975
1976     scaleTo(Math.pow(2, k));
1977     translateTo(center0 = p, l);
1978
1979     that = d3.select(that);
1980     if (duration > 0) that = that.transition().duration(duration);
1981     that.call(zoom.event);
1982   }
1983
1984   function rescale() {
1985     if (x1) x1.domain(x0.range().map(function(x) { return (x - view.x) / view.k; }).map(x0.invert));
1986     if (y1) y1.domain(y0.range().map(function(y) { return (y - view.y) / view.k; }).map(y0.invert));
1987   }
1988
1989   function zoomstarted(dispatch) {
1990     if (!zooming++) dispatch({type: "zoomstart"});
1991   }
1992
1993   function zoomed(dispatch) {
1994     rescale();
1995     dispatch({type: "zoom", scale: view.k, translate: [view.x, view.y]});
1996   }
1997
1998   function zoomended(dispatch) {
1999     if (!--zooming) dispatch({type: "zoomend"});
2000     center0 = null;
2001   }
2002
2003   function mousedowned() {
2004     var that = this,
2005         target = d3.event.target,
2006         dispatch = event.of(that, arguments),
2007         dragged = 0,
2008         subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended),
2009         location0 = location(d3.mouse(that)),
2010         dragRestore = d3_event_dragSuppress(that);
2011
2012     d3_selection_interrupt.call(that);
2013     zoomstarted(dispatch);
2014
2015     function moved() {
2016       dragged = 1;
2017       translateTo(d3.mouse(that), location0);
2018       zoomed(dispatch);
2019     }
2020
2021     function ended() {
2022       subject.on(mousemove, null).on(mouseup, null);
2023       dragRestore(dragged && d3.event.target === target);
2024       zoomended(dispatch);
2025     }
2026   }
2027
2028   // These closures persist for as long as at least one touch is active.
2029   function touchstarted() {
2030     var that = this,
2031         dispatch = event.of(that, arguments),
2032         locations0 = {}, // touchstart locations
2033         distance0 = 0, // distance² between initial touches
2034         scale0, // scale when we started touching
2035         zoomName = ".zoom-" + d3.event.changedTouches[0].identifier,
2036         touchmove = "touchmove" + zoomName,
2037         touchend = "touchend" + zoomName,
2038         targets = [],
2039         subject = d3.select(that),
2040         dragRestore = d3_event_dragSuppress(that);
2041
2042     started();
2043     zoomstarted(dispatch);
2044
2045     // Workaround for Chrome issue 412723: the touchstart listener must be set
2046     // after the touchmove listener.
2047     subject.on(mousedown, null).on(touchstart, started); // prevent duplicate events
2048
2049     // Updates locations of any touches in locations0.
2050     function relocate() {
2051       var touches = d3.touches(that);
2052       scale0 = view.k;
2053       touches.forEach(function(t) {
2054         if (t.identifier in locations0) locations0[t.identifier] = location(t);
2055       });
2056       return touches;
2057     }
2058
2059     // Temporarily override touchstart while gesture is active.
2060     function started() {
2061
2062       // Listen for touchmove and touchend on the target of touchstart.
2063       var target = d3.event.target;
2064       d3.select(target).on(touchmove, moved).on(touchend, ended);
2065       targets.push(target);
2066
2067       // Only track touches started on the same subject element.
2068       var changed = d3.event.changedTouches;
2069       for (var i = 0, n = changed.length; i < n; ++i) {
2070         locations0[changed[i].identifier] = null;
2071       }
2072
2073       var touches = relocate(),
2074           now = Date.now();
2075
2076       if (touches.length === 1) {
2077         if (now - touchtime < 500) { // dbltap
2078           var p = touches[0];
2079           zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1);
2080           d3_eventPreventDefault();
2081         }
2082         touchtime = now;
2083       } else if (touches.length > 1) {
2084         var p = touches[0], q = touches[1],
2085             dx = p[0] - q[0], dy = p[1] - q[1];
2086         distance0 = dx * dx + dy * dy;
2087       }
2088     }
2089
2090     function moved() {
2091       var touches = d3.touches(that),
2092           p0, l0,
2093           p1, l1;
2094
2095       d3_selection_interrupt.call(that);
2096
2097       for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
2098         p1 = touches[i];
2099         if (l1 = locations0[p1.identifier]) {
2100           if (l0) break;
2101           p0 = p1, l0 = l1;
2102         }
2103       }
2104
2105       if (l1) {
2106         var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1,
2107             scale1 = distance0 && Math.sqrt(distance1 / distance0);
2108         p0 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
2109         l0 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
2110         scaleTo(scale1 * scale0);
2111       }
2112
2113       touchtime = null;
2114       translateTo(p0, l0);
2115       zoomed(dispatch);
2116     }
2117
2118     function ended() {
2119       // If there are any globally-active touches remaining, remove the ended
2120       // touches from locations0.
2121       if (d3.event.touches.length) {
2122         var changed = d3.event.changedTouches;
2123         for (var i = 0, n = changed.length; i < n; ++i) {
2124           delete locations0[changed[i].identifier];
2125         }
2126         // If locations0 is not empty, then relocate and continue listening for
2127         // touchmove and touchend.
2128         for (var identifier in locations0) {
2129           return void relocate(); // locations may have detached due to rotation
2130         }
2131       }
2132       // Otherwise, remove touchmove and touchend listeners.
2133       d3.selectAll(targets).on(zoomName, null);
2134       subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
2135       dragRestore();
2136       zoomended(dispatch);
2137     }
2138   }
2139
2140   function mousewheeled() {
2141     var dispatch = event.of(this, arguments);
2142     if (mousewheelTimer) clearTimeout(mousewheelTimer);
2143     else translate0 = location(center0 = center || d3.mouse(this)), d3_selection_interrupt.call(this), zoomstarted(dispatch);
2144     mousewheelTimer = setTimeout(function() { mousewheelTimer = null; zoomended(dispatch); }, 50);
2145     d3_eventPreventDefault();
2146     scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
2147     translateTo(center0, translate0);
2148     zoomed(dispatch);
2149   }
2150
2151   function dblclicked() {
2152     var p = d3.mouse(this),
2153         k = Math.log(view.k) / Math.LN2;
2154
2155     zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1);
2156   }
2157
2158   return d3.rebind(zoom, event, "on");
2159 };
2160
2161 var d3_behavior_zoomInfinity = [0, Infinity], // default scale extent
2162     d3_behavior_zoomDelta, // initialized lazily
2163     d3_behavior_zoomWheel;
2164 function d3_functor(v) {
2165   return typeof v === "function" ? v : function() { return v; };
2166 }
2167
2168 d3.functor = d3_functor;
2169
2170 d3.touch = function(container, touches, identifier) {
2171   if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
2172   if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
2173     if ((touch = touches[i]).identifier === identifier) {
2174       return d3_mousePoint(container, touch);
2175     }
2176   }
2177 };
2178
2179 var d3_timer_queueHead,
2180     d3_timer_queueTail,
2181     d3_timer_interval, // is an interval (or frame) active?
2182     d3_timer_timeout, // is a timeout active?
2183     d3_timer_active, // active timer object
2184     d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { setTimeout(callback, 17); };
2185
2186 // The timer will continue to fire until callback returns true.
2187 d3.timer = function(callback, delay, then) {
2188   var n = arguments.length;
2189   if (n < 2) delay = 0;
2190   if (n < 3) then = Date.now();
2191
2192   // Add the callback to the tail of the queue.
2193   var time = then + delay, timer = {c: callback, t: time, f: false, n: null};
2194   if (d3_timer_queueTail) d3_timer_queueTail.n = timer;
2195   else d3_timer_queueHead = timer;
2196   d3_timer_queueTail = timer;
2197
2198   // Start animatin'!
2199   if (!d3_timer_interval) {
2200     d3_timer_timeout = clearTimeout(d3_timer_timeout);
2201     d3_timer_interval = 1;
2202     d3_timer_frame(d3_timer_step);
2203   }
2204 };
2205
2206 function d3_timer_step() {
2207   var now = d3_timer_mark(),
2208       delay = d3_timer_sweep() - now;
2209   if (delay > 24) {
2210     if (isFinite(delay)) {
2211       clearTimeout(d3_timer_timeout);
2212       d3_timer_timeout = setTimeout(d3_timer_step, delay);
2213     }
2214     d3_timer_interval = 0;
2215   } else {
2216     d3_timer_interval = 1;
2217     d3_timer_frame(d3_timer_step);
2218   }
2219 }
2220
2221 d3.timer.flush = function() {
2222   d3_timer_mark();
2223   d3_timer_sweep();
2224 };
2225
2226 function d3_timer_mark() {
2227   var now = Date.now();
2228   d3_timer_active = d3_timer_queueHead;
2229   while (d3_timer_active) {
2230     if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t);
2231     d3_timer_active = d3_timer_active.n;
2232   }
2233   return now;
2234 }
2235
2236 // Flush after callbacks to avoid concurrent queue modification.
2237 // Returns the time of the earliest active timer, post-sweep.
2238 function d3_timer_sweep() {
2239   var t0,
2240       t1 = d3_timer_queueHead,
2241       time = Infinity;
2242   while (t1) {
2243     if (t1.f) {
2244       t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
2245     } else {
2246       if (t1.t < time) time = t1.t;
2247       t1 = (t0 = t1).n;
2248     }
2249   }
2250   d3_timer_queueTail = t0;
2251   return time;
2252 }
2253 d3.geo = {};
2254
2255 d3.geo.stream = function(object, listener) {
2256   if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
2257     d3_geo_streamObjectType[object.type](object, listener);
2258   } else {
2259     d3_geo_streamGeometry(object, listener);
2260   }
2261 };
2262
2263 function d3_geo_streamGeometry(geometry, listener) {
2264   if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
2265     d3_geo_streamGeometryType[geometry.type](geometry, listener);
2266   }
2267 }
2268
2269 var d3_geo_streamObjectType = {
2270   Feature: function(feature, listener) {
2271     d3_geo_streamGeometry(feature.geometry, listener);
2272   },
2273   FeatureCollection: function(object, listener) {
2274     var features = object.features, i = -1, n = features.length;
2275     while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
2276   }
2277 };
2278
2279 var d3_geo_streamGeometryType = {
2280   Sphere: function(object, listener) {
2281     listener.sphere();
2282   },
2283   Point: function(object, listener) {
2284     object = object.coordinates;
2285     listener.point(object[0], object[1], object[2]);
2286   },
2287   MultiPoint: function(object, listener) {
2288     var coordinates = object.coordinates, i = -1, n = coordinates.length;
2289     while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
2290   },
2291   LineString: function(object, listener) {
2292     d3_geo_streamLine(object.coordinates, listener, 0);
2293   },
2294   MultiLineString: function(object, listener) {
2295     var coordinates = object.coordinates, i = -1, n = coordinates.length;
2296     while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
2297   },
2298   Polygon: function(object, listener) {
2299     d3_geo_streamPolygon(object.coordinates, listener);
2300   },
2301   MultiPolygon: function(object, listener) {
2302     var coordinates = object.coordinates, i = -1, n = coordinates.length;
2303     while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
2304   },
2305   GeometryCollection: function(object, listener) {
2306     var geometries = object.geometries, i = -1, n = geometries.length;
2307     while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
2308   }
2309 };
2310
2311 function d3_geo_streamLine(coordinates, listener, closed) {
2312   var i = -1, n = coordinates.length - closed, coordinate;
2313   listener.lineStart();
2314   while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
2315   listener.lineEnd();
2316 }
2317
2318 function d3_geo_streamPolygon(coordinates, listener) {
2319   var i = -1, n = coordinates.length;
2320   listener.polygonStart();
2321   while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
2322   listener.polygonEnd();
2323 }
2324
2325 d3.geo.length = function(object) {
2326   d3_geo_lengthSum = 0;
2327   d3.geo.stream(object, d3_geo_length);
2328   return d3_geo_lengthSum;
2329 };
2330
2331 var d3_geo_lengthSum;
2332
2333 var d3_geo_length = {
2334   sphere: d3_noop,
2335   point: d3_noop,
2336   lineStart: d3_geo_lengthLineStart,
2337   lineEnd: d3_noop,
2338   polygonStart: d3_noop,
2339   polygonEnd: d3_noop
2340 };
2341
2342 function d3_geo_lengthLineStart() {
2343   var λ0, sinφ0, cosφ0;
2344
2345   d3_geo_length.point = function(λ, φ) {
2346     λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ);
2347     d3_geo_length.point = nextPoint;
2348   };
2349
2350   d3_geo_length.lineEnd = function() {
2351     d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
2352   };
2353
2354   function nextPoint(λ, φ) {
2355     var sinφ = Math.sin(φ *= d3_radians),
2356         cosφ = Math.cos(φ),
2357         t = abs((λ *= d3_radians) - λ0),
2358         cosΔλ = Math.cos(t);
2359     d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ);
2360     λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ;
2361   }
2362 }
2363 function d3_identity(d) {
2364   return d;
2365 }
2366 function d3_true() {
2367   return true;
2368 }
2369
2370 function d3_geo_spherical(cartesian) {
2371   return [
2372     Math.atan2(cartesian[1], cartesian[0]),
2373     d3_asin(cartesian[2])
2374   ];
2375 }
2376
2377 function d3_geo_sphericalEqual(a, b) {
2378   return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
2379 }
2380
2381 // General spherical polygon clipping algorithm: takes a polygon, cuts it into
2382 // visible line segments and rejoins the segments by interpolating along the
2383 // clip edge.
2384 function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
2385   var subject = [],
2386       clip = [];
2387
2388   segments.forEach(function(segment) {
2389     if ((n = segment.length - 1) <= 0) return;
2390     var n, p0 = segment[0], p1 = segment[n];
2391
2392     // If the first and last points of a segment are coincident, then treat as
2393     // a closed ring.
2394     // TODO if all rings are closed, then the winding order of the exterior
2395     // ring should be checked.
2396     if (d3_geo_sphericalEqual(p0, p1)) {
2397       listener.lineStart();
2398       for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
2399       listener.lineEnd();
2400       return;
2401     }
2402
2403     var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true),
2404         b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
2405     a.o = b;
2406     subject.push(a);
2407     clip.push(b);
2408     a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
2409     b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
2410     a.o = b;
2411     subject.push(a);
2412     clip.push(b);
2413   });
2414   clip.sort(compare);
2415   d3_geo_clipPolygonLinkCircular(subject);
2416   d3_geo_clipPolygonLinkCircular(clip);
2417   if (!subject.length) return;
2418
2419   for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
2420     clip[i].e = entry = !entry;
2421   }
2422
2423   var start = subject[0],
2424       points,
2425       point;
2426   while (1) {
2427     // Find first unvisited intersection.
2428     var current = start,
2429         isSubject = true;
2430     while (current.v) if ((current = current.n) === start) return;
2431     points = current.z;
2432     listener.lineStart();
2433     do {
2434       current.v = current.o.v = true;
2435       if (current.e) {
2436         if (isSubject) {
2437           for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
2438         } else {
2439           interpolate(current.x, current.n.x, 1, listener);
2440         }
2441         current = current.n;
2442       } else {
2443         if (isSubject) {
2444           points = current.p.z;
2445           for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
2446         } else {
2447           interpolate(current.x, current.p.x, -1, listener);
2448         }
2449         current = current.p;
2450       }
2451       current = current.o;
2452       points = current.z;
2453       isSubject = !isSubject;
2454     } while (!current.v);
2455     listener.lineEnd();
2456   }
2457 }
2458
2459 function d3_geo_clipPolygonLinkCircular(array) {
2460   if (!(n = array.length)) return;
2461   var n,
2462       i = 0,
2463       a = array[0],
2464       b;
2465   while (++i < n) {
2466     a.n = b = array[i];
2467     b.p = a;
2468     a = b;
2469   }
2470   a.n = b = array[0];
2471   b.p = a;
2472 }
2473
2474 function d3_geo_clipPolygonIntersection(point, points, other, entry) {
2475   this.x = point;
2476   this.z = points;
2477   this.o = other; // another intersection
2478   this.e = entry; // is an entry?
2479   this.v = false; // visited
2480   this.n = this.p = null; // next & previous
2481 }
2482
2483 function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
2484   return function(rotate, listener) {
2485     var line = clipLine(listener),
2486         rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
2487
2488     var clip = {
2489       point: point,
2490       lineStart: lineStart,
2491       lineEnd: lineEnd,
2492       polygonStart: function() {
2493         clip.point = pointRing;
2494         clip.lineStart = ringStart;
2495         clip.lineEnd = ringEnd;
2496         segments = [];
2497         polygon = [];
2498       },
2499       polygonEnd: function() {
2500         clip.point = point;
2501         clip.lineStart = lineStart;
2502         clip.lineEnd = lineEnd;
2503
2504         segments = d3.merge(segments);
2505         var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
2506         if (segments.length) {
2507           if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2508           d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
2509         } else if (clipStartInside) {
2510           if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2511           listener.lineStart();
2512           interpolate(null, null, 1, listener);
2513           listener.lineEnd();
2514         }
2515         if (polygonStarted) listener.polygonEnd(), polygonStarted = false;
2516         segments = polygon = null;
2517       },
2518       sphere: function() {
2519         listener.polygonStart();
2520         listener.lineStart();
2521         interpolate(null, null, 1, listener);
2522         listener.lineEnd();
2523         listener.polygonEnd();
2524       }
2525     };
2526
2527     function point(λ, φ) {
2528       var point = rotate(λ, φ);
2529       if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
2530     }
2531     function pointLine(λ, φ) {
2532       var point = rotate(λ, φ);
2533       line.point(point[0], point[1]);
2534     }
2535     function lineStart() { clip.point = pointLine; line.lineStart(); }
2536     function lineEnd() { clip.point = point; line.lineEnd(); }
2537
2538     var segments;
2539
2540     var buffer = d3_geo_clipBufferListener(),
2541         ringListener = clipLine(buffer),
2542         polygonStarted = false,
2543         polygon,
2544         ring;
2545
2546     function pointRing(λ, φ) {
2547       ring.push([λ, φ]);
2548       var point = rotate(λ, φ);
2549       ringListener.point(point[0], point[1]);
2550     }
2551
2552     function ringStart() {
2553       ringListener.lineStart();
2554       ring = [];
2555     }
2556
2557     function ringEnd() {
2558       pointRing(ring[0][0], ring[0][1]);
2559       ringListener.lineEnd();
2560
2561       var clean = ringListener.clean(),
2562           ringSegments = buffer.buffer(),
2563           segment,
2564           n = ringSegments.length;
2565
2566       ring.pop();
2567       polygon.push(ring);
2568       ring = null;
2569
2570       if (!n) return;
2571
2572       // No intersections.
2573       if (clean & 1) {
2574         segment = ringSegments[0];
2575         var n = segment.length - 1,
2576             i = -1,
2577             point;
2578         if (n > 0) {
2579           if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
2580           listener.lineStart();
2581           while (++i < n) listener.point((point = segment[i])[0], point[1]);
2582           listener.lineEnd();
2583         }
2584         return;
2585       }
2586
2587       // Rejoin connected segments.
2588       // TODO reuse bufferListener.rejoin()?
2589       if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
2590
2591       segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
2592     }
2593
2594     return clip;
2595   };
2596 }
2597
2598 function d3_geo_clipSegmentLength1(segment) {
2599   return segment.length > 1;
2600 }
2601
2602 function d3_geo_clipBufferListener() {
2603   var lines = [],
2604       line;
2605   return {
2606     lineStart: function() { lines.push(line = []); },
2607     point: function(λ, φ) { line.push([λ, φ]); },
2608     lineEnd: d3_noop,
2609     buffer: function() {
2610       var buffer = lines;
2611       lines = [];
2612       line = null;
2613       return buffer;
2614     },
2615     rejoin: function() {
2616       if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
2617     }
2618   };
2619 }
2620
2621 // Intersection points are sorted along the clip edge. For both antimeridian
2622 // cutting and circle clipping, the same comparison is used.
2623 function d3_geo_clipSort(a, b) {
2624   return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1])
2625        - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]);
2626 }
2627
2628 var d3_geo_clipAntimeridian = d3_geo_clip(
2629     d3_true,
2630     d3_geo_clipAntimeridianLine,
2631     d3_geo_clipAntimeridianInterpolate,
2632     [-π, -π / 2]);
2633
2634 // Takes a line and cuts into visible segments. Return values:
2635 //   0: there were intersections or the line was empty.
2636 //   1: no intersections.
2637 //   2: there were intersections, and the first and last segments should be
2638 //      rejoined.
2639 function d3_geo_clipAntimeridianLine(listener) {
2640   var λ0 = NaN,
2641       φ0 = NaN,
2642       sλ0 = NaN,
2643       clean; // no intersections
2644
2645   return {
2646     lineStart: function() {
2647       listener.lineStart();
2648       clean = 1;
2649     },
2650     point: function(λ1, φ1) {
2651       var sλ1 = λ1 > 0 ? π : -π,
2652           dλ = abs(λ1 - λ0);
2653       if (abs(dλ - π) < ε) { // line crosses a pole
2654         listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
2655         listener.point(sλ0, φ0);
2656         listener.lineEnd();
2657         listener.lineStart();
2658         listener.point(sλ1, φ0);
2659         listener.point(λ1, φ0);
2660         clean = 0;
2661       } else if (sλ0 !== sλ1 && dλ >= π) { // line crosses antimeridian
2662         // handle degeneracies
2663         if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
2664         if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
2665         φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
2666         listener.point(sλ0, φ0);
2667         listener.lineEnd();
2668         listener.lineStart();
2669         listener.point(sλ1, φ0);
2670         clean = 0;
2671       }
2672       listener.point(λ0 = λ1, φ0 = φ1);
2673       sλ0 = sλ1;
2674     },
2675     lineEnd: function() {
2676       listener.lineEnd();
2677       λ0 = φ0 = NaN;
2678     },
2679     // if there are intersections, we always rejoin the first and last segments.
2680     clean: function() { return 2 - clean; }
2681   };
2682 }
2683
2684 function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
2685   var cosφ0,
2686       cosφ1,
2687       sinλ0_λ1 = Math.sin(λ0 - λ1);
2688   return abs(sinλ0_λ1) > ε
2689       ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1)
2690                  - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0))
2691                  / (cosφ0 * cosφ1 * sinλ0_λ1))
2692       : (φ0 + φ1) / 2;
2693 }
2694
2695 function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
2696   var φ;
2697   if (from == null) {
2698     φ = direction * halfπ;
2699     listener.point(-π,  φ);
2700     listener.point( 0,  φ);
2701     listener.point( π,  φ);
2702     listener.point( π,  0);
2703     listener.point( π, -φ);
2704     listener.point( 0, -φ);
2705     listener.point(-π, -φ);
2706     listener.point(-π,  0);
2707     listener.point(-π,  φ);
2708   } else if (abs(from[0] - to[0]) > ε) {
2709     var s = from[0] < to[0] ? π : -π;
2710     φ = direction * s / 2;
2711     listener.point(-s, φ);
2712     listener.point( 0, φ);
2713     listener.point( s, φ);
2714   } else {
2715     listener.point(to[0], to[1]);
2716   }
2717 }
2718 // TODO
2719 // cross and scale return new vectors,
2720 // whereas add and normalize operate in-place
2721
2722 function d3_geo_cartesian(spherical) {
2723   var λ = spherical[0],
2724       φ = spherical[1],
2725       cosφ = Math.cos(φ);
2726   return [
2727     cosφ * Math.cos(λ),
2728     cosφ * Math.sin(λ),
2729     Math.sin(φ)
2730   ];
2731 }
2732
2733 function d3_geo_cartesianDot(a, b) {
2734   return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
2735 }
2736
2737 function d3_geo_cartesianCross(a, b) {
2738   return [
2739     a[1] * b[2] - a[2] * b[1],
2740     a[2] * b[0] - a[0] * b[2],
2741     a[0] * b[1] - a[1] * b[0]
2742   ];
2743 }
2744
2745 function d3_geo_cartesianAdd(a, b) {
2746   a[0] += b[0];
2747   a[1] += b[1];
2748   a[2] += b[2];
2749 }
2750
2751 function d3_geo_cartesianScale(vector, k) {
2752   return [
2753     vector[0] * k,
2754     vector[1] * k,
2755     vector[2] * k
2756   ];
2757 }
2758
2759 function d3_geo_cartesianNormalize(d) {
2760   var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
2761   d[0] /= l;
2762   d[1] /= l;
2763   d[2] /= l;
2764 }
2765 function d3_geo_compose(a, b) {
2766
2767   function compose(x, y) {
2768     return x = a(x, y), b(x[0], x[1]);
2769   }
2770
2771   if (a.invert && b.invert) compose.invert = function(x, y) {
2772     return x = b.invert(x, y), x && a.invert(x[0], x[1]);
2773   };
2774
2775   return compose;
2776 }
2777
2778 function d3_geo_equirectangular(λ, φ) {
2779   return [λ, φ];
2780 }
2781
2782 (d3.geo.equirectangular = function() {
2783   return d3_geo_projection(d3_geo_equirectangular);
2784 }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
2785
2786 d3.geo.rotation = function(rotate) {
2787   rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
2788
2789   function forward(coordinates) {
2790     coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
2791     return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
2792   }
2793
2794   forward.invert = function(coordinates) {
2795     coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
2796     return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
2797   };
2798
2799   return forward;
2800 };
2801
2802 function d3_geo_identityRotation(λ, φ) {
2803   return [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ];
2804 }
2805
2806 d3_geo_identityRotation.invert = d3_geo_equirectangular;
2807
2808 // Note: |δλ| must be < 2π
2809 function d3_geo_rotation(δλ, δφ, δγ) {
2810   return δλ ? (δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ))
2811     : d3_geo_rotationλ(δλ))
2812     : (δφ || δγ ? d3_geo_rotationφγ(δφ, δγ)
2813     : d3_geo_identityRotation);
2814 }
2815
2816 function d3_geo_forwardRotationλ(δλ) {
2817   return function(λ, φ) {
2818     return λ += δλ, [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ];
2819   };
2820 }
2821
2822 function d3_geo_rotationλ(δλ) {
2823   var rotation = d3_geo_forwardRotationλ(δλ);
2824   rotation.invert = d3_geo_forwardRotationλ(-δλ);
2825   return rotation;
2826 }
2827
2828 function d3_geo_rotationφγ(δφ, δγ) {
2829   var cosδφ = Math.cos(δφ),
2830       sinδφ = Math.sin(δφ),
2831       cosδγ = Math.cos(δγ),
2832       sinδγ = Math.sin(δγ);
2833
2834   function rotation(λ, φ) {
2835     var cosφ = Math.cos(φ),
2836         x = Math.cos(λ) * cosφ,
2837         y = Math.sin(λ) * cosφ,
2838         z = Math.sin(φ),
2839         k = z * cosδφ + x * sinδφ;
2840     return [
2841       Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ),
2842       d3_asin(k * cosδγ + y * sinδγ)
2843     ];
2844   }
2845
2846   rotation.invert = function(λ, φ) {
2847     var cosφ = Math.cos(φ),
2848         x = Math.cos(λ) * cosφ,
2849         y = Math.sin(λ) * cosφ,
2850         z = Math.sin(φ),
2851         k = z * cosδγ - y * sinδγ;
2852     return [
2853       Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ),
2854       d3_asin(k * cosδφ - x * sinδφ)
2855     ];
2856   };
2857
2858   return rotation;
2859 }
2860
2861 d3.geo.circle = function() {
2862   var origin = [0, 0],
2863       angle,
2864       precision = 6,
2865       interpolate;
2866
2867   function circle() {
2868     var center = typeof origin === "function" ? origin.apply(this, arguments) : origin,
2869         rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert,
2870         ring = [];
2871
2872     interpolate(null, null, 1, {
2873       point: function(x, y) {
2874         ring.push(x = rotate(x, y));
2875         x[0] *= d3_degrees, x[1] *= d3_degrees;
2876       }
2877     });
2878
2879     return {type: "Polygon", coordinates: [ring]};
2880   }
2881
2882   circle.origin = function(x) {
2883     if (!arguments.length) return origin;
2884     origin = x;
2885     return circle;
2886   };
2887
2888   circle.angle = function(x) {
2889     if (!arguments.length) return angle;
2890     interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
2891     return circle;
2892   };
2893
2894   circle.precision = function(_) {
2895     if (!arguments.length) return precision;
2896     interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
2897     return circle;
2898   };
2899
2900   return circle.angle(90);
2901 };
2902
2903 // Interpolates along a circle centered at [0°, 0°], with a given radius and
2904 // precision.
2905 function d3_geo_circleInterpolate(radius, precision) {
2906   var cr = Math.cos(radius),
2907       sr = Math.sin(radius);
2908   return function(from, to, direction, listener) {
2909     var step = direction * precision;
2910     if (from != null) {
2911       from = d3_geo_circleAngle(cr, from);
2912       to = d3_geo_circleAngle(cr, to);
2913       if (direction > 0 ? from < to: from > to) from += direction * τ;
2914     } else {
2915       from = radius + direction * τ;
2916       to = radius - .5 * step;
2917     }
2918     for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
2919       listener.point((point = d3_geo_spherical([
2920         cr,
2921         -sr * Math.cos(t),
2922         -sr * Math.sin(t)
2923       ]))[0], point[1]);
2924     }
2925   };
2926 }
2927
2928 // Signed angle of a cartesian point relative to [cr, 0, 0].
2929 function d3_geo_circleAngle(cr, point) {
2930   var a = d3_geo_cartesian(point);
2931   a[0] -= cr;
2932   d3_geo_cartesianNormalize(a);
2933   var angle = d3_acos(-a[1]);
2934   return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
2935 }
2936 // Adds floating point numbers with twice the normal precision.
2937 // Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and
2938 // Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)
2939 // 305–363 (1997).
2940 // Code adapted from GeographicLib by Charles F. F. Karney,
2941 // http://geographiclib.sourceforge.net/
2942 // See lib/geographiclib/LICENSE for details.
2943
2944 function d3_adder() {}
2945
2946 d3_adder.prototype = {
2947   s: 0, // rounded value
2948   t: 0, // exact error
2949   add: function(y) {
2950     d3_adderSum(y, this.t, d3_adderTemp);
2951     d3_adderSum(d3_adderTemp.s, this.s, this);
2952     if (this.s) this.t += d3_adderTemp.t;
2953     else this.s = d3_adderTemp.t;
2954   },
2955   reset: function() {
2956     this.s = this.t = 0;
2957   },
2958   valueOf: function() {
2959     return this.s;
2960   }
2961 };
2962
2963 var d3_adderTemp = new d3_adder;
2964
2965 function d3_adderSum(a, b, o) {
2966   var x = o.s = a + b, // a + b
2967       bv = x - a, av = x - bv; // b_virtual & a_virtual
2968   o.t = (a - av) + (b - bv); // a_roundoff + b_roundoff
2969 }
2970
2971 d3.geo.area = function(object) {
2972   d3_geo_areaSum = 0;
2973   d3.geo.stream(object, d3_geo_area);
2974   return d3_geo_areaSum;
2975 };
2976
2977 var d3_geo_areaSum,
2978     d3_geo_areaRingSum = new d3_adder;
2979
2980 var d3_geo_area = {
2981   sphere: function() { d3_geo_areaSum += 4 * π; },
2982   point: d3_noop,
2983   lineStart: d3_noop,
2984   lineEnd: d3_noop,
2985
2986   // Only count area for polygon rings.
2987   polygonStart: function() {
2988     d3_geo_areaRingSum.reset();
2989     d3_geo_area.lineStart = d3_geo_areaRingStart;
2990   },
2991   polygonEnd: function() {
2992     var area = 2 * d3_geo_areaRingSum;
2993     d3_geo_areaSum += area < 0 ? 4 * π + area : area;
2994     d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
2995   }
2996 };
2997
2998 function d3_geo_areaRingStart() {
2999   var λ00, φ00, λ0, cosφ0, sinφ0; // start point and previous point
3000
3001   // For the first point, …
3002   d3_geo_area.point = function(λ, φ) {
3003     d3_geo_area.point = nextPoint;
3004     λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), sinφ0 = Math.sin(φ);
3005   };
3006
3007   // For subsequent points, …
3008   function nextPoint(λ, φ) {
3009     λ *= d3_radians;
3010     φ = φ * d3_radians / 2 + π / 4; // half the angular distance from south pole
3011
3012     // Spherical excess E for a spherical triangle with vertices: south pole,
3013     // previous point, current point.  Uses a formula derived from Cagnoli’s
3014     // theorem.  See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
3015     var dλ = λ - λ0,
3016         sdλ = dλ >= 0 ? 1 : -1,
3017         adλ = sdλ * dλ,
3018         cosφ = Math.cos(φ),
3019         sinφ = Math.sin(φ),
3020         k = sinφ0 * sinφ,
3021         u = cosφ0 * cosφ + k * Math.cos(adλ),
3022         v = k * sdλ * Math.sin(adλ);
3023     d3_geo_areaRingSum.add(Math.atan2(v, u));
3024
3025     // Advance the previous points.
3026     λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
3027   }
3028
3029   // For the last point, return to the start.
3030   d3_geo_area.lineEnd = function() {
3031     nextPoint(λ00, φ00);
3032   };
3033 }
3034
3035 function d3_geo_pointInPolygon(point, polygon) {
3036   var meridian = point[0],
3037       parallel = point[1],
3038       meridianNormal = [Math.sin(meridian), -Math.cos(meridian), 0],
3039       polarAngle = 0,
3040       winding = 0;
3041   d3_geo_areaRingSum.reset();
3042
3043   for (var i = 0, n = polygon.length; i < n; ++i) {
3044     var ring = polygon[i],
3045         m = ring.length;
3046     if (!m) continue;
3047     var point0 = ring[0],
3048         λ0 = point0[0],
3049         φ0 = point0[1] / 2 + π / 4,
3050         sinφ0 = Math.sin(φ0),
3051         cosφ0 = Math.cos(φ0),
3052         j = 1;
3053
3054     while (true) {
3055       if (j === m) j = 0;
3056       point = ring[j];
3057       var λ = point[0],
3058           φ = point[1] / 2 + π / 4,
3059           sinφ = Math.sin(φ),
3060           cosφ = Math.cos(φ),
3061           dλ = λ - λ0,
3062           sdλ = dλ >= 0 ? 1 : -1,
3063           adλ = sdλ * dλ,
3064           antimeridian = adλ > π,
3065           k = sinφ0 * sinφ;
3066       d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
3067
3068       polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
3069
3070       // Are the longitudes either side of the point's meridian, and are the
3071       // latitudes smaller than the parallel?
3072       if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
3073         var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
3074         d3_geo_cartesianNormalize(arc);
3075         var intersection = d3_geo_cartesianCross(meridianNormal, arc);
3076         d3_geo_cartesianNormalize(intersection);
3077         var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
3078         if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
3079           winding += antimeridian ^ dλ >= 0 ? 1 : -1;
3080         }
3081       }
3082       if (!j++) break;
3083       λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
3084     }
3085   }
3086
3087   // First, determine whether the South pole is inside or outside:
3088   //
3089   // It is inside if:
3090   // * the polygon winds around it in a clockwise direction.
3091   // * the polygon does not (cumulatively) wind around it, but has a negative
3092   //   (counter-clockwise) area.
3093   //
3094   // Second, count the (signed) number of times a segment crosses a meridian
3095   // from the point to the South pole.  If it is zero, then the point is the
3096   // same side as the South pole.
3097
3098   return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ (winding & 1);
3099 }
3100
3101 // Clip features against a small circle centered at [0°, 0°].
3102 function d3_geo_clipCircle(radius) {
3103   var cr = Math.cos(radius),
3104       smallRadius = cr > 0,
3105       notHemisphere = abs(cr) > ε, // TODO optimise for this common case
3106       interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
3107
3108   return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-π, radius - π]);
3109
3110   function visible(λ, φ) {
3111     return Math.cos(λ) * Math.cos(φ) > cr;
3112   }
3113
3114   // Takes a line and cuts into visible segments. Return values used for
3115   // polygon clipping:
3116   //   0: there were intersections or the line was empty.
3117   //   1: no intersections.
3118   //   2: there were intersections, and the first and last segments should be
3119   //      rejoined.
3120   function clipLine(listener) {
3121     var point0, // previous point
3122         c0, // code for previous point
3123         v0, // visibility of previous point
3124         v00, // visibility of first point
3125         clean; // no intersections
3126     return {
3127       lineStart: function() {
3128         v00 = v0 = false;
3129         clean = 1;
3130       },
3131       point: function(λ, φ) {
3132         var point1 = [λ, φ],
3133             point2,
3134             v = visible(λ, φ),
3135             c = smallRadius
3136               ? v ? 0 : code(λ, φ)
3137               : v ? code(λ + (λ < 0 ? π : -π), φ) : 0;
3138         if (!point0 && (v00 = v0 = v)) listener.lineStart();
3139         // Handle degeneracies.
3140         // TODO ignore if not clipping polygons.
3141         if (v !== v0) {
3142           point2 = intersect(point0, point1);
3143           if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
3144             point1[0] += ε;
3145             point1[1] += ε;
3146             v = visible(point1[0], point1[1]);
3147           }
3148         }
3149         if (v !== v0) {
3150           clean = 0;
3151           if (v) {
3152             // outside going in
3153             listener.lineStart();
3154             point2 = intersect(point1, point0);
3155             listener.point(point2[0], point2[1]);
3156           } else {
3157             // inside going out
3158             point2 = intersect(point0, point1);
3159             listener.point(point2[0], point2[1]);
3160             listener.lineEnd();
3161           }
3162           point0 = point2;
3163         } else if (notHemisphere && point0 && smallRadius ^ v) {
3164           var t;
3165           // If the codes for two points are different, or are both zero,
3166           // and there this segment intersects with the small circle.
3167           if (!(c & c0) && (t = intersect(point1, point0, true))) {
3168             clean = 0;
3169             if (smallRadius) {
3170               listener.lineStart();
3171               listener.point(t[0][0], t[0][1]);
3172               listener.point(t[1][0], t[1][1]);
3173               listener.lineEnd();
3174             } else {
3175               listener.point(t[1][0], t[1][1]);
3176               listener.lineEnd();
3177               listener.lineStart();
3178               listener.point(t[0][0], t[0][1]);
3179             }
3180           }
3181         }
3182         if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
3183           listener.point(point1[0], point1[1]);
3184         }
3185         point0 = point1, v0 = v, c0 = c;
3186       },
3187       lineEnd: function() {
3188         if (v0) listener.lineEnd();
3189         point0 = null;
3190       },
3191       // Rejoin first and last segments if there were intersections and the first
3192       // and last points were visible.
3193       clean: function() { return clean | ((v00 && v0) << 1); }
3194     };
3195   }
3196
3197   // Intersects the great circle between a and b with the clip circle.
3198   function intersect(a, b, two) {
3199     var pa = d3_geo_cartesian(a),
3200         pb = d3_geo_cartesian(b);
3201
3202     // We have two planes, n1.p = d1 and n2.p = d2.
3203     // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
3204     var n1 = [1, 0, 0], // normal
3205         n2 = d3_geo_cartesianCross(pa, pb),
3206         n2n2 = d3_geo_cartesianDot(n2, n2),
3207         n1n2 = n2[0], // d3_geo_cartesianDot(n1, n2),
3208         determinant = n2n2 - n1n2 * n1n2;
3209
3210     // Two polar points.
3211     if (!determinant) return !two && a;
3212
3213     var c1 =  cr * n2n2 / determinant,
3214         c2 = -cr * n1n2 / determinant,
3215         n1xn2 = d3_geo_cartesianCross(n1, n2),
3216         A = d3_geo_cartesianScale(n1, c1),
3217         B = d3_geo_cartesianScale(n2, c2);
3218     d3_geo_cartesianAdd(A, B);
3219
3220     // Solve |p(t)|^2 = 1.
3221     var u = n1xn2,
3222         w = d3_geo_cartesianDot(A, u),
3223         uu = d3_geo_cartesianDot(u, u),
3224         t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1);
3225
3226     if (t2 < 0) return;
3227
3228     var t = Math.sqrt(t2),
3229         q = d3_geo_cartesianScale(u, (-w - t) / uu);
3230     d3_geo_cartesianAdd(q, A);
3231     q = d3_geo_spherical(q);
3232     if (!two) return q;
3233
3234     // Two intersection points.
3235     var λ0 = a[0],
3236         λ1 = b[0],
3237         φ0 = a[1],
3238         φ1 = b[1],
3239         z;
3240     if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
3241     var δλ = λ1 - λ0,
3242         polar = abs(δλ - π) < ε,
3243         meridian = polar || δλ < ε;
3244
3245     if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
3246
3247     // Check that the first point is between a and b.
3248     if (meridian
3249         ? polar
3250           ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1)
3251           : φ0 <= q[1] && q[1] <= φ1
3252         : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) {
3253       var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
3254       d3_geo_cartesianAdd(q1, A);
3255       return [q, d3_geo_spherical(q1)];
3256     }
3257   }
3258
3259   // Generates a 4-bit vector representing the location of a point relative to
3260   // the small circle's bounding box.
3261   function code(λ, φ) {
3262     var r = smallRadius ? radius : π - radius,
3263         code = 0;
3264     if (λ < -r) code |= 1; // left
3265     else if (λ > r) code |= 2; // right
3266     if (φ < -r) code |= 4; // below
3267     else if (φ > r) code |= 8; // above
3268     return code;
3269   }
3270 }
3271
3272 // Liang–Barsky line clipping.
3273 function d3_geom_clipLine(x0, y0, x1, y1) {
3274   return function(line) {
3275     var a = line.a,
3276         b = line.b,
3277         ax = a.x,
3278         ay = a.y,
3279         bx = b.x,
3280         by = b.y,
3281         t0 = 0,
3282         t1 = 1,
3283         dx = bx - ax,
3284         dy = by - ay,
3285         r;
3286
3287     r = x0 - ax;
3288     if (!dx && r > 0) return;
3289     r /= dx;
3290     if (dx < 0) {
3291       if (r < t0) return;
3292       if (r < t1) t1 = r;
3293     } else if (dx > 0) {
3294       if (r > t1) return;
3295       if (r > t0) t0 = r;
3296     }
3297
3298     r = x1 - ax;
3299     if (!dx && r < 0) return;
3300     r /= dx;
3301     if (dx < 0) {
3302       if (r > t1) return;
3303       if (r > t0) t0 = r;
3304     } else if (dx > 0) {
3305       if (r < t0) return;
3306       if (r < t1) t1 = r;
3307     }
3308
3309     r = y0 - ay;
3310     if (!dy && r > 0) return;
3311     r /= dy;
3312     if (dy < 0) {
3313       if (r < t0) return;
3314       if (r < t1) t1 = r;
3315     } else if (dy > 0) {
3316       if (r > t1) return;
3317       if (r > t0) t0 = r;
3318     }
3319
3320     r = y1 - ay;
3321     if (!dy && r < 0) return;
3322     r /= dy;
3323     if (dy < 0) {
3324       if (r > t1) return;
3325       if (r > t0) t0 = r;
3326     } else if (dy > 0) {
3327       if (r < t0) return;
3328       if (r < t1) t1 = r;
3329     }
3330
3331     if (t0 > 0) line.a = {x: ax + t0 * dx, y: ay + t0 * dy};
3332     if (t1 < 1) line.b = {x: ax + t1 * dx, y: ay + t1 * dy};
3333     return line;
3334   };
3335 }
3336
3337 var d3_geo_clipExtentMAX = 1e9;
3338
3339 d3.geo.clipExtent = function() {
3340   var x0, y0, x1, y1,
3341       stream,
3342       clip,
3343       clipExtent = {
3344         stream: function(output) {
3345           if (stream) stream.valid = false;
3346           stream = clip(output);
3347           stream.valid = true; // allow caching by d3.geo.path
3348           return stream;
3349         },
3350         extent: function(_) {
3351           if (!arguments.length) return [[x0, y0], [x1, y1]];
3352           clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
3353           if (stream) stream.valid = false, stream = null;
3354           return clipExtent;
3355         }
3356       };
3357   return clipExtent.extent([[0, 0], [960, 500]]);
3358 };
3359
3360 function d3_geo_clipExtent(x0, y0, x1, y1) {
3361   return function(listener) {
3362     var listener_ = listener,
3363         bufferListener = d3_geo_clipBufferListener(),
3364         clipLine = d3_geom_clipLine(x0, y0, x1, y1),
3365         segments,
3366         polygon,
3367         ring;
3368
3369     var clip = {
3370       point: point,
3371       lineStart: lineStart,
3372       lineEnd: lineEnd,
3373       polygonStart: function() {
3374         listener = bufferListener;
3375         segments = [];
3376         polygon = [];
3377         clean = true;
3378       },
3379       polygonEnd: function() {
3380         listener = listener_;
3381         segments = d3.merge(segments);
3382         var clipStartInside = insidePolygon([x0, y1]),
3383             inside = clean && clipStartInside,
3384             visible = segments.length;
3385         if (inside || visible) {
3386           listener.polygonStart();
3387           if (inside) {
3388             listener.lineStart();
3389             interpolate(null, null, 1, listener);
3390             listener.lineEnd();
3391           }
3392           if (visible) {
3393             d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
3394           }
3395           listener.polygonEnd();
3396         }
3397         segments = polygon = ring = null;
3398       }
3399     };
3400
3401     function insidePolygon(p) {
3402       var wn = 0, // the winding number counter
3403           n = polygon.length,
3404           y = p[1];
3405
3406       for (var i = 0; i < n; ++i) {
3407         for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
3408           b = v[j];
3409           if (a[1] <= y) {
3410             if (b[1] >  y && d3_cross2d(a, b, p) > 0) ++wn;
3411           } else {
3412             if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn;
3413           }
3414           a = b;
3415         }
3416       }
3417       return wn !== 0;
3418     }
3419
3420     function interpolate(from, to, direction, listener) {
3421       var a = 0, a1 = 0;
3422       if (from == null ||
3423           (a = corner(from, direction)) !== (a1 = corner(to, direction)) ||
3424           comparePoints(from, to) < 0 ^ direction > 0) {
3425         do {
3426           listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
3427         } while ((a = (a + direction + 4) % 4) !== a1);
3428       } else {
3429         listener.point(to[0], to[1]);
3430       }
3431     }
3432
3433     function pointVisible(x, y) {
3434       return x0 <= x && x <= x1 && y0 <= y && y <= y1;
3435     }
3436
3437     function point(x, y) {
3438       if (pointVisible(x, y)) listener.point(x, y);
3439     }
3440
3441     var x__, y__, v__, // first point
3442         x_, y_, v_, // previous point
3443         first,
3444         clean;
3445
3446     function lineStart() {
3447       clip.point = linePoint;
3448       if (polygon) polygon.push(ring = []);
3449       first = true;
3450       v_ = false;
3451       x_ = y_ = NaN;
3452     }
3453
3454     function lineEnd() {
3455       // TODO rather than special-case polygons, simply handle them separately.
3456       // Ideally, coincident intersection points should be jittered to avoid
3457       // clipping issues.
3458       if (segments) {
3459         linePoint(x__, y__);
3460         if (v__ && v_) bufferListener.rejoin();
3461         segments.push(bufferListener.buffer());
3462       }
3463       clip.point = point;
3464       if (v_) listener.lineEnd();
3465     }
3466
3467     function linePoint(x, y) {
3468       x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
3469       y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
3470       var v = pointVisible(x, y);
3471       if (polygon) ring.push([x, y]);
3472       if (first) {
3473         x__ = x, y__ = y, v__ = v;
3474         first = false;
3475         if (v) {
3476           listener.lineStart();
3477           listener.point(x, y);
3478         }
3479       } else {
3480         if (v && v_) listener.point(x, y);
3481         else {
3482           var l = {a: {x: x_, y: y_}, b: {x: x, y: y}};
3483           if (clipLine(l)) {
3484             if (!v_) {
3485               listener.lineStart();
3486               listener.point(l.a.x, l.a.y);
3487             }
3488             listener.point(l.b.x, l.b.y);
3489             if (!v) listener.lineEnd();
3490             clean = false;
3491           } else if (v) {
3492             listener.lineStart();
3493             listener.point(x, y);
3494             clean = false;
3495           }
3496         }
3497       }
3498       x_ = x, y_ = y, v_ = v;
3499     }
3500
3501     return clip;
3502   };
3503
3504   function corner(p, direction) {
3505     return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3
3506         : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1
3507         : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0
3508         : direction > 0 ? 3 : 2; // abs(p[1] - y1) < ε
3509   }
3510
3511   function compare(a, b) {
3512     return comparePoints(a.x, b.x);
3513   }
3514
3515   function comparePoints(a, b) {
3516     var ca = corner(a, 1),
3517         cb = corner(b, 1);
3518     return ca !== cb ? ca - cb
3519         : ca === 0 ? b[1] - a[1]
3520         : ca === 1 ? a[0] - b[0]
3521         : ca === 2 ? a[1] - b[1]
3522         : b[0] - a[0];
3523   }
3524 }
3525
3526 function d3_geo_conic(projectAt) {
3527   var φ0 = 0,
3528       φ1 = π / 3,
3529       m = d3_geo_projectionMutator(projectAt),
3530       p = m(φ0, φ1);
3531
3532   p.parallels = function(_) {
3533     if (!arguments.length) return [φ0 / π * 180, φ1 / π * 180];
3534     return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180);
3535   };
3536
3537   return p;
3538 }
3539
3540 function d3_geo_conicEqualArea(φ0, φ1) {
3541   var sinφ0 = Math.sin(φ0),
3542       n = (sinφ0 + Math.sin(φ1)) / 2,
3543       C = 1 + sinφ0 * (2 * n - sinφ0),
3544       ρ0 = Math.sqrt(C) / n;
3545
3546   function forward(λ, φ) {
3547     var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n;
3548     return [
3549       ρ * Math.sin(λ *= n),
3550       ρ0 - ρ * Math.cos(λ)
3551     ];
3552   }
3553
3554   forward.invert = function(x, y) {
3555     var ρ0_y = ρ0 - y;
3556     return [
3557       Math.atan2(x, ρ0_y) / n,
3558       d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n))
3559     ];
3560   };
3561
3562   return forward;
3563 }
3564
3565 (d3.geo.conicEqualArea = function() {
3566   return d3_geo_conic(d3_geo_conicEqualArea);
3567 }).raw = d3_geo_conicEqualArea;
3568
3569 // ESRI:102003
3570 d3.geo.albers = function() {
3571   return d3.geo.conicEqualArea()
3572       .rotate([96, 0])
3573       .center([-.6, 38.7])
3574       .parallels([29.5, 45.5])
3575       .scale(1070);
3576 };
3577
3578 // A composite projection for the United States, configured by default for
3579 // 960×500. Also works quite well at 960×600 with scale 1285. The set of
3580 // standard parallels for each region comes from USGS, which is published here:
3581 // http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers
3582 d3.geo.albersUsa = function() {
3583   var lower48 = d3.geo.albers();
3584
3585   // EPSG:3338
3586   var alaska = d3.geo.conicEqualArea()
3587       .rotate([154, 0])
3588       .center([-2, 58.5])
3589       .parallels([55, 65]);
3590
3591   // ESRI:102007
3592   var hawaii = d3.geo.conicEqualArea()
3593       .rotate([157, 0])
3594       .center([-3, 19.9])
3595       .parallels([8, 18]);
3596
3597   var point,
3598       pointStream = {point: function(x, y) { point = [x, y]; }},
3599       lower48Point,
3600       alaskaPoint,
3601       hawaiiPoint;
3602
3603   function albersUsa(coordinates) {
3604     var x = coordinates[0], y = coordinates[1];
3605     point = null;
3606     (lower48Point(x, y), point)
3607         || (alaskaPoint(x, y), point)
3608         || hawaiiPoint(x, y);
3609     return point;
3610   }
3611
3612   albersUsa.invert = function(coordinates) {
3613     var k = lower48.scale(),
3614         t = lower48.translate(),
3615         x = (coordinates[0] - t[0]) / k,
3616         y = (coordinates[1] - t[1]) / k;
3617     return (y >= .120 && y < .234 && x >= -.425 && x < -.214 ? alaska
3618         : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii
3619         : lower48).invert(coordinates);
3620   };
3621
3622   // A naïve multi-projection stream.
3623   // The projections must have mutually exclusive clip regions on the sphere,
3624   // as this will avoid emitting interleaving lines and polygons.
3625   albersUsa.stream = function(stream) {
3626     var lower48Stream = lower48.stream(stream),
3627         alaskaStream = alaska.stream(stream),
3628         hawaiiStream = hawaii.stream(stream);
3629     return {
3630       point: function(x, y) {
3631         lower48Stream.point(x, y);
3632         alaskaStream.point(x, y);
3633         hawaiiStream.point(x, y);
3634       },
3635       sphere: function() {
3636         lower48Stream.sphere();
3637         alaskaStream.sphere();
3638         hawaiiStream.sphere();
3639       },
3640       lineStart: function() {
3641         lower48Stream.lineStart();
3642         alaskaStream.lineStart();
3643         hawaiiStream.lineStart();
3644       },
3645       lineEnd: function() {
3646         lower48Stream.lineEnd();
3647         alaskaStream.lineEnd();
3648         hawaiiStream.lineEnd();
3649       },
3650       polygonStart: function() {
3651         lower48Stream.polygonStart();
3652         alaskaStream.polygonStart();
3653         hawaiiStream.polygonStart();
3654       },
3655       polygonEnd: function() {
3656         lower48Stream.polygonEnd();
3657         alaskaStream.polygonEnd();
3658         hawaiiStream.polygonEnd();
3659       }
3660     };
3661   };
3662
3663   albersUsa.precision = function(_) {
3664     if (!arguments.length) return lower48.precision();
3665     lower48.precision(_);
3666     alaska.precision(_);
3667     hawaii.precision(_);
3668     return albersUsa;
3669   };
3670
3671   albersUsa.scale = function(_) {
3672     if (!arguments.length) return lower48.scale();
3673     lower48.scale(_);
3674     alaska.scale(_ * .35);
3675     hawaii.scale(_);
3676     return albersUsa.translate(lower48.translate());
3677   };
3678
3679   albersUsa.translate = function(_) {
3680     if (!arguments.length) return lower48.translate();
3681     var k = lower48.scale(), x = +_[0], y = +_[1];
3682
3683     lower48Point = lower48
3684         .translate(_)
3685         .clipExtent([[x - .455 * k, y - .238 * k], [x + .455 * k, y + .238 * k]])
3686         .stream(pointStream).point;
3687
3688     alaskaPoint = alaska
3689         .translate([x - .307 * k, y + .201 * k])
3690         .clipExtent([[x - .425 * k + ε, y + .120 * k + ε], [x - .214 * k - ε, y + .234 * k - ε]])
3691         .stream(pointStream).point;
3692
3693     hawaiiPoint = hawaii
3694         .translate([x - .205 * k, y + .212 * k])
3695         .clipExtent([[x - .214 * k + ε, y + .166 * k + ε], [x - .115 * k - ε, y + .234 * k - ε]])
3696         .stream(pointStream).point;
3697
3698     return albersUsa;
3699   };
3700
3701   return albersUsa.scale(1070);
3702 };
3703
3704 d3.geo.bounds = (function() {
3705   var λ0, φ0, λ1, φ1, // bounds
3706       λ_, // previous λ-coordinate
3707       λ__, φ__, // first point
3708       p0, // previous 3D point
3709       dλSum,
3710       ranges,
3711       range;
3712
3713   var bound = {
3714     point: point,
3715     lineStart: lineStart,
3716     lineEnd: lineEnd,
3717
3718     polygonStart: function() {
3719       bound.point = ringPoint;
3720       bound.lineStart = ringStart;
3721       bound.lineEnd = ringEnd;
3722       dλSum = 0;
3723       d3_geo_area.polygonStart();
3724     },
3725     polygonEnd: function() {
3726       d3_geo_area.polygonEnd();
3727       bound.point = point;
3728       bound.lineStart = lineStart;
3729       bound.lineEnd = lineEnd;
3730       if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90);
3731       else if (dλSum > ε) φ1 = 90;
3732       else if (dλSum < -ε) φ0 = -90;
3733       range[0] = λ0, range[1] = λ1;
3734     }
3735   };
3736
3737   function point(λ, φ) {
3738     ranges.push(range = [λ0 = λ, λ1 = λ]);
3739     if (φ < φ0) φ0 = φ;
3740     if (φ > φ1) φ1 = φ;
3741   }
3742
3743   function linePoint(λ, φ) {
3744     var p = d3_geo_cartesian([λ * d3_radians, φ * d3_radians]);
3745     if (p0) {
3746       var normal = d3_geo_cartesianCross(p0, p),
3747           equatorial = [normal[1], -normal[0], 0],
3748           inflection = d3_geo_cartesianCross(equatorial, normal);
3749       d3_geo_cartesianNormalize(inflection);
3750       inflection = d3_geo_spherical(inflection);
3751       var dλ = λ - λ_,
3752           s = dλ > 0 ? 1 : -1,
3753           λi = inflection[0] * d3_degrees * s,
3754           antimeridian = abs(dλ) > 180;
3755       if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
3756         var φi = inflection[1] * d3_degrees;
3757         if (φi > φ1) φ1 = φi;
3758       } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
3759         var φi = -inflection[1] * d3_degrees;
3760         if (φi < φ0) φ0 = φi;
3761       } else {
3762         if (φ < φ0) φ0 = φ;
3763         if (φ > φ1) φ1 = φ;
3764       }
3765       if (antimeridian) {
3766         if (λ < λ_) {
3767           if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3768         } else {
3769           if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3770         }
3771       } else {
3772         if (λ1 >= λ0) {
3773           if (λ < λ0) λ0 = λ;
3774           if (λ > λ1) λ1 = λ;
3775         } else {
3776           if (λ > λ_) {
3777             if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
3778           } else {
3779             if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
3780           }
3781         }
3782       }
3783     } else {
3784       point(λ, φ);
3785     }
3786     p0 = p, λ_ = λ;
3787   }
3788
3789   function lineStart() { bound.point = linePoint; }
3790   function lineEnd() {
3791     range[0] = λ0, range[1] = λ1;
3792     bound.point = point;
3793     p0 = null;
3794   }
3795
3796   function ringPoint(λ, φ) {
3797     if (p0) {
3798       var dλ = λ - λ_;
3799       dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
3800     } else λ__ = λ, φ__ = φ;
3801     d3_geo_area.point(λ, φ);
3802     linePoint(λ, φ);
3803   }
3804
3805   function ringStart() {
3806     d3_geo_area.lineStart();
3807   }
3808
3809   function ringEnd() {
3810     ringPoint(λ__, φ__);
3811     d3_geo_area.lineEnd();
3812     if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
3813     range[0] = λ0, range[1] = λ1;
3814     p0 = null;
3815   }
3816
3817   // Finds the left-right distance between two longitudes.
3818   // This is almost the same as (λ1 - λ0 + 360°) % 360°, except that we want
3819   // the distance between ±180° to be 360°.
3820   function angle(λ0, λ1) { return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; }
3821
3822   function compareRanges(a, b) { return a[0] - b[0]; }
3823
3824   function withinRange(x, range) {
3825     return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
3826   }
3827
3828   return function(feature) {
3829     φ1 = λ1 = -(λ0 = φ0 = Infinity);
3830     ranges = [];
3831
3832     d3.geo.stream(feature, bound);
3833
3834     var n = ranges.length;
3835     if (n) {
3836       // First, sort ranges by their minimum longitudes.
3837       ranges.sort(compareRanges);
3838
3839       // Then, merge any ranges that overlap.
3840       for (var i = 1, a = ranges[0], b, merged = [a]; i < n; ++i) {
3841         b = ranges[i];
3842         if (withinRange(b[0], a) || withinRange(b[1], a)) {
3843           if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
3844           if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
3845         } else {
3846           merged.push(a = b);
3847         }
3848       }
3849
3850       // Finally, find the largest gap between the merged ranges.
3851       // The final bounding box will be the inverse of this gap.
3852       var best = -Infinity, dλ;
3853       for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
3854         b = merged[i];
3855         if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
3856       }
3857     }
3858     ranges = range = null;
3859
3860     return λ0 === Infinity || φ0 === Infinity
3861         ? [[NaN, NaN], [NaN, NaN]]
3862         : [[λ0, φ0], [λ1, φ1]];
3863   };
3864 })();
3865
3866 d3.geo.centroid = function(object) {
3867   d3_geo_centroidW0 = d3_geo_centroidW1 =
3868   d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 =
3869   d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 =
3870   d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
3871   d3.geo.stream(object, d3_geo_centroid);
3872
3873   var x = d3_geo_centroidX2,
3874       y = d3_geo_centroidY2,
3875       z = d3_geo_centroidZ2,
3876       m = x * x + y * y + z * z;
3877
3878   // If the area-weighted centroid is undefined, fall back to length-weighted centroid.
3879   if (m < ε2) {
3880     x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
3881     // If the feature has zero length, fall back to arithmetic mean of point vectors.
3882     if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
3883     m = x * x + y * y + z * z;
3884     // If the feature still has an undefined centroid, then return.
3885     if (m < ε2) return [NaN, NaN];
3886   }
3887
3888   return [Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees];
3889 };
3890
3891 var d3_geo_centroidW0,
3892     d3_geo_centroidW1,
3893     d3_geo_centroidX0,
3894     d3_geo_centroidY0,
3895     d3_geo_centroidZ0,
3896     d3_geo_centroidX1,
3897     d3_geo_centroidY1,
3898     d3_geo_centroidZ1,
3899     d3_geo_centroidX2,
3900     d3_geo_centroidY2,
3901     d3_geo_centroidZ2;
3902
3903 var d3_geo_centroid = {
3904   sphere: d3_noop,
3905   point: d3_geo_centroidPoint,
3906   lineStart: d3_geo_centroidLineStart,
3907   lineEnd: d3_geo_centroidLineEnd,
3908   polygonStart: function() {
3909     d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
3910   },
3911   polygonEnd: function() {
3912     d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
3913   }
3914 };
3915
3916 // Arithmetic mean of Cartesian vectors.
3917 function d3_geo_centroidPoint(λ, φ) {
3918   λ *= d3_radians;
3919   var cosφ = Math.cos(φ *= d3_radians);
3920   d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
3921 }
3922
3923 function d3_geo_centroidPointXYZ(x, y, z) {
3924   ++d3_geo_centroidW0;
3925   d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
3926   d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
3927   d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
3928 }
3929
3930 function d3_geo_centroidLineStart() {
3931   var x0, y0, z0; // previous point
3932
3933   d3_geo_centroid.point = function(λ, φ) {
3934     λ *= d3_radians;
3935     var cosφ = Math.cos(φ *= d3_radians);
3936     x0 = cosφ * Math.cos(λ);
3937     y0 = cosφ * Math.sin(λ);
3938     z0 = Math.sin(φ);
3939     d3_geo_centroid.point = nextPoint;
3940     d3_geo_centroidPointXYZ(x0, y0, z0);
3941   };
3942
3943   function nextPoint(λ, φ) {
3944     λ *= d3_radians;
3945     var cosφ = Math.cos(φ *= d3_radians),
3946         x = cosφ * Math.cos(λ),
3947         y = cosφ * Math.sin(λ),
3948         z = Math.sin(φ),
3949         w = Math.atan2(
3950           Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w),
3951           x0 * x + y0 * y + z0 * z);
3952     d3_geo_centroidW1 += w;
3953     d3_geo_centroidX1 += w * (x0 + (x0 = x));
3954     d3_geo_centroidY1 += w * (y0 + (y0 = y));
3955     d3_geo_centroidZ1 += w * (z0 + (z0 = z));
3956     d3_geo_centroidPointXYZ(x0, y0, z0);
3957   }
3958 }
3959
3960 function d3_geo_centroidLineEnd() {
3961   d3_geo_centroid.point = d3_geo_centroidPoint;
3962 }
3963
3964 // See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
3965 // J. Applied Mechanics 42, 239 (1975).
3966 function d3_geo_centroidRingStart() {
3967   var λ00, φ00, // first point
3968       x0, y0, z0; // previous point
3969
3970   d3_geo_centroid.point = function(λ, φ) {
3971     λ00 = λ, φ00 = φ;
3972     d3_geo_centroid.point = nextPoint;
3973     λ *= d3_radians;
3974     var cosφ = Math.cos(φ *= d3_radians);
3975     x0 = cosφ * Math.cos(λ);
3976     y0 = cosφ * Math.sin(λ);
3977     z0 = Math.sin(φ);
3978     d3_geo_centroidPointXYZ(x0, y0, z0);
3979   };
3980
3981   d3_geo_centroid.lineEnd = function() {
3982     nextPoint(λ00, φ00);
3983     d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
3984     d3_geo_centroid.point = d3_geo_centroidPoint;
3985   };
3986
3987   function nextPoint(λ, φ) {
3988     λ *= d3_radians;
3989     var cosφ = Math.cos(φ *= d3_radians),
3990         x = cosφ * Math.cos(λ),
3991         y = cosφ * Math.sin(λ),
3992         z = Math.sin(φ),
3993         cx = y0 * z - z0 * y,
3994         cy = z0 * x - x0 * z,
3995         cz = x0 * y - y0 * x,
3996         m = Math.sqrt(cx * cx + cy * cy + cz * cz),
3997         u = x0 * x + y0 * y + z0 * z,
3998         v = m && -d3_acos(u) / m, // area weight
3999         w = Math.atan2(m, u); // line weight
4000     d3_geo_centroidX2 += v * cx;
4001     d3_geo_centroidY2 += v * cy;
4002     d3_geo_centroidZ2 += v * cz;
4003     d3_geo_centroidW1 += w;
4004     d3_geo_centroidX1 += w * (x0 + (x0 = x));
4005     d3_geo_centroidY1 += w * (y0 + (y0 = y));
4006     d3_geo_centroidZ1 += w * (z0 + (z0 = z));
4007     d3_geo_centroidPointXYZ(x0, y0, z0);
4008   }
4009 }
4010
4011 // TODO Unify this code with d3.geom.polygon area?
4012
4013 var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
4014   point: d3_noop,
4015   lineStart: d3_noop,
4016   lineEnd: d3_noop,
4017
4018   // Only count area for polygon rings.
4019   polygonStart: function() {
4020     d3_geo_pathAreaPolygon = 0;
4021     d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
4022   },
4023   polygonEnd: function() {
4024     d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
4025     d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
4026   }
4027 };
4028
4029 function d3_geo_pathAreaRingStart() {
4030   var x00, y00, x0, y0;
4031
4032   // For the first point, …
4033   d3_geo_pathArea.point = function(x, y) {
4034     d3_geo_pathArea.point = nextPoint;
4035     x00 = x0 = x, y00 = y0 = y;
4036   };
4037
4038   // For subsequent points, …
4039   function nextPoint(x, y) {
4040     d3_geo_pathAreaPolygon += y0 * x - x0 * y;
4041     x0 = x, y0 = y;
4042   }
4043
4044   // For the last point, return to the start.
4045   d3_geo_pathArea.lineEnd = function() {
4046     nextPoint(x00, y00);
4047   };
4048 }
4049
4050 var d3_geo_pathBoundsX0,
4051     d3_geo_pathBoundsY0,
4052     d3_geo_pathBoundsX1,
4053     d3_geo_pathBoundsY1;
4054
4055 var d3_geo_pathBounds = {
4056   point: d3_geo_pathBoundsPoint,
4057   lineStart: d3_noop,
4058   lineEnd: d3_noop,
4059   polygonStart: d3_noop,
4060   polygonEnd: d3_noop
4061 };
4062
4063 function d3_geo_pathBoundsPoint(x, y) {
4064   if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
4065   if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
4066   if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
4067   if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
4068 }
4069 function d3_geo_pathBuffer() {
4070   var pointCircle = d3_geo_pathBufferCircle(4.5),
4071       buffer = [];
4072
4073   var stream = {
4074     point: point,
4075
4076     // While inside a line, override point to moveTo then lineTo.
4077     lineStart: function() { stream.point = pointLineStart; },
4078     lineEnd: lineEnd,
4079
4080     // While inside a polygon, override lineEnd to closePath.
4081     polygonStart: function() { stream.lineEnd = lineEndPolygon; },
4082     polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; },
4083
4084     pointRadius: function(_) {
4085       pointCircle = d3_geo_pathBufferCircle(_);
4086       return stream;
4087     },
4088
4089     result: function() {
4090       if (buffer.length) {
4091         var result = buffer.join("");
4092         buffer = [];
4093         return result;
4094       }
4095     }
4096   };
4097
4098   function point(x, y) {
4099     buffer.push("M", x, ",", y, pointCircle);
4100   }
4101
4102   function pointLineStart(x, y) {
4103     buffer.push("M", x, ",", y);
4104     stream.point = pointLine;
4105   }
4106
4107   function pointLine(x, y) {
4108     buffer.push("L", x, ",", y);
4109   }
4110
4111   function lineEnd() {
4112     stream.point = point;
4113   }
4114
4115   function lineEndPolygon() {
4116     buffer.push("Z");
4117   }
4118
4119   return stream;
4120 }
4121
4122 function d3_geo_pathBufferCircle(radius) {
4123   return "m0," + radius
4124       + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius
4125       + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius
4126       + "z";
4127 }
4128
4129 // TODO Unify this code with d3.geom.polygon centroid?
4130 // TODO Enforce positive area for exterior, negative area for interior?
4131
4132 var d3_geo_pathCentroid = {
4133   point: d3_geo_pathCentroidPoint,
4134
4135   // For lines, weight by length.
4136   lineStart: d3_geo_pathCentroidLineStart,
4137   lineEnd: d3_geo_pathCentroidLineEnd,
4138
4139   // For polygons, weight by area.
4140   polygonStart: function() {
4141     d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
4142   },
4143   polygonEnd: function() {
4144     d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
4145     d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
4146     d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
4147   }
4148 };
4149
4150 function d3_geo_pathCentroidPoint(x, y) {
4151   d3_geo_centroidX0 += x;
4152   d3_geo_centroidY0 += y;
4153   ++d3_geo_centroidZ0;
4154 }
4155
4156 function d3_geo_pathCentroidLineStart() {
4157   var x0, y0;
4158
4159   d3_geo_pathCentroid.point = function(x, y) {
4160     d3_geo_pathCentroid.point = nextPoint;
4161     d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4162   };
4163
4164   function nextPoint(x, y) {
4165     var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
4166     d3_geo_centroidX1 += z * (x0 + x) / 2;
4167     d3_geo_centroidY1 += z * (y0 + y) / 2;
4168     d3_geo_centroidZ1 += z;
4169     d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4170   }
4171 }
4172
4173 function d3_geo_pathCentroidLineEnd() {
4174   d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
4175 }
4176
4177 function d3_geo_pathCentroidRingStart() {
4178   var x00, y00, x0, y0;
4179
4180   // For the first point, …
4181   d3_geo_pathCentroid.point = function(x, y) {
4182     d3_geo_pathCentroid.point = nextPoint;
4183     d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
4184   };
4185
4186   // For subsequent points, …
4187   function nextPoint(x, y) {
4188     var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
4189     d3_geo_centroidX1 += z * (x0 + x) / 2;
4190     d3_geo_centroidY1 += z * (y0 + y) / 2;
4191     d3_geo_centroidZ1 += z;
4192
4193     z = y0 * x - x0 * y;
4194     d3_geo_centroidX2 += z * (x0 + x);
4195     d3_geo_centroidY2 += z * (y0 + y);
4196     d3_geo_centroidZ2 += z * 3;
4197     d3_geo_pathCentroidPoint(x0 = x, y0 = y);
4198   }
4199
4200   // For the last point, return to the start.
4201   d3_geo_pathCentroid.lineEnd = function() {
4202     nextPoint(x00, y00);
4203   };
4204 }
4205
4206 function d3_geo_pathContext(context) {
4207   var pointRadius = 4.5;
4208
4209   var stream = {
4210     point: point,
4211
4212     // While inside a line, override point to moveTo then lineTo.
4213     lineStart: function() { stream.point = pointLineStart; },
4214     lineEnd: lineEnd,
4215
4216     // While inside a polygon, override lineEnd to closePath.
4217     polygonStart: function() { stream.lineEnd = lineEndPolygon; },
4218     polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; },
4219
4220     pointRadius: function(_) {
4221       pointRadius = _;
4222       return stream;
4223     },
4224
4225     result: d3_noop
4226   };
4227
4228   function point(x, y) {
4229     context.moveTo(x + pointRadius, y);
4230     context.arc(x, y, pointRadius, 0, τ);
4231   }
4232
4233   function pointLineStart(x, y) {
4234     context.moveTo(x, y);
4235     stream.point = pointLine;
4236   }
4237
4238   function pointLine(x, y) {
4239     context.lineTo(x, y);
4240   }
4241
4242   function lineEnd() {
4243     stream.point = point;
4244   }
4245
4246   function lineEndPolygon() {
4247     context.closePath();
4248   }
4249
4250   return stream;
4251 }
4252
4253 function d3_geo_resample(project) {
4254   var δ2 = .5, // precision, px²
4255       cosMinDistance = Math.cos(30 * d3_radians), // cos(minimum angular distance)
4256       maxDepth = 16;
4257
4258   function resample(stream) {
4259     return (maxDepth ? resampleRecursive : resampleNone)(stream);
4260   }
4261
4262   function resampleNone(stream) {
4263     return d3_geo_transformPoint(stream, function(x, y) {
4264       x = project(x, y);
4265       stream.point(x[0], x[1]);
4266     });
4267   }
4268
4269   function resampleRecursive(stream) {
4270     var λ00, φ00, x00, y00, a00, b00, c00, // first point
4271         λ0, x0, y0, a0, b0, c0; // previous point
4272
4273     var resample = {
4274       point: point,
4275       lineStart: lineStart,
4276       lineEnd: lineEnd,
4277       polygonStart: function() { stream.polygonStart(); resample.lineStart = ringStart; },
4278       polygonEnd: function() { stream.polygonEnd(); resample.lineStart = lineStart; }
4279     };
4280
4281     function point(x, y) {
4282       x = project(x, y);
4283       stream.point(x[0], x[1]);
4284     }
4285
4286     function lineStart() {
4287       x0 = NaN;
4288       resample.point = linePoint;
4289       stream.lineStart();
4290     }
4291
4292     function linePoint(λ, φ) {
4293       var c = d3_geo_cartesian([λ, φ]), p = project(λ, φ);
4294       resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
4295       stream.point(x0, y0);
4296     }
4297
4298     function lineEnd() {
4299       resample.point = point;
4300       stream.lineEnd();
4301     }
4302
4303     function ringStart() {
4304       lineStart();
4305       resample.point = ringPoint;
4306       resample.lineEnd = ringEnd;
4307     }
4308
4309     function ringPoint(λ, φ) {
4310       linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
4311       resample.point = linePoint;
4312     }
4313
4314     function ringEnd() {
4315       resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
4316       resample.lineEnd = lineEnd;
4317       lineEnd();
4318     }
4319
4320     return resample;
4321   }
4322
4323   function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
4324     var dx = x1 - x0,
4325         dy = y1 - y0,
4326         d2 = dx * dx + dy * dy;
4327     if (d2 > 4 * δ2 && depth--) {
4328       var a = a0 + a1,
4329           b = b0 + b1,
4330           c = c0 + c1,
4331           m = Math.sqrt(a * a + b * b + c * c),
4332           φ2 = Math.asin(c /= m),
4333           λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a),
4334           p = project(λ2, φ2),
4335           x2 = p[0],
4336           y2 = p[1],
4337           dx2 = x2 - x0,
4338           dy2 = y2 - y0,
4339           dz = dy * dx2 - dx * dy2;
4340       if (dz * dz / d2 > δ2 // perpendicular projected distance
4341           || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 // midpoint close to an end
4342           || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
4343         resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
4344         stream.point(x2, y2);
4345         resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
4346       }
4347     }
4348   }
4349
4350   resample.precision = function(_) {
4351     if (!arguments.length) return Math.sqrt(δ2);
4352     maxDepth = (δ2 = _ * _) > 0 && 16;
4353     return resample;
4354   };
4355
4356   return resample;
4357 }
4358
4359 d3.geo.path = function() {
4360   var pointRadius = 4.5,
4361       projection,
4362       context,
4363       projectStream,
4364       contextStream,
4365       cacheStream;
4366
4367   function path(object) {
4368     if (object) {
4369       if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
4370       if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
4371       d3.geo.stream(object, cacheStream);
4372     }
4373     return contextStream.result();
4374   }
4375
4376   path.area = function(object) {
4377     d3_geo_pathAreaSum = 0;
4378     d3.geo.stream(object, projectStream(d3_geo_pathArea));
4379     return d3_geo_pathAreaSum;
4380   };
4381
4382   path.centroid = function(object) {
4383     d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 =
4384     d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 =
4385     d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
4386     d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
4387     return d3_geo_centroidZ2 ? [d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2]
4388         : d3_geo_centroidZ1 ? [d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1]
4389         : d3_geo_centroidZ0 ? [d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0]
4390         : [NaN, NaN];
4391   };
4392
4393   path.bounds = function(object) {
4394     d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
4395     d3.geo.stream(object, projectStream(d3_geo_pathBounds));
4396     return [[d3_geo_pathBoundsX0, d3_geo_pathBoundsY0], [d3_geo_pathBoundsX1, d3_geo_pathBoundsY1]];
4397   };
4398
4399   path.projection = function(_) {
4400     if (!arguments.length) return projection;
4401     projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
4402     return reset();
4403   };
4404
4405   path.context = function(_) {
4406     if (!arguments.length) return context;
4407     contextStream = (context = _) == null ? new d3_geo_pathBuffer : new d3_geo_pathContext(_);
4408     if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
4409     return reset();
4410   };
4411
4412   path.pointRadius = function(_) {
4413     if (!arguments.length) return pointRadius;
4414     pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
4415     return path;
4416   };
4417
4418   function reset() {
4419     cacheStream = null;
4420     return path;
4421   }
4422
4423   return path.projection(d3.geo.albersUsa()).context(null);
4424 };
4425
4426 function d3_geo_pathProjectStream(project) {
4427   var resample = d3_geo_resample(function(x, y) { return project([x * d3_degrees, y * d3_degrees]); });
4428   return function(stream) { return d3_geo_projectionRadians(resample(stream)); };
4429 }
4430
4431 d3.geo.transform = function(methods) {
4432   return {
4433     stream: function(stream) {
4434       var transform = new d3_geo_transform(stream);
4435       for (var k in methods) transform[k] = methods[k];
4436       return transform;
4437     }
4438   };
4439 };
4440
4441 function d3_geo_transform(stream) {
4442   this.stream = stream;
4443 }
4444
4445 d3_geo_transform.prototype = {
4446   point: function(x, y) { this.stream.point(x, y); },
4447   sphere: function() { this.stream.sphere(); },
4448   lineStart: function() { this.stream.lineStart(); },
4449   lineEnd: function() { this.stream.lineEnd(); },
4450   polygonStart: function() { this.stream.polygonStart(); },
4451   polygonEnd: function() { this.stream.polygonEnd(); }
4452 };
4453
4454 function d3_geo_transformPoint(stream, point) {
4455   return {
4456     point: point,
4457     sphere: function() { stream.sphere(); },
4458     lineStart: function() { stream.lineStart(); },
4459     lineEnd: function() { stream.lineEnd(); },
4460     polygonStart: function() { stream.polygonStart(); },
4461     polygonEnd: function() { stream.polygonEnd(); },
4462   };
4463 }
4464
4465 d3.geo.projection = d3_geo_projection;
4466 d3.geo.projectionMutator = d3_geo_projectionMutator;
4467
4468 function d3_geo_projection(project) {
4469   return d3_geo_projectionMutator(function() { return project; })();
4470 }
4471
4472 function d3_geo_projectionMutator(projectAt) {
4473   var project,
4474       rotate,
4475       projectRotate,
4476       projectResample = d3_geo_resample(function(x, y) { x = project(x, y); return [x[0] * k + δx, δy - x[1] * k]; }),
4477       k = 150, // scale
4478       x = 480, y = 250, // translate
4479       λ = 0, φ = 0, // center
4480       δλ = 0, δφ = 0, δγ = 0, // rotate
4481       δx, δy, // center
4482       preclip = d3_geo_clipAntimeridian,
4483       postclip = d3_identity,
4484       clipAngle = null,
4485       clipExtent = null,
4486       stream;
4487
4488   function projection(point) {
4489     point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
4490     return [point[0] * k + δx, δy - point[1] * k];
4491   }
4492
4493   function invert(point) {
4494     point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k);
4495     return point && [point[0] * d3_degrees, point[1] * d3_degrees];
4496   }
4497
4498   projection.stream = function(output) {
4499     if (stream) stream.valid = false;
4500     stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
4501     stream.valid = true; // allow caching by d3.geo.path
4502     return stream;
4503   };
4504
4505   projection.clipAngle = function(_) {
4506     if (!arguments.length) return clipAngle;
4507     preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
4508     return invalidate();
4509   };
4510
4511   projection.clipExtent = function(_) {
4512     if (!arguments.length) return clipExtent;
4513     clipExtent = _;
4514     postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
4515     return invalidate();
4516   };
4517
4518   projection.scale = function(_) {
4519     if (!arguments.length) return k;
4520     k = +_;
4521     return reset();
4522   };
4523
4524   projection.translate = function(_) {
4525     if (!arguments.length) return [x, y];
4526     x = +_[0];
4527     y = +_[1];
4528     return reset();
4529   };
4530
4531   projection.center = function(_) {
4532     if (!arguments.length) return [λ * d3_degrees, φ * d3_degrees];
4533     λ = _[0] % 360 * d3_radians;
4534     φ = _[1] % 360 * d3_radians;
4535     return reset();
4536   };
4537
4538   projection.rotate = function(_) {
4539     if (!arguments.length) return [δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees];
4540     δλ = _[0] % 360 * d3_radians;
4541     δφ = _[1] % 360 * d3_radians;
4542     δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
4543     return reset();
4544   };
4545
4546   d3.rebind(projection, projectResample, "precision");
4547
4548   function reset() {
4549     projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
4550     var center = project(λ, φ);
4551     δx = x - center[0] * k;
4552     δy = y + center[1] * k;
4553     return invalidate();
4554   }
4555
4556   function invalidate() {
4557     if (stream) stream.valid = false, stream = null;
4558     return projection;
4559   }
4560
4561   return function() {
4562     project = projectAt.apply(this, arguments);
4563     projection.invert = project.invert && invert;
4564     return reset();
4565   };
4566 }
4567
4568 function d3_geo_projectionRadians(stream) {
4569   return d3_geo_transformPoint(stream, function(x, y) {
4570     stream.point(x * d3_radians, y * d3_radians);
4571   });
4572 }
4573
4574 function d3_geo_mercator(λ, φ) {
4575   return [λ, Math.log(Math.tan(π / 4 + φ / 2))];
4576 }
4577
4578 d3_geo_mercator.invert = function(x, y) {
4579   return [x, 2 * Math.atan(Math.exp(y)) - halfπ];
4580 };
4581
4582 function d3_geo_mercatorProjection(project) {
4583   var m = d3_geo_projection(project),
4584       scale = m.scale,
4585       translate = m.translate,
4586       clipExtent = m.clipExtent,
4587       clipAuto;
4588
4589   m.scale = function() {
4590     var v = scale.apply(m, arguments);
4591     return v === m ? (clipAuto ? m.clipExtent(null) : m) : v;
4592   };
4593
4594   m.translate = function() {
4595     var v = translate.apply(m, arguments);
4596     return v === m ? (clipAuto ? m.clipExtent(null) : m) : v;
4597   };
4598
4599   m.clipExtent = function(_) {
4600     var v = clipExtent.apply(m, arguments);
4601     if (v === m) {
4602       if (clipAuto = _ == null) {
4603         var k = π * scale(), t = translate();
4604         clipExtent([[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]]);
4605       }
4606     } else if (clipAuto) {
4607       v = null;
4608     }
4609     return v;
4610   };
4611
4612   return m.clipExtent(null);
4613 }
4614
4615 (d3.geo.mercator = function() {
4616   return d3_geo_mercatorProjection(d3_geo_mercator);
4617 }).raw = d3_geo_mercator;
4618 d3.geom = {};
4619
4620 d3.geom.polygon = function(coordinates) {
4621   d3_subclass(coordinates, d3_geom_polygonPrototype);
4622   return coordinates;
4623 };
4624
4625 var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
4626
4627 d3_geom_polygonPrototype.area = function() {
4628   var i = -1,
4629       n = this.length,
4630       a,
4631       b = this[n - 1],
4632       area = 0;
4633
4634   while (++i < n) {
4635     a = b;
4636     b = this[i];
4637     area += a[1] * b[0] - a[0] * b[1];
4638   }
4639
4640   return area * .5;
4641 };
4642
4643 d3_geom_polygonPrototype.centroid = function(k) {
4644   var i = -1,
4645       n = this.length,
4646       x = 0,
4647       y = 0,
4648       a,
4649       b = this[n - 1],
4650       c;
4651
4652   if (!arguments.length) k = -1 / (6 * this.area());
4653
4654   while (++i < n) {
4655     a = b;
4656     b = this[i];
4657     c = a[0] * b[1] - b[0] * a[1];
4658     x += (a[0] + b[0]) * c;
4659     y += (a[1] + b[1]) * c;
4660   }
4661
4662   return [x * k, y * k];
4663 };
4664
4665 // The Sutherland-Hodgman clipping algorithm.
4666 // Note: requires the clip polygon to be counterclockwise and convex.
4667 d3_geom_polygonPrototype.clip = function(subject) {
4668   var input,
4669       closed = d3_geom_polygonClosed(subject),
4670       i = -1,
4671       n = this.length - d3_geom_polygonClosed(this),
4672       j,
4673       m,
4674       a = this[n - 1],
4675       b,
4676       c,
4677       d;
4678
4679   while (++i < n) {
4680     input = subject.slice();
4681     subject.length = 0;
4682     b = this[i];
4683     c = input[(m = input.length - closed) - 1];
4684     j = -1;
4685     while (++j < m) {
4686       d = input[j];
4687       if (d3_geom_polygonInside(d, a, b)) {
4688         if (!d3_geom_polygonInside(c, a, b)) {
4689           subject.push(d3_geom_polygonIntersect(c, d, a, b));
4690         }
4691         subject.push(d);
4692       } else if (d3_geom_polygonInside(c, a, b)) {
4693         subject.push(d3_geom_polygonIntersect(c, d, a, b));
4694       }
4695       c = d;
4696     }
4697     if (closed) subject.push(subject[0]);
4698     a = b;
4699   }
4700
4701   return subject;
4702 };
4703
4704 function d3_geom_polygonInside(p, a, b) {
4705   return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
4706 }
4707
4708 // Intersect two infinite lines cd and ab.
4709 function d3_geom_polygonIntersect(c, d, a, b) {
4710   var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3,
4711       y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3,
4712       ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);
4713   return [x1 + ua * x21, y1 + ua * y21];
4714 }
4715
4716 // Returns true if the polygon is closed.
4717 function d3_geom_polygonClosed(coordinates) {
4718   var a = coordinates[0],
4719       b = coordinates[coordinates.length - 1];
4720   return !(a[0] - b[0] || a[1] - b[1]);
4721 }
4722 function d3_geom_pointX(d) {
4723   return d[0];
4724 }
4725
4726 function d3_geom_pointY(d) {
4727   return d[1];
4728 }
4729
4730 /**
4731  * Computes the 2D convex hull of a set of points using the monotone chain
4732  * algorithm:
4733  * http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain)
4734  *
4735  * The runtime of this algorithm is O(n log n), where n is the number of input
4736  * points. However in practice it outperforms other O(n log n) hulls.
4737  *
4738  * @param vertices [[x1, y1], [x2, y2], ...]
4739  * @returns polygon [[x1, y1], [x2, y2], ...]
4740  */
4741 d3.geom.hull = function(vertices) {
4742   var x = d3_geom_pointX,
4743       y = d3_geom_pointY;
4744
4745   if (arguments.length) return hull(vertices);
4746
4747   function hull(data) {
4748     // Hull of < 3 points is not well-defined
4749     if (data.length < 3) return [];
4750
4751     var fx = d3_functor(x),
4752         fy = d3_functor(y),
4753         i,
4754         n = data.length,
4755         points = [], // of the form [[x0, y0, 0], ..., [xn, yn, n]]
4756         flippedPoints = [];
4757
4758     for (i = 0 ; i < n; i++) {
4759       points.push([+fx.call(this, data[i], i), +fy.call(this, data[i], i), i]);
4760     }
4761
4762     // sort ascending by x-coord first, y-coord second
4763     points.sort(d3_geom_hullOrder);
4764
4765     // we flip bottommost points across y axis so we can use the upper hull routine on both
4766     for (i = 0; i < n; i++) flippedPoints.push([points[i][0], -points[i][1]]);
4767
4768     var upper = d3_geom_hullUpper(points),
4769         lower = d3_geom_hullUpper(flippedPoints);
4770
4771     // construct the polygon, removing possible duplicate endpoints
4772     var skipLeft = lower[0] === upper[0],
4773         skipRight  = lower[lower.length - 1] === upper[upper.length - 1],
4774         polygon = [];
4775
4776     // add upper hull in r->l order
4777     // then add lower hull in l->r order
4778     for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]);
4779     for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]);
4780
4781     return polygon;
4782   }
4783
4784   hull.x = function(_) {
4785     return arguments.length ? (x = _, hull) : x;
4786   };
4787
4788   hull.y = function(_) {
4789     return arguments.length ? (y = _, hull) : y;
4790   };
4791
4792   return hull;
4793 };
4794
4795 // finds the 'upper convex hull' (see wiki link above)
4796 // assumes points arg has >=3 elements, is sorted by x, unique in y
4797 // returns array of indices into points in left to right order
4798 function d3_geom_hullUpper(points) {
4799   var n = points.length,
4800       hull = [0, 1],
4801       hs = 2; // hull size
4802
4803   for (var i = 2; i < n; i++) {
4804     while (hs > 1 && d3_cross2d(points[hull[hs-2]], points[hull[hs-1]], points[i]) <= 0) --hs;
4805     hull[hs++] = i;
4806   }
4807
4808   // we slice to make sure that the points we 'popped' from hull don't stay behind
4809   return hull.slice(0, hs);
4810 }
4811
4812 // comparator for ascending sort by x-coord first, y-coord second
4813 function d3_geom_hullOrder(a, b) {
4814   return a[0] - b[0] || a[1] - b[1];
4815 }
4816 // import "../transition/transition";
4817
4818 d3_selectionPrototype.transition = function(name) {
4819   var id = d3_transitionInheritId || ++d3_transitionId,
4820       ns = d3_transitionNamespace(name),
4821       subgroups = [],
4822       subgroup,
4823       node,
4824       transition = d3_transitionInherit || {time: Date.now(), ease: d3_ease_cubicInOut, delay: 0, duration: 250};
4825
4826   for (var j = -1, m = this.length; ++j < m;) {
4827     subgroups.push(subgroup = []);
4828     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4829       if (node = group[i]) d3_transitionNode(node, i, ns, id, transition);
4830       subgroup.push(node);
4831     }
4832   }
4833
4834   return d3_transition(subgroups, ns, id);
4835 };
4836 // import "../transition/transition";
4837
4838 // TODO Interrupt transitions for all namespaces?
4839 d3_selectionPrototype.interrupt = function(name) {
4840   return this.each(name == null
4841       ? d3_selection_interrupt
4842       : d3_selection_interruptNS(d3_transitionNamespace(name)));
4843 };
4844
4845 var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace());
4846
4847 function d3_selection_interruptNS(ns) {
4848   return function() {
4849     var lock, active;
4850     if ((lock = this[ns]) && (active = lock[lock.active])) {
4851       if (--lock.count) delete lock[lock.active];
4852       else delete this[ns];
4853       lock.active += .5;
4854       active.event && active.event.interrupt.call(this, this.__data__, active.index);
4855     }
4856   };
4857 }
4858
4859 function d3_transition(groups, ns, id) {
4860   d3_subclass(groups, d3_transitionPrototype);
4861
4862   // Note: read-only!
4863   groups.namespace = ns;
4864   groups.id = id;
4865
4866   return groups;
4867 }
4868
4869 var d3_transitionPrototype = [],
4870     d3_transitionId = 0,
4871     d3_transitionInheritId,
4872     d3_transitionInherit;
4873
4874 d3_transitionPrototype.call = d3_selectionPrototype.call;
4875 d3_transitionPrototype.empty = d3_selectionPrototype.empty;
4876 d3_transitionPrototype.node = d3_selectionPrototype.node;
4877 d3_transitionPrototype.size = d3_selectionPrototype.size;
4878
4879 d3.transition = function(selection, name) {
4880   return selection && selection.transition
4881       ? (d3_transitionInheritId ? selection.transition(name) : selection)
4882       : d3.selection().transition(selection);
4883 };
4884
4885 d3.transition.prototype = d3_transitionPrototype;
4886
4887
4888 d3_transitionPrototype.select = function(selector) {
4889   var id = this.id,
4890       ns = this.namespace,
4891       subgroups = [],
4892       subgroup,
4893       subnode,
4894       node;
4895
4896   selector = d3_selection_selector(selector);
4897
4898   for (var j = -1, m = this.length; ++j < m;) {
4899     subgroups.push(subgroup = []);
4900     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4901       if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
4902         if ("__data__" in node) subnode.__data__ = node.__data__;
4903         d3_transitionNode(subnode, i, ns, id, node[ns][id]);
4904         subgroup.push(subnode);
4905       } else {
4906         subgroup.push(null);
4907       }
4908     }
4909   }
4910
4911   return d3_transition(subgroups, ns, id);
4912 };
4913
4914 d3_transitionPrototype.selectAll = function(selector) {
4915   var id = this.id,
4916       ns = this.namespace,
4917       subgroups = [],
4918       subgroup,
4919       subnodes,
4920       node,
4921       subnode,
4922       transition;
4923
4924   selector = d3_selection_selectorAll(selector);
4925
4926   for (var j = -1, m = this.length; ++j < m;) {
4927     for (var group = this[j], i = -1, n = group.length; ++i < n;) {
4928       if (node = group[i]) {
4929         transition = node[ns][id];
4930         subnodes = selector.call(node, node.__data__, i, j);
4931         subgroups.push(subgroup = []);
4932         for (var k = -1, o = subnodes.length; ++k < o;) {
4933           if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition);
4934           subgroup.push(subnode);
4935         }
4936       }
4937     }
4938   }
4939
4940   return d3_transition(subgroups, ns, id);
4941 };
4942
4943 d3_transitionPrototype.filter = function(filter) {
4944   var subgroups = [],
4945       subgroup,
4946       group,
4947       node;
4948
4949   if (typeof filter !== "function") filter = d3_selection_filter(filter);
4950
4951   for (var j = 0, m = this.length; j < m; j++) {
4952     subgroups.push(subgroup = []);
4953     for (var group = this[j], i = 0, n = group.length; i < n; i++) {
4954       if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
4955         subgroup.push(node);
4956       }
4957     }
4958   }
4959
4960   return d3_transition(subgroups, this.namespace, this.id);
4961 };
4962 d3.color = d3_color;
4963
4964 function d3_color() {}
4965
4966 d3_color.prototype.toString = function() {
4967   return this.rgb() + "";
4968 };
4969
4970 d3.hsl = d3_hsl;
4971
4972 function d3_hsl(h, s, l) {
4973   return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l)
4974       : arguments.length < 2 ? (h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l)
4975       : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl))
4976       : new d3_hsl(h, s, l);
4977 }
4978
4979 var d3_hslPrototype = d3_hsl.prototype = new d3_color;
4980
4981 d3_hslPrototype.brighter = function(k) {
4982   k = Math.pow(0.7, arguments.length ? k : 1);
4983   return new d3_hsl(this.h, this.s, this.l / k);
4984 };
4985
4986 d3_hslPrototype.darker = function(k) {
4987   k = Math.pow(0.7, arguments.length ? k : 1);
4988   return new d3_hsl(this.h, this.s, k * this.l);
4989 };
4990
4991 d3_hslPrototype.rgb = function() {
4992   return d3_hsl_rgb(this.h, this.s, this.l);
4993 };
4994
4995 function d3_hsl_rgb(h, s, l) {
4996   var m1,
4997       m2;
4998
4999   /* Some simple corrections for h, s and l. */
5000   h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
5001   s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
5002   l = l < 0 ? 0 : l > 1 ? 1 : l;
5003
5004   /* From FvD 13.37, CSS Color Module Level 3 */
5005   m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
5006   m1 = 2 * l - m2;
5007
5008   function v(h) {
5009     if (h > 360) h -= 360;
5010     else if (h < 0) h += 360;
5011     if (h < 60) return m1 + (m2 - m1) * h / 60;
5012     if (h < 180) return m2;
5013     if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
5014     return m1;
5015   }
5016
5017   function vv(h) {
5018     return Math.round(v(h) * 255);
5019   }
5020
5021   return new d3_rgb(vv(h + 120), vv(h), vv(h - 120));
5022 }
5023
5024 d3.hcl = d3_hcl;
5025
5026 function d3_hcl(h, c, l) {
5027   return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l)
5028       : arguments.length < 2 ? (h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l)
5029       : (h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b)
5030       : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b)))
5031       : new d3_hcl(h, c, l);
5032 }
5033
5034 var d3_hclPrototype = d3_hcl.prototype = new d3_color;
5035
5036 d3_hclPrototype.brighter = function(k) {
5037   return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
5038 };
5039
5040 d3_hclPrototype.darker = function(k) {
5041   return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
5042 };
5043
5044 d3_hclPrototype.rgb = function() {
5045   return d3_hcl_lab(this.h, this.c, this.l).rgb();
5046 };
5047
5048 function d3_hcl_lab(h, c, l) {
5049   if (isNaN(h)) h = 0;
5050   if (isNaN(c)) c = 0;
5051   return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
5052 }
5053
5054 d3.lab = d3_lab;
5055
5056 function d3_lab(l, a, b) {
5057   return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b)
5058       : arguments.length < 2 ? (l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b)
5059       : (l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l)
5060       : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b)))
5061       : new d3_lab(l, a, b);
5062 }
5063
5064 // Corresponds roughly to RGB brighter/darker
5065 var d3_lab_K = 18;
5066
5067 // D65 standard referent
5068 var d3_lab_X = 0.950470,
5069     d3_lab_Y = 1,
5070     d3_lab_Z = 1.088830;
5071
5072 var d3_labPrototype = d3_lab.prototype = new d3_color;
5073
5074 d3_labPrototype.brighter = function(k) {
5075   return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
5076 };
5077
5078 d3_labPrototype.darker = function(k) {
5079   return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
5080 };
5081
5082 d3_labPrototype.rgb = function() {
5083   return d3_lab_rgb(this.l, this.a, this.b);
5084 };
5085
5086 function d3_lab_rgb(l, a, b) {
5087   var y = (l + 16) / 116,
5088       x = y + a / 500,
5089       z = y - b / 200;
5090   x = d3_lab_xyz(x) * d3_lab_X;
5091   y = d3_lab_xyz(y) * d3_lab_Y;
5092   z = d3_lab_xyz(z) * d3_lab_Z;
5093   return new d3_rgb(
5094     d3_xyz_rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z),
5095     d3_xyz_rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z),
5096     d3_xyz_rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z)
5097   );
5098 }
5099
5100 function d3_lab_hcl(l, a, b) {
5101   return l > 0
5102       ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l)
5103       : new d3_hcl(NaN, NaN, l);
5104 }
5105
5106 function d3_lab_xyz(x) {
5107   return x > 0.206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
5108 }
5109 function d3_xyz_lab(x) {
5110   return x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
5111 }
5112
5113 function d3_xyz_rgb(r) {
5114   return Math.round(255 * (r <= 0.00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - 0.055));
5115 }
5116
5117 d3.rgb = d3_rgb;
5118
5119 function d3_rgb(r, g, b) {
5120   return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b)
5121       : arguments.length < 2 ? (r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b)
5122       : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb))
5123       : new d3_rgb(r, g, b);
5124 }
5125
5126 function d3_rgbNumber(value) {
5127   return new d3_rgb(value >> 16, value >> 8 & 0xff, value & 0xff);
5128 }
5129
5130 function d3_rgbString(value) {
5131   return d3_rgbNumber(value) + "";
5132 }
5133
5134 var d3_rgbPrototype = d3_rgb.prototype = new d3_color;
5135
5136 d3_rgbPrototype.brighter = function(k) {
5137   k = Math.pow(0.7, arguments.length ? k : 1);
5138   var r = this.r,
5139       g = this.g,
5140       b = this.b,
5141       i = 30;
5142   if (!r && !g && !b) return new d3_rgb(i, i, i);
5143   if (r && r < i) r = i;
5144   if (g && g < i) g = i;
5145   if (b && b < i) b = i;
5146   return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k));
5147 };
5148
5149 d3_rgbPrototype.darker = function(k) {
5150   k = Math.pow(0.7, arguments.length ? k : 1);
5151   return new d3_rgb(k * this.r, k * this.g, k * this.b);
5152 };
5153
5154 d3_rgbPrototype.hsl = function() {
5155   return d3_rgb_hsl(this.r, this.g, this.b);
5156 };
5157
5158 d3_rgbPrototype.toString = function() {
5159   return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
5160 };
5161
5162 function d3_rgb_hex(v) {
5163   return v < 0x10
5164       ? "0" + Math.max(0, v).toString(16)
5165       : Math.min(255, v).toString(16);
5166 }
5167
5168 function d3_rgb_parse(format, rgb, hsl) {
5169   var r = 0, // red channel; int in [0, 255]
5170       g = 0, // green channel; int in [0, 255]
5171       b = 0, // blue channel; int in [0, 255]
5172       m1, // CSS color specification match
5173       m2, // CSS color specification type (e.g., rgb)
5174       color;
5175
5176   /* Handle hsl, rgb. */
5177   m1 = /([a-z]+)\((.*)\)/i.exec(format);
5178   if (m1) {
5179     m2 = m1[2].split(",");
5180     switch (m1[1]) {
5181       case "hsl": {
5182         return hsl(
5183           parseFloat(m2[0]), // degrees
5184           parseFloat(m2[1]) / 100, // percentage
5185           parseFloat(m2[2]) / 100 // percentage
5186         );
5187       }
5188       case "rgb": {
5189         return rgb(
5190           d3_rgb_parseNumber(m2[0]),
5191           d3_rgb_parseNumber(m2[1]),
5192           d3_rgb_parseNumber(m2[2])
5193         );
5194       }
5195     }
5196   }
5197
5198   /* Named colors. */
5199   if (color = d3_rgb_names.get(format.toLowerCase())) {
5200     return rgb(color.r, color.g, color.b);
5201   }
5202
5203   /* Hexadecimal colors: #rgb and #rrggbb. */
5204   if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) {
5205     if (format.length === 4) {
5206       r = (color & 0xf00) >> 4; r = (r >> 4) | r;
5207       g = (color & 0xf0); g = (g >> 4) | g;
5208       b = (color & 0xf); b = (b << 4) | b;
5209     } else if (format.length === 7) {
5210       r = (color & 0xff0000) >> 16;
5211       g = (color & 0xff00) >> 8;
5212       b = (color & 0xff);
5213     }
5214   }
5215
5216   return rgb(r, g, b);
5217 }
5218
5219 function d3_rgb_hsl(r, g, b) {
5220   var min = Math.min(r /= 255, g /= 255, b /= 255),
5221       max = Math.max(r, g, b),
5222       d = max - min,
5223       h,
5224       s,
5225       l = (max + min) / 2;
5226   if (d) {
5227     s = l < .5 ? d / (max + min) : d / (2 - max - min);
5228     if (r == max) h = (g - b) / d + (g < b ? 6 : 0);
5229     else if (g == max) h = (b - r) / d + 2;
5230     else h = (r - g) / d + 4;
5231     h *= 60;
5232   } else {
5233     h = NaN;
5234     s = l > 0 && l < 1 ? 0 : h;
5235   }
5236   return new d3_hsl(h, s, l);
5237 }
5238
5239 function d3_rgb_lab(r, g, b) {
5240   r = d3_rgb_xyz(r);
5241   g = d3_rgb_xyz(g);
5242   b = d3_rgb_xyz(b);
5243   var x = d3_xyz_lab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / d3_lab_X),
5244       y = d3_xyz_lab((0.2126729 * r + 0.7151522 * g + 0.0721750 * b) / d3_lab_Y),
5245       z = d3_xyz_lab((0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / d3_lab_Z);
5246   return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
5247 }
5248
5249 function d3_rgb_xyz(r) {
5250   return (r /= 255) <= 0.04045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
5251 }
5252
5253 function d3_rgb_parseNumber(c) { // either integer or percentage
5254   var f = parseFloat(c);
5255   return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
5256 }
5257
5258 var d3_rgb_names = d3.map({
5259   aliceblue: 0xf0f8ff,
5260   antiquewhite: 0xfaebd7,
5261   aqua: 0x00ffff,
5262   aquamarine: 0x7fffd4,
5263   azure: 0xf0ffff,
5264   beige: 0xf5f5dc,
5265   bisque: 0xffe4c4,
5266   black: 0x000000,
5267   blanchedalmond: 0xffebcd,
5268   blue: 0x0000ff,
5269   blueviolet: 0x8a2be2,
5270   brown: 0xa52a2a,
5271   burlywood: 0xdeb887,
5272   cadetblue: 0x5f9ea0,
5273   chartreuse: 0x7fff00,
5274   chocolate: 0xd2691e,
5275   coral: 0xff7f50,
5276   cornflowerblue: 0x6495ed,
5277   cornsilk: 0xfff8dc,
5278   crimson: 0xdc143c,
5279   cyan: 0x00ffff,
5280   darkblue: 0x00008b,
5281   darkcyan: 0x008b8b,
5282   darkgoldenrod: 0xb8860b,
5283   darkgray: 0xa9a9a9,
5284   darkgreen: 0x006400,
5285   darkgrey: 0xa9a9a9,
5286   darkkhaki: 0xbdb76b,
5287   darkmagenta: 0x8b008b,
5288   darkolivegreen: 0x556b2f,
5289   darkorange: 0xff8c00,
5290   darkorchid: 0x9932cc,
5291   darkred: 0x8b0000,
5292   darksalmon: 0xe9967a,
5293   darkseagreen: 0x8fbc8f,
5294   darkslateblue: 0x483d8b,
5295   darkslategray: 0x2f4f4f,
5296   darkslategrey: 0x2f4f4f,
5297   darkturquoise: 0x00ced1,
5298   darkviolet: 0x9400d3,
5299   deeppink: 0xff1493,
5300   deepskyblue: 0x00bfff,
5301   dimgray: 0x696969,
5302   dimgrey: 0x696969,
5303   dodgerblue: 0x1e90ff,
5304   firebrick: 0xb22222,
5305   floralwhite: 0xfffaf0,
5306   forestgreen: 0x228b22,
5307   fuchsia: 0xff00ff,
5308   gainsboro: 0xdcdcdc,
5309   ghostwhite: 0xf8f8ff,
5310   gold: 0xffd700,
5311   goldenrod: 0xdaa520,
5312   gray: 0x808080,
5313   green: 0x008000,
5314   greenyellow: 0xadff2f,
5315   grey: 0x808080,
5316   honeydew: 0xf0fff0,
5317   hotpink: 0xff69b4,
5318   indianred: 0xcd5c5c,
5319   indigo: 0x4b0082,
5320   ivory: 0xfffff0,
5321   khaki: 0xf0e68c,
5322   lavender: 0xe6e6fa,
5323   lavenderblush: 0xfff0f5,
5324   lawngreen: 0x7cfc00,
5325   lemonchiffon: 0xfffacd,
5326   lightblue: 0xadd8e6,
5327   lightcoral: 0xf08080,
5328   lightcyan: 0xe0ffff,
5329   lightgoldenrodyellow: 0xfafad2,
5330   lightgray: 0xd3d3d3,
5331   lightgreen: 0x90ee90,
5332   lightgrey: 0xd3d3d3,
5333   lightpink: 0xffb6c1,
5334   lightsalmon: 0xffa07a,
5335   lightseagreen: 0x20b2aa,
5336   lightskyblue: 0x87cefa,
5337   lightslategray: 0x778899,
5338   lightslategrey: 0x778899,
5339   lightsteelblue: 0xb0c4de,
5340   lightyellow: 0xffffe0,
5341   lime: 0x00ff00,
5342   limegreen: 0x32cd32,
5343   linen: 0xfaf0e6,
5344   magenta: 0xff00ff,
5345   maroon: 0x800000,
5346   mediumaquamarine: 0x66cdaa,
5347   mediumblue: 0x0000cd,
5348   mediumorchid: 0xba55d3,
5349   mediumpurple: 0x9370db,
5350   mediumseagreen: 0x3cb371,
5351   mediumslateblue: 0x7b68ee,
5352   mediumspringgreen: 0x00fa9a,
5353   mediumturquoise: 0x48d1cc,
5354   mediumvioletred: 0xc71585,
5355   midnightblue: 0x191970,
5356   mintcream: 0xf5fffa,
5357   mistyrose: 0xffe4e1,
5358   moccasin: 0xffe4b5,
5359   navajowhite: 0xffdead,
5360   navy: 0x000080,
5361   oldlace: 0xfdf5e6,
5362   olive: 0x808000,
5363   olivedrab: 0x6b8e23,
5364   orange: 0xffa500,
5365   orangered: 0xff4500,
5366   orchid: 0xda70d6,
5367   palegoldenrod: 0xeee8aa,
5368   palegreen: 0x98fb98,
5369   paleturquoise: 0xafeeee,
5370   palevioletred: 0xdb7093,
5371   papayawhip: 0xffefd5,
5372   peachpuff: 0xffdab9,
5373   peru: 0xcd853f,
5374   pink: 0xffc0cb,
5375   plum: 0xdda0dd,
5376   powderblue: 0xb0e0e6,
5377   purple: 0x800080,
5378   rebeccapurple: 0x663399,
5379   red: 0xff0000,
5380   rosybrown: 0xbc8f8f,
5381   royalblue: 0x4169e1,
5382   saddlebrown: 0x8b4513,
5383   salmon: 0xfa8072,
5384   sandybrown: 0xf4a460,
5385   seagreen: 0x2e8b57,
5386   seashell: 0xfff5ee,
5387   sienna: 0xa0522d,
5388   silver: 0xc0c0c0,
5389   skyblue: 0x87ceeb,
5390   slateblue: 0x6a5acd,
5391   slategray: 0x708090,
5392   slategrey: 0x708090,
5393   snow: 0xfffafa,
5394   springgreen: 0x00ff7f,
5395   steelblue: 0x4682b4,
5396   tan: 0xd2b48c,
5397   teal: 0x008080,
5398   thistle: 0xd8bfd8,
5399   tomato: 0xff6347,
5400   turquoise: 0x40e0d0,
5401   violet: 0xee82ee,
5402   wheat: 0xf5deb3,
5403   white: 0xffffff,
5404   whitesmoke: 0xf5f5f5,
5405   yellow: 0xffff00,
5406   yellowgreen: 0x9acd32
5407 });
5408
5409 d3_rgb_names.forEach(function(key, value) {
5410   d3_rgb_names.set(key, d3_rgbNumber(value));
5411 });
5412
5413 d3.interpolateRgb = d3_interpolateRgb;
5414
5415 function d3_interpolateRgb(a, b) {
5416   a = d3.rgb(a);
5417   b = d3.rgb(b);
5418   var ar = a.r,
5419       ag = a.g,
5420       ab = a.b,
5421       br = b.r - ar,
5422       bg = b.g - ag,
5423       bb = b.b - ab;
5424   return function(t) {
5425     return "#"
5426         + d3_rgb_hex(Math.round(ar + br * t))
5427         + d3_rgb_hex(Math.round(ag + bg * t))
5428         + d3_rgb_hex(Math.round(ab + bb * t));
5429   };
5430 }
5431
5432 d3.interpolateObject = d3_interpolateObject;
5433
5434 function d3_interpolateObject(a, b) {
5435   var i = {},
5436       c = {},
5437       k;
5438   for (k in a) {
5439     if (k in b) {
5440       i[k] = d3_interpolate(a[k], b[k]);
5441     } else {
5442       c[k] = a[k];
5443     }
5444   }
5445   for (k in b) {
5446     if (!(k in a)) {
5447       c[k] = b[k];
5448     }
5449   }
5450   return function(t) {
5451     for (k in i) c[k] = i[k](t);
5452     return c;
5453   };
5454 }
5455
5456 d3.interpolateArray = d3_interpolateArray;
5457
5458 function d3_interpolateArray(a, b) {
5459   var x = [],
5460       c = [],
5461       na = a.length,
5462       nb = b.length,
5463       n0 = Math.min(a.length, b.length),
5464       i;
5465   for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
5466   for (; i < na; ++i) c[i] = a[i];
5467   for (; i < nb; ++i) c[i] = b[i];
5468   return function(t) {
5469     for (i = 0; i < n0; ++i) c[i] = x[i](t);
5470     return c;
5471   };
5472 }
5473 d3.interpolateNumber = d3_interpolateNumber;
5474
5475 function d3_interpolateNumber(a, b) {
5476   a = +a, b = +b;
5477   return function(t) { return a * (1 - t) + b * t; };
5478 }
5479
5480 d3.interpolateString = d3_interpolateString;
5481
5482 function d3_interpolateString(a, b) {
5483   var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, // scan index for next number in b
5484       am, // current match in a
5485       bm, // current match in b
5486       bs, // string preceding current number in b, if any
5487       i = -1, // index in s
5488       s = [], // string constants and placeholders
5489       q = []; // number interpolators
5490
5491   // Coerce inputs to strings.
5492   a = a + "", b = b + "";
5493
5494   // Interpolate pairs of numbers in a & b.
5495   while ((am = d3_interpolate_numberA.exec(a))
5496       && (bm = d3_interpolate_numberB.exec(b))) {
5497     if ((bs = bm.index) > bi) { // a string precedes the next number in b
5498       bs = b.slice(bi, bs);
5499       if (s[i]) s[i] += bs; // coalesce with previous string
5500       else s[++i] = bs;
5501     }
5502     if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
5503       if (s[i]) s[i] += bm; // coalesce with previous string
5504       else s[++i] = bm;
5505     } else { // interpolate non-matching numbers
5506       s[++i] = null;
5507       q.push({i: i, x: d3_interpolateNumber(am, bm)});
5508     }
5509     bi = d3_interpolate_numberB.lastIndex;
5510   }
5511
5512   // Add remains of b.
5513   if (bi < b.length) {
5514     bs = b.slice(bi);
5515     if (s[i]) s[i] += bs; // coalesce with previous string
5516     else s[++i] = bs;
5517   }
5518
5519   // Special optimization for only a single match.
5520   // Otherwise, interpolate each of the numbers and rejoin the string.
5521   return s.length < 2
5522       ? (q[0] ? (b = q[0].x, function(t) { return b(t) + ""; })
5523       : function() { return b; })
5524       : (b = q.length, function(t) {
5525           for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
5526           return s.join("");
5527         });
5528 }
5529
5530 var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
5531     d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g");
5532
5533 d3.interpolate = d3_interpolate;
5534
5535 function d3_interpolate(a, b) {
5536   var i = d3.interpolators.length, f;
5537   while (--i >= 0 && !(f = d3.interpolators[i](a, b)));
5538   return f;
5539 }
5540
5541 d3.interpolators = [
5542   function(a, b) {
5543     var t = typeof b;
5544     return (t === "string" ? (d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString)
5545         : b instanceof d3_color ? d3_interpolateRgb
5546         : Array.isArray(b) ? d3_interpolateArray
5547         : t === "object" && isNaN(b) ? d3_interpolateObject
5548         : d3_interpolateNumber)(a, b);
5549   }
5550 ];
5551
5552 d3.transform = function(string) {
5553   var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
5554   return (d3.transform = function(string) {
5555     if (string != null) {
5556       g.setAttribute("transform", string);
5557       var t = g.transform.baseVal.consolidate();
5558     }
5559     return new d3_transform(t ? t.matrix : d3_transformIdentity);
5560   })(string);
5561 };
5562
5563 // Compute x-scale and normalize the first row.
5564 // Compute shear and make second row orthogonal to first.
5565 // Compute y-scale and normalize the second row.
5566 // Finally, compute the rotation.
5567 function d3_transform(m) {
5568   var r0 = [m.a, m.b],
5569       r1 = [m.c, m.d],
5570       kx = d3_transformNormalize(r0),
5571       kz = d3_transformDot(r0, r1),
5572       ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
5573   if (r0[0] * r1[1] < r1[0] * r0[1]) {
5574     r0[0] *= -1;
5575     r0[1] *= -1;
5576     kx *= -1;
5577     kz *= -1;
5578   }
5579   this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
5580   this.translate = [m.e, m.f];
5581   this.scale = [kx, ky];
5582   this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
5583 };
5584
5585 d3_transform.prototype.toString = function() {
5586   return "translate(" + this.translate
5587       + ")rotate(" + this.rotate
5588       + ")skewX(" + this.skew
5589       + ")scale(" + this.scale
5590       + ")";
5591 };
5592
5593 function d3_transformDot(a, b) {
5594   return a[0] * b[0] + a[1] * b[1];
5595 }
5596
5597 function d3_transformNormalize(a) {
5598   var k = Math.sqrt(d3_transformDot(a, a));
5599   if (k) {
5600     a[0] /= k;
5601     a[1] /= k;
5602   }
5603   return k;
5604 }
5605
5606 function d3_transformCombine(a, b, k) {
5607   a[0] += k * b[0];
5608   a[1] += k * b[1];
5609   return a;
5610 }
5611
5612 var d3_transformIdentity = {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0};
5613
5614 d3.interpolateTransform = d3_interpolateTransform;
5615
5616 function d3_interpolateTransform(a, b) {
5617   var s = [], // string constants and placeholders
5618       q = [], // number interpolators
5619       n,
5620       A = d3.transform(a),
5621       B = d3.transform(b),
5622       ta = A.translate,
5623       tb = B.translate,
5624       ra = A.rotate,
5625       rb = B.rotate,
5626       wa = A.skew,
5627       wb = B.skew,
5628       ka = A.scale,
5629       kb = B.scale;
5630
5631   if (ta[0] != tb[0] || ta[1] != tb[1]) {
5632     s.push("translate(", null, ",", null, ")");
5633     q.push({i: 1, x: d3_interpolateNumber(ta[0], tb[0])}, {i: 3, x: d3_interpolateNumber(ta[1], tb[1])});
5634   } else if (tb[0] || tb[1]) {
5635     s.push("translate(" + tb + ")");
5636   } else {
5637     s.push("");
5638   }
5639
5640   if (ra != rb) {
5641     if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; // shortest path
5642     q.push({i: s.push(s.pop() + "rotate(", null, ")") - 2, x: d3_interpolateNumber(ra, rb)});
5643   } else if (rb) {
5644     s.push(s.pop() + "rotate(" + rb + ")");
5645   }
5646
5647   if (wa != wb) {
5648     q.push({i: s.push(s.pop() + "skewX(", null, ")") - 2, x: d3_interpolateNumber(wa, wb)});
5649   } else if (wb) {
5650     s.push(s.pop() + "skewX(" + wb + ")");
5651   }
5652
5653   if (ka[0] != kb[0] || ka[1] != kb[1]) {
5654     n = s.push(s.pop() + "scale(", null, ",", null, ")");
5655     q.push({i: n - 4, x: d3_interpolateNumber(ka[0], kb[0])}, {i: n - 2, x: d3_interpolateNumber(ka[1], kb[1])});
5656   } else if (kb[0] != 1 || kb[1] != 1) {
5657     s.push(s.pop() + "scale(" + kb + ")");
5658   }
5659
5660   n = q.length;
5661   return function(t) {
5662     var i = -1, o;
5663     while (++i < n) s[(o = q[i]).i] = o.x(t);
5664     return s.join("");
5665   };
5666 }
5667
5668 d3_transitionPrototype.tween = function(name, tween) {
5669   var id = this.id, ns = this.namespace;
5670   if (arguments.length < 2) return this.node()[ns][id].tween.get(name);
5671   return d3_selection_each(this, tween == null
5672         ? function(node) { node[ns][id].tween.remove(name); }
5673         : function(node) { node[ns][id].tween.set(name, tween); });
5674 };
5675
5676 function d3_transition_tween(groups, name, value, tween) {
5677   var id = groups.id, ns = groups.namespace;
5678   return d3_selection_each(groups, typeof value === "function"
5679       ? function(node, i, j) { node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j))); }
5680       : (value = tween(value), function(node) { node[ns][id].tween.set(name, value); }));
5681 }
5682
5683 d3_transitionPrototype.attr = function(nameNS, value) {
5684   if (arguments.length < 2) {
5685
5686     // For attr(object), the object specifies the names and values of the
5687     // attributes to transition. The values may be functions that are
5688     // evaluated for each element.
5689     for (value in nameNS) this.attr(value, nameNS[value]);
5690     return this;
5691   }
5692
5693   var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate,
5694       name = d3.ns.qualify(nameNS);
5695
5696   // For attr(string, null), remove the attribute with the specified name.
5697   function attrNull() {
5698     this.removeAttribute(name);
5699   }
5700   function attrNullNS() {
5701     this.removeAttributeNS(name.space, name.local);
5702   }
5703
5704   // For attr(string, string), set the attribute with the specified name.
5705   function attrTween(b) {
5706     return b == null ? attrNull : (b += "", function() {
5707       var a = this.getAttribute(name), i;
5708       return a !== b && (i = interpolate(a, b), function(t) { this.setAttribute(name, i(t)); });
5709     });
5710   }
5711   function attrTweenNS(b) {
5712     return b == null ? attrNullNS : (b += "", function() {
5713       var a = this.getAttributeNS(name.space, name.local), i;
5714       return a !== b && (i = interpolate(a, b), function(t) { this.setAttributeNS(name.space, name.local, i(t)); });
5715     });
5716   }
5717
5718   return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
5719 };
5720
5721 d3_transitionPrototype.attrTween = function(nameNS, tween) {
5722   var name = d3.ns.qualify(nameNS);
5723
5724   function attrTween(d, i) {
5725     var f = tween.call(this, d, i, this.getAttribute(name));
5726     return f && function(t) { this.setAttribute(name, f(t)); };
5727   }
5728   function attrTweenNS(d, i) {
5729     var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
5730     return f && function(t) { this.setAttributeNS(name.space, name.local, f(t)); };
5731   }
5732
5733   return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
5734 };
5735
5736 d3_transitionPrototype.style = function(name, value, priority) {
5737   var n = arguments.length;
5738   if (n < 3) {
5739
5740     // For style(object) or style(object, string), the object specifies the
5741     // names and values of the attributes to set or remove. The values may be
5742     // functions that are evaluated for each element. The optional string
5743     // specifies the priority.
5744     if (typeof name !== "string") {
5745       if (n < 2) value = "";
5746       for (priority in name) this.style(priority, name[priority], value);
5747       return this;
5748     }
5749
5750     // For style(string, string) or style(string, function), use the default
5751     // priority. The priority is ignored for style(string, null).
5752     priority = "";
5753   }
5754
5755   // For style(name, null) or style(name, null, priority), remove the style
5756   // property with the specified name. The priority is ignored.
5757   function styleNull() {
5758     this.style.removeProperty(name);
5759   }
5760
5761   // For style(name, string) or style(name, string, priority), set the style
5762   // property with the specified name, using the specified priority.
5763   // Otherwise, a name, value and priority are specified, and handled as below.
5764   function styleString(b) {
5765     return b == null ? styleNull : (b += "", function() {
5766       var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i;
5767       return a !== b && (i = d3_interpolate(a, b), function(t) { this.style.setProperty(name, i(t), priority); });
5768     });
5769   }
5770
5771   return d3_transition_tween(this, "style." + name, value, styleString);
5772 };
5773
5774 d3_transitionPrototype.styleTween = function(name, tween, priority) {
5775   if (arguments.length < 3) priority = "";
5776
5777   function styleTween(d, i) {
5778     var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name));
5779     return f && function(t) { this.style.setProperty(name, f(t), priority); };
5780   }
5781
5782   return this.tween("style." + name, styleTween);
5783 };
5784
5785 d3_transitionPrototype.text = function(value) {
5786   return d3_transition_tween(this, "text", value, d3_transition_text);
5787 };
5788
5789 function d3_transition_text(b) {
5790   if (b == null) b = "";
5791   return function() { this.textContent = b; };
5792 }
5793
5794 d3_transitionPrototype.remove = function() {
5795   var ns = this.namespace;
5796   return this.each("end.transition", function() {
5797     var p;
5798     if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this);
5799   });
5800 };
5801
5802 var d3_ease_default = function() { return d3_identity; };
5803
5804 var d3_ease = d3.map({
5805   linear: d3_ease_default,
5806   poly: d3_ease_poly,
5807   quad: function() { return d3_ease_quad; },
5808   cubic: function() { return d3_ease_cubic; },
5809   sin: function() { return d3_ease_sin; },
5810   exp: function() { return d3_ease_exp; },
5811   circle: function() { return d3_ease_circle; },
5812   elastic: d3_ease_elastic,
5813   back: d3_ease_back,
5814   bounce: function() { return d3_ease_bounce; }
5815 });
5816
5817 var d3_ease_mode = d3.map({
5818   "in": d3_identity,
5819   "out": d3_ease_reverse,
5820   "in-out": d3_ease_reflect,
5821   "out-in": function(f) { return d3_ease_reflect(d3_ease_reverse(f)); }
5822 });
5823
5824 d3.ease = function(name) {
5825   var i = name.indexOf("-"),
5826       t = i >= 0 ? name.slice(0, i) : name,
5827       m = i >= 0 ? name.slice(i + 1) : "in";
5828   t = d3_ease.get(t) || d3_ease_default;
5829   m = d3_ease_mode.get(m) || d3_identity;
5830   return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
5831 };
5832
5833 function d3_ease_clamp(f) {
5834   return function(t) {
5835     return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
5836   };
5837 }
5838
5839 function d3_ease_reverse(f) {
5840   return function(t) {
5841     return 1 - f(1 - t);
5842   };
5843 }
5844
5845 function d3_ease_reflect(f) {
5846   return function(t) {
5847     return .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t)));
5848   };
5849 }
5850
5851 function d3_ease_quad(t) {
5852   return t * t;
5853 }
5854
5855 function d3_ease_cubic(t) {
5856   return t * t * t;
5857 }
5858
5859 // Optimized clamp(reflect(poly(3))).
5860 function d3_ease_cubicInOut(t) {
5861   if (t <= 0) return 0;
5862   if (t >= 1) return 1;
5863   var t2 = t * t, t3 = t2 * t;
5864   return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
5865 }
5866
5867 function d3_ease_poly(e) {
5868   return function(t) {
5869     return Math.pow(t, e);
5870   };
5871 }
5872
5873 function d3_ease_sin(t) {
5874   return 1 - Math.cos(t * halfπ);
5875 }
5876
5877 function d3_ease_exp(t) {
5878   return Math.pow(2, 10 * (t - 1));
5879 }
5880
5881 function d3_ease_circle(t) {
5882   return 1 - Math.sqrt(1 - t * t);
5883 }
5884
5885 function d3_ease_elastic(a, p) {
5886   var s;
5887   if (arguments.length < 2) p = 0.45;
5888   if (arguments.length) s = p / τ * Math.asin(1 / a);
5889   else a = 1, s = p / 4;
5890   return function(t) {
5891     return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p);
5892   };
5893 }
5894
5895 function d3_ease_back(s) {
5896   if (!s) s = 1.70158;
5897   return function(t) {
5898     return t * t * ((s + 1) * t - s);
5899   };
5900 }
5901
5902 function d3_ease_bounce(t) {
5903   return t < 1 / 2.75 ? 7.5625 * t * t
5904       : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75
5905       : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375
5906       : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
5907 }
5908
5909 d3_transitionPrototype.ease = function(value) {
5910   var id = this.id, ns = this.namespace;
5911   if (arguments.length < 1) return this.node()[ns][id].ease;
5912   if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
5913   return d3_selection_each(this, function(node) { node[ns][id].ease = value; });
5914 };
5915
5916 d3_transitionPrototype.delay = function(value) {
5917   var id = this.id, ns = this.namespace;
5918   if (arguments.length < 1) return this.node()[ns][id].delay;
5919   return d3_selection_each(this, typeof value === "function"
5920       ? function(node, i, j) { node[ns][id].delay = +value.call(node, node.__data__, i, j); }
5921       : (value = +value, function(node) { node[ns][id].delay = value; }));
5922 };
5923
5924 d3_transitionPrototype.duration = function(value) {
5925   var id = this.id, ns = this.namespace;
5926   if (arguments.length < 1) return this.node()[ns][id].duration;
5927   return d3_selection_each(this, typeof value === "function"
5928       ? function(node, i, j) { node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j)); }
5929       : (value = Math.max(1, value), function(node) { node[ns][id].duration = value; }));
5930 };
5931
5932 d3_transitionPrototype.each = function(type, listener) {
5933   var id = this.id, ns = this.namespace;
5934   if (arguments.length < 2) {
5935     var inherit = d3_transitionInherit,
5936         inheritId = d3_transitionInheritId;
5937     try {
5938       d3_transitionInheritId = id;
5939       d3_selection_each(this, function(node, i, j) {
5940         d3_transitionInherit = node[ns][id];
5941         type.call(node, node.__data__, i, j);
5942       });
5943     } finally {
5944       d3_transitionInherit = inherit;
5945       d3_transitionInheritId = inheritId;
5946     }
5947   } else {
5948     d3_selection_each(this, function(node) {
5949       var transition = node[ns][id];
5950       (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener);
5951     });
5952   }
5953   return this;
5954 };
5955
5956 d3_transitionPrototype.transition = function() {
5957   var id0 = this.id,
5958       id1 = ++d3_transitionId,
5959       ns = this.namespace,
5960       subgroups = [],
5961       subgroup,
5962       group,
5963       node,
5964       transition;
5965
5966   for (var j = 0, m = this.length; j < m; j++) {
5967     subgroups.push(subgroup = []);
5968     for (var group = this[j], i = 0, n = group.length; i < n; i++) {
5969       if (node = group[i]) {
5970         transition = node[ns][id0];
5971         d3_transitionNode(node, i, ns, id1, {time: transition.time, ease: transition.ease, delay: transition.delay + transition.duration, duration: transition.duration});
5972       }
5973       subgroup.push(node);
5974     }
5975   }
5976
5977   return d3_transition(subgroups, ns, id1);
5978 };
5979
5980 function d3_transitionNamespace(name) {
5981   return name == null ? "__transition__" : "__transition_" + name + "__";
5982 }
5983
5984 function d3_transitionNode(node, i, ns, id, inherit) {
5985   var lock = node[ns] || (node[ns] = {active: 0, count: 0}),
5986       transition = lock[id];
5987
5988   if (!transition) {
5989     var time = inherit.time;
5990
5991     transition = lock[id] = {
5992       tween: new d3_Map,
5993       time: time,
5994       delay: inherit.delay,
5995       duration: inherit.duration,
5996       ease: inherit.ease,
5997       index: i
5998     };
5999
6000     inherit = null; // allow gc
6001
6002     ++lock.count;
6003
6004     d3.timer(function(elapsed) {
6005       var delay = transition.delay,
6006           duration,
6007           ease,
6008           timer = d3_timer_active,
6009           tweened = [];
6010
6011       timer.t = delay + time;
6012       if (delay <= elapsed) return start(elapsed - delay);
6013       timer.c = start;
6014
6015       function start(elapsed) {
6016         if (lock.active > id) return stop();
6017
6018         var active = lock[lock.active];
6019         if (active) {
6020           --lock.count;
6021           delete lock[lock.active];
6022           active.event && active.event.interrupt.call(node, node.__data__, active.index);
6023         }
6024
6025         lock.active = id;
6026
6027         transition.event && transition.event.start.call(node, node.__data__, i);
6028
6029         transition.tween.forEach(function(key, value) {
6030           if (value = value.call(node, node.__data__, i)) {
6031             tweened.push(value);
6032           }
6033         });
6034
6035         // Deferred capture to allow tweens to initialize ease & duration.
6036         ease = transition.ease;
6037         duration = transition.duration;
6038
6039         d3.timer(function() { // defer to end of current frame
6040           timer.c = tick(elapsed || 1) ? d3_true : tick;
6041           return 1;
6042         }, 0, time);
6043       }
6044
6045       function tick(elapsed) {
6046         if (lock.active !== id) return 1;
6047
6048         var t = elapsed / duration,
6049             e = ease(t),
6050             n = tweened.length;
6051
6052         while (n > 0) {
6053           tweened[--n].call(node, e);
6054         }
6055
6056         if (t >= 1) {
6057           transition.event && transition.event.end.call(node, node.__data__, i);
6058           return stop();
6059         }
6060       }
6061
6062       function stop() {
6063         if (--lock.count) delete lock[id];
6064         else delete node[ns];
6065         return 1;
6066       }
6067     }, 0, time);
6068   }
6069 }
6070
6071 d3.xhr = d3_xhrType(d3_identity);
6072
6073 function d3_xhrType(response) {
6074   return function(url, mimeType, callback) {
6075     if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, mimeType = null;
6076     return d3_xhr(url, mimeType, response, callback);
6077   };
6078 }
6079
6080 function d3_xhr(url, mimeType, response, callback) {
6081   var xhr = {},
6082       dispatch = d3.dispatch("beforesend", "progress", "load", "error"),
6083       headers = {},
6084       request = new XMLHttpRequest,
6085       responseType = null;
6086
6087   // If IE does not support CORS, use XDomainRequest.
6088   if (this.XDomainRequest
6089       && !("withCredentials" in request)
6090       && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest;
6091
6092   "onload" in request
6093       ? request.onload = request.onerror = respond
6094       : request.onreadystatechange = function() { request.readyState > 3 && respond(); };
6095
6096   function respond() {
6097     var status = request.status, result;
6098     if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) {
6099       try {
6100         result = response.call(xhr, request);
6101       } catch (e) {
6102         dispatch.error.call(xhr, e);
6103         return;
6104       }
6105       dispatch.load.call(xhr, result);
6106     } else {
6107       dispatch.error.call(xhr, request);
6108     }
6109   }
6110
6111   request.onprogress = function(event) {
6112     var o = d3.event;
6113     d3.event = event;
6114     try { dispatch.progress.call(xhr, request); }
6115     finally { d3.event = o; }
6116   };
6117
6118   xhr.header = function(name, value) {
6119     name = (name + "").toLowerCase();
6120     if (arguments.length < 2) return headers[name];
6121     if (value == null) delete headers[name];
6122     else headers[name] = value + "";
6123     return xhr;
6124   };
6125
6126   // If mimeType is non-null and no Accept header is set, a default is used.
6127   xhr.mimeType = function(value) {
6128     if (!arguments.length) return mimeType;
6129     mimeType = value == null ? null : value + "";
6130     return xhr;
6131   };
6132
6133   // Specifies what type the response value should take;
6134   // for instance, arraybuffer, blob, document, or text.
6135   xhr.responseType = function(value) {
6136     if (!arguments.length) return responseType;
6137     responseType = value;
6138     return xhr;
6139   };
6140
6141   // Specify how to convert the response content to a specific type;
6142   // changes the callback value on "load" events.
6143   xhr.response = function(value) {
6144     response = value;
6145     return xhr;
6146   };
6147
6148   // Convenience methods.
6149   ["get", "post"].forEach(function(method) {
6150     xhr[method] = function() {
6151       return xhr.send.apply(xhr, [method].concat(d3_array(arguments)));
6152     };
6153   });
6154
6155   // If callback is non-null, it will be used for error and load events.
6156   xhr.send = function(method, data, callback) {
6157     if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
6158     request.open(method, url, true);
6159     if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
6160     if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
6161     if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
6162     if (responseType != null) request.responseType = responseType;
6163     if (callback != null) xhr.on("error", callback).on("load", function(request) { callback(null, request); });
6164     dispatch.beforesend.call(xhr, request);
6165     request.send(data == null ? null : data);
6166     return xhr;
6167   };
6168
6169   xhr.abort = function() {
6170     request.abort();
6171     return xhr;
6172   };
6173
6174   d3.rebind(xhr, dispatch, "on");
6175
6176   return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
6177 };
6178
6179 function d3_xhr_fixCallback(callback) {
6180   return callback.length === 1
6181       ? function(error, request) { callback(error == null ? request : null); }
6182       : callback;
6183 }
6184
6185 function d3_xhrHasResponse(request) {
6186   var type = request.responseType;
6187   return type && type !== "text"
6188       ? request.response // null on error
6189       : request.responseText; // "" on error
6190 }
6191
6192 d3.text = d3_xhrType(function(request) {
6193   return request.responseText;
6194 });
6195
6196 d3.json = function(url, callback) {
6197   return d3_xhr(url, "application/json", d3_json, callback);
6198 };
6199
6200 function d3_json(request) {
6201   return JSON.parse(request.responseText);
6202 }
6203
6204 d3.html = function(url, callback) {
6205   return d3_xhr(url, "text/html", d3_html, callback);
6206 };
6207
6208 function d3_html(request) {
6209   var range = d3_document.createRange();
6210   range.selectNode(d3_document.body);
6211   return range.createContextualFragment(request.responseText);
6212 }
6213
6214 d3.xml = d3_xhrType(function(request) {
6215   return request.responseXML;
6216 });
6217   if (typeof define === "function" && define.amd) define(d3);
6218   else if (typeof module === "object" && module.exports) module.exports = d3;
6219   this.d3 = d3;
6220 }();
6221 d3.combobox = function() {
6222     var event = d3.dispatch('accept'),
6223         data = [],
6224         suggestions = [],
6225         minItems = 2,
6226         caseSensitive = false;
6227
6228     var fetcher = function(val, cb) {
6229         cb(data.filter(function(d) {
6230             return d.value
6231                 .toString()
6232                 .toLowerCase()
6233                 .indexOf(val.toLowerCase()) !== -1;
6234         }));
6235     };
6236
6237     var combobox = function(input, attachTo) {
6238         var idx = -1,
6239             container = d3.select(document.body)
6240                 .selectAll('div.combobox')
6241                 .filter(function(d) { return d === input.node(); }),
6242             shown = !container.empty();
6243
6244         input
6245             .classed('combobox-input', true)
6246             .on('focus.typeahead', focus)
6247             .on('blur.typeahead', blur)
6248             .on('keydown.typeahead', keydown)
6249             .on('keyup.typeahead', keyup)
6250             .on('input.typeahead', change)
6251             .each(function() {
6252                 var parent = this.parentNode,
6253                     sibling = this.nextSibling;
6254
6255                 var caret = d3.select(parent).selectAll('.combobox-caret')
6256                     .filter(function(d) { return d === input.node(); })
6257                     .data([input.node()]);
6258
6259                 caret.enter().insert('div', function() { return sibling; })
6260                     .attr('class', 'combobox-caret');
6261
6262                 caret
6263                     .on('mousedown', function () {
6264                         // prevent the form element from blurring. it blurs
6265                         // on mousedown
6266                         d3.event.stopPropagation();
6267                         d3.event.preventDefault();
6268                         if (!shown) {
6269                             input.node().focus();
6270                             fetch('', render);
6271                         } else {
6272                             hide();
6273                         }
6274                     });
6275             });
6276
6277         function focus() {
6278             fetch(value(), render);
6279         }
6280
6281         function blur() {
6282             window.setTimeout(hide, 150);
6283         }
6284
6285         function show() {
6286             if (!shown) {
6287                 container = d3.select(document.body)
6288                     .insert('div', ':first-child')
6289                     .datum(input.node())
6290                     .attr('class', 'combobox')
6291                     .style({
6292                         position: 'absolute',
6293                         display: 'block',
6294                         left: '0px'
6295                     })
6296                     .on('mousedown', function () {
6297                         // prevent moving focus out of the text field
6298                         d3.event.preventDefault();
6299                     });
6300
6301                 d3.select(document.body)
6302                     .on('scroll.combobox', render, true);
6303
6304                 shown = true;
6305             }
6306         }
6307
6308         function hide() {
6309             if (shown) {
6310                 idx = -1;
6311                 container.remove();
6312
6313                 d3.select(document.body)
6314                     .on('scroll.combobox', null);
6315
6316                 shown = false;
6317             }
6318         }
6319
6320         function keydown() {
6321            switch (d3.event.keyCode) {
6322                // backspace, delete
6323                case 8:
6324                case 46:
6325                    input.on('input.typeahead', function() {
6326                        idx = -1;
6327                        render();
6328                        var start = input.property('selectionStart');
6329                        input.node().setSelectionRange(start, start);
6330                        input.on('input.typeahead', change);
6331                    });
6332                    break;
6333                // tab
6334                case 9:
6335                    container.selectAll('a.selected').each(event.accept);
6336                    break;
6337                // return
6338                case 13:
6339                    d3.event.preventDefault();
6340                    break;
6341                // up arrow
6342                case 38:
6343                    nav(-1);
6344                    d3.event.preventDefault();
6345                    break;
6346                // down arrow
6347                case 40:
6348                    nav(+1);
6349                    d3.event.preventDefault();
6350                    break;
6351            }
6352            d3.event.stopPropagation();
6353         }
6354
6355         function keyup() {
6356             switch (d3.event.keyCode) {
6357                 // escape
6358                 case 27:
6359                     hide();
6360                     break;
6361                 // return
6362                 case 13:
6363                     container.selectAll('a.selected').each(event.accept);
6364                     hide();
6365                     break;
6366             }
6367         }
6368
6369         function change() {
6370             fetch(value(), function() {
6371                 autocomplete();
6372                 render();
6373             });
6374         }
6375
6376         function nav(dir) {
6377             if (!suggestions.length) return;
6378             idx = Math.max(Math.min(idx + dir, suggestions.length - 1), 0);
6379             input.property('value', suggestions[idx].value);
6380             render();
6381             ensureVisible();
6382         }
6383
6384         function value() {
6385             var value = input.property('value'),
6386                 start = input.property('selectionStart'),
6387                 end = input.property('selectionEnd');
6388
6389             if (start && end) {
6390                 value = value.substring(0, start);
6391             }
6392
6393             return value;
6394         }
6395
6396         function fetch(v, cb) {
6397             fetcher.call(input, v, function(_) {
6398                 suggestions = _;
6399                 cb();
6400             });
6401         }
6402
6403         function autocomplete() {
6404             var v = caseSensitive ? value() : value().toLowerCase();
6405             idx = -1;
6406             if (!v) return;
6407
6408             for (var i = 0; i < suggestions.length; i++) {
6409                 var suggestion = suggestions[i].value,
6410                     compare = caseSensitive ? suggestion : suggestion.toLowerCase();
6411
6412                 if (compare.indexOf(v) === 0) {
6413                     idx = i;
6414                     input.property('value', suggestion);
6415                     input.node().setSelectionRange(v.length, suggestion.length);
6416                     return;
6417                 }
6418             }
6419         }
6420
6421         function render() {
6422             if (suggestions.length >= minItems && document.activeElement === input.node()) {
6423                 show();
6424             } else {
6425                 hide();
6426                 return;
6427             }
6428
6429             var options = container
6430                 .selectAll('a.combobox-option')
6431                 .data(suggestions, function(d) { return d.value; });
6432
6433             options.enter().append('a')
6434                 .attr('class', 'combobox-option')
6435                 .text(function(d) { return d.value; });
6436
6437             options
6438                 .attr('title', function(d) { return d.title; })
6439                 .classed('selected', function(d, i) { return i == idx; })
6440                 .on('mouseover', select)
6441                 .on('click', accept)
6442                 .order();
6443
6444             options.exit()
6445                 .remove();
6446
6447             var node = attachTo ? attachTo.node() : input.node(),
6448                 rect = node.getBoundingClientRect();
6449
6450             container.style({
6451                 'left': rect.left + 'px',
6452                 'width': rect.width + 'px',
6453                 'top': rect.height + rect.top + 'px'
6454             });
6455         }
6456
6457         function select(d, i) {
6458             idx = i;
6459             render();
6460         }
6461
6462         function ensureVisible() {
6463             var node = container.selectAll('a.selected').node();
6464             if (node) node.scrollIntoView();
6465         }
6466
6467         function accept(d) {
6468             if (!shown) return;
6469             input
6470                 .property('value', d.value)
6471                 .trigger('change');
6472             event.accept(d);
6473             hide();
6474         }
6475     };
6476
6477     combobox.fetcher = function(_) {
6478         if (!arguments.length) return fetcher;
6479         fetcher = _;
6480         return combobox;
6481     };
6482
6483     combobox.data = function(_) {
6484         if (!arguments.length) return data;
6485         data = _;
6486         return combobox;
6487     };
6488
6489     combobox.minItems = function(_) {
6490         if (!arguments.length) return minItems;
6491         minItems = _;
6492         return combobox;
6493     };
6494
6495     combobox.caseSensitive = function(_) {
6496         if (!arguments.length) return caseSensitive;
6497         caseSensitive = _;
6498         return combobox;
6499     };
6500
6501     return d3.rebind(combobox, event, 'on');
6502 };
6503
6504 d3.combobox.off = function(input) {
6505     data = null;
6506     fetcher = null;
6507
6508     input
6509         .on('focus.typeahead', null)
6510         .on('blur.typeahead', null)
6511         .on('keydown.typeahead', null)
6512         .on('keyup.typeahead', null)
6513         .on('input.typeahead', null)
6514         .each(function() {
6515             d3.select(this.parentNode).selectAll('.combobox-caret')
6516                 .filter(function(d) { return d === input.node(); })
6517                 .on('mousedown', null);
6518         });
6519
6520     d3.select(document.body)
6521         .on('scroll.combobox', null);
6522 };
6523 d3.geo.tile = function() {
6524   var size = [960, 500],
6525       scale = 256,
6526       scaleExtent = [0, 20],
6527       translate = [size[0] / 2, size[1] / 2],
6528       zoomDelta = 0;
6529
6530   function bound(_) {
6531       return Math.min(scaleExtent[1], Math.max(scaleExtent[0], _));
6532   }
6533
6534   function tile() {
6535     var z = Math.max(Math.log(scale) / Math.LN2 - 8, 0),
6536         z0 = bound(Math.round(z + zoomDelta)),
6537         k = Math.pow(2, z - z0 + 8),
6538         origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k],
6539         tiles = [],
6540         cols = d3.range(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))),
6541         rows = d3.range(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1])));
6542
6543     rows.forEach(function(y) {
6544       cols.forEach(function(x) {
6545         tiles.push([x, y, z0]);
6546       });
6547     });
6548
6549     tiles.translate = origin;
6550     tiles.scale = k;
6551
6552     return tiles;
6553   }
6554
6555   tile.scaleExtent = function(_) {
6556     if (!arguments.length) return scaleExtent;
6557     scaleExtent = _;
6558     return tile;
6559   };
6560
6561   tile.size = function(_) {
6562     if (!arguments.length) return size;
6563     size = _;
6564     return tile;
6565   };
6566
6567   tile.scale = function(_) {
6568     if (!arguments.length) return scale;
6569     scale = _;
6570     return tile;
6571   };
6572
6573   tile.translate = function(_) {
6574     if (!arguments.length) return translate;
6575     translate = _;
6576     return tile;
6577   };
6578
6579   tile.zoomDelta = function(_) {
6580     if (!arguments.length) return zoomDelta;
6581     zoomDelta = +_;
6582     return tile;
6583   };
6584
6585   return tile;
6586 };
6587 d3.jsonp = function (url, callback) {
6588   function rand() {
6589     var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
6590       c = '', i = -1;
6591     while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
6592     return c;
6593   }
6594
6595   function create(url) {
6596     var e = url.match(/callback=d3.jsonp.(\w+)/),
6597       c = e ? e[1] : rand();
6598     d3.jsonp[c] = function(data) {
6599       callback(data);
6600       delete d3.jsonp[c];
6601       script.remove();
6602     };
6603     return 'd3.jsonp.' + c;
6604   }
6605
6606   var cb = create(url),
6607     script = d3.select('head')
6608     .append('script')
6609     .attr('type', 'text/javascript')
6610     .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
6611 };
6612 /*
6613  * This code is licensed under the MIT license.
6614  *
6615  * Copyright © 2013, iD authors.
6616  *
6617  * Portions copyright © 2011, Keith Cirkel
6618  * See https://github.com/keithamus/jwerty
6619  *
6620  */
6621 d3.keybinding = function(namespace) {
6622     var bindings = [];
6623
6624     function matches(binding, event) {
6625         for (var p in binding.event) {
6626             if (event[p] != binding.event[p])
6627                 return false;
6628         }
6629
6630         return (!binding.capture) === (event.eventPhase !== Event.CAPTURING_PHASE);
6631     }
6632
6633     function capture() {
6634         for (var i = 0; i < bindings.length; i++) {
6635             var binding = bindings[i];
6636             if (matches(binding, d3.event)) {
6637                 binding.callback();
6638             }
6639         }
6640     }
6641
6642     function bubble() {
6643         var tagName = d3.select(d3.event.target).node().tagName;
6644         if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
6645             return;
6646         }
6647         capture();
6648     }
6649
6650     function keybinding(selection) {
6651         selection = selection || d3.select(document);
6652         selection.on('keydown.capture' + namespace, capture, true);
6653         selection.on('keydown.bubble' + namespace, bubble, false);
6654         return keybinding;
6655     }
6656
6657     keybinding.off = function(selection) {
6658         selection = selection || d3.select(document);
6659         selection.on('keydown.capture' + namespace, null);
6660         selection.on('keydown.bubble' + namespace, null);
6661         return keybinding;
6662     };
6663
6664     keybinding.on = function(code, callback, capture) {
6665         var binding = {
6666             event: {
6667                 keyCode: 0,
6668                 shiftKey: false,
6669                 ctrlKey: false,
6670                 altKey: false,
6671                 metaKey: false
6672             },
6673             capture: capture,
6674             callback: callback
6675         };
6676
6677         code = code.toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
6678
6679         for (var i = 0; i < code.length; i++) {
6680             // Normalise matching errors
6681             if (code[i] === '++') code[i] = '+';
6682
6683             if (code[i] in d3.keybinding.modifierCodes) {
6684                 binding.event[d3.keybinding.modifierProperties[d3.keybinding.modifierCodes[code[i]]]] = true;
6685             } else if (code[i] in d3.keybinding.keyCodes) {
6686                 binding.event.keyCode = d3.keybinding.keyCodes[code[i]];
6687             }
6688         }
6689
6690         bindings.push(binding);
6691
6692         return keybinding;
6693     };
6694
6695     return keybinding;
6696 };
6697
6698 (function () {
6699     d3.keybinding.modifierCodes = {
6700         // Shift key, ⇧
6701         '⇧': 16, shift: 16,
6702         // CTRL key, on Mac: ⌃
6703         '⌃': 17, ctrl: 17,
6704         // ALT key, on Mac: ⌥ (Alt)
6705         '⌥': 18, alt: 18, option: 18,
6706         // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
6707         '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
6708     };
6709
6710     d3.keybinding.modifierProperties = {
6711         16: 'shiftKey',
6712         17: 'ctrlKey',
6713         18: 'altKey',
6714         91: 'metaKey'
6715     };
6716
6717     d3.keybinding.keyCodes = {
6718         // Backspace key, on Mac: ⌫ (Backspace)
6719         '⌫': 8, backspace: 8,
6720         // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
6721         '⇥': 9, '⇆': 9, tab: 9,
6722         // Return key, ↩
6723         '↩': 13, 'return': 13, enter: 13, '⌅': 13,
6724         // Pause/Break key
6725         'pause': 19, 'pause-break': 19,
6726         // Caps Lock key, ⇪
6727         '⇪': 20, caps: 20, 'caps-lock': 20,
6728         // Escape key, on Mac: ⎋, on Windows: Esc
6729         '⎋': 27, escape: 27, esc: 27,
6730         // Space key
6731         space: 32,
6732         // Page-Up key, or pgup, on Mac: ↖
6733         '↖': 33, pgup: 33, 'page-up': 33,
6734         // Page-Down key, or pgdown, on Mac: ↘
6735         '↘': 34, pgdown: 34, 'page-down': 34,
6736         // END key, on Mac: ⇟
6737         '⇟': 35, end: 35,
6738         // HOME key, on Mac: ⇞
6739         '⇞': 36, home: 36,
6740         // Insert key, or ins
6741         ins: 45, insert: 45,
6742         // Delete key, on Mac: ⌦ (Delete)
6743         '⌦': 46, del: 46, 'delete': 46,
6744         // Left Arrow Key, or ←
6745         '←': 37, left: 37, 'arrow-left': 37,
6746         // Up Arrow Key, or ↑
6747         '↑': 38, up: 38, 'arrow-up': 38,
6748         // Right Arrow Key, or →
6749         '→': 39, right: 39, 'arrow-right': 39,
6750         // Up Arrow Key, or ↓
6751         '↓': 40, down: 40, 'arrow-down': 40,
6752         // odities, printing characters that come out wrong:
6753         // Firefox Equals
6754         'ffequals': 61,
6755         // Num-Multiply, or *
6756         '*': 106, star: 106, asterisk: 106, multiply: 106,
6757         // Num-Plus or +
6758         '+': 107, 'plus': 107,
6759         // Num-Subtract, or -
6760         '-': 109, subtract: 109,
6761         // Firefox Minus
6762         'ffplus': 171,
6763         // Firefox Minus
6764         'ffminus': 173,
6765         // Semicolon
6766         ';': 186, semicolon: 186,
6767         // = or equals
6768         '=': 187, 'equals': 187,
6769         // Comma, or ,
6770         ',': 188, comma: 188,
6771         'dash': 189, //???
6772         // Period, or ., or full-stop
6773         '.': 190, period: 190, 'full-stop': 190,
6774         // Slash, or /, or forward-slash
6775         '/': 191, slash: 191, 'forward-slash': 191,
6776         // Tick, or `, or back-quote
6777         '`': 192, tick: 192, 'back-quote': 192,
6778         // Open bracket, or [
6779         '[': 219, 'open-bracket': 219,
6780         // Back slash, or \
6781         '\\': 220, 'back-slash': 220,
6782         // Close backet, or ]
6783         ']': 221, 'close-bracket': 221,
6784         // Apostrophe, or Quote, or '
6785         '\'': 222, quote: 222, apostrophe: 222
6786     };
6787
6788     // NUMPAD 0-9
6789     var i = 95, n = 0;
6790     while (++i < 106) {
6791         d3.keybinding.keyCodes['num-' + n] = i;
6792         ++n;
6793     }
6794
6795     // 0-9
6796     i = 47; n = 0;
6797     while (++i < 58) {
6798         d3.keybinding.keyCodes[n] = i;
6799         ++n;
6800     }
6801
6802     // F1-F25
6803     i = 111; n = 1;
6804     while (++i < 136) {
6805         d3.keybinding.keyCodes['f' + n] = i;
6806         ++n;
6807     }
6808
6809     // a-z
6810     i = 64;
6811     while (++i < 91) {
6812         d3.keybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
6813     }
6814 })();
6815 d3.selection.prototype.one = function (type, listener, capture) {
6816     var target = this, typeOnce = type + ".once";
6817     function one() {
6818         target.on(typeOnce, null);
6819         listener.apply(this, arguments);
6820     }
6821     target.on(typeOnce, one, capture);
6822     return this;
6823 };
6824 d3.selection.prototype.dimensions = function (dimensions) {
6825     var refresh = (function(node) {
6826         var cr = node.getBoundingClientRect();
6827         prop = [cr.width, cr.height];
6828         this.property('__dimensions__', prop);
6829         return prop;
6830     }).bind(this);
6831
6832     var node = this.node();
6833
6834     if (!arguments.length) {
6835         if (!node) return [0,0];
6836         return this.property('__dimensions__') || refresh(node);
6837     }
6838     if (dimensions === null) {
6839         if (!node) return [0,0];
6840         return refresh(node);
6841     }
6842
6843     return this
6844         .property('__dimensions__', [dimensions[0], dimensions[1]])
6845         .attr({width: dimensions[0], height: dimensions[1]});
6846 };
6847 d3.selection.prototype.trigger = function (type) {
6848     this.each(function() {
6849         var evt = document.createEvent('HTMLEvents');
6850         evt.initEvent(type, true, true);
6851         this.dispatchEvent(evt);
6852     });
6853 };
6854 // Tooltips and svg mask used to highlight certain features
6855 d3.curtain = function() {
6856
6857     var event = d3.dispatch(),
6858         surface,
6859         tooltip,
6860         darkness;
6861
6862     function curtain(selection) {
6863
6864         surface = selection.append('svg')
6865             .attr('id', 'curtain')
6866             .style({
6867                 'z-index': 1000,
6868                 'pointer-events': 'none',
6869                 'position': 'absolute',
6870                 'top': 0,
6871                 'left': 0
6872             });
6873
6874         darkness = surface.append('path')
6875             .attr({
6876                 x: 0,
6877                 y: 0,
6878                 'class': 'curtain-darkness'
6879             });
6880
6881         d3.select(window).on('resize.curtain', resize);
6882
6883         tooltip = selection.append('div')
6884             .attr('class', 'tooltip')
6885             .style('z-index', 1002);
6886
6887         tooltip.append('div').attr('class', 'tooltip-arrow');
6888         tooltip.append('div').attr('class', 'tooltip-inner');
6889
6890         resize();
6891
6892         function resize() {
6893             surface.attr({
6894                 width: window.innerWidth,
6895                 height: window.innerHeight
6896             });
6897             curtain.cut(darkness.datum());
6898         }
6899     }
6900
6901     curtain.reveal = function(box, text, tooltipclass, duration) {
6902         if (typeof box === 'string') box = d3.select(box).node();
6903         if (box.getBoundingClientRect) box = box.getBoundingClientRect();
6904
6905         curtain.cut(box, duration);
6906
6907         if (text) {
6908             // pseudo markdown bold text hack
6909             var parts = text.split('**');
6910             var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
6911             if (parts[1]) html += '<span class="bold">' + parts[1] + '</span>';
6912
6913             var dimensions = tooltip.classed('in', true)
6914                 .select('.tooltip-inner')
6915                     .html(html)
6916                     .dimensions();
6917
6918             var pos;
6919
6920             var w = window.innerWidth,
6921                 h = window.innerHeight;
6922
6923             if (box.top + box.height < Math.min(100, box.width + box.left)) {
6924                 side = 'bottom';
6925                 pos = [box.left + box.width / 2 - dimensions[0]/ 2, box.top + box.height];
6926
6927             } else if (box.left + box.width + 300 < window.innerWidth) {
6928                 side = 'right';
6929                 pos = [box.left + box.width, box.top + box.height / 2 - dimensions[1] / 2];
6930
6931             } else if (box.left > 300) {
6932                 side = 'left';
6933                 pos = [box.left - 200, box.top + box.height / 2 - dimensions[1] / 2];
6934             } else {
6935                 side = 'bottom';
6936                 pos = [box.left, box.top + box.height];
6937             }
6938
6939             pos = [
6940                 Math.min(Math.max(10, pos[0]), w - dimensions[0] - 10),
6941                 Math.min(Math.max(10, pos[1]), h - dimensions[1] - 10)
6942             ];
6943
6944
6945             if (duration !== 0 || !tooltip.classed(side)) tooltip.call(iD.ui.Toggle(true));
6946
6947             tooltip
6948                 .style('top', pos[1] + 'px')
6949                 .style('left', pos[0] + 'px')
6950                 .attr('class', 'curtain-tooltip tooltip in ' + side + ' ' + tooltipclass)
6951                 .select('.tooltip-inner')
6952                     .html(html);
6953
6954         } else {
6955             tooltip.call(iD.ui.Toggle(false));
6956         }
6957     };
6958
6959     curtain.cut = function(datum, duration) {
6960         darkness.datum(datum);
6961
6962         (duration === 0 ? darkness : darkness.transition().duration(duration || 600))
6963             .attr('d', function(d) {
6964                 var string = "M 0,0 L 0," + window.innerHeight + " L " +
6965                     window.innerWidth + "," + window.innerHeight + "L" +
6966                     window.innerWidth + ",0 Z";
6967
6968                 if (!d) return string;
6969                 return string + 'M' +
6970                     d.left + ',' + d.top + 'L' +
6971                     d.left + ',' + (d.top + d.height) + 'L' +
6972                     (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
6973                     (d.left + d.width) + ',' + (d.top) + 'Z';
6974
6975             });
6976     };
6977
6978     curtain.remove = function() {
6979         surface.remove();
6980         tooltip.remove();
6981     };
6982
6983     return d3.rebind(curtain, event, 'on');
6984 };
6985 // Like selection.property('value', ...), but avoids no-op value sets,
6986 // which can result in layout/repaint thrashing in some situations.
6987 d3.selection.prototype.value = function(value) {
6988     function d3_selection_value(value) {
6989       function valueNull() {
6990         delete this.value;
6991       }
6992
6993       function valueConstant() {
6994         if (this.value !== value) this.value = value;
6995       }
6996
6997       function valueFunction() {
6998         var x = value.apply(this, arguments);
6999         if (x == null) delete this.value;
7000         else if (this.value !== x) this.value = x;
7001       }
7002
7003       return value == null
7004           ? valueNull : (typeof value === "function"
7005           ? valueFunction : valueConstant);
7006     }
7007
7008     if (!arguments.length) return this.property('value');
7009     return this.each(d3_selection_value(value));
7010 };
7011 // Copyright (c) 2006, 2008 Tony Garnock-Jones <tonyg@lshift.net>
7012 // Copyright (c) 2006, 2008 LShift Ltd. <query@lshift.net>
7013 //
7014 // Permission is hereby granted, free of charge, to any person
7015 // obtaining a copy of this software and associated documentation files
7016 // (the "Software"), to deal in the Software without restriction,
7017 // including without limitation the rights to use, copy, modify, merge,
7018 // publish, distribute, sublicense, and/or sell copies of the Software,
7019 // and to permit persons to whom the Software is furnished to do so,
7020 // subject to the following conditions:
7021 //
7022 // The above copyright notice and this permission notice shall be
7023 // included in all copies or substantial portions of the Software.
7024 //
7025 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
7026 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
7027 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
7028 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
7029 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
7030 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7031 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7032 // SOFTWARE.
7033
7034 // source:  https://bitbucket.org/lshift/synchrotron/src
7035
7036 Diff3 = (function() {
7037     'use strict';
7038
7039     var diff3 = {
7040         longest_common_subsequence: function(file1, file2) {
7041             /* Text diff algorithm following Hunt and McIlroy 1976.
7042              * J. W. Hunt and M. D. McIlroy, An algorithm for differential file
7043              * comparison, Bell Telephone Laboratories CSTR #41 (1976)
7044              * http://www.cs.dartmouth.edu/~doug/
7045              *
7046              * Expects two arrays of strings.
7047              */
7048             var equivalenceClasses;
7049             var file2indices;
7050             var newCandidate;
7051             var candidates;
7052             var line;
7053             var c, i, j, jX, r, s;
7054
7055             equivalenceClasses = {};
7056             for (j = 0; j < file2.length; j++) {
7057                 line = file2[j];
7058                 if (equivalenceClasses[line]) {
7059                     equivalenceClasses[line].push(j);
7060                 } else {
7061                     equivalenceClasses[line] = [j];
7062                 }
7063             }
7064
7065             candidates = [{file1index: -1,
7066                            file2index: -1,
7067                            chain: null}];
7068
7069             for (i = 0; i < file1.length; i++) {
7070                 line = file1[i];
7071                 file2indices = equivalenceClasses[line] || [];
7072
7073                 r = 0;
7074                 c = candidates[0];
7075
7076                 for (jX = 0; jX < file2indices.length; jX++) {
7077                     j = file2indices[jX];
7078
7079                     for (s = 0; s < candidates.length; s++) {
7080                         if ((candidates[s].file2index < j) &&
7081                             ((s == candidates.length - 1) ||
7082                              (candidates[s + 1].file2index > j)))
7083                             break;
7084                     }
7085
7086                     if (s < candidates.length) {
7087                         newCandidate = {file1index: i,
7088                                         file2index: j,
7089                                         chain: candidates[s]};
7090                         if (r == candidates.length) {
7091                             candidates.push(c);
7092                         } else {
7093                             candidates[r] = c;
7094                         }
7095                         r = s + 1;
7096                         c = newCandidate;
7097                         if (r == candidates.length) {
7098                             break; // no point in examining further (j)s
7099                         }
7100                     }
7101                 }
7102
7103                 candidates[r] = c;
7104             }
7105
7106             // At this point, we know the LCS: it's in the reverse of the
7107             // linked-list through .chain of
7108             // candidates[candidates.length - 1].
7109
7110             return candidates[candidates.length - 1];
7111         },
7112
7113         diff_comm: function(file1, file2) {
7114             // We apply the LCS to build a "comm"-style picture of the
7115             // differences between file1 and file2.
7116
7117             var result = [];
7118             var tail1 = file1.length;
7119             var tail2 = file2.length;
7120             var common = {common: []};
7121
7122             function processCommon() {
7123                 if (common.common.length) {
7124                     common.common.reverse();
7125                     result.push(common);
7126                     common = {common: []};
7127                 }
7128             }
7129
7130             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7131                  candidate !== null;
7132                  candidate = candidate.chain)
7133             {
7134                 var different = {file1: [], file2: []};
7135
7136                 while (--tail1 > candidate.file1index) {
7137                     different.file1.push(file1[tail1]);
7138                 }
7139
7140                 while (--tail2 > candidate.file2index) {
7141                     different.file2.push(file2[tail2]);
7142                 }
7143
7144                 if (different.file1.length || different.file2.length) {
7145                     processCommon();
7146                     different.file1.reverse();
7147                     different.file2.reverse();
7148                     result.push(different);
7149                 }
7150
7151                 if (tail1 >= 0) {
7152                     common.common.push(file1[tail1]);
7153                 }
7154             }
7155
7156             processCommon();
7157
7158             result.reverse();
7159             return result;
7160         },
7161
7162         diff_patch: function(file1, file2) {
7163             // We apply the LCD to build a JSON representation of a
7164             // diff(1)-style patch.
7165
7166             var result = [];
7167             var tail1 = file1.length;
7168             var tail2 = file2.length;
7169
7170             function chunkDescription(file, offset, length) {
7171                 var chunk = [];
7172                 for (var i = 0; i < length; i++) {
7173                     chunk.push(file[offset + i]);
7174                 }
7175                 return {offset: offset,
7176                         length: length,
7177                         chunk: chunk};
7178             }
7179
7180             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7181                  candidate !== null;
7182                  candidate = candidate.chain)
7183             {
7184                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7185                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7186                 tail1 = candidate.file1index;
7187                 tail2 = candidate.file2index;
7188
7189                 if (mismatchLength1 || mismatchLength2) {
7190                     result.push({file1: chunkDescription(file1,
7191                                                          candidate.file1index + 1,
7192                                                          mismatchLength1),
7193                                  file2: chunkDescription(file2,
7194                                                          candidate.file2index + 1,
7195                                                          mismatchLength2)});
7196                 }
7197             }
7198
7199             result.reverse();
7200             return result;
7201         },
7202
7203         strip_patch: function(patch) {
7204         // Takes the output of Diff3.diff_patch(), and removes
7205         // information from it. It can still be used by patch(),
7206         // below, but can no longer be inverted.
7207         var newpatch = [];
7208         for (var i = 0; i < patch.length; i++) {
7209             var chunk = patch[i];
7210             newpatch.push({file1: {offset: chunk.file1.offset,
7211                        length: chunk.file1.length},
7212                    file2: {chunk: chunk.file2.chunk}});
7213         }
7214         return newpatch;
7215         },
7216
7217         invert_patch: function(patch) {
7218             // Takes the output of Diff3.diff_patch(), and inverts the
7219             // sense of it, so that it can be applied to file2 to give
7220             // file1 rather than the other way around.
7221
7222             for (var i = 0; i < patch.length; i++) {
7223                 var chunk = patch[i];
7224                 var tmp = chunk.file1;
7225                 chunk.file1 = chunk.file2;
7226                 chunk.file2 = tmp;
7227             }
7228         },
7229
7230         patch: function (file, patch) {
7231             // Applies a patch to a file.
7232             //
7233             // Given file1 and file2, Diff3.patch(file1,
7234             // Diff3.diff_patch(file1, file2)) should give file2.
7235
7236             var result = [];
7237             var commonOffset = 0;
7238
7239             function copyCommon(targetOffset) {
7240                 while (commonOffset < targetOffset) {
7241                     result.push(file[commonOffset]);
7242                     commonOffset++;
7243                 }
7244             }
7245
7246             for (var chunkIndex = 0; chunkIndex < patch.length; chunkIndex++) {
7247                 var chunk = patch[chunkIndex];
7248                 copyCommon(chunk.file1.offset);
7249                 for (var lineIndex = 0; lineIndex < chunk.file2.chunk.length; lineIndex++) {
7250                     result.push(chunk.file2.chunk[lineIndex]);
7251                 }
7252                 commonOffset += chunk.file1.length;
7253             }
7254
7255             copyCommon(file.length);
7256             return result;
7257         },
7258
7259         diff_indices: function(file1, file2) {
7260             // We apply the LCS to give a simple representation of the
7261             // offsets and lengths of mismatched chunks in the input
7262             // files. This is used by diff3_merge_indices below.
7263
7264             var result = [];
7265             var tail1 = file1.length;
7266             var tail2 = file2.length;
7267
7268             for (var candidate = Diff3.longest_common_subsequence(file1, file2);
7269                  candidate !== null;
7270                  candidate = candidate.chain)
7271             {
7272                 var mismatchLength1 = tail1 - candidate.file1index - 1;
7273                 var mismatchLength2 = tail2 - candidate.file2index - 1;
7274                 tail1 = candidate.file1index;
7275                 tail2 = candidate.file2index;
7276
7277                 if (mismatchLength1 || mismatchLength2) {
7278                     result.push({file1: [tail1 + 1, mismatchLength1],
7279                                  file2: [tail2 + 1, mismatchLength2]});
7280                 }
7281             }
7282
7283             result.reverse();
7284             return result;
7285         },
7286
7287         diff3_merge_indices: function (a, o, b) {
7288             // Given three files, A, O, and B, where both A and B are
7289             // independently derived from O, returns a fairly complicated
7290             // internal representation of merge decisions it's taken. The
7291             // interested reader may wish to consult
7292             //
7293             // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce. "A
7294             // Formal Investigation of Diff3." In Arvind and Prasad,
7295             // editors, Foundations of Software Technology and Theoretical
7296             // Computer Science (FSTTCS), December 2007.
7297             //
7298             // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
7299             var i;
7300
7301             var m1 = Diff3.diff_indices(o, a);
7302             var m2 = Diff3.diff_indices(o, b);
7303
7304             var hunks = [];
7305             function addHunk(h, side) {
7306                 hunks.push([h.file1[0], side, h.file1[1], h.file2[0], h.file2[1]]);
7307             }
7308             for (i = 0; i < m1.length; i++) { addHunk(m1[i], 0); }
7309             for (i = 0; i < m2.length; i++) { addHunk(m2[i], 2); }
7310             hunks.sort();
7311
7312             var result = [];
7313             var commonOffset = 0;
7314             function copyCommon(targetOffset) {
7315                 if (targetOffset > commonOffset) {
7316                     result.push([1, commonOffset, targetOffset - commonOffset]);
7317                     commonOffset = targetOffset;
7318                 }
7319             }
7320
7321             for (var hunkIndex = 0; hunkIndex < hunks.length; hunkIndex++) {
7322                 var firstHunkIndex = hunkIndex;
7323                 var hunk = hunks[hunkIndex];
7324                 var regionLhs = hunk[0];
7325                 var regionRhs = regionLhs + hunk[2];
7326                 while (hunkIndex < hunks.length - 1) {
7327                     var maybeOverlapping = hunks[hunkIndex + 1];
7328                     var maybeLhs = maybeOverlapping[0];
7329                     if (maybeLhs > regionRhs) break;
7330                     regionRhs = maybeLhs + maybeOverlapping[2];
7331                     hunkIndex++;
7332                 }
7333
7334                 copyCommon(regionLhs);
7335                 if (firstHunkIndex == hunkIndex) {
7336             // The "overlap" was only one hunk long, meaning that
7337             // there's no conflict here. Either a and o were the
7338             // same, or b and o were the same.
7339                     if (hunk[4] > 0) {
7340                         result.push([hunk[1], hunk[3], hunk[4]]);
7341                     }
7342                 } else {
7343             // A proper conflict. Determine the extents of the
7344             // regions involved from a, o and b. Effectively merge
7345             // all the hunks on the left into one giant hunk, and
7346             // do the same for the right; then, correct for skew
7347             // in the regions of o that each side changed, and
7348             // report appropriate spans for the three sides.
7349             var regions = {
7350                 0: [a.length, -1, o.length, -1],
7351                 2: [b.length, -1, o.length, -1]
7352             };
7353                     for (i = firstHunkIndex; i <= hunkIndex; i++) {
7354                 hunk = hunks[i];
7355                         var side = hunk[1];
7356                 var r = regions[side];
7357                 var oLhs = hunk[0];
7358                 var oRhs = oLhs + hunk[2];
7359                         var abLhs = hunk[3];
7360                         var abRhs = abLhs + hunk[4];
7361                 r[0] = Math.min(abLhs, r[0]);
7362                 r[1] = Math.max(abRhs, r[1]);
7363                 r[2] = Math.min(oLhs, r[2]);
7364                 r[3] = Math.max(oRhs, r[3]);
7365                     }
7366             var aLhs = regions[0][0] + (regionLhs - regions[0][2]);
7367             var aRhs = regions[0][1] + (regionRhs - regions[0][3]);
7368             var bLhs = regions[2][0] + (regionLhs - regions[2][2]);
7369             var bRhs = regions[2][1] + (regionRhs - regions[2][3]);
7370                     result.push([-1,
7371                      aLhs,      aRhs      - aLhs,
7372                      regionLhs, regionRhs - regionLhs,
7373                      bLhs,      bRhs      - bLhs]);
7374                 }
7375                 commonOffset = regionRhs;
7376             }
7377
7378             copyCommon(o.length);
7379             return result;
7380         },
7381
7382         diff3_merge: function (a, o, b, excludeFalseConflicts) {
7383             // Applies the output of Diff3.diff3_merge_indices to actually
7384             // construct the merged file; the returned result alternates
7385             // between "ok" and "conflict" blocks.
7386
7387             var result = [];
7388             var files = [a, o, b];
7389             var indices = Diff3.diff3_merge_indices(a, o, b);
7390
7391             var okLines = [];
7392             function flushOk() {
7393                 if (okLines.length) {
7394                     result.push({ok: okLines});
7395                 }
7396                 okLines = [];
7397             }
7398             function pushOk(xs) {
7399                 for (var j = 0; j < xs.length; j++) {
7400                     okLines.push(xs[j]);
7401                 }
7402             }
7403
7404             function isTrueConflict(rec) {
7405                 if (rec[2] != rec[6]) return true;
7406                 var aoff = rec[1];
7407                 var boff = rec[5];
7408                 for (var j = 0; j < rec[2]; j++) {
7409                     if (a[j + aoff] != b[j + boff]) return true;
7410                 }
7411                 return false;
7412             }
7413
7414             for (var i = 0; i < indices.length; i++) {
7415                 var x = indices[i];
7416                 var side = x[0];
7417                 if (side == -1) {
7418                     if (excludeFalseConflicts && !isTrueConflict(x)) {
7419                         pushOk(files[0].slice(x[1], x[1] + x[2]));
7420                     } else {
7421                         flushOk();
7422                         result.push({conflict: {a: a.slice(x[1], x[1] + x[2]),
7423                                                 aIndex: x[1],
7424                                                 o: o.slice(x[3], x[3] + x[4]),
7425                                                 oIndex: x[3],
7426                                                 b: b.slice(x[5], x[5] + x[6]),
7427                                                 bIndex: x[5]}});
7428                     }
7429                 } else {
7430                     pushOk(files[side].slice(x[1], x[1] + x[2]));
7431                 }
7432             }
7433
7434             flushOk();
7435             return result;
7436         }
7437     };
7438     return diff3;
7439 })();
7440
7441 if (typeof module !== 'undefined') module.exports = Diff3;
7442 var JXON = new (function () {
7443   var
7444     sValueProp = "keyValue", sAttributesProp = "keyAttributes", sAttrPref = "@", /* you can customize these values */
7445     aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
7446
7447   function parseText (sValue) {
7448     if (rIsNull.test(sValue)) { return null; }
7449     if (rIsBool.test(sValue)) { return sValue.toLowerCase() === "true"; }
7450     if (isFinite(sValue)) { return parseFloat(sValue); }
7451     if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
7452     return sValue;
7453   }
7454
7455   function EmptyTree () { }
7456   EmptyTree.prototype.toString = function () { return "null"; };
7457   EmptyTree.prototype.valueOf = function () { return null; };
7458
7459   function objectify (vValue) {
7460     return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
7461   }
7462
7463   function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
7464     var
7465       nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
7466       bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
7467
7468     var
7469       sProp, vContent, nLength = 0, sCollectedTxt = "",
7470       vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
7471
7472     if (bChildren) {
7473       for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
7474         oNode = oParentNode.childNodes.item(nItem);
7475         if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is "CDATASection" (4) */
7476         else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is "Text" (3) */
7477         else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is "Element" (1) */
7478       }
7479     }
7480
7481     var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
7482
7483     if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
7484
7485     for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
7486       sProp = aCache[nElId].nodeName.toLowerCase();
7487       vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
7488       if (vResult.hasOwnProperty(sProp)) {
7489         if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
7490         vResult[sProp].push(vContent);
7491       } else {
7492         vResult[sProp] = vContent;
7493         nLength++;
7494       }
7495     }
7496
7497     if (bAttributes) {
7498       var
7499         nAttrLen = oParentNode.attributes.length,
7500         sAPrefix = bNesteAttr ? "" : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
7501
7502       for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
7503         oAttrib = oParentNode.attributes.item(nAttrib);
7504         oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
7505       }
7506
7507       if (bNesteAttr) {
7508         if (bFreeze) { Object.freeze(oAttrParent); }
7509         vResult[sAttributesProp] = oAttrParent;
7510         nLength -= nAttrLen - 1;
7511       }
7512     }
7513
7514     if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
7515       vResult[sValueProp] = vBuiltVal;
7516     } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
7517       vResult = vBuiltVal;
7518     }
7519
7520     if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
7521
7522     aCache.length = nLevelStart;
7523
7524     return vResult;
7525   }
7526
7527   function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
7528     var vValue, oChild;
7529
7530     if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
7531       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
7532     } else if (oParentObj.constructor === Date) {
7533       oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));    
7534     }
7535
7536     for (var sName in oParentObj) {
7537       vValue = oParentObj[sName];
7538       if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
7539       if (sName === sValueProp) {
7540         if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
7541       } else if (sName === sAttributesProp) { /* verbosity level is 3 */
7542         for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
7543       } else if (sName.charAt(0) === sAttrPref) {
7544         oParentEl.setAttribute(sName.slice(1), vValue);
7545       } else if (vValue.constructor === Array) {
7546         for (var nItem = 0; nItem < vValue.length; nItem++) {
7547           oChild = oXMLDoc.createElement(sName);
7548           loadObjTree(oXMLDoc, oChild, vValue[nItem]);
7549           oParentEl.appendChild(oChild);
7550         }
7551       } else {
7552         oChild = oXMLDoc.createElement(sName);
7553         if (vValue instanceof Object) {
7554           loadObjTree(oXMLDoc, oChild, vValue);
7555         } else if (vValue !== null && vValue !== true) {
7556           oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
7557         }
7558         oParentEl.appendChild(oChild);
7559      }
7560    }
7561   }
7562
7563   this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
7564     var _nVerb = arguments.length > 1 && typeof nVerbosity === "number" ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
7565     return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);    
7566   };
7567
7568   this.unbuild = function (oObjTree) {    
7569     var oNewDoc = document.implementation.createDocument("", "", null);
7570     loadObjTree(oNewDoc, oNewDoc, oObjTree);
7571     return oNewDoc;
7572   };
7573
7574   this.stringify = function (oObjTree) {
7575     return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
7576   };
7577 })();
7578 // var myObject = JXON.build(doc);
7579 // we got our javascript object! try: alert(JSON.stringify(myObject));
7580
7581 // var newDoc = JXON.unbuild(myObject);
7582 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
7583 /**
7584  * @license
7585  * lodash 3.9.3 (Custom Build) <https://lodash.com/>
7586  * 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"`
7587  * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
7588  * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
7589  * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
7590  * Available under MIT license <https://lodash.com/license>
7591  */
7592 ;(function() {
7593
7594   /** Used as a safe reference for `undefined` in pre-ES5 environments. */
7595   var undefined;
7596
7597   /** Used as the semantic version number. */
7598   var VERSION = '3.9.3';
7599
7600   /** Used to compose bitmasks for wrapper metadata. */
7601   var BIND_FLAG = 1,
7602       BIND_KEY_FLAG = 2,
7603       CURRY_BOUND_FLAG = 4,
7604       CURRY_FLAG = 8,
7605       CURRY_RIGHT_FLAG = 16,
7606       PARTIAL_FLAG = 32,
7607       PARTIAL_RIGHT_FLAG = 64,
7608       ARY_FLAG = 128,
7609       REARG_FLAG = 256;
7610
7611   /** Used to detect when a function becomes hot. */
7612   var HOT_COUNT = 150,
7613       HOT_SPAN = 16;
7614
7615   /** Used to indicate the type of lazy iteratees. */
7616   var LAZY_DROP_WHILE_FLAG = 0,
7617       LAZY_FILTER_FLAG = 1,
7618       LAZY_MAP_FLAG = 2;
7619
7620   /** Used as the `TypeError` message for "Functions" methods. */
7621   var FUNC_ERROR_TEXT = 'Expected a function';
7622
7623   /** Used as the internal argument placeholder. */
7624   var PLACEHOLDER = '__lodash_placeholder__';
7625
7626   /** `Object#toString` result references. */
7627   var argsTag = '[object Arguments]',
7628       arrayTag = '[object Array]',
7629       boolTag = '[object Boolean]',
7630       dateTag = '[object Date]',
7631       errorTag = '[object Error]',
7632       funcTag = '[object Function]',
7633       mapTag = '[object Map]',
7634       numberTag = '[object Number]',
7635       objectTag = '[object Object]',
7636       regexpTag = '[object RegExp]',
7637       setTag = '[object Set]',
7638       stringTag = '[object String]',
7639       weakMapTag = '[object WeakMap]';
7640
7641   var arrayBufferTag = '[object ArrayBuffer]',
7642       float32Tag = '[object Float32Array]',
7643       float64Tag = '[object Float64Array]',
7644       int8Tag = '[object Int8Array]',
7645       int16Tag = '[object Int16Array]',
7646       int32Tag = '[object Int32Array]',
7647       uint8Tag = '[object Uint8Array]',
7648       uint8ClampedTag = '[object Uint8ClampedArray]',
7649       uint16Tag = '[object Uint16Array]',
7650       uint32Tag = '[object Uint32Array]';
7651
7652   /** Used to match property names within property paths. */
7653   var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,
7654       reIsPlainProp = /^\w*$/,
7655       rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g;
7656
7657   /**
7658    * Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special).
7659    * In addition to special characters the forward slash is escaped to allow for
7660    * easier `eval` use and `Function` compilation.
7661    */
7662   var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g,
7663       reHasRegExpChars = RegExp(reRegExpChars.source);
7664
7665   /** Used to match backslashes in property paths. */
7666   var reEscapeChar = /\\(\\)?/g;
7667
7668   /** Used to match `RegExp` flags from their coerced string values. */
7669   var reFlags = /\w*$/;
7670
7671   /** Used to detect host constructors (Safari > 5). */
7672   var reIsHostCtor = /^\[object .+?Constructor\]$/;
7673
7674   /** Used to detect unsigned integer values. */
7675   var reIsUint = /^\d+$/;
7676
7677   /** Used to fix the JScript `[[DontEnum]]` bug. */
7678   var shadowProps = [
7679     'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
7680     'toLocaleString', 'toString', 'valueOf'
7681   ];
7682
7683   /** Used to identify `toStringTag` values of typed arrays. */
7684   var typedArrayTags = {};
7685   typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
7686   typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
7687   typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
7688   typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
7689   typedArrayTags[uint32Tag] = true;
7690   typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
7691   typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
7692   typedArrayTags[dateTag] = typedArrayTags[errorTag] =
7693   typedArrayTags[funcTag] = typedArrayTags[mapTag] =
7694   typedArrayTags[numberTag] = typedArrayTags[objectTag] =
7695   typedArrayTags[regexpTag] = typedArrayTags[setTag] =
7696   typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
7697
7698   /** Used to identify `toStringTag` values supported by `_.clone`. */
7699   var cloneableTags = {};
7700   cloneableTags[argsTag] = cloneableTags[arrayTag] =
7701   cloneableTags[arrayBufferTag] = cloneableTags[boolTag] =
7702   cloneableTags[dateTag] = cloneableTags[float32Tag] =
7703   cloneableTags[float64Tag] = cloneableTags[int8Tag] =
7704   cloneableTags[int16Tag] = cloneableTags[int32Tag] =
7705   cloneableTags[numberTag] = cloneableTags[objectTag] =
7706   cloneableTags[regexpTag] = cloneableTags[stringTag] =
7707   cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
7708   cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
7709   cloneableTags[errorTag] = cloneableTags[funcTag] =
7710   cloneableTags[mapTag] = cloneableTags[setTag] =
7711   cloneableTags[weakMapTag] = false;
7712
7713   /** Used as an internal `_.debounce` options object by `_.throttle`. */
7714   var debounceOptions = {
7715     'leading': false,
7716     'maxWait': 0,
7717     'trailing': false
7718   };
7719
7720   /** Used to determine if values are of the language type `Object`. */
7721   var objectTypes = {
7722     'function': true,
7723     'object': true
7724   };
7725
7726   /** Detect free variable `exports`. */
7727   var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
7728
7729   /** Detect free variable `module`. */
7730   var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
7731
7732   /** Detect free variable `global` from Node.js. */
7733   var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global;
7734
7735   /** Detect free variable `self`. */
7736   var freeSelf = objectTypes[typeof self] && self && self.Object && self;
7737
7738   /** Detect free variable `window`. */
7739   var freeWindow = objectTypes[typeof window] && window && window.Object && window;
7740
7741   /** Detect the popular CommonJS extension `module.exports`. */
7742   var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
7743
7744   /**
7745    * Used as a reference to the global object.
7746    *
7747    * The `this` value is used if it's the global object to avoid Greasemonkey's
7748    * restricted `window` object, otherwise the `window` object is used.
7749    */
7750   var root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this;
7751
7752   /*--------------------------------------------------------------------------*/
7753
7754   /**
7755    * The base implementation of `_.findIndex` and `_.findLastIndex` without
7756    * support for callback shorthands and `this` binding.
7757    *
7758    * @private
7759    * @param {Array} array The array to search.
7760    * @param {Function} predicate The function invoked per iteration.
7761    * @param {boolean} [fromRight] Specify iterating from right to left.
7762    * @returns {number} Returns the index of the matched value, else `-1`.
7763    */
7764   function baseFindIndex(array, predicate, fromRight) {
7765     var length = array.length,
7766         index = fromRight ? length : -1;
7767
7768     while ((fromRight ? index-- : ++index < length)) {
7769       if (predicate(array[index], index, array)) {
7770         return index;
7771       }
7772     }
7773     return -1;
7774   }
7775
7776   /**
7777    * The base implementation of `_.indexOf` without support for binary searches.
7778    *
7779    * @private
7780    * @param {Array} array The array to search.
7781    * @param {*} value The value to search for.
7782    * @param {number} fromIndex The index to search from.
7783    * @returns {number} Returns the index of the matched value, else `-1`.
7784    */
7785   function baseIndexOf(array, value, fromIndex) {
7786     if (value !== value) {
7787       return indexOfNaN(array, fromIndex);
7788     }
7789     var index = fromIndex - 1,
7790         length = array.length;
7791
7792     while (++index < length) {
7793       if (array[index] === value) {
7794         return index;
7795       }
7796     }
7797     return -1;
7798   }
7799
7800   /**
7801    * The base implementation of `_.isFunction` without support for environments
7802    * with incorrect `typeof` results.
7803    *
7804    * @private
7805    * @param {*} value The value to check.
7806    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
7807    */
7808   function baseIsFunction(value) {
7809     // Avoid a Chakra JIT bug in compatibility modes of IE 11.
7810     // See https://github.com/jashkenas/underscore/issues/1621 for more details.
7811     return typeof value == 'function' || false;
7812   }
7813
7814   /**
7815    * Converts `value` to a string if it's not one. An empty string is returned
7816    * for `null` or `undefined` values.
7817    *
7818    * @private
7819    * @param {*} value The value to process.
7820    * @returns {string} Returns the string.
7821    */
7822   function baseToString(value) {
7823     if (typeof value == 'string') {
7824       return value;
7825     }
7826     return value == null ? '' : (value + '');
7827   }
7828
7829   /**
7830    * Gets the index at which the first occurrence of `NaN` is found in `array`.
7831    *
7832    * @private
7833    * @param {Array} array The array to search.
7834    * @param {number} fromIndex The index to search from.
7835    * @param {boolean} [fromRight] Specify iterating from right to left.
7836    * @returns {number} Returns the index of the matched `NaN`, else `-1`.
7837    */
7838   function indexOfNaN(array, fromIndex, fromRight) {
7839     var length = array.length,
7840         index = fromIndex + (fromRight ? 0 : -1);
7841
7842     while ((fromRight ? index-- : ++index < length)) {
7843       var other = array[index];
7844       if (other !== other) {
7845         return index;
7846       }
7847     }
7848     return -1;
7849   }
7850
7851   /**
7852    * Checks if `value` is a host object in IE < 9.
7853    *
7854    * @private
7855    * @param {*} value The value to check.
7856    * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
7857    */
7858   var isHostObject = (function() {
7859     try {
7860       Object({ 'toString': 0 } + '');
7861     } catch(e) {
7862       return function() { return false; };
7863     }
7864     return function(value) {
7865       // IE < 9 presents many host objects as `Object` objects that can coerce
7866       // to strings despite having improperly defined `toString` methods.
7867       return typeof value.toString != 'function' && typeof (value + '') == 'string';
7868     };
7869   }());
7870
7871   /**
7872    * Checks if `value` is object-like.
7873    *
7874    * @private
7875    * @param {*} value The value to check.
7876    * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
7877    */
7878   function isObjectLike(value) {
7879     return !!value && typeof value == 'object';
7880   }
7881
7882   /**
7883    * Replaces all `placeholder` elements in `array` with an internal placeholder
7884    * and returns an array of their indexes.
7885    *
7886    * @private
7887    * @param {Array} array The array to modify.
7888    * @param {*} placeholder The placeholder to replace.
7889    * @returns {Array} Returns the new array of placeholder indexes.
7890    */
7891   function replaceHolders(array, placeholder) {
7892     var index = -1,
7893         length = array.length,
7894         resIndex = -1,
7895         result = [];
7896
7897     while (++index < length) {
7898       if (array[index] === placeholder) {
7899         array[index] = PLACEHOLDER;
7900         result[++resIndex] = index;
7901       }
7902     }
7903     return result;
7904   }
7905
7906   /**
7907    * An implementation of `_.uniq` optimized for sorted arrays without support
7908    * for callback shorthands and `this` binding.
7909    *
7910    * @private
7911    * @param {Array} array The array to inspect.
7912    * @param {Function} [iteratee] The function invoked per iteration.
7913    * @returns {Array} Returns the new duplicate-value-free array.
7914    */
7915   function sortedUniq(array, iteratee) {
7916     var seen,
7917         index = -1,
7918         length = array.length,
7919         resIndex = -1,
7920         result = [];
7921
7922     while (++index < length) {
7923       var value = array[index],
7924           computed = iteratee ? iteratee(value, index, array) : value;
7925
7926       if (!index || seen !== computed) {
7927         seen = computed;
7928         result[++resIndex] = value;
7929       }
7930     }
7931     return result;
7932   }
7933
7934   /*--------------------------------------------------------------------------*/
7935
7936   /** Used for native method references. */
7937   var arrayProto = Array.prototype,
7938       errorProto = Error.prototype,
7939       objectProto = Object.prototype,
7940       stringProto = String.prototype;
7941
7942   /** Used to resolve the decompiled source of functions. */
7943   var fnToString = Function.prototype.toString;
7944
7945   /** Used to check objects for own properties. */
7946   var hasOwnProperty = objectProto.hasOwnProperty;
7947
7948   /**
7949    * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
7950    * of values.
7951    */
7952   var objToString = objectProto.toString;
7953
7954   /** Used to detect if a method is native. */
7955   var reIsNative = RegExp('^' +
7956     escapeRegExp(fnToString.call(hasOwnProperty))
7957     .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
7958   );
7959
7960   /** Native method references. */
7961   var ArrayBuffer = getNative(root, 'ArrayBuffer'),
7962       bufferSlice = getNative(ArrayBuffer && new ArrayBuffer(0), 'slice'),
7963       ceil = Math.ceil,
7964       floor = Math.floor,
7965       getPrototypeOf = getNative(Object, 'getPrototypeOf'),
7966       push = arrayProto.push,
7967       propertyIsEnumerable = objectProto.propertyIsEnumerable,
7968       Set = getNative(root, 'Set'),
7969       splice = arrayProto.splice,
7970       Uint8Array = getNative(root, 'Uint8Array'),
7971       WeakMap = getNative(root, 'WeakMap');
7972
7973   /** Used to clone array buffers. */
7974   var Float64Array = (function() {
7975     // Safari 5 errors when using an array buffer to initialize a typed array
7976     // where the array buffer's `byteLength` is not a multiple of the typed
7977     // array's `BYTES_PER_ELEMENT`.
7978     try {
7979       var func = getNative(root, 'Float64Array'),
7980           result = new func(new ArrayBuffer(10), 0, 1) && func;
7981     } catch(e) {}
7982     return result || null;
7983   }());
7984
7985   /* Native method references for those with the same name as other `lodash` methods. */
7986   var nativeCreate = getNative(Object, 'create'),
7987       nativeIsArray = getNative(Array, 'isArray'),
7988       nativeKeys = getNative(Object, 'keys'),
7989       nativeMax = Math.max,
7990       nativeMin = Math.min,
7991       nativeNow = getNative(Date, 'now');
7992
7993   /** Used as references for `-Infinity` and `Infinity`. */
7994   var POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
7995
7996   /** Used as references for the maximum length and index of an array. */
7997   var MAX_ARRAY_LENGTH = 4294967295,
7998       MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
7999       HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
8000
8001   /** Used as the size, in bytes, of each `Float64Array` element. */
8002   var FLOAT64_BYTES_PER_ELEMENT = Float64Array ? Float64Array.BYTES_PER_ELEMENT : 0;
8003
8004   /**
8005    * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer)
8006    * of an array-like value.
8007    */
8008   var MAX_SAFE_INTEGER = 9007199254740991;
8009
8010   /** Used to store function metadata. */
8011   var metaMap = WeakMap && new WeakMap;
8012
8013   /** Used to lookup unminified function names. */
8014   var realNames = {};
8015
8016   /** Used to lookup a type array constructors by `toStringTag`. */
8017   var ctorByTag = {};
8018   ctorByTag[float32Tag] = root.Float32Array;
8019   ctorByTag[float64Tag] = root.Float64Array;
8020   ctorByTag[int8Tag] = root.Int8Array;
8021   ctorByTag[int16Tag] = root.Int16Array;
8022   ctorByTag[int32Tag] = root.Int32Array;
8023   ctorByTag[uint8Tag] = root.Uint8Array;
8024   ctorByTag[uint8ClampedTag] = root.Uint8ClampedArray;
8025   ctorByTag[uint16Tag] = root.Uint16Array;
8026   ctorByTag[uint32Tag] = root.Uint32Array;
8027
8028   /** Used to avoid iterating over non-enumerable properties in IE < 9. */
8029   var nonEnumProps = {};
8030   nonEnumProps[arrayTag] = nonEnumProps[dateTag] = nonEnumProps[numberTag] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
8031   nonEnumProps[boolTag] = nonEnumProps[stringTag] = { 'constructor': true, 'toString': true, 'valueOf': true };
8032   nonEnumProps[errorTag] = nonEnumProps[funcTag] = nonEnumProps[regexpTag] = { 'constructor': true, 'toString': true };
8033   nonEnumProps[objectTag] = { 'constructor': true };
8034
8035   arrayEach(shadowProps, function(key) {
8036     for (var tag in nonEnumProps) {
8037       if (hasOwnProperty.call(nonEnumProps, tag)) {
8038         var props = nonEnumProps[tag];
8039         props[key] = hasOwnProperty.call(props, key);
8040       }
8041     }
8042   });
8043
8044   /*------------------------------------------------------------------------*/
8045
8046   /**
8047    * Creates a `lodash` object which wraps `value` to enable implicit chaining.
8048    * Methods that operate on and return arrays, collections, and functions can
8049    * be chained together. Methods that return a boolean or single value will
8050    * automatically end the chain returning the unwrapped value. Explicit chaining
8051    * may be enabled using `_.chain`. The execution of chained methods is lazy,
8052    * that is, execution is deferred until `_#value` is implicitly or explicitly
8053    * called.
8054    *
8055    * Lazy evaluation allows several methods to support shortcut fusion. Shortcut
8056    * fusion is an optimization that merges iteratees to avoid creating intermediate
8057    * arrays and reduce the number of iteratee executions.
8058    *
8059    * Chaining is supported in custom builds as long as the `_#value` method is
8060    * directly or indirectly included in the build.
8061    *
8062    * In addition to lodash methods, wrappers have `Array` and `String` methods.
8063    *
8064    * The wrapper `Array` methods are:
8065    * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`,
8066    * `splice`, and `unshift`
8067    *
8068    * The wrapper `String` methods are:
8069    * `replace` and `split`
8070    *
8071    * The wrapper methods that support shortcut fusion are:
8072    * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`,
8073    * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`,
8074    * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`,
8075    * and `where`
8076    *
8077    * The chainable wrapper methods are:
8078    * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`,
8079    * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`,
8080    * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defer`, `delay`,
8081    * `difference`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `fill`,
8082    * `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, `forEach`,
8083    * `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `functions`,
8084    * `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, `invoke`, `keys`,
8085    * `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
8086    * `memoize`, `merge`, `method`, `methodOf`, `mixin`, `negate`, `omit`, `once`,
8087    * `pairs`, `partial`, `partialRight`, `partition`, `pick`, `plant`, `pluck`,
8088    * `property`, `propertyOf`, `pull`, `pullAt`, `push`, `range`, `rearg`,
8089    * `reject`, `remove`, `rest`, `restParam`, `reverse`, `set`, `shuffle`,
8090    * `slice`, `sort`, `sortBy`, `sortByAll`, `sortByOrder`, `splice`, `spread`,
8091    * `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `tap`, `throttle`,
8092    * `thru`, `times`, `toArray`, `toPlainObject`, `transform`, `union`, `uniq`,
8093    * `unshift`, `unzip`, `unzipWith`, `values`, `valuesIn`, `where`, `without`,
8094    * `wrap`, `xor`, `zip`, `zipObject`, `zipWith`
8095    *
8096    * The wrapper methods that are **not** chainable by default are:
8097    * `add`, `attempt`, `camelCase`, `capitalize`, `clone`, `cloneDeep`, `deburr`,
8098    * `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`,
8099    * `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, `get`,
8100    * `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`, `inRange`, `isArguments`,
8101    * `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`,
8102    * `isFinite` `isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`,
8103    * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`,
8104    * `isTypedArray`, `join`, `kebabCase`, `last`, `lastIndexOf`, `lt`, `lte`,
8105    * `max`, `min`, `noConflict`, `noop`, `now`, `pad`, `padLeft`, `padRight`,
8106    * `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`,
8107    * `runInContext`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`,
8108    * `sortedLastIndex`, `startCase`, `startsWith`, `sum`, `template`, `trim`,
8109    * `trimLeft`, `trimRight`, `trunc`, `unescape`, `uniqueId`, `value`, and `words`
8110    *
8111    * The wrapper method `sample` will return a wrapped value when `n` is provided,
8112    * otherwise an unwrapped value is returned.
8113    *
8114    * @name _
8115    * @constructor
8116    * @category Chain
8117    * @param {*} value The value to wrap in a `lodash` instance.
8118    * @returns {Object} Returns the new `lodash` wrapper instance.
8119    * @example
8120    *
8121    * var wrapped = _([1, 2, 3]);
8122    *
8123    * // returns an unwrapped value
8124    * wrapped.reduce(function(total, n) {
8125    *   return total + n;
8126    * });
8127    * // => 6
8128    *
8129    * // returns a wrapped value
8130    * var squares = wrapped.map(function(n) {
8131    *   return n * n;
8132    * });
8133    *
8134    * _.isArray(squares);
8135    * // => false
8136    *
8137    * _.isArray(squares.value());
8138    * // => true
8139    */
8140   function lodash(value) {
8141     if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
8142       if (value instanceof LodashWrapper) {
8143         return value;
8144       }
8145       if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) {
8146         return wrapperClone(value);
8147       }
8148     }
8149     return new LodashWrapper(value);
8150   }
8151
8152   /**
8153    * The function whose prototype all chaining wrappers inherit from.
8154    *
8155    * @private
8156    */
8157   function baseLodash() {
8158     // No operation performed.
8159   }
8160
8161   /**
8162    * The base constructor for creating `lodash` wrapper objects.
8163    *
8164    * @private
8165    * @param {*} value The value to wrap.
8166    * @param {boolean} [chainAll] Enable chaining for all wrapper methods.
8167    * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value.
8168    */
8169   function LodashWrapper(value, chainAll, actions) {
8170     this.__wrapped__ = value;
8171     this.__actions__ = actions || [];
8172     this.__chain__ = !!chainAll;
8173   }
8174
8175   /**
8176    * An object environment feature flags.
8177    *
8178    * @static
8179    * @memberOf _
8180    * @type Object
8181    */
8182   var support = lodash.support = {};
8183
8184   (function(x) {
8185     var Ctor = function() { this.x = x; },
8186         object = { '0': x, 'length': x },
8187         props = [];
8188
8189     Ctor.prototype = { 'valueOf': x, 'y': x };
8190     for (var key in new Ctor) { props.push(key); }
8191
8192     /**
8193      * Detect if the `toStringTag` of `arguments` objects is resolvable
8194      * (all but Firefox < 4, IE < 9).
8195      *
8196      * @memberOf _.support
8197      * @type boolean
8198      */
8199     support.argsTag = objToString.call(arguments) == argsTag;
8200
8201     /**
8202      * Detect if `name` or `message` properties of `Error.prototype` are
8203      * enumerable by default (IE < 9, Safari < 5.1).
8204      *
8205      * @memberOf _.support
8206      * @type boolean
8207      */
8208     support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') ||
8209       propertyIsEnumerable.call(errorProto, 'name');
8210
8211     /**
8212      * Detect if `prototype` properties are enumerable by default.
8213      *
8214      * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
8215      * (if the prototype or a property on the prototype has been set)
8216      * incorrectly set the `[[Enumerable]]` value of a function's `prototype`
8217      * property to `true`.
8218      *
8219      * @memberOf _.support
8220      * @type boolean
8221      */
8222     support.enumPrototypes = propertyIsEnumerable.call(Ctor, 'prototype');
8223
8224     /**
8225      * Detect if properties shadowing those on `Object.prototype` are non-enumerable.
8226      *
8227      * In IE < 9 an object's own properties, shadowing non-enumerable ones,
8228      * are made non-enumerable as well (a.k.a the JScript `[[DontEnum]]` bug).
8229      *
8230      * @memberOf _.support
8231      * @type boolean
8232      */
8233     support.nonEnumShadows = !/valueOf/.test(props);
8234
8235     /**
8236      * Detect if own properties are iterated after inherited properties (IE < 9).
8237      *
8238      * @memberOf _.support
8239      * @type boolean
8240      */
8241     support.ownLast = props[0] != 'x';
8242
8243     /**
8244      * Detect if `Array#shift` and `Array#splice` augment array-like objects
8245      * correctly.
8246      *
8247      * Firefox < 10, compatibility modes of IE 8, and IE < 9 have buggy Array
8248      * `shift()` and `splice()` functions that fail to remove the last element,
8249      * `value[0]`, of array-like objects even though the "length" property is
8250      * set to `0`. The `shift()` method is buggy in compatibility modes of IE 8,
8251      * while `splice()` is buggy regardless of mode in IE < 9.
8252      *
8253      * @memberOf _.support
8254      * @type boolean
8255      */
8256     support.spliceObjects = (splice.call(object, 0, 1), !object[0]);
8257
8258     /**
8259      * Detect lack of support for accessing string characters by index.
8260      *
8261      * IE < 8 can't access characters by index. IE 8 can only access characters
8262      * by index on string literals, not string objects.
8263      *
8264      * @memberOf _.support
8265      * @type boolean
8266      */
8267     support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
8268   }(1, 0));
8269
8270   /*------------------------------------------------------------------------*/
8271
8272   /**
8273    * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
8274    *
8275    * @private
8276    * @param {*} value The value to wrap.
8277    */
8278   function LazyWrapper(value) {
8279     this.__wrapped__ = value;
8280     this.__actions__ = null;
8281     this.__dir__ = 1;
8282     this.__dropCount__ = 0;
8283     this.__filtered__ = false;
8284     this.__iteratees__ = null;
8285     this.__takeCount__ = POSITIVE_INFINITY;
8286     this.__views__ = null;
8287   }
8288
8289   /**
8290    * Creates a clone of the lazy wrapper object.
8291    *
8292    * @private
8293    * @name clone
8294    * @memberOf LazyWrapper
8295    * @returns {Object} Returns the cloned `LazyWrapper` object.
8296    */
8297   function lazyClone() {
8298     var actions = this.__actions__,
8299         iteratees = this.__iteratees__,
8300         views = this.__views__,
8301         result = new LazyWrapper(this.__wrapped__);
8302
8303     result.__actions__ = actions ? arrayCopy(actions) : null;
8304     result.__dir__ = this.__dir__;
8305     result.__filtered__ = this.__filtered__;
8306     result.__iteratees__ = iteratees ? arrayCopy(iteratees) : null;
8307     result.__takeCount__ = this.__takeCount__;
8308     result.__views__ = views ? arrayCopy(views) : null;
8309     return result;
8310   }
8311
8312   /**
8313    * Reverses the direction of lazy iteration.
8314    *
8315    * @private
8316    * @name reverse
8317    * @memberOf LazyWrapper
8318    * @returns {Object} Returns the new reversed `LazyWrapper` object.
8319    */
8320   function lazyReverse() {
8321     if (this.__filtered__) {
8322       var result = new LazyWrapper(this);
8323       result.__dir__ = -1;
8324       result.__filtered__ = true;
8325     } else {
8326       result = this.clone();
8327       result.__dir__ *= -1;
8328     }
8329     return result;
8330   }
8331
8332   /**
8333    * Extracts the unwrapped value from its lazy wrapper.
8334    *
8335    * @private
8336    * @name value
8337    * @memberOf LazyWrapper
8338    * @returns {*} Returns the unwrapped value.
8339    */
8340   function lazyValue() {
8341     var array = this.__wrapped__.value();
8342     if (!isArray(array)) {
8343       return baseWrapperValue(array, this.__actions__);
8344     }
8345     var dir = this.__dir__,
8346         isRight = dir < 0,
8347         view = getView(0, array.length, this.__views__),
8348         start = view.start,
8349         end = view.end,
8350         length = end - start,
8351         index = isRight ? end : (start - 1),
8352         takeCount = nativeMin(length, this.__takeCount__),
8353         iteratees = this.__iteratees__,
8354         iterLength = iteratees ? iteratees.length : 0,
8355         resIndex = 0,
8356         result = [];
8357
8358     outer:
8359     while (length-- && resIndex < takeCount) {
8360       index += dir;
8361
8362       var iterIndex = -1,
8363           value = array[index];
8364
8365       while (++iterIndex < iterLength) {
8366         var data = iteratees[iterIndex],
8367             iteratee = data.iteratee,
8368             type = data.type;
8369
8370         if (type == LAZY_DROP_WHILE_FLAG) {
8371           if (data.done && (isRight ? (index > data.index) : (index < data.index))) {
8372             data.count = 0;
8373             data.done = false;
8374           }
8375           data.index = index;
8376           if (!data.done) {
8377             var limit = data.limit;
8378             if (!(data.done = limit > -1 ? (data.count++ >= limit) : !iteratee(value))) {
8379               continue outer;
8380             }
8381           }
8382         } else {
8383           var computed = iteratee(value);
8384           if (type == LAZY_MAP_FLAG) {
8385             value = computed;
8386           } else if (!computed) {
8387             if (type == LAZY_FILTER_FLAG) {
8388               continue outer;
8389             } else {
8390               break outer;
8391             }
8392           }
8393         }
8394       }
8395       result[resIndex++] = value;
8396     }
8397     return result;
8398   }
8399
8400   /*------------------------------------------------------------------------*/
8401
8402   /**
8403    *
8404    * Creates a cache object to store unique values.
8405    *
8406    * @private
8407    * @param {Array} [values] The values to cache.
8408    */
8409   function SetCache(values) {
8410     var length = values ? values.length : 0;
8411
8412     this.data = { 'hash': nativeCreate(null), 'set': new Set };
8413     while (length--) {
8414       this.push(values[length]);
8415     }
8416   }
8417
8418   /**
8419    * Checks if `value` is in `cache` mimicking the return signature of
8420    * `_.indexOf` by returning `0` if the value is found, else `-1`.
8421    *
8422    * @private
8423    * @param {Object} cache The cache to search.
8424    * @param {*} value The value to search for.
8425    * @returns {number} Returns `0` if `value` is found, else `-1`.
8426    */
8427   function cacheIndexOf(cache, value) {
8428     var data = cache.data,
8429         result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value];
8430
8431     return result ? 0 : -1;
8432   }
8433
8434   /**
8435    * Adds `value` to the cache.
8436    *
8437    * @private
8438    * @name push
8439    * @memberOf SetCache
8440    * @param {*} value The value to cache.
8441    */
8442   function cachePush(value) {
8443     var data = this.data;
8444     if (typeof value == 'string' || isObject(value)) {
8445       data.set.add(value);
8446     } else {
8447       data.hash[value] = true;
8448     }
8449   }
8450
8451   /*------------------------------------------------------------------------*/
8452
8453   /**
8454    * Copies the values of `source` to `array`.
8455    *
8456    * @private
8457    * @param {Array} source The array to copy values from.
8458    * @param {Array} [array=[]] The array to copy values to.
8459    * @returns {Array} Returns `array`.
8460    */
8461   function arrayCopy(source, array) {
8462     var index = -1,
8463         length = source.length;
8464
8465     array || (array = Array(length));
8466     while (++index < length) {
8467       array[index] = source[index];
8468     }
8469     return array;
8470   }
8471
8472   /**
8473    * A specialized version of `_.forEach` for arrays without support for callback
8474    * shorthands and `this` binding.
8475    *
8476    * @private
8477    * @param {Array} array The array to iterate over.
8478    * @param {Function} iteratee The function invoked per iteration.
8479    * @returns {Array} Returns `array`.
8480    */
8481   function arrayEach(array, iteratee) {
8482     var index = -1,
8483         length = array.length;
8484
8485     while (++index < length) {
8486       if (iteratee(array[index], index, array) === false) {
8487         break;
8488       }
8489     }
8490     return array;
8491   }
8492
8493   /**
8494    * A specialized version of `_.every` for arrays without support for callback
8495    * shorthands and `this` binding.
8496    *
8497    * @private
8498    * @param {Array} array The array to iterate over.
8499    * @param {Function} predicate The function invoked per iteration.
8500    * @returns {boolean} Returns `true` if all elements pass the predicate check,
8501    *  else `false`.
8502    */
8503   function arrayEvery(array, predicate) {
8504     var index = -1,
8505         length = array.length;
8506
8507     while (++index < length) {
8508       if (!predicate(array[index], index, array)) {
8509         return false;
8510       }
8511     }
8512     return true;
8513   }
8514
8515   /**
8516    * A specialized version of `_.filter` for arrays without support for callback
8517    * shorthands and `this` binding.
8518    *
8519    * @private
8520    * @param {Array} array The array to iterate over.
8521    * @param {Function} predicate The function invoked per iteration.
8522    * @returns {Array} Returns the new filtered array.
8523    */
8524   function arrayFilter(array, predicate) {
8525     var index = -1,
8526         length = array.length,
8527         resIndex = -1,
8528         result = [];
8529
8530     while (++index < length) {
8531       var value = array[index];
8532       if (predicate(value, index, array)) {
8533         result[++resIndex] = value;
8534       }
8535     }
8536     return result;
8537   }
8538
8539   /**
8540    * A specialized version of `_.map` for arrays without support for callback
8541    * shorthands and `this` binding.
8542    *
8543    * @private
8544    * @param {Array} array The array to iterate over.
8545    * @param {Function} iteratee The function invoked per iteration.
8546    * @returns {Array} Returns the new mapped array.
8547    */
8548   function arrayMap(array, iteratee) {
8549     var index = -1,
8550         length = array.length,
8551         result = Array(length);
8552
8553     while (++index < length) {
8554       result[index] = iteratee(array[index], index, array);
8555     }
8556     return result;
8557   }
8558
8559   /**
8560    * A specialized version of `_.reduce` for arrays without support for callback
8561    * shorthands and `this` binding.
8562    *
8563    * @private
8564    * @param {Array} array The array to iterate over.
8565    * @param {Function} iteratee The function invoked per iteration.
8566    * @param {*} [accumulator] The initial value.
8567    * @param {boolean} [initFromArray] Specify using the first element of `array`
8568    *  as the initial value.
8569    * @returns {*} Returns the accumulated value.
8570    */
8571   function arrayReduce(array, iteratee, accumulator, initFromArray) {
8572     var index = -1,
8573         length = array.length;
8574
8575     if (initFromArray && length) {
8576       accumulator = array[++index];
8577     }
8578     while (++index < length) {
8579       accumulator = iteratee(accumulator, array[index], index, array);
8580     }
8581     return accumulator;
8582   }
8583
8584   /**
8585    * A specialized version of `_.some` for arrays without support for callback
8586    * shorthands and `this` binding.
8587    *
8588    * @private
8589    * @param {Array} array The array to iterate over.
8590    * @param {Function} predicate The function invoked per iteration.
8591    * @returns {boolean} Returns `true` if any element passes the predicate check,
8592    *  else `false`.
8593    */
8594   function arraySome(array, predicate) {
8595     var index = -1,
8596         length = array.length;
8597
8598     while (++index < length) {
8599       if (predicate(array[index], index, array)) {
8600         return true;
8601       }
8602     }
8603     return false;
8604   }
8605
8606   /**
8607    * A specialized version of `_.assign` for customizing assigned values without
8608    * support for argument juggling, multiple sources, and `this` binding `customizer`
8609    * functions.
8610    *
8611    * @private
8612    * @param {Object} object The destination object.
8613    * @param {Object} source The source object.
8614    * @param {Function} customizer The function to customize assigned values.
8615    * @returns {Object} Returns `object`.
8616    */
8617   function assignWith(object, source, customizer) {
8618     var index = -1,
8619         props = keys(source),
8620         length = props.length;
8621
8622     while (++index < length) {
8623       var key = props[index],
8624           value = object[key],
8625           result = customizer(value, source[key], key, object, source);
8626
8627       if ((result === result ? (result !== value) : (value === value)) ||
8628           (value === undefined && !(key in object))) {
8629         object[key] = result;
8630       }
8631     }
8632     return object;
8633   }
8634
8635   /**
8636    * The base implementation of `_.assign` without support for argument juggling,
8637    * multiple sources, and `customizer` functions.
8638    *
8639    * @private
8640    * @param {Object} object The destination object.
8641    * @param {Object} source The source object.
8642    * @returns {Object} Returns `object`.
8643    */
8644   function baseAssign(object, source) {
8645     return source == null
8646       ? object
8647       : baseCopy(source, keys(source), object);
8648   }
8649
8650   /**
8651    * Copies properties of `source` to `object`.
8652    *
8653    * @private
8654    * @param {Object} source The object to copy properties from.
8655    * @param {Array} props The property names to copy.
8656    * @param {Object} [object={}] The object to copy properties to.
8657    * @returns {Object} Returns `object`.
8658    */
8659   function baseCopy(source, props, object) {
8660     object || (object = {});
8661
8662     var index = -1,
8663         length = props.length;
8664
8665     while (++index < length) {
8666       var key = props[index];
8667       object[key] = source[key];
8668     }
8669     return object;
8670   }
8671
8672   /**
8673    * The base implementation of `_.callback` which supports specifying the
8674    * number of arguments to provide to `func`.
8675    *
8676    * @private
8677    * @param {*} [func=_.identity] The value to convert to a callback.
8678    * @param {*} [thisArg] The `this` binding of `func`.
8679    * @param {number} [argCount] The number of arguments to provide to `func`.
8680    * @returns {Function} Returns the callback.
8681    */
8682   function baseCallback(func, thisArg, argCount) {
8683     var type = typeof func;
8684     if (type == 'function') {
8685       return thisArg === undefined
8686         ? func
8687         : bindCallback(func, thisArg, argCount);
8688     }
8689     if (func == null) {
8690       return identity;
8691     }
8692     if (type == 'object') {
8693       return baseMatches(func);
8694     }
8695     return thisArg === undefined
8696       ? property(func)
8697       : baseMatchesProperty(func, thisArg);
8698   }
8699
8700   /**
8701    * The base implementation of `_.clone` without support for argument juggling
8702    * and `this` binding `customizer` functions.
8703    *
8704    * @private
8705    * @param {*} value The value to clone.
8706    * @param {boolean} [isDeep] Specify a deep clone.
8707    * @param {Function} [customizer] The function to customize cloning values.
8708    * @param {string} [key] The key of `value`.
8709    * @param {Object} [object] The object `value` belongs to.
8710    * @param {Array} [stackA=[]] Tracks traversed source objects.
8711    * @param {Array} [stackB=[]] Associates clones with source counterparts.
8712    * @returns {*} Returns the cloned value.
8713    */
8714   function baseClone(value, isDeep, customizer, key, object, stackA, stackB) {
8715     var result;
8716     if (customizer) {
8717       result = object ? customizer(value, key, object) : customizer(value);
8718     }
8719     if (result !== undefined) {
8720       return result;
8721     }
8722     if (!isObject(value)) {
8723       return value;
8724     }
8725     var isArr = isArray(value);
8726     if (isArr) {
8727       result = initCloneArray(value);
8728       if (!isDeep) {
8729         return arrayCopy(value, result);
8730       }
8731     } else {
8732       var tag = objToString.call(value),
8733           isFunc = tag == funcTag;
8734
8735       if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
8736         if (isHostObject(value)) {
8737           return object ? value : {};
8738         }
8739         result = initCloneObject(isFunc ? {} : value);
8740         if (!isDeep) {
8741           return baseAssign(result, value);
8742         }
8743       } else {
8744         return cloneableTags[tag]
8745           ? initCloneByTag(value, tag, isDeep)
8746           : (object ? value : {});
8747       }
8748     }
8749     // Check for circular references and return corresponding clone.
8750     stackA || (stackA = []);
8751     stackB || (stackB = []);
8752
8753     var length = stackA.length;
8754     while (length--) {
8755       if (stackA[length] == value) {
8756         return stackB[length];
8757       }
8758     }
8759     // Add the source value to the stack of traversed objects and associate it with its clone.
8760     stackA.push(value);
8761     stackB.push(result);
8762
8763     // Recursively populate clone (susceptible to call stack limits).
8764     (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) {
8765       result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB);
8766     });
8767     return result;
8768   }
8769
8770   /**
8771    * The base implementation of `_.create` without support for assigning
8772    * properties to the created object.
8773    *
8774    * @private
8775    * @param {Object} prototype The object to inherit from.
8776    * @returns {Object} Returns the new object.
8777    */
8778   var baseCreate = (function() {
8779     function object() {}
8780     return function(prototype) {
8781       if (isObject(prototype)) {
8782         object.prototype = prototype;
8783         var result = new object;
8784         object.prototype = null;
8785       }
8786       return result || {};
8787     };
8788   }());
8789
8790   /**
8791    * The base implementation of `_.difference` which accepts a single array
8792    * of values to exclude.
8793    *
8794    * @private
8795    * @param {Array} array The array to inspect.
8796    * @param {Array} values The values to exclude.
8797    * @returns {Array} Returns the new array of filtered values.
8798    */
8799   function baseDifference(array, values) {
8800     var length = array ? array.length : 0,
8801         result = [];
8802
8803     if (!length) {
8804       return result;
8805     }
8806     var index = -1,
8807         indexOf = getIndexOf(),
8808         isCommon = indexOf == baseIndexOf,
8809         cache = (isCommon && values.length >= 200) ? createCache(values) : null,
8810         valuesLength = values.length;
8811
8812     if (cache) {
8813       indexOf = cacheIndexOf;
8814       isCommon = false;
8815       values = cache;
8816     }
8817     outer:
8818     while (++index < length) {
8819       var value = array[index];
8820
8821       if (isCommon && value === value) {
8822         var valuesIndex = valuesLength;
8823         while (valuesIndex--) {
8824           if (values[valuesIndex] === value) {
8825             continue outer;
8826           }
8827         }
8828         result.push(value);
8829       }
8830       else if (indexOf(values, value, 0) < 0) {
8831         result.push(value);
8832       }
8833     }
8834     return result;
8835   }
8836
8837   /**
8838    * The base implementation of `_.forEach` without support for callback
8839    * shorthands and `this` binding.
8840    *
8841    * @private
8842    * @param {Array|Object|string} collection The collection to iterate over.
8843    * @param {Function} iteratee The function invoked per iteration.
8844    * @returns {Array|Object|string} Returns `collection`.
8845    */
8846   var baseEach = createBaseEach(baseForOwn);
8847
8848   /**
8849    * The base implementation of `_.every` without support for callback
8850    * shorthands and `this` binding.
8851    *
8852    * @private
8853    * @param {Array|Object|string} collection The collection to iterate over.
8854    * @param {Function} predicate The function invoked per iteration.
8855    * @returns {boolean} Returns `true` if all elements pass the predicate check,
8856    *  else `false`
8857    */
8858   function baseEvery(collection, predicate) {
8859     var result = true;
8860     baseEach(collection, function(value, index, collection) {
8861       result = !!predicate(value, index, collection);
8862       return result;
8863     });
8864     return result;
8865   }
8866
8867   /**
8868    * The base implementation of `_.filter` without support for callback
8869    * shorthands and `this` binding.
8870    *
8871    * @private
8872    * @param {Array|Object|string} collection The collection to iterate over.
8873    * @param {Function} predicate The function invoked per iteration.
8874    * @returns {Array} Returns the new filtered array.
8875    */
8876   function baseFilter(collection, predicate) {
8877     var result = [];
8878     baseEach(collection, function(value, index, collection) {
8879       if (predicate(value, index, collection)) {
8880         result.push(value);
8881       }
8882     });
8883     return result;
8884   }
8885
8886   /**
8887    * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`,
8888    * without support for callback shorthands and `this` binding, which iterates
8889    * over `collection` using the provided `eachFunc`.
8890    *
8891    * @private
8892    * @param {Array|Object|string} collection The collection to search.
8893    * @param {Function} predicate The function invoked per iteration.
8894    * @param {Function} eachFunc The function to iterate over `collection`.
8895    * @param {boolean} [retKey] Specify returning the key of the found element
8896    *  instead of the element itself.
8897    * @returns {*} Returns the found element or its key, else `undefined`.
8898    */
8899   function baseFind(collection, predicate, eachFunc, retKey) {
8900     var result;
8901     eachFunc(collection, function(value, key, collection) {
8902       if (predicate(value, key, collection)) {
8903         result = retKey ? key : value;
8904         return false;
8905       }
8906     });
8907     return result;
8908   }
8909
8910   /**
8911    * The base implementation of `_.flatten` with added support for restricting
8912    * flattening and specifying the start index.
8913    *
8914    * @private
8915    * @param {Array} array The array to flatten.
8916    * @param {boolean} [isDeep] Specify a deep flatten.
8917    * @param {boolean} [isStrict] Restrict flattening to arrays-like objects.
8918    * @returns {Array} Returns the new flattened array.
8919    */
8920   function baseFlatten(array, isDeep, isStrict) {
8921     var index = -1,
8922         length = array.length,
8923         resIndex = -1,
8924         result = [];
8925
8926     while (++index < length) {
8927       var value = array[index];
8928       if (isObjectLike(value) && isArrayLike(value) &&
8929           (isStrict || isArray(value) || isArguments(value))) {
8930         if (isDeep) {
8931           // Recursively flatten arrays (susceptible to call stack limits).
8932           value = baseFlatten(value, isDeep, isStrict);
8933         }
8934         var valIndex = -1,
8935             valLength = value.length;
8936
8937         while (++valIndex < valLength) {
8938           result[++resIndex] = value[valIndex];
8939         }
8940       } else if (!isStrict) {
8941         result[++resIndex] = value;
8942       }
8943     }
8944     return result;
8945   }
8946
8947   /**
8948    * The base implementation of `baseForIn` and `baseForOwn` which iterates
8949    * over `object` properties returned by `keysFunc` invoking `iteratee` for
8950    * each property. Iteratee functions may exit iteration early by explicitly
8951    * returning `false`.
8952    *
8953    * @private
8954    * @param {Object} object The object to iterate over.
8955    * @param {Function} iteratee The function invoked per iteration.
8956    * @param {Function} keysFunc The function to get the keys of `object`.
8957    * @returns {Object} Returns `object`.
8958    */
8959   var baseFor = createBaseFor();
8960
8961   /**
8962    * The base implementation of `_.forIn` without support for callback
8963    * shorthands and `this` binding.
8964    *
8965    * @private
8966    * @param {Object} object The object to iterate over.
8967    * @param {Function} iteratee The function invoked per iteration.
8968    * @returns {Object} Returns `object`.
8969    */
8970   function baseForIn(object, iteratee) {
8971     return baseFor(object, iteratee, keysIn);
8972   }
8973
8974   /**
8975    * The base implementation of `_.forOwn` without support for callback
8976    * shorthands and `this` binding.
8977    *
8978    * @private
8979    * @param {Object} object The object to iterate over.
8980    * @param {Function} iteratee The function invoked per iteration.
8981    * @returns {Object} Returns `object`.
8982    */
8983   function baseForOwn(object, iteratee) {
8984     return baseFor(object, iteratee, keys);
8985   }
8986
8987   /**
8988    * The base implementation of `_.functions` which creates an array of
8989    * `object` function property names filtered from those provided.
8990    *
8991    * @private
8992    * @param {Object} object The object to inspect.
8993    * @param {Array} props The property names to filter.
8994    * @returns {Array} Returns the new array of filtered property names.
8995    */
8996   function baseFunctions(object, props) {
8997     var index = -1,
8998         length = props.length,
8999         resIndex = -1,
9000         result = [];
9001
9002     while (++index < length) {
9003       var key = props[index];
9004       if (isFunction(object[key])) {
9005         result[++resIndex] = key;
9006       }
9007     }
9008     return result;
9009   }
9010
9011   /**
9012    * The base implementation of `get` without support for string paths
9013    * and default values.
9014    *
9015    * @private
9016    * @param {Object} object The object to query.
9017    * @param {Array} path The path of the property to get.
9018    * @param {string} [pathKey] The key representation of path.
9019    * @returns {*} Returns the resolved value.
9020    */
9021   function baseGet(object, path, pathKey) {
9022     if (object == null) {
9023       return;
9024     }
9025     object = toObject(object);
9026     if (pathKey !== undefined && pathKey in object) {
9027       path = [pathKey];
9028     }
9029     var index = 0,
9030         length = path.length;
9031
9032     while (object != null && index < length) {
9033       object = toObject(object)[path[index++]];
9034     }
9035     return (index && index == length) ? object : undefined;
9036   }
9037
9038   /**
9039    * The base implementation of `_.isEqual` without support for `this` binding
9040    * `customizer` functions.
9041    *
9042    * @private
9043    * @param {*} value The value to compare.
9044    * @param {*} other The other value to compare.
9045    * @param {Function} [customizer] The function to customize comparing values.
9046    * @param {boolean} [isLoose] Specify performing partial comparisons.
9047    * @param {Array} [stackA] Tracks traversed `value` objects.
9048    * @param {Array} [stackB] Tracks traversed `other` objects.
9049    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
9050    */
9051   function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) {
9052     if (value === other) {
9053       return true;
9054     }
9055     if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
9056       return value !== value && other !== other;
9057     }
9058     return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB);
9059   }
9060
9061   /**
9062    * A specialized version of `baseIsEqual` for arrays and objects which performs
9063    * deep comparisons and tracks traversed objects enabling objects with circular
9064    * references to be compared.
9065    *
9066    * @private
9067    * @param {Object} object The object to compare.
9068    * @param {Object} other The other object to compare.
9069    * @param {Function} equalFunc The function to determine equivalents of values.
9070    * @param {Function} [customizer] The function to customize comparing objects.
9071    * @param {boolean} [isLoose] Specify performing partial comparisons.
9072    * @param {Array} [stackA=[]] Tracks traversed `value` objects.
9073    * @param {Array} [stackB=[]] Tracks traversed `other` objects.
9074    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9075    */
9076   function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
9077     var objIsArr = isArray(object),
9078         othIsArr = isArray(other),
9079         objTag = arrayTag,
9080         othTag = arrayTag;
9081
9082     if (!objIsArr) {
9083       objTag = objToString.call(object);
9084       if (objTag == argsTag) {
9085         objTag = objectTag;
9086       } else if (objTag != objectTag) {
9087         objIsArr = isTypedArray(object);
9088       }
9089     }
9090     if (!othIsArr) {
9091       othTag = objToString.call(other);
9092       if (othTag == argsTag) {
9093         othTag = objectTag;
9094       } else if (othTag != objectTag) {
9095         othIsArr = isTypedArray(other);
9096       }
9097     }
9098     var objIsObj = objTag == objectTag && !isHostObject(object),
9099         othIsObj = othTag == objectTag && !isHostObject(other),
9100         isSameTag = objTag == othTag;
9101
9102     if (isSameTag && !(objIsArr || objIsObj)) {
9103       return equalByTag(object, other, objTag);
9104     }
9105     if (!isLoose) {
9106       var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
9107           othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
9108
9109       if (objIsWrapped || othIsWrapped) {
9110         return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB);
9111       }
9112     }
9113     if (!isSameTag) {
9114       return false;
9115     }
9116     // Assume cyclic values are equal.
9117     // For more information on detecting circular references see https://es5.github.io/#JO.
9118     stackA || (stackA = []);
9119     stackB || (stackB = []);
9120
9121     var length = stackA.length;
9122     while (length--) {
9123       if (stackA[length] == object) {
9124         return stackB[length] == other;
9125       }
9126     }
9127     // Add `object` and `other` to the stack of traversed objects.
9128     stackA.push(object);
9129     stackB.push(other);
9130
9131     var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB);
9132
9133     stackA.pop();
9134     stackB.pop();
9135
9136     return result;
9137   }
9138
9139   /**
9140    * The base implementation of `_.isMatch` without support for callback
9141    * shorthands and `this` binding.
9142    *
9143    * @private
9144    * @param {Object} object The object to inspect.
9145    * @param {Array} matchData The propery names, values, and compare flags to match.
9146    * @param {Function} [customizer] The function to customize comparing objects.
9147    * @returns {boolean} Returns `true` if `object` is a match, else `false`.
9148    */
9149   function baseIsMatch(object, matchData, customizer) {
9150     var index = matchData.length,
9151         length = index,
9152         noCustomizer = !customizer;
9153
9154     if (object == null) {
9155       return !length;
9156     }
9157     object = toObject(object);
9158     while (index--) {
9159       var data = matchData[index];
9160       if ((noCustomizer && data[2])
9161             ? data[1] !== object[data[0]]
9162             : !(data[0] in object)
9163           ) {
9164         return false;
9165       }
9166     }
9167     while (++index < length) {
9168       data = matchData[index];
9169       var key = data[0],
9170           objValue = object[key],
9171           srcValue = data[1];
9172
9173       if (noCustomizer && data[2]) {
9174         if (objValue === undefined && !(key in object)) {
9175           return false;
9176         }
9177       } else {
9178         var result = customizer ? customizer(objValue, srcValue, key) : undefined;
9179         if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, true) : result)) {
9180           return false;
9181         }
9182       }
9183     }
9184     return true;
9185   }
9186
9187   /**
9188    * The base implementation of `_.map` without support for callback shorthands
9189    * and `this` binding.
9190    *
9191    * @private
9192    * @param {Array|Object|string} collection The collection to iterate over.
9193    * @param {Function} iteratee The function invoked per iteration.
9194    * @returns {Array} Returns the new mapped array.
9195    */
9196   function baseMap(collection, iteratee) {
9197     var index = -1,
9198         result = isArrayLike(collection) ? Array(collection.length) : [];
9199
9200     baseEach(collection, function(value, key, collection) {
9201       result[++index] = iteratee(value, key, collection);
9202     });
9203     return result;
9204   }
9205
9206   /**
9207    * The base implementation of `_.matches` which does not clone `source`.
9208    *
9209    * @private
9210    * @param {Object} source The object of property values to match.
9211    * @returns {Function} Returns the new function.
9212    */
9213   function baseMatches(source) {
9214     var matchData = getMatchData(source);
9215     if (matchData.length == 1 && matchData[0][2]) {
9216       var key = matchData[0][0],
9217           value = matchData[0][1];
9218
9219       return function(object) {
9220         if (object == null) {
9221           return false;
9222         }
9223         object = toObject(object);
9224         return object[key] === value && (value !== undefined || (key in object));
9225       };
9226     }
9227     return function(object) {
9228       return baseIsMatch(object, matchData);
9229     };
9230   }
9231
9232   /**
9233    * The base implementation of `_.matchesProperty` which does not clone `srcValue`.
9234    *
9235    * @private
9236    * @param {string} path The path of the property to get.
9237    * @param {*} srcValue The value to compare.
9238    * @returns {Function} Returns the new function.
9239    */
9240   function baseMatchesProperty(path, srcValue) {
9241     var isArr = isArray(path),
9242         isCommon = isKey(path) && isStrictComparable(srcValue),
9243         pathKey = (path + '');
9244
9245     path = toPath(path);
9246     return function(object) {
9247       if (object == null) {
9248         return false;
9249       }
9250       var key = pathKey;
9251       object = toObject(object);
9252       if ((isArr || !isCommon) && !(key in object)) {
9253         object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
9254         if (object == null) {
9255           return false;
9256         }
9257         key = last(path);
9258         object = toObject(object);
9259       }
9260       return object[key] === srcValue
9261         ? (srcValue !== undefined || (key in object))
9262         : baseIsEqual(srcValue, object[key], undefined, true);
9263     };
9264   }
9265
9266   /**
9267    * The base implementation of `_.merge` without support for argument juggling,
9268    * multiple sources, and `this` binding `customizer` functions.
9269    *
9270    * @private
9271    * @param {Object} object The destination object.
9272    * @param {Object} source The source object.
9273    * @param {Function} [customizer] The function to customize merging properties.
9274    * @param {Array} [stackA=[]] Tracks traversed source objects.
9275    * @param {Array} [stackB=[]] Associates values with source counterparts.
9276    * @returns {Object} Returns `object`.
9277    */
9278   function baseMerge(object, source, customizer, stackA, stackB) {
9279     if (!isObject(object)) {
9280       return object;
9281     }
9282     var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)),
9283         props = isSrcArr ? null : keys(source);
9284
9285     arrayEach(props || source, function(srcValue, key) {
9286       if (props) {
9287         key = srcValue;
9288         srcValue = source[key];
9289       }
9290       if (isObjectLike(srcValue)) {
9291         stackA || (stackA = []);
9292         stackB || (stackB = []);
9293         baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
9294       }
9295       else {
9296         var value = object[key],
9297             result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
9298             isCommon = result === undefined;
9299
9300         if (isCommon) {
9301           result = srcValue;
9302         }
9303         if ((result !== undefined || (isSrcArr && !(key in object))) &&
9304             (isCommon || (result === result ? (result !== value) : (value === value)))) {
9305           object[key] = result;
9306         }
9307       }
9308     });
9309     return object;
9310   }
9311
9312   /**
9313    * A specialized version of `baseMerge` for arrays and objects which performs
9314    * deep merges and tracks traversed objects enabling objects with circular
9315    * references to be merged.
9316    *
9317    * @private
9318    * @param {Object} object The destination object.
9319    * @param {Object} source The source object.
9320    * @param {string} key The key of the value to merge.
9321    * @param {Function} mergeFunc The function to merge values.
9322    * @param {Function} [customizer] The function to customize merging properties.
9323    * @param {Array} [stackA=[]] Tracks traversed source objects.
9324    * @param {Array} [stackB=[]] Associates values with source counterparts.
9325    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
9326    */
9327   function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) {
9328     var length = stackA.length,
9329         srcValue = source[key];
9330
9331     while (length--) {
9332       if (stackA[length] == srcValue) {
9333         object[key] = stackB[length];
9334         return;
9335       }
9336     }
9337     var value = object[key],
9338         result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
9339         isCommon = result === undefined;
9340
9341     if (isCommon) {
9342       result = srcValue;
9343       if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) {
9344         result = isArray(value)
9345           ? value
9346           : (isArrayLike(value) ? arrayCopy(value) : []);
9347       }
9348       else if (isPlainObject(srcValue) || isArguments(srcValue)) {
9349         result = isArguments(value)
9350           ? toPlainObject(value)
9351           : (isPlainObject(value) ? value : {});
9352       }
9353       else {
9354         isCommon = false;
9355       }
9356     }
9357     // Add the source value to the stack of traversed objects and associate
9358     // it with its merged value.
9359     stackA.push(srcValue);
9360     stackB.push(result);
9361
9362     if (isCommon) {
9363       // Recursively merge objects and arrays (susceptible to call stack limits).
9364       object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB);
9365     } else if (result === result ? (result !== value) : (value === value)) {
9366       object[key] = result;
9367     }
9368   }
9369
9370   /**
9371    * The base implementation of `_.property` without support for deep paths.
9372    *
9373    * @private
9374    * @param {string} key The key of the property to get.
9375    * @returns {Function} Returns the new function.
9376    */
9377   function baseProperty(key) {
9378     return function(object) {
9379       return object == null ? undefined : toObject(object)[key];
9380     };
9381   }
9382
9383   /**
9384    * A specialized version of `baseProperty` which supports deep paths.
9385    *
9386    * @private
9387    * @param {Array|string} path The path of the property to get.
9388    * @returns {Function} Returns the new function.
9389    */
9390   function basePropertyDeep(path) {
9391     var pathKey = (path + '');
9392     path = toPath(path);
9393     return function(object) {
9394       return baseGet(object, path, pathKey);
9395     };
9396   }
9397
9398   /**
9399    * The base implementation of `_.reduce` and `_.reduceRight` without support
9400    * for callback shorthands and `this` binding, which iterates over `collection`
9401    * using the provided `eachFunc`.
9402    *
9403    * @private
9404    * @param {Array|Object|string} collection The collection to iterate over.
9405    * @param {Function} iteratee The function invoked per iteration.
9406    * @param {*} accumulator The initial value.
9407    * @param {boolean} initFromCollection Specify using the first or last element
9408    *  of `collection` as the initial value.
9409    * @param {Function} eachFunc The function to iterate over `collection`.
9410    * @returns {*} Returns the accumulated value.
9411    */
9412   function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) {
9413     eachFunc(collection, function(value, index, collection) {
9414       accumulator = initFromCollection
9415         ? (initFromCollection = false, value)
9416         : iteratee(accumulator, value, index, collection);
9417     });
9418     return accumulator;
9419   }
9420
9421   /**
9422    * The base implementation of `setData` without support for hot loop detection.
9423    *
9424    * @private
9425    * @param {Function} func The function to associate metadata with.
9426    * @param {*} data The metadata.
9427    * @returns {Function} Returns `func`.
9428    */
9429   var baseSetData = !metaMap ? identity : function(func, data) {
9430     metaMap.set(func, data);
9431     return func;
9432   };
9433
9434   /**
9435    * The base implementation of `_.slice` without an iteratee call guard.
9436    *
9437    * @private
9438    * @param {Array} array The array to slice.
9439    * @param {number} [start=0] The start position.
9440    * @param {number} [end=array.length] The end position.
9441    * @returns {Array} Returns the slice of `array`.
9442    */
9443   function baseSlice(array, start, end) {
9444     var index = -1,
9445         length = array.length;
9446
9447     start = start == null ? 0 : (+start || 0);
9448     if (start < 0) {
9449       start = -start > length ? 0 : (length + start);
9450     }
9451     end = (end === undefined || end > length) ? length : (+end || 0);
9452     if (end < 0) {
9453       end += length;
9454     }
9455     length = start > end ? 0 : ((end - start) >>> 0);
9456     start >>>= 0;
9457
9458     var result = Array(length);
9459     while (++index < length) {
9460       result[index] = array[index + start];
9461     }
9462     return result;
9463   }
9464
9465   /**
9466    * The base implementation of `_.some` without support for callback shorthands
9467    * and `this` binding.
9468    *
9469    * @private
9470    * @param {Array|Object|string} collection The collection to iterate over.
9471    * @param {Function} predicate The function invoked per iteration.
9472    * @returns {boolean} Returns `true` if any element passes the predicate check,
9473    *  else `false`.
9474    */
9475   function baseSome(collection, predicate) {
9476     var result;
9477
9478     baseEach(collection, function(value, index, collection) {
9479       result = predicate(value, index, collection);
9480       return !result;
9481     });
9482     return !!result;
9483   }
9484
9485   /**
9486    * The base implementation of `_.uniq` without support for callback shorthands
9487    * and `this` binding.
9488    *
9489    * @private
9490    * @param {Array} array The array to inspect.
9491    * @param {Function} [iteratee] The function invoked per iteration.
9492    * @returns {Array} Returns the new duplicate-value-free array.
9493    */
9494   function baseUniq(array, iteratee) {
9495     var index = -1,
9496         indexOf = getIndexOf(),
9497         length = array.length,
9498         isCommon = indexOf == baseIndexOf,
9499         isLarge = isCommon && length >= 200,
9500         seen = isLarge ? createCache() : null,
9501         result = [];
9502
9503     if (seen) {
9504       indexOf = cacheIndexOf;
9505       isCommon = false;
9506     } else {
9507       isLarge = false;
9508       seen = iteratee ? [] : result;
9509     }
9510     outer:
9511     while (++index < length) {
9512       var value = array[index],
9513           computed = iteratee ? iteratee(value, index, array) : value;
9514
9515       if (isCommon && value === value) {
9516         var seenIndex = seen.length;
9517         while (seenIndex--) {
9518           if (seen[seenIndex] === computed) {
9519             continue outer;
9520           }
9521         }
9522         if (iteratee) {
9523           seen.push(computed);
9524         }
9525         result.push(value);
9526       }
9527       else if (indexOf(seen, computed, 0) < 0) {
9528         if (iteratee || isLarge) {
9529           seen.push(computed);
9530         }
9531         result.push(value);
9532       }
9533     }
9534     return result;
9535   }
9536
9537   /**
9538    * The base implementation of `_.values` and `_.valuesIn` which creates an
9539    * array of `object` property values corresponding to the property names
9540    * of `props`.
9541    *
9542    * @private
9543    * @param {Object} object The object to query.
9544    * @param {Array} props The property names to get values for.
9545    * @returns {Object} Returns the array of property values.
9546    */
9547   function baseValues(object, props) {
9548     var index = -1,
9549         length = props.length,
9550         result = Array(length);
9551
9552     while (++index < length) {
9553       result[index] = object[props[index]];
9554     }
9555     return result;
9556   }
9557
9558   /**
9559    * The base implementation of `wrapperValue` which returns the result of
9560    * performing a sequence of actions on the unwrapped `value`, where each
9561    * successive action is supplied the return value of the previous.
9562    *
9563    * @private
9564    * @param {*} value The unwrapped value.
9565    * @param {Array} actions Actions to peform to resolve the unwrapped value.
9566    * @returns {*} Returns the resolved value.
9567    */
9568   function baseWrapperValue(value, actions) {
9569     var result = value;
9570     if (result instanceof LazyWrapper) {
9571       result = result.value();
9572     }
9573     var index = -1,
9574         length = actions.length;
9575
9576     while (++index < length) {
9577       var args = [result],
9578           action = actions[index];
9579
9580       push.apply(args, action.args);
9581       result = action.func.apply(action.thisArg, args);
9582     }
9583     return result;
9584   }
9585
9586   /**
9587    * Performs a binary search of `array` to determine the index at which `value`
9588    * should be inserted into `array` in order to maintain its sort order.
9589    *
9590    * @private
9591    * @param {Array} array The sorted array to inspect.
9592    * @param {*} value The value to evaluate.
9593    * @param {boolean} [retHighest] Specify returning the highest qualified index.
9594    * @returns {number} Returns the index at which `value` should be inserted
9595    *  into `array`.
9596    */
9597   function binaryIndex(array, value, retHighest) {
9598     var low = 0,
9599         high = array ? array.length : low;
9600
9601     if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
9602       while (low < high) {
9603         var mid = (low + high) >>> 1,
9604             computed = array[mid];
9605
9606         if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) {
9607           low = mid + 1;
9608         } else {
9609           high = mid;
9610         }
9611       }
9612       return high;
9613     }
9614     return binaryIndexBy(array, value, identity, retHighest);
9615   }
9616
9617   /**
9618    * This function is like `binaryIndex` except that it invokes `iteratee` for
9619    * `value` and each element of `array` to compute their sort ranking. The
9620    * iteratee is invoked with one argument; (value).
9621    *
9622    * @private
9623    * @param {Array} array The sorted array to inspect.
9624    * @param {*} value The value to evaluate.
9625    * @param {Function} iteratee The function invoked per iteration.
9626    * @param {boolean} [retHighest] Specify returning the highest qualified index.
9627    * @returns {number} Returns the index at which `value` should be inserted
9628    *  into `array`.
9629    */
9630   function binaryIndexBy(array, value, iteratee, retHighest) {
9631     value = iteratee(value);
9632
9633     var low = 0,
9634         high = array ? array.length : 0,
9635         valIsNaN = value !== value,
9636         valIsNull = value === null,
9637         valIsUndef = value === undefined;
9638
9639     while (low < high) {
9640       var mid = floor((low + high) / 2),
9641           computed = iteratee(array[mid]),
9642           isDef = computed !== undefined,
9643           isReflexive = computed === computed;
9644
9645       if (valIsNaN) {
9646         var setLow = isReflexive || retHighest;
9647       } else if (valIsNull) {
9648         setLow = isReflexive && isDef && (retHighest || computed != null);
9649       } else if (valIsUndef) {
9650         setLow = isReflexive && (retHighest || isDef);
9651       } else if (computed == null) {
9652         setLow = false;
9653       } else {
9654         setLow = retHighest ? (computed <= value) : (computed < value);
9655       }
9656       if (setLow) {
9657         low = mid + 1;
9658       } else {
9659         high = mid;
9660       }
9661     }
9662     return nativeMin(high, MAX_ARRAY_INDEX);
9663   }
9664
9665   /**
9666    * A specialized version of `baseCallback` which only supports `this` binding
9667    * and specifying the number of arguments to provide to `func`.
9668    *
9669    * @private
9670    * @param {Function} func The function to bind.
9671    * @param {*} thisArg The `this` binding of `func`.
9672    * @param {number} [argCount] The number of arguments to provide to `func`.
9673    * @returns {Function} Returns the callback.
9674    */
9675   function bindCallback(func, thisArg, argCount) {
9676     if (typeof func != 'function') {
9677       return identity;
9678     }
9679     if (thisArg === undefined) {
9680       return func;
9681     }
9682     switch (argCount) {
9683       case 1: return function(value) {
9684         return func.call(thisArg, value);
9685       };
9686       case 3: return function(value, index, collection) {
9687         return func.call(thisArg, value, index, collection);
9688       };
9689       case 4: return function(accumulator, value, index, collection) {
9690         return func.call(thisArg, accumulator, value, index, collection);
9691       };
9692       case 5: return function(value, other, key, object, source) {
9693         return func.call(thisArg, value, other, key, object, source);
9694       };
9695     }
9696     return function() {
9697       return func.apply(thisArg, arguments);
9698     };
9699   }
9700
9701   /**
9702    * Creates a clone of the given array buffer.
9703    *
9704    * @private
9705    * @param {ArrayBuffer} buffer The array buffer to clone.
9706    * @returns {ArrayBuffer} Returns the cloned array buffer.
9707    */
9708   function bufferClone(buffer) {
9709     return bufferSlice.call(buffer, 0);
9710   }
9711   if (!bufferSlice) {
9712     // PhantomJS has `ArrayBuffer` and `Uint8Array` but not `Float64Array`.
9713     bufferClone = !(ArrayBuffer && Uint8Array) ? constant(null) : function(buffer) {
9714       var byteLength = buffer.byteLength,
9715           floatLength = Float64Array ? floor(byteLength / FLOAT64_BYTES_PER_ELEMENT) : 0,
9716           offset = floatLength * FLOAT64_BYTES_PER_ELEMENT,
9717           result = new ArrayBuffer(byteLength);
9718
9719       if (floatLength) {
9720         var view = new Float64Array(result, 0, floatLength);
9721         view.set(new Float64Array(buffer, 0, floatLength));
9722       }
9723       if (byteLength != offset) {
9724         view = new Uint8Array(result, offset);
9725         view.set(new Uint8Array(buffer, offset));
9726       }
9727       return result;
9728     };
9729   }
9730
9731   /**
9732    * Creates an array that is the composition of partially applied arguments,
9733    * placeholders, and provided arguments into a single array of arguments.
9734    *
9735    * @private
9736    * @param {Array|Object} args The provided arguments.
9737    * @param {Array} partials The arguments to prepend to those provided.
9738    * @param {Array} holders The `partials` placeholder indexes.
9739    * @returns {Array} Returns the new array of composed arguments.
9740    */
9741   function composeArgs(args, partials, holders) {
9742     var holdersLength = holders.length,
9743         argsIndex = -1,
9744         argsLength = nativeMax(args.length - holdersLength, 0),
9745         leftIndex = -1,
9746         leftLength = partials.length,
9747         result = Array(argsLength + leftLength);
9748
9749     while (++leftIndex < leftLength) {
9750       result[leftIndex] = partials[leftIndex];
9751     }
9752     while (++argsIndex < holdersLength) {
9753       result[holders[argsIndex]] = args[argsIndex];
9754     }
9755     while (argsLength--) {
9756       result[leftIndex++] = args[argsIndex++];
9757     }
9758     return result;
9759   }
9760
9761   /**
9762    * This function is like `composeArgs` except that the arguments composition
9763    * is tailored for `_.partialRight`.
9764    *
9765    * @private
9766    * @param {Array|Object} args The provided arguments.
9767    * @param {Array} partials The arguments to append to those provided.
9768    * @param {Array} holders The `partials` placeholder indexes.
9769    * @returns {Array} Returns the new array of composed arguments.
9770    */
9771   function composeArgsRight(args, partials, holders) {
9772     var holdersIndex = -1,
9773         holdersLength = holders.length,
9774         argsIndex = -1,
9775         argsLength = nativeMax(args.length - holdersLength, 0),
9776         rightIndex = -1,
9777         rightLength = partials.length,
9778         result = Array(argsLength + rightLength);
9779
9780     while (++argsIndex < argsLength) {
9781       result[argsIndex] = args[argsIndex];
9782     }
9783     var offset = argsIndex;
9784     while (++rightIndex < rightLength) {
9785       result[offset + rightIndex] = partials[rightIndex];
9786     }
9787     while (++holdersIndex < holdersLength) {
9788       result[offset + holders[holdersIndex]] = args[argsIndex++];
9789     }
9790     return result;
9791   }
9792
9793   /**
9794    * Creates a function that aggregates a collection, creating an accumulator
9795    * object composed from the results of running each element in the collection
9796    * through an iteratee.
9797    *
9798    * **Note:** This function is used to create `_.countBy`, `_.groupBy`, `_.indexBy`,
9799    * and `_.partition`.
9800    *
9801    * @private
9802    * @param {Function} setter The function to set keys and values of the accumulator object.
9803    * @param {Function} [initializer] The function to initialize the accumulator object.
9804    * @returns {Function} Returns the new aggregator function.
9805    */
9806   function createAggregator(setter, initializer) {
9807     return function(collection, iteratee, thisArg) {
9808       var result = initializer ? initializer() : {};
9809       iteratee = getCallback(iteratee, thisArg, 3);
9810
9811       if (isArray(collection)) {
9812         var index = -1,
9813             length = collection.length;
9814
9815         while (++index < length) {
9816           var value = collection[index];
9817           setter(result, value, iteratee(value, index, collection), collection);
9818         }
9819       } else {
9820         baseEach(collection, function(value, key, collection) {
9821           setter(result, value, iteratee(value, key, collection), collection);
9822         });
9823       }
9824       return result;
9825     };
9826   }
9827
9828   /**
9829    * Creates a function that assigns properties of source object(s) to a given
9830    * destination object.
9831    *
9832    * **Note:** This function is used to create `_.assign`, `_.defaults`, and `_.merge`.
9833    *
9834    * @private
9835    * @param {Function} assigner The function to assign values.
9836    * @returns {Function} Returns the new assigner function.
9837    */
9838   function createAssigner(assigner) {
9839     return restParam(function(object, sources) {
9840       var index = -1,
9841           length = object == null ? 0 : sources.length,
9842           customizer = length > 2 ? sources[length - 2] : undefined,
9843           guard = length > 2 ? sources[2] : undefined,
9844           thisArg = length > 1 ? sources[length - 1] : undefined;
9845
9846       if (typeof customizer == 'function') {
9847         customizer = bindCallback(customizer, thisArg, 5);
9848         length -= 2;
9849       } else {
9850         customizer = typeof thisArg == 'function' ? thisArg : undefined;
9851         length -= (customizer ? 1 : 0);
9852       }
9853       if (guard && isIterateeCall(sources[0], sources[1], guard)) {
9854         customizer = length < 3 ? undefined : customizer;
9855         length = 1;
9856       }
9857       while (++index < length) {
9858         var source = sources[index];
9859         if (source) {
9860           assigner(object, source, customizer);
9861         }
9862       }
9863       return object;
9864     });
9865   }
9866
9867   /**
9868    * Creates a `baseEach` or `baseEachRight` function.
9869    *
9870    * @private
9871    * @param {Function} eachFunc The function to iterate over a collection.
9872    * @param {boolean} [fromRight] Specify iterating from right to left.
9873    * @returns {Function} Returns the new base function.
9874    */
9875   function createBaseEach(eachFunc, fromRight) {
9876     return function(collection, iteratee) {
9877       var length = collection ? getLength(collection) : 0;
9878       if (!isLength(length)) {
9879         return eachFunc(collection, iteratee);
9880       }
9881       var index = fromRight ? length : -1,
9882           iterable = toObject(collection);
9883
9884       while ((fromRight ? index-- : ++index < length)) {
9885         if (iteratee(iterable[index], index, iterable) === false) {
9886           break;
9887         }
9888       }
9889       return collection;
9890     };
9891   }
9892
9893   /**
9894    * Creates a base function for `_.forIn` or `_.forInRight`.
9895    *
9896    * @private
9897    * @param {boolean} [fromRight] Specify iterating from right to left.
9898    * @returns {Function} Returns the new base function.
9899    */
9900   function createBaseFor(fromRight) {
9901     return function(object, iteratee, keysFunc) {
9902       var iterable = toObject(object),
9903           props = keysFunc(object),
9904           length = props.length,
9905           index = fromRight ? length : -1;
9906
9907       while ((fromRight ? index-- : ++index < length)) {
9908         var key = props[index];
9909         if (iteratee(iterable[key], key, iterable) === false) {
9910           break;
9911         }
9912       }
9913       return object;
9914     };
9915   }
9916
9917   /**
9918    * Creates a function that wraps `func` and invokes it with the `this`
9919    * binding of `thisArg`.
9920    *
9921    * @private
9922    * @param {Function} func The function to bind.
9923    * @param {*} [thisArg] The `this` binding of `func`.
9924    * @returns {Function} Returns the new bound function.
9925    */
9926   function createBindWrapper(func, thisArg) {
9927     var Ctor = createCtorWrapper(func);
9928
9929     function wrapper() {
9930       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
9931       return fn.apply(thisArg, arguments);
9932     }
9933     return wrapper;
9934   }
9935
9936   /**
9937    * Creates a `Set` cache object to optimize linear searches of large arrays.
9938    *
9939    * @private
9940    * @param {Array} [values] The values to cache.
9941    * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`.
9942    */
9943   var createCache = !(nativeCreate && Set) ? constant(null) : function(values) {
9944     return new SetCache(values);
9945   };
9946
9947   /**
9948    * Creates a function that produces an instance of `Ctor` regardless of
9949    * whether it was invoked as part of a `new` expression or by `call` or `apply`.
9950    *
9951    * @private
9952    * @param {Function} Ctor The constructor to wrap.
9953    * @returns {Function} Returns the new wrapped function.
9954    */
9955   function createCtorWrapper(Ctor) {
9956     return function() {
9957       // Use a `switch` statement to work with class constructors.
9958       // See https://people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-function-objects-call-thisargument-argumentslist
9959       // for more details.
9960       var args = arguments;
9961       switch (args.length) {
9962         case 0: return new Ctor;
9963         case 1: return new Ctor(args[0]);
9964         case 2: return new Ctor(args[0], args[1]);
9965         case 3: return new Ctor(args[0], args[1], args[2]);
9966         case 4: return new Ctor(args[0], args[1], args[2], args[3]);
9967         case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
9968       }
9969       var thisBinding = baseCreate(Ctor.prototype),
9970           result = Ctor.apply(thisBinding, args);
9971
9972       // Mimic the constructor's `return` behavior.
9973       // See https://es5.github.io/#x13.2.2 for more details.
9974       return isObject(result) ? result : thisBinding;
9975     };
9976   }
9977
9978   /**
9979    * Creates a `_.find` or `_.findLast` function.
9980    *
9981    * @private
9982    * @param {Function} eachFunc The function to iterate over a collection.
9983    * @param {boolean} [fromRight] Specify iterating from right to left.
9984    * @returns {Function} Returns the new find function.
9985    */
9986   function createFind(eachFunc, fromRight) {
9987     return function(collection, predicate, thisArg) {
9988       predicate = getCallback(predicate, thisArg, 3);
9989       if (isArray(collection)) {
9990         var index = baseFindIndex(collection, predicate, fromRight);
9991         return index > -1 ? collection[index] : undefined;
9992       }
9993       return baseFind(collection, predicate, eachFunc);
9994     };
9995   }
9996
9997   /**
9998    * Creates a function for `_.forEach` or `_.forEachRight`.
9999    *
10000    * @private
10001    * @param {Function} arrayFunc The function to iterate over an array.
10002    * @param {Function} eachFunc The function to iterate over a collection.
10003    * @returns {Function} Returns the new each function.
10004    */
10005   function createForEach(arrayFunc, eachFunc) {
10006     return function(collection, iteratee, thisArg) {
10007       return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
10008         ? arrayFunc(collection, iteratee)
10009         : eachFunc(collection, bindCallback(iteratee, thisArg, 3));
10010     };
10011   }
10012
10013   /**
10014    * Creates a function for `_.forOwn` or `_.forOwnRight`.
10015    *
10016    * @private
10017    * @param {Function} objectFunc The function to iterate over an object.
10018    * @returns {Function} Returns the new each function.
10019    */
10020   function createForOwn(objectFunc) {
10021     return function(object, iteratee, thisArg) {
10022       if (typeof iteratee != 'function' || thisArg !== undefined) {
10023         iteratee = bindCallback(iteratee, thisArg, 3);
10024       }
10025       return objectFunc(object, iteratee);
10026     };
10027   }
10028
10029   /**
10030    * Creates a function for `_.reduce` or `_.reduceRight`.
10031    *
10032    * @private
10033    * @param {Function} arrayFunc The function to iterate over an array.
10034    * @param {Function} eachFunc The function to iterate over a collection.
10035    * @returns {Function} Returns the new each function.
10036    */
10037   function createReduce(arrayFunc, eachFunc) {
10038     return function(collection, iteratee, accumulator, thisArg) {
10039       var initFromArray = arguments.length < 3;
10040       return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
10041         ? arrayFunc(collection, iteratee, accumulator, initFromArray)
10042         : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc);
10043     };
10044   }
10045
10046   /**
10047    * Creates a function that wraps `func` and invokes it with optional `this`
10048    * binding of, partial application, and currying.
10049    *
10050    * @private
10051    * @param {Function|string} func The function or method name to reference.
10052    * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
10053    * @param {*} [thisArg] The `this` binding of `func`.
10054    * @param {Array} [partials] The arguments to prepend to those provided to the new function.
10055    * @param {Array} [holders] The `partials` placeholder indexes.
10056    * @param {Array} [partialsRight] The arguments to append to those provided to the new function.
10057    * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
10058    * @param {Array} [argPos] The argument positions of the new function.
10059    * @param {number} [ary] The arity cap of `func`.
10060    * @param {number} [arity] The arity of `func`.
10061    * @returns {Function} Returns the new wrapped function.
10062    */
10063   function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
10064     var isAry = bitmask & ARY_FLAG,
10065         isBind = bitmask & BIND_FLAG,
10066         isBindKey = bitmask & BIND_KEY_FLAG,
10067         isCurry = bitmask & CURRY_FLAG,
10068         isCurryBound = bitmask & CURRY_BOUND_FLAG,
10069         isCurryRight = bitmask & CURRY_RIGHT_FLAG,
10070         Ctor = isBindKey ? null : createCtorWrapper(func);
10071
10072     function wrapper() {
10073       // Avoid `arguments` object use disqualifying optimizations by
10074       // converting it to an array before providing it to other functions.
10075       var length = arguments.length,
10076           index = length,
10077           args = Array(length);
10078
10079       while (index--) {
10080         args[index] = arguments[index];
10081       }
10082       if (partials) {
10083         args = composeArgs(args, partials, holders);
10084       }
10085       if (partialsRight) {
10086         args = composeArgsRight(args, partialsRight, holdersRight);
10087       }
10088       if (isCurry || isCurryRight) {
10089         var placeholder = wrapper.placeholder,
10090             argsHolders = replaceHolders(args, placeholder);
10091
10092         length -= argsHolders.length;
10093         if (length < arity) {
10094           var newArgPos = argPos ? arrayCopy(argPos) : null,
10095               newArity = nativeMax(arity - length, 0),
10096               newsHolders = isCurry ? argsHolders : null,
10097               newHoldersRight = isCurry ? null : argsHolders,
10098               newPartials = isCurry ? args : null,
10099               newPartialsRight = isCurry ? null : args;
10100
10101           bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
10102           bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
10103
10104           if (!isCurryBound) {
10105             bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
10106           }
10107           var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity],
10108               result = createHybridWrapper.apply(undefined, newData);
10109
10110           if (isLaziable(func)) {
10111             setData(result, newData);
10112           }
10113           result.placeholder = placeholder;
10114           return result;
10115         }
10116       }
10117       var thisBinding = isBind ? thisArg : this,
10118           fn = isBindKey ? thisBinding[func] : func;
10119
10120       if (argPos) {
10121         args = reorder(args, argPos);
10122       }
10123       if (isAry && ary < args.length) {
10124         args.length = ary;
10125       }
10126       if (this && this !== root && this instanceof wrapper) {
10127         fn = Ctor || createCtorWrapper(func);
10128       }
10129       return fn.apply(thisBinding, args);
10130     }
10131     return wrapper;
10132   }
10133
10134   /**
10135    * Creates a function that wraps `func` and invokes it with the optional `this`
10136    * binding of `thisArg` and the `partials` prepended to those provided to
10137    * the wrapper.
10138    *
10139    * @private
10140    * @param {Function} func The function to partially apply arguments to.
10141    * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
10142    * @param {*} thisArg The `this` binding of `func`.
10143    * @param {Array} partials The arguments to prepend to those provided to the new function.
10144    * @returns {Function} Returns the new bound function.
10145    */
10146   function createPartialWrapper(func, bitmask, thisArg, partials) {
10147     var isBind = bitmask & BIND_FLAG,
10148         Ctor = createCtorWrapper(func);
10149
10150     function wrapper() {
10151       // Avoid `arguments` object use disqualifying optimizations by
10152       // converting it to an array before providing it `func`.
10153       var argsIndex = -1,
10154           argsLength = arguments.length,
10155           leftIndex = -1,
10156           leftLength = partials.length,
10157           args = Array(argsLength + leftLength);
10158
10159       while (++leftIndex < leftLength) {
10160         args[leftIndex] = partials[leftIndex];
10161       }
10162       while (argsLength--) {
10163         args[leftIndex++] = arguments[++argsIndex];
10164       }
10165       var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
10166       return fn.apply(isBind ? thisArg : this, args);
10167     }
10168     return wrapper;
10169   }
10170
10171   /**
10172    * Creates a function that either curries or invokes `func` with optional
10173    * `this` binding and partially applied arguments.
10174    *
10175    * @private
10176    * @param {Function|string} func The function or method name to reference.
10177    * @param {number} bitmask The bitmask of flags.
10178    *  The bitmask may be composed of the following flags:
10179    *     1 - `_.bind`
10180    *     2 - `_.bindKey`
10181    *     4 - `_.curry` or `_.curryRight` of a bound function
10182    *     8 - `_.curry`
10183    *    16 - `_.curryRight`
10184    *    32 - `_.partial`
10185    *    64 - `_.partialRight`
10186    *   128 - `_.rearg`
10187    *   256 - `_.ary`
10188    * @param {*} [thisArg] The `this` binding of `func`.
10189    * @param {Array} [partials] The arguments to be partially applied.
10190    * @param {Array} [holders] The `partials` placeholder indexes.
10191    * @param {Array} [argPos] The argument positions of the new function.
10192    * @param {number} [ary] The arity cap of `func`.
10193    * @param {number} [arity] The arity of `func`.
10194    * @returns {Function} Returns the new wrapped function.
10195    */
10196   function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
10197     var isBindKey = bitmask & BIND_KEY_FLAG;
10198     if (!isBindKey && typeof func != 'function') {
10199       throw new TypeError(FUNC_ERROR_TEXT);
10200     }
10201     var length = partials ? partials.length : 0;
10202     if (!length) {
10203       bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
10204       partials = holders = null;
10205     }
10206     length -= (holders ? holders.length : 0);
10207     if (bitmask & PARTIAL_RIGHT_FLAG) {
10208       var partialsRight = partials,
10209           holdersRight = holders;
10210
10211       partials = holders = null;
10212     }
10213     var data = isBindKey ? null : getData(func),
10214         newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
10215
10216     if (data) {
10217       mergeData(newData, data);
10218       bitmask = newData[1];
10219       arity = newData[9];
10220     }
10221     newData[9] = arity == null
10222       ? (isBindKey ? 0 : func.length)
10223       : (nativeMax(arity - length, 0) || 0);
10224
10225     if (bitmask == BIND_FLAG) {
10226       var result = createBindWrapper(newData[0], newData[2]);
10227     } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) {
10228       result = createPartialWrapper.apply(undefined, newData);
10229     } else {
10230       result = createHybridWrapper.apply(undefined, newData);
10231     }
10232     var setter = data ? baseSetData : setData;
10233     return setter(result, newData);
10234   }
10235
10236   /**
10237    * A specialized version of `baseIsEqualDeep` for arrays with support for
10238    * partial deep comparisons.
10239    *
10240    * @private
10241    * @param {Array} array The array to compare.
10242    * @param {Array} other The other array to compare.
10243    * @param {Function} equalFunc The function to determine equivalents of values.
10244    * @param {Function} [customizer] The function to customize comparing arrays.
10245    * @param {boolean} [isLoose] Specify performing partial comparisons.
10246    * @param {Array} [stackA] Tracks traversed `value` objects.
10247    * @param {Array} [stackB] Tracks traversed `other` objects.
10248    * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
10249    */
10250   function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) {
10251     var index = -1,
10252         arrLength = array.length,
10253         othLength = other.length;
10254
10255     if (arrLength != othLength && !(isLoose && othLength > arrLength)) {
10256       return false;
10257     }
10258     // Ignore non-index properties.
10259     while (++index < arrLength) {
10260       var arrValue = array[index],
10261           othValue = other[index],
10262           result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined;
10263
10264       if (result !== undefined) {
10265         if (result) {
10266           continue;
10267         }
10268         return false;
10269       }
10270       // Recursively compare arrays (susceptible to call stack limits).
10271       if (isLoose) {
10272         if (!arraySome(other, function(othValue) {
10273               return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB);
10274             })) {
10275           return false;
10276         }
10277       } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) {
10278         return false;
10279       }
10280     }
10281     return true;
10282   }
10283
10284   /**
10285    * A specialized version of `baseIsEqualDeep` for comparing objects of
10286    * the same `toStringTag`.
10287    *
10288    * **Note:** This function only supports comparing values with tags of
10289    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
10290    *
10291    * @private
10292    * @param {Object} value The object to compare.
10293    * @param {Object} other The other object to compare.
10294    * @param {string} tag The `toStringTag` of the objects to compare.
10295    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
10296    */
10297   function equalByTag(object, other, tag) {
10298     switch (tag) {
10299       case boolTag:
10300       case dateTag:
10301         // Coerce dates and booleans to numbers, dates to milliseconds and booleans
10302         // to `1` or `0` treating invalid dates coerced to `NaN` as not equal.
10303         return +object == +other;
10304
10305       case errorTag:
10306         return object.name == other.name && object.message == other.message;
10307
10308       case numberTag:
10309         // Treat `NaN` vs. `NaN` as equal.
10310         return (object != +object)
10311           ? other != +other
10312           : object == +other;
10313
10314       case regexpTag:
10315       case stringTag:
10316         // Coerce regexes to strings and treat strings primitives and string
10317         // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details.
10318         return object == (other + '');
10319     }
10320     return false;
10321   }
10322
10323   /**
10324    * A specialized version of `baseIsEqualDeep` for objects with support for
10325    * partial deep comparisons.
10326    *
10327    * @private
10328    * @param {Object} object The object to compare.
10329    * @param {Object} other The other object to compare.
10330    * @param {Function} equalFunc The function to determine equivalents of values.
10331    * @param {Function} [customizer] The function to customize comparing values.
10332    * @param {boolean} [isLoose] Specify performing partial comparisons.
10333    * @param {Array} [stackA] Tracks traversed `value` objects.
10334    * @param {Array} [stackB] Tracks traversed `other` objects.
10335    * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
10336    */
10337   function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
10338     var objProps = keys(object),
10339         objLength = objProps.length,
10340         othProps = keys(other),
10341         othLength = othProps.length;
10342
10343     if (objLength != othLength && !isLoose) {
10344       return false;
10345     }
10346     var index = objLength;
10347     while (index--) {
10348       var key = objProps[index];
10349       if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) {
10350         return false;
10351       }
10352     }
10353     var skipCtor = isLoose;
10354     while (++index < objLength) {
10355       key = objProps[index];
10356       var objValue = object[key],
10357           othValue = other[key],
10358           result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined;
10359
10360       // Recursively compare objects (susceptible to call stack limits).
10361       if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) {
10362         return false;
10363       }
10364       skipCtor || (skipCtor = key == 'constructor');
10365     }
10366     if (!skipCtor) {
10367       var objCtor = object.constructor,
10368           othCtor = other.constructor;
10369
10370       // Non `Object` object instances with different constructors are not equal.
10371       if (objCtor != othCtor &&
10372           ('constructor' in object && 'constructor' in other) &&
10373           !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
10374             typeof othCtor == 'function' && othCtor instanceof othCtor)) {
10375         return false;
10376       }
10377     }
10378     return true;
10379   }
10380
10381   /**
10382    * Gets the appropriate "callback" function. If the `_.callback` method is
10383    * customized this function returns the custom method, otherwise it returns
10384    * the `baseCallback` function. If arguments are provided the chosen function
10385    * is invoked with them and its result is returned.
10386    *
10387    * @private
10388    * @returns {Function} Returns the chosen function or its result.
10389    */
10390   function getCallback(func, thisArg, argCount) {
10391     var result = lodash.callback || callback;
10392     result = result === callback ? baseCallback : result;
10393     return argCount ? result(func, thisArg, argCount) : result;
10394   }
10395
10396   /**
10397    * Gets metadata for `func`.
10398    *
10399    * @private
10400    * @param {Function} func The function to query.
10401    * @returns {*} Returns the metadata for `func`.
10402    */
10403   var getData = !metaMap ? noop : function(func) {
10404     return metaMap.get(func);
10405   };
10406
10407   /**
10408    * Gets the name of `func`.
10409    *
10410    * @private
10411    * @param {Function} func The function to query.
10412    * @returns {string} Returns the function name.
10413    */
10414   function getFuncName(func) {
10415     var result = func.name,
10416         array = realNames[result],
10417         length = array ? array.length : 0;
10418
10419     while (length--) {
10420       var data = array[length],
10421           otherFunc = data.func;
10422       if (otherFunc == null || otherFunc == func) {
10423         return data.name;
10424       }
10425     }
10426     return result;
10427   }
10428
10429   /**
10430    * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
10431    * customized this function returns the custom method, otherwise it returns
10432    * the `baseIndexOf` function. If arguments are provided the chosen function
10433    * is invoked with them and its result is returned.
10434    *
10435    * @private
10436    * @returns {Function|number} Returns the chosen function or its result.
10437    */
10438   function getIndexOf(collection, target, fromIndex) {
10439     var result = lodash.indexOf || indexOf;
10440     result = result === indexOf ? baseIndexOf : result;
10441     return collection ? result(collection, target, fromIndex) : result;
10442   }
10443
10444   /**
10445    * Gets the "length" property value of `object`.
10446    *
10447    * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792)
10448    * that affects Safari on at least iOS 8.1-8.3 ARM64.
10449    *
10450    * @private
10451    * @param {Object} object The object to query.
10452    * @returns {*} Returns the "length" value.
10453    */
10454   var getLength = baseProperty('length');
10455
10456   /**
10457    * Gets the propery names, values, and compare flags of `object`.
10458    *
10459    * @private
10460    * @param {Object} object The object to query.
10461    * @returns {Array} Returns the match data of `object`.
10462    */
10463   function getMatchData(object) {
10464     var result = pairs(object),
10465         length = result.length;
10466
10467     while (length--) {
10468       result[length][2] = isStrictComparable(result[length][1]);
10469     }
10470     return result;
10471   }
10472
10473   /**
10474    * Gets the native function at `key` of `object`.
10475    *
10476    * @private
10477    * @param {Object} object The object to query.
10478    * @param {string} key The key of the method to get.
10479    * @returns {*} Returns the function if it's native, else `undefined`.
10480    */
10481   function getNative(object, key) {
10482     var value = object == null ? undefined : object[key];
10483     return isNative(value) ? value : undefined;
10484   }
10485
10486   /**
10487    * Gets the view, applying any `transforms` to the `start` and `end` positions.
10488    *
10489    * @private
10490    * @param {number} start The start of the view.
10491    * @param {number} end The end of the view.
10492    * @param {Array} [transforms] The transformations to apply to the view.
10493    * @returns {Object} Returns an object containing the `start` and `end`
10494    *  positions of the view.
10495    */
10496   function getView(start, end, transforms) {
10497     var index = -1,
10498         length = transforms ? transforms.length : 0;
10499
10500     while (++index < length) {
10501       var data = transforms[index],
10502           size = data.size;
10503
10504       switch (data.type) {
10505         case 'drop':      start += size; break;
10506         case 'dropRight': end -= size; break;
10507         case 'take':      end = nativeMin(end, start + size); break;
10508         case 'takeRight': start = nativeMax(start, end - size); break;
10509       }
10510     }
10511     return { 'start': start, 'end': end };
10512   }
10513
10514   /**
10515    * Initializes an array clone.
10516    *
10517    * @private
10518    * @param {Array} array The array to clone.
10519    * @returns {Array} Returns the initialized clone.
10520    */
10521   function initCloneArray(array) {
10522     var length = array.length,
10523         result = new array.constructor(length);
10524
10525     // Add array properties assigned by `RegExp#exec`.
10526     if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
10527       result.index = array.index;
10528       result.input = array.input;
10529     }
10530     return result;
10531   }
10532
10533   /**
10534    * Initializes an object clone.
10535    *
10536    * @private
10537    * @param {Object} object The object to clone.
10538    * @returns {Object} Returns the initialized clone.
10539    */
10540   function initCloneObject(object) {
10541     var Ctor = object.constructor;
10542     if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) {
10543       Ctor = Object;
10544     }
10545     return new Ctor;
10546   }
10547
10548   /**
10549    * Initializes an object clone based on its `toStringTag`.
10550    *
10551    * **Note:** This function only supports cloning values with tags of
10552    * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
10553    *
10554    * @private
10555    * @param {Object} object The object to clone.
10556    * @param {string} tag The `toStringTag` of the object to clone.
10557    * @param {boolean} [isDeep] Specify a deep clone.
10558    * @returns {Object} Returns the initialized clone.
10559    */
10560   function initCloneByTag(object, tag, isDeep) {
10561     var Ctor = object.constructor;
10562     switch (tag) {
10563       case arrayBufferTag:
10564         return bufferClone(object);
10565
10566       case boolTag:
10567       case dateTag:
10568         return new Ctor(+object);
10569
10570       case float32Tag: case float64Tag:
10571       case int8Tag: case int16Tag: case int32Tag:
10572       case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
10573         // Safari 5 mobile incorrectly has `Object` as the constructor of typed arrays.
10574         if (Ctor instanceof Ctor) {
10575           Ctor = ctorByTag[tag];
10576         }
10577         var buffer = object.buffer;
10578         return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length);
10579
10580       case numberTag:
10581       case stringTag:
10582         return new Ctor(object);
10583
10584       case regexpTag:
10585         var result = new Ctor(object.source, reFlags.exec(object));
10586         result.lastIndex = object.lastIndex;
10587     }
10588     return result;
10589   }
10590
10591   /**
10592    * Checks if `value` is array-like.
10593    *
10594    * @private
10595    * @param {*} value The value to check.
10596    * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
10597    */
10598   function isArrayLike(value) {
10599     return value != null && isLength(getLength(value));
10600   }
10601
10602   /**
10603    * Checks if `value` is a valid array-like index.
10604    *
10605    * @private
10606    * @param {*} value The value to check.
10607    * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
10608    * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
10609    */
10610   function isIndex(value, length) {
10611     value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1;
10612     length = length == null ? MAX_SAFE_INTEGER : length;
10613     return value > -1 && value % 1 == 0 && value < length;
10614   }
10615
10616   /**
10617    * Checks if the provided arguments are from an iteratee call.
10618    *
10619    * @private
10620    * @param {*} value The potential iteratee value argument.
10621    * @param {*} index The potential iteratee index or key argument.
10622    * @param {*} object The potential iteratee object argument.
10623    * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`.
10624    */
10625   function isIterateeCall(value, index, object) {
10626     if (!isObject(object)) {
10627       return false;
10628     }
10629     var type = typeof index;
10630     if (type == 'number'
10631         ? (isArrayLike(object) && isIndex(index, object.length))
10632         : (type == 'string' && index in object)) {
10633       var other = object[index];
10634       return value === value ? (value === other) : (other !== other);
10635     }
10636     return false;
10637   }
10638
10639   /**
10640    * Checks if `value` is a property name and not a property path.
10641    *
10642    * @private
10643    * @param {*} value The value to check.
10644    * @param {Object} [object] The object to query keys on.
10645    * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
10646    */
10647   function isKey(value, object) {
10648     var type = typeof value;
10649     if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') {
10650       return true;
10651     }
10652     if (isArray(value)) {
10653       return false;
10654     }
10655     var result = !reIsDeepProp.test(value);
10656     return result || (object != null && value in toObject(object));
10657   }
10658
10659   /**
10660    * Checks if `func` has a lazy counterpart.
10661    *
10662    * @private
10663    * @param {Function} func The function to check.
10664    * @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`.
10665    */
10666   function isLaziable(func) {
10667     var funcName = getFuncName(func);
10668     if (!(funcName in LazyWrapper.prototype)) {
10669       return false;
10670     }
10671     var other = lodash[funcName];
10672     if (func === other) {
10673       return true;
10674     }
10675     var data = getData(other);
10676     return !!data && func === data[0];
10677   }
10678
10679   /**
10680    * Checks if `value` is a valid array-like length.
10681    *
10682    * **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength).
10683    *
10684    * @private
10685    * @param {*} value The value to check.
10686    * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
10687    */
10688   function isLength(value) {
10689     return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
10690   }
10691
10692   /**
10693    * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
10694    *
10695    * @private
10696    * @param {*} value The value to check.
10697    * @returns {boolean} Returns `true` if `value` if suitable for strict
10698    *  equality comparisons, else `false`.
10699    */
10700   function isStrictComparable(value) {
10701     return value === value && !isObject(value);
10702   }
10703
10704   /**
10705    * Merges the function metadata of `source` into `data`.
10706    *
10707    * Merging metadata reduces the number of wrappers required to invoke a function.
10708    * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
10709    * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg`
10710    * augment function arguments, making the order in which they are executed important,
10711    * preventing the merging of metadata. However, we make an exception for a safe
10712    * common case where curried functions have `_.ary` and or `_.rearg` applied.
10713    *
10714    * @private
10715    * @param {Array} data The destination metadata.
10716    * @param {Array} source The source metadata.
10717    * @returns {Array} Returns `data`.
10718    */
10719   function mergeData(data, source) {
10720     var bitmask = data[1],
10721         srcBitmask = source[1],
10722         newBitmask = bitmask | srcBitmask,
10723         isCommon = newBitmask < ARY_FLAG;
10724
10725     var isCombo =
10726       (srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) ||
10727       (srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) ||
10728       (srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG);
10729
10730     // Exit early if metadata can't be merged.
10731     if (!(isCommon || isCombo)) {
10732       return data;
10733     }
10734     // Use source `thisArg` if available.
10735     if (srcBitmask & BIND_FLAG) {
10736       data[2] = source[2];
10737       // Set when currying a bound function.
10738       newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG;
10739     }
10740     // Compose partial arguments.
10741     var value = source[3];
10742     if (value) {
10743       var partials = data[3];
10744       data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value);
10745       data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]);
10746     }
10747     // Compose partial right arguments.
10748     value = source[5];
10749     if (value) {
10750       partials = data[5];
10751       data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value);
10752       data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]);
10753     }
10754     // Use source `argPos` if available.
10755     value = source[7];
10756     if (value) {
10757       data[7] = arrayCopy(value);
10758     }
10759     // Use source `ary` if it's smaller.
10760     if (srcBitmask & ARY_FLAG) {
10761       data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
10762     }
10763     // Use source `arity` if one is not provided.
10764     if (data[9] == null) {
10765       data[9] = source[9];
10766     }
10767     // Use source `func` and merge bitmasks.
10768     data[0] = source[0];
10769     data[1] = newBitmask;
10770
10771     return data;
10772   }
10773
10774   /**
10775    * A specialized version of `_.pick` which picks `object` properties specified
10776    * by `props`.
10777    *
10778    * @private
10779    * @param {Object} object The source object.
10780    * @param {string[]} props The property names to pick.
10781    * @returns {Object} Returns the new object.
10782    */
10783   function pickByArray(object, props) {
10784     object = toObject(object);
10785
10786     var index = -1,
10787         length = props.length,
10788         result = {};
10789
10790     while (++index < length) {
10791       var key = props[index];
10792       if (key in object) {
10793         result[key] = object[key];
10794       }
10795     }
10796     return result;
10797   }
10798
10799   /**
10800    * A specialized version of `_.pick` which picks `object` properties `predicate`
10801    * returns truthy for.
10802    *
10803    * @private
10804    * @param {Object} object The source object.
10805    * @param {Function} predicate The function invoked per iteration.
10806    * @returns {Object} Returns the new object.
10807    */
10808   function pickByCallback(object, predicate) {
10809     var result = {};
10810     baseForIn(object, function(value, key, object) {
10811       if (predicate(value, key, object)) {
10812         result[key] = value;
10813       }
10814     });
10815     return result;
10816   }
10817
10818   /**
10819    * Reorder `array` according to the specified indexes where the element at
10820    * the first index is assigned as the first element, the element at
10821    * the second index is assigned as the second element, and so on.
10822    *
10823    * @private
10824    * @param {Array} array The array to reorder.
10825    * @param {Array} indexes The arranged array indexes.
10826    * @returns {Array} Returns `array`.
10827    */
10828   function reorder(array, indexes) {
10829     var arrLength = array.length,
10830         length = nativeMin(indexes.length, arrLength),
10831         oldArray = arrayCopy(array);
10832
10833     while (length--) {
10834       var index = indexes[length];
10835       array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
10836     }
10837     return array;
10838   }
10839
10840   /**
10841    * Sets metadata for `func`.
10842    *
10843    * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
10844    * period of time, it will trip its breaker and transition to an identity function
10845    * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070)
10846    * for more details.
10847    *
10848    * @private
10849    * @param {Function} func The function to associate metadata with.
10850    * @param {*} data The metadata.
10851    * @returns {Function} Returns `func`.
10852    */
10853   var setData = (function() {
10854     var count = 0,
10855         lastCalled = 0;
10856
10857     return function(key, value) {
10858       var stamp = now(),
10859           remaining = HOT_SPAN - (stamp - lastCalled);
10860
10861       lastCalled = stamp;
10862       if (remaining > 0) {
10863         if (++count >= HOT_COUNT) {
10864           return key;
10865         }
10866       } else {
10867         count = 0;
10868       }
10869       return baseSetData(key, value);
10870     };
10871   }());
10872
10873   /**
10874    * A fallback implementation of `_.isPlainObject` which checks if `value`
10875    * is an object created by the `Object` constructor or has a `[[Prototype]]`
10876    * of `null`.
10877    *
10878    * @private
10879    * @param {*} value The value to check.
10880    * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
10881    */
10882   function shimIsPlainObject(value) {
10883     var Ctor,
10884         support = lodash.support;
10885
10886     // Exit early for non `Object` objects.
10887     if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isHostObject(value)) ||
10888         (!hasOwnProperty.call(value, 'constructor') &&
10889           (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor))) ||
10890         (!support.argsTag && isArguments(value))) {
10891       return false;
10892     }
10893     // IE < 9 iterates inherited properties before own properties. If the first
10894     // iterated property is an object's own property then there are no inherited
10895     // enumerable properties.
10896     var result;
10897     if (support.ownLast) {
10898       baseForIn(value, function(subValue, key, object) {
10899         result = hasOwnProperty.call(object, key);
10900         return false;
10901       });
10902       return result !== false;
10903     }
10904     // In most environments an object's own properties are iterated before
10905     // its inherited properties. If the last iterated property is an object's
10906     // own property then there are no inherited enumerable properties.
10907     baseForIn(value, function(subValue, key) {
10908       result = key;
10909     });
10910     return result === undefined || hasOwnProperty.call(value, result);
10911   }
10912
10913   /**
10914    * A fallback implementation of `Object.keys` which creates an array of the
10915    * own enumerable property names of `object`.
10916    *
10917    * @private
10918    * @param {Object} object The object to query.
10919    * @returns {Array} Returns the array of property names.
10920    */
10921   function shimKeys(object) {
10922     var props = keysIn(object),
10923         propsLength = props.length,
10924         length = propsLength && object.length;
10925
10926     var allowIndexes = !!length && isLength(length) &&
10927       (isArray(object) || isArguments(object) || isString(object));
10928
10929     var index = -1,
10930         result = [];
10931
10932     while (++index < propsLength) {
10933       var key = props[index];
10934       if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) {
10935         result.push(key);
10936       }
10937     }
10938     return result;
10939   }
10940
10941   /**
10942    * Converts `value` to an object if it's not one.
10943    *
10944    * @private
10945    * @param {*} value The value to process.
10946    * @returns {Object} Returns the object.
10947    */
10948   function toObject(value) {
10949     if (lodash.support.unindexedChars && isString(value)) {
10950       var index = -1,
10951           length = value.length,
10952           result = Object(value);
10953
10954       while (++index < length) {
10955         result[index] = value.charAt(index);
10956       }
10957       return result;
10958     }
10959     return isObject(value) ? value : Object(value);
10960   }
10961
10962   /**
10963    * Converts `value` to property path array if it's not one.
10964    *
10965    * @private
10966    * @param {*} value The value to process.
10967    * @returns {Array} Returns the property path array.
10968    */
10969   function toPath(value) {
10970     if (isArray(value)) {
10971       return value;
10972     }
10973     var result = [];
10974     baseToString(value).replace(rePropName, function(match, number, quote, string) {
10975       result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
10976     });
10977     return result;
10978   }
10979
10980   /**
10981    * Creates a clone of `wrapper`.
10982    *
10983    * @private
10984    * @param {Object} wrapper The wrapper to clone.
10985    * @returns {Object} Returns the cloned wrapper.
10986    */
10987   function wrapperClone(wrapper) {
10988     return wrapper instanceof LazyWrapper
10989       ? wrapper.clone()
10990       : new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__, arrayCopy(wrapper.__actions__));
10991   }
10992
10993   /*------------------------------------------------------------------------*/
10994
10995   /**
10996    * Creates an array of elements split into groups the length of `size`.
10997    * If `collection` can't be split evenly, the final chunk will be the remaining
10998    * elements.
10999    *
11000    * @static
11001    * @memberOf _
11002    * @category Array
11003    * @param {Array} array The array to process.
11004    * @param {number} [size=1] The length of each chunk.
11005    * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
11006    * @returns {Array} Returns the new array containing chunks.
11007    * @example
11008    *
11009    * _.chunk(['a', 'b', 'c', 'd'], 2);
11010    * // => [['a', 'b'], ['c', 'd']]
11011    *
11012    * _.chunk(['a', 'b', 'c', 'd'], 3);
11013    * // => [['a', 'b', 'c'], ['d']]
11014    */
11015   function chunk(array, size, guard) {
11016     if (guard ? isIterateeCall(array, size, guard) : size == null) {
11017       size = 1;
11018     } else {
11019       size = nativeMax(+size || 1, 1);
11020     }
11021     var index = 0,
11022         length = array ? array.length : 0,
11023         resIndex = -1,
11024         result = Array(ceil(length / size));
11025
11026     while (index < length) {
11027       result[++resIndex] = baseSlice(array, index, (index += size));
11028     }
11029     return result;
11030   }
11031
11032   /**
11033    * Creates an array with all falsey values removed. The values `false`, `null`,
11034    * `0`, `""`, `undefined`, and `NaN` are falsey.
11035    *
11036    * @static
11037    * @memberOf _
11038    * @category Array
11039    * @param {Array} array The array to compact.
11040    * @returns {Array} Returns the new array of filtered values.
11041    * @example
11042    *
11043    * _.compact([0, 1, false, 2, '', 3]);
11044    * // => [1, 2, 3]
11045    */
11046   function compact(array) {
11047     var index = -1,
11048         length = array ? array.length : 0,
11049         resIndex = -1,
11050         result = [];
11051
11052     while (++index < length) {
11053       var value = array[index];
11054       if (value) {
11055         result[++resIndex] = value;
11056       }
11057     }
11058     return result;
11059   }
11060
11061   /**
11062    * Creates an array of unique `array` values not included in the other
11063    * provided arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11064    * for equality comparisons.
11065    *
11066    * @static
11067    * @memberOf _
11068    * @category Array
11069    * @param {Array} array The array to inspect.
11070    * @param {...Array} [values] The arrays of values to exclude.
11071    * @returns {Array} Returns the new array of filtered values.
11072    * @example
11073    *
11074    * _.difference([1, 2, 3], [4, 2]);
11075    * // => [1, 3]
11076    */
11077   var difference = restParam(function(array, values) {
11078     return isArrayLike(array)
11079       ? baseDifference(array, baseFlatten(values, false, true))
11080       : [];
11081   });
11082
11083   /**
11084    * Gets the first element of `array`.
11085    *
11086    * @static
11087    * @memberOf _
11088    * @alias head
11089    * @category Array
11090    * @param {Array} array The array to query.
11091    * @returns {*} Returns the first element of `array`.
11092    * @example
11093    *
11094    * _.first([1, 2, 3]);
11095    * // => 1
11096    *
11097    * _.first([]);
11098    * // => undefined
11099    */
11100   function first(array) {
11101     return array ? array[0] : undefined;
11102   }
11103
11104   /**
11105    * Flattens a nested array. If `isDeep` is `true` the array is recursively
11106    * flattened, otherwise it is only flattened a single level.
11107    *
11108    * @static
11109    * @memberOf _
11110    * @category Array
11111    * @param {Array} array The array to flatten.
11112    * @param {boolean} [isDeep] Specify a deep flatten.
11113    * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
11114    * @returns {Array} Returns the new flattened array.
11115    * @example
11116    *
11117    * _.flatten([1, [2, 3, [4]]]);
11118    * // => [1, 2, 3, [4]]
11119    *
11120    * // using `isDeep`
11121    * _.flatten([1, [2, 3, [4]]], true);
11122    * // => [1, 2, 3, 4]
11123    */
11124   function flatten(array, isDeep, guard) {
11125     var length = array ? array.length : 0;
11126     if (guard && isIterateeCall(array, isDeep, guard)) {
11127       isDeep = false;
11128     }
11129     return length ? baseFlatten(array, isDeep) : [];
11130   }
11131
11132   /**
11133    * Gets the index at which the first occurrence of `value` is found in `array`
11134    * using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11135    * for equality comparisons. If `fromIndex` is negative, it is used as the offset
11136    * from the end of `array`. If `array` is sorted providing `true` for `fromIndex`
11137    * performs a faster binary search.
11138    *
11139    * @static
11140    * @memberOf _
11141    * @category Array
11142    * @param {Array} array The array to search.
11143    * @param {*} value The value to search for.
11144    * @param {boolean|number} [fromIndex=0] The index to search from or `true`
11145    *  to perform a binary search on a sorted array.
11146    * @returns {number} Returns the index of the matched value, else `-1`.
11147    * @example
11148    *
11149    * _.indexOf([1, 2, 1, 2], 2);
11150    * // => 1
11151    *
11152    * // using `fromIndex`
11153    * _.indexOf([1, 2, 1, 2], 2, 2);
11154    * // => 3
11155    *
11156    * // performing a binary search
11157    * _.indexOf([1, 1, 2, 2], 2, true);
11158    * // => 2
11159    */
11160   function indexOf(array, value, fromIndex) {
11161     var length = array ? array.length : 0;
11162     if (!length) {
11163       return -1;
11164     }
11165     if (typeof fromIndex == 'number') {
11166       fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex;
11167     } else if (fromIndex) {
11168       var index = binaryIndex(array, value),
11169           other = array[index];
11170
11171       if (value === value ? (value === other) : (other !== other)) {
11172         return index;
11173       }
11174       return -1;
11175     }
11176     return baseIndexOf(array, value, fromIndex || 0);
11177   }
11178
11179   /**
11180    * Creates an array of unique values that are included in all of the provided
11181    * arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11182    * for equality comparisons.
11183    *
11184    * @static
11185    * @memberOf _
11186    * @category Array
11187    * @param {...Array} [arrays] The arrays to inspect.
11188    * @returns {Array} Returns the new array of shared values.
11189    * @example
11190    * _.intersection([1, 2], [4, 2], [2, 1]);
11191    * // => [2]
11192    */
11193   var intersection = restParam(function(arrays) {
11194     var othLength = arrays.length,
11195         othIndex = othLength,
11196         caches = Array(length),
11197         indexOf = getIndexOf(),
11198         isCommon = indexOf == baseIndexOf,
11199         result = [];
11200
11201     while (othIndex--) {
11202       var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : [];
11203       caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null;
11204     }
11205     var array = arrays[0],
11206         index = -1,
11207         length = array ? array.length : 0,
11208         seen = caches[0];
11209
11210     outer:
11211     while (++index < length) {
11212       value = array[index];
11213       if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) {
11214         var othIndex = othLength;
11215         while (--othIndex) {
11216           var cache = caches[othIndex];
11217           if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) {
11218             continue outer;
11219           }
11220         }
11221         if (seen) {
11222           seen.push(value);
11223         }
11224         result.push(value);
11225       }
11226     }
11227     return result;
11228   });
11229
11230   /**
11231    * Gets the last element of `array`.
11232    *
11233    * @static
11234    * @memberOf _
11235    * @category Array
11236    * @param {Array} array The array to query.
11237    * @returns {*} Returns the last element of `array`.
11238    * @example
11239    *
11240    * _.last([1, 2, 3]);
11241    * // => 3
11242    */
11243   function last(array) {
11244     var length = array ? array.length : 0;
11245     return length ? array[length - 1] : undefined;
11246   }
11247
11248   /**
11249    * Creates an array of unique values, in order, from all of the provided arrays
11250    * using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11251    * for equality comparisons.
11252    *
11253    * @static
11254    * @memberOf _
11255    * @category Array
11256    * @param {...Array} [arrays] The arrays to inspect.
11257    * @returns {Array} Returns the new array of combined values.
11258    * @example
11259    *
11260    * _.union([1, 2], [4, 2], [2, 1]);
11261    * // => [1, 2, 4]
11262    */
11263   var union = restParam(function(arrays) {
11264     return baseUniq(baseFlatten(arrays, false, true));
11265   });
11266
11267   /**
11268    * Creates a duplicate-free version of an array, using
11269    * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11270    * for equality comparisons, in which only the first occurence of each element
11271    * is kept. Providing `true` for `isSorted` performs a faster search algorithm
11272    * for sorted arrays. If an iteratee function is provided it is invoked for
11273    * each element in the array to generate the criterion by which uniqueness
11274    * is computed. The `iteratee` is bound to `thisArg` and invoked with three
11275    * arguments: (value, index, array).
11276    *
11277    * If a property name is provided for `iteratee` the created `_.property`
11278    * style callback returns the property value of the given element.
11279    *
11280    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11281    * style callback returns `true` for elements that have a matching property
11282    * value, else `false`.
11283    *
11284    * If an object is provided for `iteratee` the created `_.matches` style
11285    * callback returns `true` for elements that have the properties of the given
11286    * object, else `false`.
11287    *
11288    * @static
11289    * @memberOf _
11290    * @alias unique
11291    * @category Array
11292    * @param {Array} array The array to inspect.
11293    * @param {boolean} [isSorted] Specify the array is sorted.
11294    * @param {Function|Object|string} [iteratee] The function invoked per iteration.
11295    * @param {*} [thisArg] The `this` binding of `iteratee`.
11296    * @returns {Array} Returns the new duplicate-value-free array.
11297    * @example
11298    *
11299    * _.uniq([2, 1, 2]);
11300    * // => [2, 1]
11301    *
11302    * // using `isSorted`
11303    * _.uniq([1, 1, 2], true);
11304    * // => [1, 2]
11305    *
11306    * // using an iteratee function
11307    * _.uniq([1, 2.5, 1.5, 2], function(n) {
11308    *   return this.floor(n);
11309    * }, Math);
11310    * // => [1, 2.5]
11311    *
11312    * // using the `_.property` callback shorthand
11313    * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
11314    * // => [{ 'x': 1 }, { 'x': 2 }]
11315    */
11316   function uniq(array, isSorted, iteratee, thisArg) {
11317     var length = array ? array.length : 0;
11318     if (!length) {
11319       return [];
11320     }
11321     if (isSorted != null && typeof isSorted != 'boolean') {
11322       thisArg = iteratee;
11323       iteratee = isIterateeCall(array, isSorted, thisArg) ? null : isSorted;
11324       isSorted = false;
11325     }
11326     var callback = getCallback();
11327     if (!(iteratee == null && callback === baseCallback)) {
11328       iteratee = callback(iteratee, thisArg, 3);
11329     }
11330     return (isSorted && getIndexOf() == baseIndexOf)
11331       ? sortedUniq(array, iteratee)
11332       : baseUniq(array, iteratee);
11333   }
11334
11335   /**
11336    * Creates an array excluding all provided values using
11337    * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11338    * for equality comparisons.
11339    *
11340    * @static
11341    * @memberOf _
11342    * @category Array
11343    * @param {Array} array The array to filter.
11344    * @param {...*} [values] The values to exclude.
11345    * @returns {Array} Returns the new array of filtered values.
11346    * @example
11347    *
11348    * _.without([1, 2, 1, 3], 1, 2);
11349    * // => [3]
11350    */
11351   var without = restParam(function(array, values) {
11352     return isArrayLike(array)
11353       ? baseDifference(array, values)
11354       : [];
11355   });
11356
11357   /*------------------------------------------------------------------------*/
11358
11359   /**
11360    * Creates a `lodash` object that wraps `value` with explicit method
11361    * chaining enabled.
11362    *
11363    * @static
11364    * @memberOf _
11365    * @category Chain
11366    * @param {*} value The value to wrap.
11367    * @returns {Object} Returns the new `lodash` wrapper instance.
11368    * @example
11369    *
11370    * var users = [
11371    *   { 'user': 'barney',  'age': 36 },
11372    *   { 'user': 'fred',    'age': 40 },
11373    *   { 'user': 'pebbles', 'age': 1 }
11374    * ];
11375    *
11376    * var youngest = _.chain(users)
11377    *   .sortBy('age')
11378    *   .map(function(chr) {
11379    *     return chr.user + ' is ' + chr.age;
11380    *   })
11381    *   .first()
11382    *   .value();
11383    * // => 'pebbles is 1'
11384    */
11385   function chain(value) {
11386     var result = lodash(value);
11387     result.__chain__ = true;
11388     return result;
11389   }
11390
11391   /**
11392    * This method invokes `interceptor` and returns `value`. The interceptor is
11393    * bound to `thisArg` and invoked with one argument; (value). The purpose of
11394    * this method is to "tap into" a method chain in order to perform operations
11395    * on intermediate results within the chain.
11396    *
11397    * @static
11398    * @memberOf _
11399    * @category Chain
11400    * @param {*} value The value to provide to `interceptor`.
11401    * @param {Function} interceptor The function to invoke.
11402    * @param {*} [thisArg] The `this` binding of `interceptor`.
11403    * @returns {*} Returns `value`.
11404    * @example
11405    *
11406    * _([1, 2, 3])
11407    *  .tap(function(array) {
11408    *    array.pop();
11409    *  })
11410    *  .reverse()
11411    *  .value();
11412    * // => [2, 1]
11413    */
11414   function tap(value, interceptor, thisArg) {
11415     interceptor.call(thisArg, value);
11416     return value;
11417   }
11418
11419   /**
11420    * This method is like `_.tap` except that it returns the result of `interceptor`.
11421    *
11422    * @static
11423    * @memberOf _
11424    * @category Chain
11425    * @param {*} value The value to provide to `interceptor`.
11426    * @param {Function} interceptor The function to invoke.
11427    * @param {*} [thisArg] The `this` binding of `interceptor`.
11428    * @returns {*} Returns the result of `interceptor`.
11429    * @example
11430    *
11431    * _('  abc  ')
11432    *  .chain()
11433    *  .trim()
11434    *  .thru(function(value) {
11435    *    return [value];
11436    *  })
11437    *  .value();
11438    * // => ['abc']
11439    */
11440   function thru(value, interceptor, thisArg) {
11441     return interceptor.call(thisArg, value);
11442   }
11443
11444   /**
11445    * Enables explicit method chaining on the wrapper object.
11446    *
11447    * @name chain
11448    * @memberOf _
11449    * @category Chain
11450    * @returns {Object} Returns the new `lodash` wrapper instance.
11451    * @example
11452    *
11453    * var users = [
11454    *   { 'user': 'barney', 'age': 36 },
11455    *   { 'user': 'fred',   'age': 40 }
11456    * ];
11457    *
11458    * // without explicit chaining
11459    * _(users).first();
11460    * // => { 'user': 'barney', 'age': 36 }
11461    *
11462    * // with explicit chaining
11463    * _(users).chain()
11464    *   .first()
11465    *   .pick('user')
11466    *   .value();
11467    * // => { 'user': 'barney' }
11468    */
11469   function wrapperChain() {
11470     return chain(this);
11471   }
11472
11473   /**
11474    * Executes the chained sequence and returns the wrapped result.
11475    *
11476    * @name commit
11477    * @memberOf _
11478    * @category Chain
11479    * @returns {Object} Returns the new `lodash` wrapper instance.
11480    * @example
11481    *
11482    * var array = [1, 2];
11483    * var wrapper = _(array).push(3);
11484    *
11485    * console.log(array);
11486    * // => [1, 2]
11487    *
11488    * wrapper = wrapper.commit();
11489    * console.log(array);
11490    * // => [1, 2, 3]
11491    *
11492    * wrapper.last();
11493    * // => 3
11494    *
11495    * console.log(array);
11496    * // => [1, 2, 3]
11497    */
11498   function wrapperCommit() {
11499     return new LodashWrapper(this.value(), this.__chain__);
11500   }
11501
11502   /**
11503    * Creates a clone of the chained sequence planting `value` as the wrapped value.
11504    *
11505    * @name plant
11506    * @memberOf _
11507    * @category Chain
11508    * @returns {Object} Returns the new `lodash` wrapper instance.
11509    * @example
11510    *
11511    * var array = [1, 2];
11512    * var wrapper = _(array).map(function(value) {
11513    *   return Math.pow(value, 2);
11514    * });
11515    *
11516    * var other = [3, 4];
11517    * var otherWrapper = wrapper.plant(other);
11518    *
11519    * otherWrapper.value();
11520    * // => [9, 16]
11521    *
11522    * wrapper.value();
11523    * // => [1, 4]
11524    */
11525   function wrapperPlant(value) {
11526     var result,
11527         parent = this;
11528
11529     while (parent instanceof baseLodash) {
11530       var clone = wrapperClone(parent);
11531       if (result) {
11532         previous.__wrapped__ = clone;
11533       } else {
11534         result = clone;
11535       }
11536       var previous = clone;
11537       parent = parent.__wrapped__;
11538     }
11539     previous.__wrapped__ = value;
11540     return result;
11541   }
11542
11543   /**
11544    * Reverses the wrapped array so the first element becomes the last, the
11545    * second element becomes the second to last, and so on.
11546    *
11547    * **Note:** This method mutates the wrapped array.
11548    *
11549    * @name reverse
11550    * @memberOf _
11551    * @category Chain
11552    * @returns {Object} Returns the new reversed `lodash` wrapper instance.
11553    * @example
11554    *
11555    * var array = [1, 2, 3];
11556    *
11557    * _(array).reverse().value()
11558    * // => [3, 2, 1]
11559    *
11560    * console.log(array);
11561    * // => [3, 2, 1]
11562    */
11563   function wrapperReverse() {
11564     var value = this.__wrapped__;
11565     if (value instanceof LazyWrapper) {
11566       if (this.__actions__.length) {
11567         value = new LazyWrapper(this);
11568       }
11569       return new LodashWrapper(value.reverse(), this.__chain__);
11570     }
11571     return this.thru(function(value) {
11572       return value.reverse();
11573     });
11574   }
11575
11576   /**
11577    * Produces the result of coercing the unwrapped value to a string.
11578    *
11579    * @name toString
11580    * @memberOf _
11581    * @category Chain
11582    * @returns {string} Returns the coerced string value.
11583    * @example
11584    *
11585    * _([1, 2, 3]).toString();
11586    * // => '1,2,3'
11587    */
11588   function wrapperToString() {
11589     return (this.value() + '');
11590   }
11591
11592   /**
11593    * Executes the chained sequence to extract the unwrapped value.
11594    *
11595    * @name value
11596    * @memberOf _
11597    * @alias run, toJSON, valueOf
11598    * @category Chain
11599    * @returns {*} Returns the resolved unwrapped value.
11600    * @example
11601    *
11602    * _([1, 2, 3]).value();
11603    * // => [1, 2, 3]
11604    */
11605   function wrapperValue() {
11606     return baseWrapperValue(this.__wrapped__, this.__actions__);
11607   }
11608
11609   /*------------------------------------------------------------------------*/
11610
11611   /**
11612    * Checks if `predicate` returns truthy for **all** elements of `collection`.
11613    * The predicate is bound to `thisArg` and invoked with three arguments:
11614    * (value, index|key, collection).
11615    *
11616    * If a property name is provided for `predicate` the created `_.property`
11617    * style callback returns the property value of the given element.
11618    *
11619    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11620    * style callback returns `true` for elements that have a matching property
11621    * value, else `false`.
11622    *
11623    * If an object is provided for `predicate` the created `_.matches` style
11624    * callback returns `true` for elements that have the properties of the given
11625    * object, else `false`.
11626    *
11627    * @static
11628    * @memberOf _
11629    * @alias all
11630    * @category Collection
11631    * @param {Array|Object|string} collection The collection to iterate over.
11632    * @param {Function|Object|string} [predicate=_.identity] The function invoked
11633    *  per iteration.
11634    * @param {*} [thisArg] The `this` binding of `predicate`.
11635    * @returns {boolean} Returns `true` if all elements pass the predicate check,
11636    *  else `false`.
11637    * @example
11638    *
11639    * _.every([true, 1, null, 'yes'], Boolean);
11640    * // => false
11641    *
11642    * var users = [
11643    *   { 'user': 'barney', 'active': false },
11644    *   { 'user': 'fred',   'active': false }
11645    * ];
11646    *
11647    * // using the `_.matches` callback shorthand
11648    * _.every(users, { 'user': 'barney', 'active': false });
11649    * // => false
11650    *
11651    * // using the `_.matchesProperty` callback shorthand
11652    * _.every(users, 'active', false);
11653    * // => true
11654    *
11655    * // using the `_.property` callback shorthand
11656    * _.every(users, 'active');
11657    * // => false
11658    */
11659   function every(collection, predicate, thisArg) {
11660     var func = isArray(collection) ? arrayEvery : baseEvery;
11661     if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
11662       predicate = null;
11663     }
11664     if (typeof predicate != 'function' || thisArg !== undefined) {
11665       predicate = getCallback(predicate, thisArg, 3);
11666     }
11667     return func(collection, predicate);
11668   }
11669
11670   /**
11671    * Iterates over elements of `collection`, returning an array of all elements
11672    * `predicate` returns truthy for. The predicate is bound to `thisArg` and
11673    * invoked with three arguments: (value, index|key, collection).
11674    *
11675    * If a property name is provided for `predicate` the created `_.property`
11676    * style callback returns the property value of the given element.
11677    *
11678    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11679    * style callback returns `true` for elements that have a matching property
11680    * value, else `false`.
11681    *
11682    * If an object is provided for `predicate` the created `_.matches` style
11683    * callback returns `true` for elements that have the properties of the given
11684    * object, else `false`.
11685    *
11686    * @static
11687    * @memberOf _
11688    * @alias select
11689    * @category Collection
11690    * @param {Array|Object|string} collection The collection to iterate over.
11691    * @param {Function|Object|string} [predicate=_.identity] The function invoked
11692    *  per iteration.
11693    * @param {*} [thisArg] The `this` binding of `predicate`.
11694    * @returns {Array} Returns the new filtered array.
11695    * @example
11696    *
11697    * _.filter([4, 5, 6], function(n) {
11698    *   return n % 2 == 0;
11699    * });
11700    * // => [4, 6]
11701    *
11702    * var users = [
11703    *   { 'user': 'barney', 'age': 36, 'active': true },
11704    *   { 'user': 'fred',   'age': 40, 'active': false }
11705    * ];
11706    *
11707    * // using the `_.matches` callback shorthand
11708    * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user');
11709    * // => ['barney']
11710    *
11711    * // using the `_.matchesProperty` callback shorthand
11712    * _.pluck(_.filter(users, 'active', false), 'user');
11713    * // => ['fred']
11714    *
11715    * // using the `_.property` callback shorthand
11716    * _.pluck(_.filter(users, 'active'), 'user');
11717    * // => ['barney']
11718    */
11719   function filter(collection, predicate, thisArg) {
11720     var func = isArray(collection) ? arrayFilter : baseFilter;
11721     predicate = getCallback(predicate, thisArg, 3);
11722     return func(collection, predicate);
11723   }
11724
11725   /**
11726    * Iterates over elements of `collection`, returning the first element
11727    * `predicate` returns truthy for. The predicate is bound to `thisArg` and
11728    * invoked with three arguments: (value, index|key, collection).
11729    *
11730    * If a property name is provided for `predicate` the created `_.property`
11731    * style callback returns the property value of the given element.
11732    *
11733    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11734    * style callback returns `true` for elements that have a matching property
11735    * value, else `false`.
11736    *
11737    * If an object is provided for `predicate` the created `_.matches` style
11738    * callback returns `true` for elements that have the properties of the given
11739    * object, else `false`.
11740    *
11741    * @static
11742    * @memberOf _
11743    * @alias detect
11744    * @category Collection
11745    * @param {Array|Object|string} collection The collection to search.
11746    * @param {Function|Object|string} [predicate=_.identity] The function invoked
11747    *  per iteration.
11748    * @param {*} [thisArg] The `this` binding of `predicate`.
11749    * @returns {*} Returns the matched element, else `undefined`.
11750    * @example
11751    *
11752    * var users = [
11753    *   { 'user': 'barney',  'age': 36, 'active': true },
11754    *   { 'user': 'fred',    'age': 40, 'active': false },
11755    *   { 'user': 'pebbles', 'age': 1,  'active': true }
11756    * ];
11757    *
11758    * _.result(_.find(users, function(chr) {
11759    *   return chr.age < 40;
11760    * }), 'user');
11761    * // => 'barney'
11762    *
11763    * // using the `_.matches` callback shorthand
11764    * _.result(_.find(users, { 'age': 1, 'active': true }), 'user');
11765    * // => 'pebbles'
11766    *
11767    * // using the `_.matchesProperty` callback shorthand
11768    * _.result(_.find(users, 'active', false), 'user');
11769    * // => 'fred'
11770    *
11771    * // using the `_.property` callback shorthand
11772    * _.result(_.find(users, 'active'), 'user');
11773    * // => 'barney'
11774    */
11775   var find = createFind(baseEach);
11776
11777   /**
11778    * Iterates over elements of `collection` invoking `iteratee` for each element.
11779    * The `iteratee` is bound to `thisArg` and invoked with three arguments:
11780    * (value, index|key, collection). Iteratee functions may exit iteration early
11781    * by explicitly returning `false`.
11782    *
11783    * **Note:** As with other "Collections" methods, objects with a "length" property
11784    * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
11785    * may be used for object iteration.
11786    *
11787    * @static
11788    * @memberOf _
11789    * @alias each
11790    * @category Collection
11791    * @param {Array|Object|string} collection The collection to iterate over.
11792    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
11793    * @param {*} [thisArg] The `this` binding of `iteratee`.
11794    * @returns {Array|Object|string} Returns `collection`.
11795    * @example
11796    *
11797    * _([1, 2]).forEach(function(n) {
11798    *   console.log(n);
11799    * }).value();
11800    * // => logs each value from left to right and returns the array
11801    *
11802    * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) {
11803    *   console.log(n, key);
11804    * });
11805    * // => logs each value-key pair and returns the object (iteration order is not guaranteed)
11806    */
11807   var forEach = createForEach(arrayEach, baseEach);
11808
11809   /**
11810    * Creates an object composed of keys generated from the results of running
11811    * each element of `collection` through `iteratee`. The corresponding value
11812    * of each key is an array of the elements responsible for generating the key.
11813    * The `iteratee` is bound to `thisArg` and invoked with three arguments:
11814    * (value, index|key, collection).
11815    *
11816    * If a property name is provided for `iteratee` the created `_.property`
11817    * style callback returns the property value of the given element.
11818    *
11819    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11820    * style callback returns `true` for elements that have a matching property
11821    * value, else `false`.
11822    *
11823    * If an object is provided for `iteratee` the created `_.matches` style
11824    * callback returns `true` for elements that have the properties of the given
11825    * object, else `false`.
11826    *
11827    * @static
11828    * @memberOf _
11829    * @category Collection
11830    * @param {Array|Object|string} collection The collection to iterate over.
11831    * @param {Function|Object|string} [iteratee=_.identity] The function invoked
11832    *  per iteration.
11833    * @param {*} [thisArg] The `this` binding of `iteratee`.
11834    * @returns {Object} Returns the composed aggregate object.
11835    * @example
11836    *
11837    * _.groupBy([4.2, 6.1, 6.4], function(n) {
11838    *   return Math.floor(n);
11839    * });
11840    * // => { '4': [4.2], '6': [6.1, 6.4] }
11841    *
11842    * _.groupBy([4.2, 6.1, 6.4], function(n) {
11843    *   return this.floor(n);
11844    * }, Math);
11845    * // => { '4': [4.2], '6': [6.1, 6.4] }
11846    *
11847    * // using the `_.property` callback shorthand
11848    * _.groupBy(['one', 'two', 'three'], 'length');
11849    * // => { '3': ['one', 'two'], '5': ['three'] }
11850    */
11851   var groupBy = createAggregator(function(result, value, key) {
11852     if (hasOwnProperty.call(result, key)) {
11853       result[key].push(value);
11854     } else {
11855       result[key] = [value];
11856     }
11857   });
11858
11859   /**
11860    * Checks if `value` is in `collection` using
11861    * [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
11862    * for equality comparisons. If `fromIndex` is negative, it is used as the offset
11863    * from the end of `collection`.
11864    *
11865    * @static
11866    * @memberOf _
11867    * @alias contains, include
11868    * @category Collection
11869    * @param {Array|Object|string} collection The collection to search.
11870    * @param {*} target The value to search for.
11871    * @param {number} [fromIndex=0] The index to search from.
11872    * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`.
11873    * @returns {boolean} Returns `true` if a matching element is found, else `false`.
11874    * @example
11875    *
11876    * _.includes([1, 2, 3], 1);
11877    * // => true
11878    *
11879    * _.includes([1, 2, 3], 1, 2);
11880    * // => false
11881    *
11882    * _.includes({ 'user': 'fred', 'age': 40 }, 'fred');
11883    * // => true
11884    *
11885    * _.includes('pebbles', 'eb');
11886    * // => true
11887    */
11888   function includes(collection, target, fromIndex, guard) {
11889     var length = collection ? getLength(collection) : 0;
11890     if (!isLength(length)) {
11891       collection = values(collection);
11892       length = collection.length;
11893     }
11894     if (!length) {
11895       return false;
11896     }
11897     if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) {
11898       fromIndex = 0;
11899     } else {
11900       fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
11901     }
11902     return (typeof collection == 'string' || !isArray(collection) && isString(collection))
11903       ? (fromIndex < length && collection.indexOf(target, fromIndex) > -1)
11904       : (getIndexOf(collection, target, fromIndex) > -1);
11905   }
11906
11907   /**
11908    * Creates an array of values by running each element in `collection` through
11909    * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three
11910    * arguments: (value, index|key, collection).
11911    *
11912    * If a property name is provided for `iteratee` the created `_.property`
11913    * style callback returns the property value of the given element.
11914    *
11915    * If a value is also provided for `thisArg` the created `_.matchesProperty`
11916    * style callback returns `true` for elements that have a matching property
11917    * value, else `false`.
11918    *
11919    * If an object is provided for `iteratee` the created `_.matches` style
11920    * callback returns `true` for elements that have the properties of the given
11921    * object, else `false`.
11922    *
11923    * Many lodash methods are guarded to work as iteratees for methods like
11924    * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
11925    *
11926    * The guarded methods are:
11927    * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`,
11928    * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`,
11929    * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`,
11930    * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`,
11931    * `sum`, `uniq`, and `words`
11932    *
11933    * @static
11934    * @memberOf _
11935    * @alias collect
11936    * @category Collection
11937    * @param {Array|Object|string} collection The collection to iterate over.
11938    * @param {Function|Object|string} [iteratee=_.identity] The function invoked
11939    *  per iteration.
11940    * @param {*} [thisArg] The `this` binding of `iteratee`.
11941    * @returns {Array} Returns the new mapped array.
11942    * @example
11943    *
11944    * function timesThree(n) {
11945    *   return n * 3;
11946    * }
11947    *
11948    * _.map([1, 2], timesThree);
11949    * // => [3, 6]
11950    *
11951    * _.map({ 'a': 1, 'b': 2 }, timesThree);
11952    * // => [3, 6] (iteration order is not guaranteed)
11953    *
11954    * var users = [
11955    *   { 'user': 'barney' },
11956    *   { 'user': 'fred' }
11957    * ];
11958    *
11959    * // using the `_.property` callback shorthand
11960    * _.map(users, 'user');
11961    * // => ['barney', 'fred']
11962    */
11963   function map(collection, iteratee, thisArg) {
11964     var func = isArray(collection) ? arrayMap : baseMap;
11965     iteratee = getCallback(iteratee, thisArg, 3);
11966     return func(collection, iteratee);
11967   }
11968
11969   /**
11970    * Gets the property value of `path` from all elements in `collection`.
11971    *
11972    * @static
11973    * @memberOf _
11974    * @category Collection
11975    * @param {Array|Object|string} collection The collection to iterate over.
11976    * @param {Array|string} path The path of the property to pluck.
11977    * @returns {Array} Returns the property values.
11978    * @example
11979    *
11980    * var users = [
11981    *   { 'user': 'barney', 'age': 36 },
11982    *   { 'user': 'fred',   'age': 40 }
11983    * ];
11984    *
11985    * _.pluck(users, 'user');
11986    * // => ['barney', 'fred']
11987    *
11988    * var userIndex = _.indexBy(users, 'user');
11989    * _.pluck(userIndex, 'age');
11990    * // => [36, 40] (iteration order is not guaranteed)
11991    */
11992   function pluck(collection, path) {
11993     return map(collection, property(path));
11994   }
11995
11996   /**
11997    * Reduces `collection` to a value which is the accumulated result of running
11998    * each element in `collection` through `iteratee`, where each successive
11999    * invocation is supplied the return value of the previous. If `accumulator`
12000    * is not provided the first element of `collection` is used as the initial
12001    * value. The `iteratee` is bound to `thisArg` and invoked with four arguments:
12002    * (accumulator, value, index|key, collection).
12003    *
12004    * Many lodash methods are guarded to work as iteratees for methods like
12005    * `_.reduce`, `_.reduceRight`, and `_.transform`.
12006    *
12007    * The guarded methods are:
12008    * `assign`, `defaults`, `includes`, `merge`, `sortByAll`, and `sortByOrder`
12009    *
12010    * @static
12011    * @memberOf _
12012    * @alias foldl, inject
12013    * @category Collection
12014    * @param {Array|Object|string} collection The collection to iterate over.
12015    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12016    * @param {*} [accumulator] The initial value.
12017    * @param {*} [thisArg] The `this` binding of `iteratee`.
12018    * @returns {*} Returns the accumulated value.
12019    * @example
12020    *
12021    * _.reduce([1, 2], function(total, n) {
12022    *   return total + n;
12023    * });
12024    * // => 3
12025    *
12026    * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) {
12027    *   result[key] = n * 3;
12028    *   return result;
12029    * }, {});
12030    * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed)
12031    */
12032   var reduce = createReduce(arrayReduce, baseEach);
12033
12034   /**
12035    * The opposite of `_.filter`; this method returns the elements of `collection`
12036    * that `predicate` does **not** return truthy for.
12037    *
12038    * @static
12039    * @memberOf _
12040    * @category Collection
12041    * @param {Array|Object|string} collection The collection to iterate over.
12042    * @param {Function|Object|string} [predicate=_.identity] The function invoked
12043    *  per iteration.
12044    * @param {*} [thisArg] The `this` binding of `predicate`.
12045    * @returns {Array} Returns the new filtered array.
12046    * @example
12047    *
12048    * _.reject([1, 2, 3, 4], function(n) {
12049    *   return n % 2 == 0;
12050    * });
12051    * // => [1, 3]
12052    *
12053    * var users = [
12054    *   { 'user': 'barney', 'age': 36, 'active': false },
12055    *   { 'user': 'fred',   'age': 40, 'active': true }
12056    * ];
12057    *
12058    * // using the `_.matches` callback shorthand
12059    * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user');
12060    * // => ['barney']
12061    *
12062    * // using the `_.matchesProperty` callback shorthand
12063    * _.pluck(_.reject(users, 'active', false), 'user');
12064    * // => ['fred']
12065    *
12066    * // using the `_.property` callback shorthand
12067    * _.pluck(_.reject(users, 'active'), 'user');
12068    * // => ['barney']
12069    */
12070   function reject(collection, predicate, thisArg) {
12071     var func = isArray(collection) ? arrayFilter : baseFilter;
12072     predicate = getCallback(predicate, thisArg, 3);
12073     return func(collection, function(value, index, collection) {
12074       return !predicate(value, index, collection);
12075     });
12076   }
12077
12078   /**
12079    * Checks if `predicate` returns truthy for **any** element of `collection`.
12080    * The function returns as soon as it finds a passing value and does not iterate
12081    * over the entire collection. The predicate is bound to `thisArg` and invoked
12082    * with three arguments: (value, index|key, collection).
12083    *
12084    * If a property name is provided for `predicate` the created `_.property`
12085    * style callback returns the property value of the given element.
12086    *
12087    * If a value is also provided for `thisArg` the created `_.matchesProperty`
12088    * style callback returns `true` for elements that have a matching property
12089    * value, else `false`.
12090    *
12091    * If an object is provided for `predicate` the created `_.matches` style
12092    * callback returns `true` for elements that have the properties of the given
12093    * object, else `false`.
12094    *
12095    * @static
12096    * @memberOf _
12097    * @alias any
12098    * @category Collection
12099    * @param {Array|Object|string} collection The collection to iterate over.
12100    * @param {Function|Object|string} [predicate=_.identity] The function invoked
12101    *  per iteration.
12102    * @param {*} [thisArg] The `this` binding of `predicate`.
12103    * @returns {boolean} Returns `true` if any element passes the predicate check,
12104    *  else `false`.
12105    * @example
12106    *
12107    * _.some([null, 0, 'yes', false], Boolean);
12108    * // => true
12109    *
12110    * var users = [
12111    *   { 'user': 'barney', 'active': true },
12112    *   { 'user': 'fred',   'active': false }
12113    * ];
12114    *
12115    * // using the `_.matches` callback shorthand
12116    * _.some(users, { 'user': 'barney', 'active': false });
12117    * // => false
12118    *
12119    * // using the `_.matchesProperty` callback shorthand
12120    * _.some(users, 'active', false);
12121    * // => true
12122    *
12123    * // using the `_.property` callback shorthand
12124    * _.some(users, 'active');
12125    * // => true
12126    */
12127   function some(collection, predicate, thisArg) {
12128     var func = isArray(collection) ? arraySome : baseSome;
12129     if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
12130       predicate = null;
12131     }
12132     if (typeof predicate != 'function' || thisArg !== undefined) {
12133       predicate = getCallback(predicate, thisArg, 3);
12134     }
12135     return func(collection, predicate);
12136   }
12137
12138   /*------------------------------------------------------------------------*/
12139
12140   /**
12141    * Gets the number of milliseconds that have elapsed since the Unix epoch
12142    * (1 January 1970 00:00:00 UTC).
12143    *
12144    * @static
12145    * @memberOf _
12146    * @category Date
12147    * @example
12148    *
12149    * _.defer(function(stamp) {
12150    *   console.log(_.now() - stamp);
12151    * }, _.now());
12152    * // => logs the number of milliseconds it took for the deferred function to be invoked
12153    */
12154   var now = nativeNow || function() {
12155     return new Date().getTime();
12156   };
12157
12158   /*------------------------------------------------------------------------*/
12159
12160   /**
12161    * Creates a function that invokes `func` with the `this` binding of `thisArg`
12162    * and prepends any additional `_.bind` arguments to those provided to the
12163    * bound function.
12164    *
12165    * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
12166    * may be used as a placeholder for partially applied arguments.
12167    *
12168    * **Note:** Unlike native `Function#bind` this method does not set the "length"
12169    * property of bound functions.
12170    *
12171    * @static
12172    * @memberOf _
12173    * @category Function
12174    * @param {Function} func The function to bind.
12175    * @param {*} thisArg The `this` binding of `func`.
12176    * @param {...*} [partials] The arguments to be partially applied.
12177    * @returns {Function} Returns the new bound function.
12178    * @example
12179    *
12180    * var greet = function(greeting, punctuation) {
12181    *   return greeting + ' ' + this.user + punctuation;
12182    * };
12183    *
12184    * var object = { 'user': 'fred' };
12185    *
12186    * var bound = _.bind(greet, object, 'hi');
12187    * bound('!');
12188    * // => 'hi fred!'
12189    *
12190    * // using placeholders
12191    * var bound = _.bind(greet, object, _, '!');
12192    * bound('hi');
12193    * // => 'hi fred!'
12194    */
12195   var bind = restParam(function(func, thisArg, partials) {
12196     var bitmask = BIND_FLAG;
12197     if (partials.length) {
12198       var holders = replaceHolders(partials, bind.placeholder);
12199       bitmask |= PARTIAL_FLAG;
12200     }
12201     return createWrapper(func, bitmask, thisArg, partials, holders);
12202   });
12203
12204   /**
12205    * Creates a debounced function that delays invoking `func` until after `wait`
12206    * milliseconds have elapsed since the last time the debounced function was
12207    * invoked. The debounced function comes with a `cancel` method to cancel
12208    * delayed invocations. Provide an options object to indicate that `func`
12209    * should be invoked on the leading and/or trailing edge of the `wait` timeout.
12210    * Subsequent calls to the debounced function return the result of the last
12211    * `func` invocation.
12212    *
12213    * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
12214    * on the trailing edge of the timeout only if the the debounced function is
12215    * invoked more than once during the `wait` timeout.
12216    *
12217    * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
12218    * for details over the differences between `_.debounce` and `_.throttle`.
12219    *
12220    * @static
12221    * @memberOf _
12222    * @category Function
12223    * @param {Function} func The function to debounce.
12224    * @param {number} [wait=0] The number of milliseconds to delay.
12225    * @param {Object} [options] The options object.
12226    * @param {boolean} [options.leading=false] Specify invoking on the leading
12227    *  edge of the timeout.
12228    * @param {number} [options.maxWait] The maximum time `func` is allowed to be
12229    *  delayed before it is invoked.
12230    * @param {boolean} [options.trailing=true] Specify invoking on the trailing
12231    *  edge of the timeout.
12232    * @returns {Function} Returns the new debounced function.
12233    * @example
12234    *
12235    * // avoid costly calculations while the window size is in flux
12236    * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
12237    *
12238    * // invoke `sendMail` when the click event is fired, debouncing subsequent calls
12239    * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
12240    *   'leading': true,
12241    *   'trailing': false
12242    * }));
12243    *
12244    * // ensure `batchLog` is invoked once after 1 second of debounced calls
12245    * var source = new EventSource('/stream');
12246    * jQuery(source).on('message', _.debounce(batchLog, 250, {
12247    *   'maxWait': 1000
12248    * }));
12249    *
12250    * // cancel a debounced call
12251    * var todoChanges = _.debounce(batchLog, 1000);
12252    * Object.observe(models.todo, todoChanges);
12253    *
12254    * Object.observe(models, function(changes) {
12255    *   if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
12256    *     todoChanges.cancel();
12257    *   }
12258    * }, ['delete']);
12259    *
12260    * // ...at some point `models.todo` is changed
12261    * models.todo.completed = true;
12262    *
12263    * // ...before 1 second has passed `models.todo` is deleted
12264    * // which cancels the debounced `todoChanges` call
12265    * delete models.todo;
12266    */
12267   function debounce(func, wait, options) {
12268     var args,
12269         maxTimeoutId,
12270         result,
12271         stamp,
12272         thisArg,
12273         timeoutId,
12274         trailingCall,
12275         lastCalled = 0,
12276         maxWait = false,
12277         trailing = true;
12278
12279     if (typeof func != 'function') {
12280       throw new TypeError(FUNC_ERROR_TEXT);
12281     }
12282     wait = wait < 0 ? 0 : (+wait || 0);
12283     if (options === true) {
12284       var leading = true;
12285       trailing = false;
12286     } else if (isObject(options)) {
12287       leading = options.leading;
12288       maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
12289       trailing = 'trailing' in options ? options.trailing : trailing;
12290     }
12291
12292     function cancel() {
12293       if (timeoutId) {
12294         clearTimeout(timeoutId);
12295       }
12296       if (maxTimeoutId) {
12297         clearTimeout(maxTimeoutId);
12298       }
12299       maxTimeoutId = timeoutId = trailingCall = undefined;
12300     }
12301
12302     function delayed() {
12303       var remaining = wait - (now() - stamp);
12304       if (remaining <= 0 || remaining > wait) {
12305         if (maxTimeoutId) {
12306           clearTimeout(maxTimeoutId);
12307         }
12308         var isCalled = trailingCall;
12309         maxTimeoutId = timeoutId = trailingCall = undefined;
12310         if (isCalled) {
12311           lastCalled = now();
12312           result = func.apply(thisArg, args);
12313           if (!timeoutId && !maxTimeoutId) {
12314             args = thisArg = null;
12315           }
12316         }
12317       } else {
12318         timeoutId = setTimeout(delayed, remaining);
12319       }
12320     }
12321
12322     function maxDelayed() {
12323       if (timeoutId) {
12324         clearTimeout(timeoutId);
12325       }
12326       maxTimeoutId = timeoutId = trailingCall = undefined;
12327       if (trailing || (maxWait !== wait)) {
12328         lastCalled = now();
12329         result = func.apply(thisArg, args);
12330         if (!timeoutId && !maxTimeoutId) {
12331           args = thisArg = null;
12332         }
12333       }
12334     }
12335
12336     function debounced() {
12337       args = arguments;
12338       stamp = now();
12339       thisArg = this;
12340       trailingCall = trailing && (timeoutId || !leading);
12341
12342       if (maxWait === false) {
12343         var leadingCall = leading && !timeoutId;
12344       } else {
12345         if (!maxTimeoutId && !leading) {
12346           lastCalled = stamp;
12347         }
12348         var remaining = maxWait - (stamp - lastCalled),
12349             isCalled = remaining <= 0 || remaining > maxWait;
12350
12351         if (isCalled) {
12352           if (maxTimeoutId) {
12353             maxTimeoutId = clearTimeout(maxTimeoutId);
12354           }
12355           lastCalled = stamp;
12356           result = func.apply(thisArg, args);
12357         }
12358         else if (!maxTimeoutId) {
12359           maxTimeoutId = setTimeout(maxDelayed, remaining);
12360         }
12361       }
12362       if (isCalled && timeoutId) {
12363         timeoutId = clearTimeout(timeoutId);
12364       }
12365       else if (!timeoutId && wait !== maxWait) {
12366         timeoutId = setTimeout(delayed, wait);
12367       }
12368       if (leadingCall) {
12369         isCalled = true;
12370         result = func.apply(thisArg, args);
12371       }
12372       if (isCalled && !timeoutId && !maxTimeoutId) {
12373         args = thisArg = null;
12374       }
12375       return result;
12376     }
12377     debounced.cancel = cancel;
12378     return debounced;
12379   }
12380
12381   /**
12382    * Creates a function that invokes `func` with the `this` binding of the
12383    * created function and arguments from `start` and beyond provided as an array.
12384    *
12385    * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
12386    *
12387    * @static
12388    * @memberOf _
12389    * @category Function
12390    * @param {Function} func The function to apply a rest parameter to.
12391    * @param {number} [start=func.length-1] The start position of the rest parameter.
12392    * @returns {Function} Returns the new function.
12393    * @example
12394    *
12395    * var say = _.restParam(function(what, names) {
12396    *   return what + ' ' + _.initial(names).join(', ') +
12397    *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
12398    * });
12399    *
12400    * say('hello', 'fred', 'barney', 'pebbles');
12401    * // => 'hello fred, barney, & pebbles'
12402    */
12403   function restParam(func, start) {
12404     if (typeof func != 'function') {
12405       throw new TypeError(FUNC_ERROR_TEXT);
12406     }
12407     start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0);
12408     return function() {
12409       var args = arguments,
12410           index = -1,
12411           length = nativeMax(args.length - start, 0),
12412           rest = Array(length);
12413
12414       while (++index < length) {
12415         rest[index] = args[start + index];
12416       }
12417       switch (start) {
12418         case 0: return func.call(this, rest);
12419         case 1: return func.call(this, args[0], rest);
12420         case 2: return func.call(this, args[0], args[1], rest);
12421       }
12422       var otherArgs = Array(start + 1);
12423       index = -1;
12424       while (++index < start) {
12425         otherArgs[index] = args[index];
12426       }
12427       otherArgs[start] = rest;
12428       return func.apply(this, otherArgs);
12429     };
12430   }
12431
12432   /**
12433    * Creates a throttled function that only invokes `func` at most once per
12434    * every `wait` milliseconds. The throttled function comes with a `cancel`
12435    * method to cancel delayed invocations. Provide an options object to indicate
12436    * that `func` should be invoked on the leading and/or trailing edge of the
12437    * `wait` timeout. Subsequent calls to the throttled function return the
12438    * result of the last `func` call.
12439    *
12440    * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
12441    * on the trailing edge of the timeout only if the the throttled function is
12442    * invoked more than once during the `wait` timeout.
12443    *
12444    * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
12445    * for details over the differences between `_.throttle` and `_.debounce`.
12446    *
12447    * @static
12448    * @memberOf _
12449    * @category Function
12450    * @param {Function} func The function to throttle.
12451    * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
12452    * @param {Object} [options] The options object.
12453    * @param {boolean} [options.leading=true] Specify invoking on the leading
12454    *  edge of the timeout.
12455    * @param {boolean} [options.trailing=true] Specify invoking on the trailing
12456    *  edge of the timeout.
12457    * @returns {Function} Returns the new throttled function.
12458    * @example
12459    *
12460    * // avoid excessively updating the position while scrolling
12461    * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
12462    *
12463    * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes
12464    * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
12465    *   'trailing': false
12466    * }));
12467    *
12468    * // cancel a trailing throttled call
12469    * jQuery(window).on('popstate', throttled.cancel);
12470    */
12471   function throttle(func, wait, options) {
12472     var leading = true,
12473         trailing = true;
12474
12475     if (typeof func != 'function') {
12476       throw new TypeError(FUNC_ERROR_TEXT);
12477     }
12478     if (options === false) {
12479       leading = false;
12480     } else if (isObject(options)) {
12481       leading = 'leading' in options ? !!options.leading : leading;
12482       trailing = 'trailing' in options ? !!options.trailing : trailing;
12483     }
12484     debounceOptions.leading = leading;
12485     debounceOptions.maxWait = +wait;
12486     debounceOptions.trailing = trailing;
12487     return debounce(func, wait, debounceOptions);
12488   }
12489
12490   /*------------------------------------------------------------------------*/
12491
12492   /**
12493    * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned,
12494    * otherwise they are assigned by reference. If `customizer` is provided it is
12495    * invoked to produce the cloned values. If `customizer` returns `undefined`
12496    * cloning is handled by the method instead. The `customizer` is bound to
12497    * `thisArg` and invoked with two argument; (value [, index|key, object]).
12498    *
12499    * **Note:** This method is loosely based on the
12500    * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
12501    * The enumerable properties of `arguments` objects and objects created by
12502    * constructors other than `Object` are cloned to plain `Object` objects. An
12503    * empty object is returned for uncloneable values such as functions, DOM nodes,
12504    * Maps, Sets, and WeakMaps.
12505    *
12506    * @static
12507    * @memberOf _
12508    * @category Lang
12509    * @param {*} value The value to clone.
12510    * @param {boolean} [isDeep] Specify a deep clone.
12511    * @param {Function} [customizer] The function to customize cloning values.
12512    * @param {*} [thisArg] The `this` binding of `customizer`.
12513    * @returns {*} Returns the cloned value.
12514    * @example
12515    *
12516    * var users = [
12517    *   { 'user': 'barney' },
12518    *   { 'user': 'fred' }
12519    * ];
12520    *
12521    * var shallow = _.clone(users);
12522    * shallow[0] === users[0];
12523    * // => true
12524    *
12525    * var deep = _.clone(users, true);
12526    * deep[0] === users[0];
12527    * // => false
12528    *
12529    * // using a customizer callback
12530    * var el = _.clone(document.body, function(value) {
12531    *   if (_.isElement(value)) {
12532    *     return value.cloneNode(false);
12533    *   }
12534    * });
12535    *
12536    * el === document.body
12537    * // => false
12538    * el.nodeName
12539    * // => BODY
12540    * el.childNodes.length;
12541    * // => 0
12542    */
12543   function clone(value, isDeep, customizer, thisArg) {
12544     if (isDeep && typeof isDeep != 'boolean' && isIterateeCall(value, isDeep, customizer)) {
12545       isDeep = false;
12546     }
12547     else if (typeof isDeep == 'function') {
12548       thisArg = customizer;
12549       customizer = isDeep;
12550       isDeep = false;
12551     }
12552     return typeof customizer == 'function'
12553       ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 1))
12554       : baseClone(value, isDeep);
12555   }
12556
12557   /**
12558    * Creates a deep clone of `value`. If `customizer` is provided it is invoked
12559    * to produce the cloned values. If `customizer` returns `undefined` cloning
12560    * is handled by the method instead. The `customizer` is bound to `thisArg`
12561    * and invoked with two argument; (value [, index|key, object]).
12562    *
12563    * **Note:** This method is loosely based on the
12564    * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
12565    * The enumerable properties of `arguments` objects and objects created by
12566    * constructors other than `Object` are cloned to plain `Object` objects. An
12567    * empty object is returned for uncloneable values such as functions, DOM nodes,
12568    * Maps, Sets, and WeakMaps.
12569    *
12570    * @static
12571    * @memberOf _
12572    * @category Lang
12573    * @param {*} value The value to deep clone.
12574    * @param {Function} [customizer] The function to customize cloning values.
12575    * @param {*} [thisArg] The `this` binding of `customizer`.
12576    * @returns {*} Returns the deep cloned value.
12577    * @example
12578    *
12579    * var users = [
12580    *   { 'user': 'barney' },
12581    *   { 'user': 'fred' }
12582    * ];
12583    *
12584    * var deep = _.cloneDeep(users);
12585    * deep[0] === users[0];
12586    * // => false
12587    *
12588    * // using a customizer callback
12589    * var el = _.cloneDeep(document.body, function(value) {
12590    *   if (_.isElement(value)) {
12591    *     return value.cloneNode(true);
12592    *   }
12593    * });
12594    *
12595    * el === document.body
12596    * // => false
12597    * el.nodeName
12598    * // => BODY
12599    * el.childNodes.length;
12600    * // => 20
12601    */
12602   function cloneDeep(value, customizer, thisArg) {
12603     return typeof customizer == 'function'
12604       ? baseClone(value, true, bindCallback(customizer, thisArg, 1))
12605       : baseClone(value, true);
12606   }
12607
12608   /**
12609    * Checks if `value` is classified as an `arguments` object.
12610    *
12611    * @static
12612    * @memberOf _
12613    * @category Lang
12614    * @param {*} value The value to check.
12615    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12616    * @example
12617    *
12618    * _.isArguments(function() { return arguments; }());
12619    * // => true
12620    *
12621    * _.isArguments([1, 2, 3]);
12622    * // => false
12623    */
12624   function isArguments(value) {
12625     return isObjectLike(value) && isArrayLike(value) && objToString.call(value) == argsTag;
12626   }
12627   // Fallback for environments without a `toStringTag` for `arguments` objects.
12628   if (!support.argsTag) {
12629     isArguments = function(value) {
12630       return isObjectLike(value) && isArrayLike(value) &&
12631         hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');
12632     };
12633   }
12634
12635   /**
12636    * Checks if `value` is classified as an `Array` object.
12637    *
12638    * @static
12639    * @memberOf _
12640    * @category Lang
12641    * @param {*} value The value to check.
12642    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12643    * @example
12644    *
12645    * _.isArray([1, 2, 3]);
12646    * // => true
12647    *
12648    * _.isArray(function() { return arguments; }());
12649    * // => false
12650    */
12651   var isArray = nativeIsArray || function(value) {
12652     return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag;
12653   };
12654
12655   /**
12656    * Checks if `value` is empty. A value is considered empty unless it is an
12657    * `arguments` object, array, string, or jQuery-like collection with a length
12658    * greater than `0` or an object with own enumerable properties.
12659    *
12660    * @static
12661    * @memberOf _
12662    * @category Lang
12663    * @param {Array|Object|string} value The value to inspect.
12664    * @returns {boolean} Returns `true` if `value` is empty, else `false`.
12665    * @example
12666    *
12667    * _.isEmpty(null);
12668    * // => true
12669    *
12670    * _.isEmpty(true);
12671    * // => true
12672    *
12673    * _.isEmpty(1);
12674    * // => true
12675    *
12676    * _.isEmpty([1, 2, 3]);
12677    * // => false
12678    *
12679    * _.isEmpty({ 'a': 1 });
12680    * // => false
12681    */
12682   function isEmpty(value) {
12683     if (value == null) {
12684       return true;
12685     }
12686     if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) ||
12687         (isObjectLike(value) && isFunction(value.splice)))) {
12688       return !value.length;
12689     }
12690     return !keys(value).length;
12691   }
12692
12693   /**
12694    * Performs a deep comparison between two values to determine if they are
12695    * equivalent. If `customizer` is provided it is invoked to compare values.
12696    * If `customizer` returns `undefined` comparisons are handled by the method
12697    * instead. The `customizer` is bound to `thisArg` and invoked with three
12698    * arguments: (value, other [, index|key]).
12699    *
12700    * **Note:** This method supports comparing arrays, booleans, `Date` objects,
12701    * numbers, `Object` objects, regexes, and strings. Objects are compared by
12702    * their own, not inherited, enumerable properties. Functions and DOM nodes
12703    * are **not** supported. Provide a customizer function to extend support
12704    * for comparing other values.
12705    *
12706    * @static
12707    * @memberOf _
12708    * @alias eq
12709    * @category Lang
12710    * @param {*} value The value to compare.
12711    * @param {*} other The other value to compare.
12712    * @param {Function} [customizer] The function to customize value comparisons.
12713    * @param {*} [thisArg] The `this` binding of `customizer`.
12714    * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
12715    * @example
12716    *
12717    * var object = { 'user': 'fred' };
12718    * var other = { 'user': 'fred' };
12719    *
12720    * object == other;
12721    * // => false
12722    *
12723    * _.isEqual(object, other);
12724    * // => true
12725    *
12726    * // using a customizer callback
12727    * var array = ['hello', 'goodbye'];
12728    * var other = ['hi', 'goodbye'];
12729    *
12730    * _.isEqual(array, other, function(value, other) {
12731    *   if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) {
12732    *     return true;
12733    *   }
12734    * });
12735    * // => true
12736    */
12737   function isEqual(value, other, customizer, thisArg) {
12738     customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined;
12739     var result = customizer ? customizer(value, other) : undefined;
12740     return  result === undefined ? baseIsEqual(value, other, customizer) : !!result;
12741   }
12742
12743   /**
12744    * Checks if `value` is classified as a `Function` object.
12745    *
12746    * @static
12747    * @memberOf _
12748    * @category Lang
12749    * @param {*} value The value to check.
12750    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12751    * @example
12752    *
12753    * _.isFunction(_);
12754    * // => true
12755    *
12756    * _.isFunction(/abc/);
12757    * // => false
12758    */
12759   var isFunction = !(baseIsFunction(/x/) || (Uint8Array && !baseIsFunction(Uint8Array))) ? baseIsFunction : function(value) {
12760     // The use of `Object#toString` avoids issues with the `typeof` operator
12761     // in older versions of Chrome and Safari which return 'function' for regexes
12762     // and Safari 8 equivalents which return 'object' for typed array constructors.
12763     return objToString.call(value) == funcTag;
12764   };
12765
12766   /**
12767    * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
12768    * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
12769    *
12770    * @static
12771    * @memberOf _
12772    * @category Lang
12773    * @param {*} value The value to check.
12774    * @returns {boolean} Returns `true` if `value` is an object, else `false`.
12775    * @example
12776    *
12777    * _.isObject({});
12778    * // => true
12779    *
12780    * _.isObject([1, 2, 3]);
12781    * // => true
12782    *
12783    * _.isObject(1);
12784    * // => false
12785    */
12786   function isObject(value) {
12787     // Avoid a V8 JIT bug in Chrome 19-20.
12788     // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
12789     var type = typeof value;
12790     return !!value && (type == 'object' || type == 'function');
12791   }
12792
12793   /**
12794    * Checks if `value` is a native function.
12795    *
12796    * @static
12797    * @memberOf _
12798    * @category Lang
12799    * @param {*} value The value to check.
12800    * @returns {boolean} Returns `true` if `value` is a native function, else `false`.
12801    * @example
12802    *
12803    * _.isNative(Array.prototype.push);
12804    * // => true
12805    *
12806    * _.isNative(_);
12807    * // => false
12808    */
12809   function isNative(value) {
12810     if (value == null) {
12811       return false;
12812     }
12813     if (objToString.call(value) == funcTag) {
12814       return reIsNative.test(fnToString.call(value));
12815     }
12816     return isObjectLike(value) && (isHostObject(value) ? reIsNative : reIsHostCtor).test(value);
12817   }
12818
12819   /**
12820    * Checks if `value` is a plain object, that is, an object created by the
12821    * `Object` constructor or one with a `[[Prototype]]` of `null`.
12822    *
12823    * **Note:** This method assumes objects created by the `Object` constructor
12824    * have no inherited enumerable properties.
12825    *
12826    * @static
12827    * @memberOf _
12828    * @category Lang
12829    * @param {*} value The value to check.
12830    * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
12831    * @example
12832    *
12833    * function Foo() {
12834    *   this.a = 1;
12835    * }
12836    *
12837    * _.isPlainObject(new Foo);
12838    * // => false
12839    *
12840    * _.isPlainObject([1, 2, 3]);
12841    * // => false
12842    *
12843    * _.isPlainObject({ 'x': 0, 'y': 0 });
12844    * // => true
12845    *
12846    * _.isPlainObject(Object.create(null));
12847    * // => true
12848    */
12849   var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
12850     if (!(value && objToString.call(value) == objectTag) || (!lodash.support.argsTag && isArguments(value))) {
12851       return false;
12852     }
12853     var valueOf = getNative(value, 'valueOf'),
12854         objProto = valueOf && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
12855
12856     return objProto
12857       ? (value == objProto || getPrototypeOf(value) == objProto)
12858       : shimIsPlainObject(value);
12859   };
12860
12861   /**
12862    * Checks if `value` is classified as a `String` primitive or object.
12863    *
12864    * @static
12865    * @memberOf _
12866    * @category Lang
12867    * @param {*} value The value to check.
12868    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12869    * @example
12870    *
12871    * _.isString('abc');
12872    * // => true
12873    *
12874    * _.isString(1);
12875    * // => false
12876    */
12877   function isString(value) {
12878     return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag);
12879   }
12880
12881   /**
12882    * Checks if `value` is classified as a typed array.
12883    *
12884    * @static
12885    * @memberOf _
12886    * @category Lang
12887    * @param {*} value The value to check.
12888    * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
12889    * @example
12890    *
12891    * _.isTypedArray(new Uint8Array);
12892    * // => true
12893    *
12894    * _.isTypedArray([]);
12895    * // => false
12896    */
12897   function isTypedArray(value) {
12898     return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
12899   }
12900
12901   /**
12902    * Converts `value` to a plain object flattening inherited enumerable
12903    * properties of `value` to own properties of the plain object.
12904    *
12905    * @static
12906    * @memberOf _
12907    * @category Lang
12908    * @param {*} value The value to convert.
12909    * @returns {Object} Returns the converted plain object.
12910    * @example
12911    *
12912    * function Foo() {
12913    *   this.b = 2;
12914    * }
12915    *
12916    * Foo.prototype.c = 3;
12917    *
12918    * _.assign({ 'a': 1 }, new Foo);
12919    * // => { 'a': 1, 'b': 2 }
12920    *
12921    * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
12922    * // => { 'a': 1, 'b': 2, 'c': 3 }
12923    */
12924   function toPlainObject(value) {
12925     return baseCopy(value, keysIn(value));
12926   }
12927
12928   /*------------------------------------------------------------------------*/
12929
12930   /**
12931    * Assigns own enumerable properties of source object(s) to the destination
12932    * object. Subsequent sources overwrite property assignments of previous sources.
12933    * If `customizer` is provided it is invoked to produce the assigned values.
12934    * The `customizer` is bound to `thisArg` and invoked with five arguments:
12935    * (objectValue, sourceValue, key, object, source).
12936    *
12937    * **Note:** This method mutates `object` and is based on
12938    * [`Object.assign`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign).
12939    *
12940    * @static
12941    * @memberOf _
12942    * @alias extend
12943    * @category Object
12944    * @param {Object} object The destination object.
12945    * @param {...Object} [sources] The source objects.
12946    * @param {Function} [customizer] The function to customize assigned values.
12947    * @param {*} [thisArg] The `this` binding of `customizer`.
12948    * @returns {Object} Returns `object`.
12949    * @example
12950    *
12951    * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' });
12952    * // => { 'user': 'fred', 'age': 40 }
12953    *
12954    * // using a customizer callback
12955    * var defaults = _.partialRight(_.assign, function(value, other) {
12956    *   return _.isUndefined(value) ? other : value;
12957    * });
12958    *
12959    * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
12960    * // => { 'user': 'barney', 'age': 36 }
12961    */
12962   var assign = createAssigner(function(object, source, customizer) {
12963     return customizer
12964       ? assignWith(object, source, customizer)
12965       : baseAssign(object, source);
12966   });
12967
12968   /**
12969    * Iterates over own enumerable properties of an object invoking `iteratee`
12970    * for each property. The `iteratee` is bound to `thisArg` and invoked with
12971    * three arguments: (value, key, object). Iteratee functions may exit iteration
12972    * early by explicitly returning `false`.
12973    *
12974    * @static
12975    * @memberOf _
12976    * @category Object
12977    * @param {Object} object The object to iterate over.
12978    * @param {Function} [iteratee=_.identity] The function invoked per iteration.
12979    * @param {*} [thisArg] The `this` binding of `iteratee`.
12980    * @returns {Object} Returns `object`.
12981    * @example
12982    *
12983    * function Foo() {
12984    *   this.a = 1;
12985    *   this.b = 2;
12986    * }
12987    *
12988    * Foo.prototype.c = 3;
12989    *
12990    * _.forOwn(new Foo, function(value, key) {
12991    *   console.log(key);
12992    * });
12993    * // => logs 'a' and 'b' (iteration order is not guaranteed)
12994    */
12995   var forOwn = createForOwn(baseForOwn);
12996
12997   /**
12998    * Creates an array of the own enumerable property names of `object`.
12999    *
13000    * **Note:** Non-object values are coerced to objects. See the
13001    * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys)
13002    * for more details.
13003    *
13004    * @static
13005    * @memberOf _
13006    * @category Object
13007    * @param {Object} object The object to query.
13008    * @returns {Array} Returns the array of property names.
13009    * @example
13010    *
13011    * function Foo() {
13012    *   this.a = 1;
13013    *   this.b = 2;
13014    * }
13015    *
13016    * Foo.prototype.c = 3;
13017    *
13018    * _.keys(new Foo);
13019    * // => ['a', 'b'] (iteration order is not guaranteed)
13020    *
13021    * _.keys('hi');
13022    * // => ['0', '1']
13023    */
13024   var keys = !nativeKeys ? shimKeys : function(object) {
13025     var Ctor = object == null ? null : object.constructor;
13026     if ((typeof Ctor == 'function' && Ctor.prototype === object) ||
13027         (typeof object == 'function' ? lodash.support.enumPrototypes : isArrayLike(object))) {
13028       return shimKeys(object);
13029     }
13030     return isObject(object) ? nativeKeys(object) : [];
13031   };
13032
13033   /**
13034    * Creates an array of the own and inherited enumerable property names of `object`.
13035    *
13036    * **Note:** Non-object values are coerced to objects.
13037    *
13038    * @static
13039    * @memberOf _
13040    * @category Object
13041    * @param {Object} object The object to query.
13042    * @returns {Array} Returns the array of property names.
13043    * @example
13044    *
13045    * function Foo() {
13046    *   this.a = 1;
13047    *   this.b = 2;
13048    * }
13049    *
13050    * Foo.prototype.c = 3;
13051    *
13052    * _.keysIn(new Foo);
13053    * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
13054    */
13055   function keysIn(object) {
13056     if (object == null) {
13057       return [];
13058     }
13059     if (!isObject(object)) {
13060       object = Object(object);
13061     }
13062     var length = object.length,
13063         support = lodash.support;
13064
13065     length = (length && isLength(length) &&
13066       (isArray(object) || isArguments(object) || isString(object)) && length) || 0;
13067
13068     var Ctor = object.constructor,
13069         index = -1,
13070         proto = (isFunction(Ctor) && Ctor.prototype) || objectProto,
13071         isProto = proto === object,
13072         result = Array(length),
13073         skipIndexes = length > 0,
13074         skipErrorProps = support.enumErrorProps && (object === errorProto || object instanceof Error),
13075         skipProto = support.enumPrototypes && isFunction(object);
13076
13077     while (++index < length) {
13078       result[index] = (index + '');
13079     }
13080     // lodash skips the `constructor` property when it infers it is iterating
13081     // over a `prototype` object because IE < 9 can't set the `[[Enumerable]]`
13082     // attribute of an existing property and the `constructor` property of a
13083     // prototype defaults to non-enumerable.
13084     for (var key in object) {
13085       if (!(skipProto && key == 'prototype') &&
13086           !(skipErrorProps && (key == 'message' || key == 'name')) &&
13087           !(skipIndexes && isIndex(key, length)) &&
13088           !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
13089         result.push(key);
13090       }
13091     }
13092     if (support.nonEnumShadows && object !== objectProto) {
13093       var tag = object === stringProto ? stringTag : (object === errorProto ? errorTag : objToString.call(object)),
13094           nonEnums = nonEnumProps[tag] || nonEnumProps[objectTag];
13095
13096       if (tag == objectTag) {
13097         proto = objectProto;
13098       }
13099       length = shadowProps.length;
13100       while (length--) {
13101         key = shadowProps[length];
13102         var nonEnum = nonEnums[key];
13103         if (!(isProto && nonEnum) &&
13104             (nonEnum ? hasOwnProperty.call(object, key) : object[key] !== proto[key])) {
13105           result.push(key);
13106         }
13107       }
13108     }
13109     return result;
13110   }
13111
13112   /**
13113    * Recursively merges own enumerable properties of the source object(s), that
13114    * don't resolve to `undefined` into the destination object. Subsequent sources
13115    * overwrite property assignments of previous sources. If `customizer` is
13116    * provided it is invoked to produce the merged values of the destination and
13117    * source properties. If `customizer` returns `undefined` merging is handled
13118    * by the method instead. The `customizer` is bound to `thisArg` and invoked
13119    * with five arguments: (objectValue, sourceValue, key, object, source).
13120    *
13121    * @static
13122    * @memberOf _
13123    * @category Object
13124    * @param {Object} object The destination object.
13125    * @param {...Object} [sources] The source objects.
13126    * @param {Function} [customizer] The function to customize assigned values.
13127    * @param {*} [thisArg] The `this` binding of `customizer`.
13128    * @returns {Object} Returns `object`.
13129    * @example
13130    *
13131    * var users = {
13132    *   'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
13133    * };
13134    *
13135    * var ages = {
13136    *   'data': [{ 'age': 36 }, { 'age': 40 }]
13137    * };
13138    *
13139    * _.merge(users, ages);
13140    * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
13141    *
13142    * // using a customizer callback
13143    * var object = {
13144    *   'fruits': ['apple'],
13145    *   'vegetables': ['beet']
13146    * };
13147    *
13148    * var other = {
13149    *   'fruits': ['banana'],
13150    *   'vegetables': ['carrot']
13151    * };
13152    *
13153    * _.merge(object, other, function(a, b) {
13154    *   if (_.isArray(a)) {
13155    *     return a.concat(b);
13156    *   }
13157    * });
13158    * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
13159    */
13160   var merge = createAssigner(baseMerge);
13161
13162   /**
13163    * The opposite of `_.pick`; this method creates an object composed of the
13164    * own and inherited enumerable properties of `object` that are not omitted.
13165    *
13166    * @static
13167    * @memberOf _
13168    * @category Object
13169    * @param {Object} object The source object.
13170    * @param {Function|...(string|string[])} [predicate] The function invoked per
13171    *  iteration or property names to omit, specified as individual property
13172    *  names or arrays of property names.
13173    * @param {*} [thisArg] The `this` binding of `predicate`.
13174    * @returns {Object} Returns the new object.
13175    * @example
13176    *
13177    * var object = { 'user': 'fred', 'age': 40 };
13178    *
13179    * _.omit(object, 'age');
13180    * // => { 'user': 'fred' }
13181    *
13182    * _.omit(object, _.isNumber);
13183    * // => { 'user': 'fred' }
13184    */
13185   var omit = restParam(function(object, props) {
13186     if (object == null) {
13187       return {};
13188     }
13189     if (typeof props[0] != 'function') {
13190       var props = arrayMap(baseFlatten(props), String);
13191       return pickByArray(object, baseDifference(keysIn(object), props));
13192     }
13193     var predicate = bindCallback(props[0], props[1], 3);
13194     return pickByCallback(object, function(value, key, object) {
13195       return !predicate(value, key, object);
13196     });
13197   });
13198
13199   /**
13200    * Creates a two dimensional array of the key-value pairs for `object`,
13201    * e.g. `[[key1, value1], [key2, value2]]`.
13202    *
13203    * @static
13204    * @memberOf _
13205    * @category Object
13206    * @param {Object} object The object to query.
13207    * @returns {Array} Returns the new array of key-value pairs.
13208    * @example
13209    *
13210    * _.pairs({ 'barney': 36, 'fred': 40 });
13211    * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed)
13212    */
13213   function pairs(object) {
13214     object = toObject(object);
13215
13216     var index = -1,
13217         props = keys(object),
13218         length = props.length,
13219         result = Array(length);
13220
13221     while (++index < length) {
13222       var key = props[index];
13223       result[index] = [key, object[key]];
13224     }
13225     return result;
13226   }
13227
13228   /**
13229    * Creates an object composed of the picked `object` properties. Property
13230    * names may be specified as individual arguments or as arrays of property
13231    * names. If `predicate` is provided it is invoked for each property of `object`
13232    * picking the properties `predicate` returns truthy for. The predicate is
13233    * bound to `thisArg` and invoked with three arguments: (value, key, object).
13234    *
13235    * @static
13236    * @memberOf _
13237    * @category Object
13238    * @param {Object} object The source object.
13239    * @param {Function|...(string|string[])} [predicate] The function invoked per
13240    *  iteration or property names to pick, specified as individual property
13241    *  names or arrays of property names.
13242    * @param {*} [thisArg] The `this` binding of `predicate`.
13243    * @returns {Object} Returns the new object.
13244    * @example
13245    *
13246    * var object = { 'user': 'fred', 'age': 40 };
13247    *
13248    * _.pick(object, 'user');
13249    * // => { 'user': 'fred' }
13250    *
13251    * _.pick(object, _.isString);
13252    * // => { 'user': 'fred' }
13253    */
13254   var pick = restParam(function(object, props) {
13255     if (object == null) {
13256       return {};
13257     }
13258     return typeof props[0] == 'function'
13259       ? pickByCallback(object, bindCallback(props[0], props[1], 3))
13260       : pickByArray(object, baseFlatten(props));
13261   });
13262
13263   /**
13264    * Creates an array of the own enumerable property values of `object`.
13265    *
13266    * **Note:** Non-object values are coerced to objects.
13267    *
13268    * @static
13269    * @memberOf _
13270    * @category Object
13271    * @param {Object} object The object to query.
13272    * @returns {Array} Returns the array of property values.
13273    * @example
13274    *
13275    * function Foo() {
13276    *   this.a = 1;
13277    *   this.b = 2;
13278    * }
13279    *
13280    * Foo.prototype.c = 3;
13281    *
13282    * _.values(new Foo);
13283    * // => [1, 2] (iteration order is not guaranteed)
13284    *
13285    * _.values('hi');
13286    * // => ['h', 'i']
13287    */
13288   function values(object) {
13289     return baseValues(object, keys(object));
13290   }
13291
13292   /*------------------------------------------------------------------------*/
13293
13294   /**
13295    * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?",
13296    * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`.
13297    *
13298    * @static
13299    * @memberOf _
13300    * @category String
13301    * @param {string} [string=''] The string to escape.
13302    * @returns {string} Returns the escaped string.
13303    * @example
13304    *
13305    * _.escapeRegExp('[lodash](https://lodash.com/)');
13306    * // => '\[lodash\]\(https:\/\/lodash\.com\/\)'
13307    */
13308   function escapeRegExp(string) {
13309     string = baseToString(string);
13310     return (string && reHasRegExpChars.test(string))
13311       ? string.replace(reRegExpChars, '\\$&')
13312       : string;
13313   }
13314
13315   /*------------------------------------------------------------------------*/
13316
13317   /**
13318    * Creates a function that invokes `func` with the `this` binding of `thisArg`
13319    * and arguments of the created function. If `func` is a property name the
13320    * created callback returns the property value for a given element. If `func`
13321    * is an object the created callback returns `true` for elements that contain
13322    * the equivalent object properties, otherwise it returns `false`.
13323    *
13324    * @static
13325    * @memberOf _
13326    * @alias iteratee
13327    * @category Utility
13328    * @param {*} [func=_.identity] The value to convert to a callback.
13329    * @param {*} [thisArg] The `this` binding of `func`.
13330    * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
13331    * @returns {Function} Returns the callback.
13332    * @example
13333    *
13334    * var users = [
13335    *   { 'user': 'barney', 'age': 36 },
13336    *   { 'user': 'fred',   'age': 40 }
13337    * ];
13338    *
13339    * // wrap to create custom callback shorthands
13340    * _.callback = _.wrap(_.callback, function(callback, func, thisArg) {
13341    *   var match = /^(.+?)__([gl]t)(.+)$/.exec(func);
13342    *   if (!match) {
13343    *     return callback(func, thisArg);
13344    *   }
13345    *   return function(object) {
13346    *     return match[2] == 'gt'
13347    *       ? object[match[1]] > match[3]
13348    *       : object[match[1]] < match[3];
13349    *   };
13350    * });
13351    *
13352    * _.filter(users, 'age__gt36');
13353    * // => [{ 'user': 'fred', 'age': 40 }]
13354    */
13355   function callback(func, thisArg, guard) {
13356     if (guard && isIterateeCall(func, thisArg, guard)) {
13357       thisArg = null;
13358     }
13359     return isObjectLike(func)
13360       ? matches(func)
13361       : baseCallback(func, thisArg);
13362   }
13363
13364   /**
13365    * Creates a function that returns `value`.
13366    *
13367    * @static
13368    * @memberOf _
13369    * @category Utility
13370    * @param {*} value The value to return from the new function.
13371    * @returns {Function} Returns the new function.
13372    * @example
13373    *
13374    * var object = { 'user': 'fred' };
13375    * var getter = _.constant(object);
13376    *
13377    * getter() === object;
13378    * // => true
13379    */
13380   function constant(value) {
13381     return function() {
13382       return value;
13383     };
13384   }
13385
13386   /**
13387    * This method returns the first argument provided to it.
13388    *
13389    * @static
13390    * @memberOf _
13391    * @category Utility
13392    * @param {*} value Any value.
13393    * @returns {*} Returns `value`.
13394    * @example
13395    *
13396    * var object = { 'user': 'fred' };
13397    *
13398    * _.identity(object) === object;
13399    * // => true
13400    */
13401   function identity(value) {
13402     return value;
13403   }
13404
13405   /**
13406    * Creates a function that performs a deep comparison between a given object
13407    * and `source`, returning `true` if the given object has equivalent property
13408    * values, else `false`.
13409    *
13410    * **Note:** This method supports comparing arrays, booleans, `Date` objects,
13411    * numbers, `Object` objects, regexes, and strings. Objects are compared by
13412    * their own, not inherited, enumerable properties. For comparing a single
13413    * own or inherited property value see `_.matchesProperty`.
13414    *
13415    * @static
13416    * @memberOf _
13417    * @category Utility
13418    * @param {Object} source The object of property values to match.
13419    * @returns {Function} Returns the new function.
13420    * @example
13421    *
13422    * var users = [
13423    *   { 'user': 'barney', 'age': 36, 'active': true },
13424    *   { 'user': 'fred',   'age': 40, 'active': false }
13425    * ];
13426    *
13427    * _.filter(users, _.matches({ 'age': 40, 'active': false }));
13428    * // => [{ 'user': 'fred', 'age': 40, 'active': false }]
13429    */
13430   function matches(source) {
13431     return baseMatches(baseClone(source, true));
13432   }
13433
13434   /**
13435    * Adds all own enumerable function properties of a source object to the
13436    * destination object. If `object` is a function then methods are added to
13437    * its prototype as well.
13438    *
13439    * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
13440    * avoid conflicts caused by modifying the original.
13441    *
13442    * @static
13443    * @memberOf _
13444    * @category Utility
13445    * @param {Function|Object} [object=lodash] The destination object.
13446    * @param {Object} source The object of functions to add.
13447    * @param {Object} [options] The options object.
13448    * @param {boolean} [options.chain=true] Specify whether the functions added
13449    *  are chainable.
13450    * @returns {Function|Object} Returns `object`.
13451    * @example
13452    *
13453    * function vowels(string) {
13454    *   return _.filter(string, function(v) {
13455    *     return /[aeiou]/i.test(v);
13456    *   });
13457    * }
13458    *
13459    * _.mixin({ 'vowels': vowels });
13460    * _.vowels('fred');
13461    * // => ['e']
13462    *
13463    * _('fred').vowels().value();
13464    * // => ['e']
13465    *
13466    * _.mixin({ 'vowels': vowels }, { 'chain': false });
13467    * _('fred').vowels();
13468    * // => ['e']
13469    */
13470   function mixin(object, source, options) {
13471     if (options == null) {
13472       var isObj = isObject(source),
13473           props = isObj ? keys(source) : null,
13474           methodNames = (props && props.length) ? baseFunctions(source, props) : null;
13475
13476       if (!(methodNames ? methodNames.length : isObj)) {
13477         methodNames = false;
13478         options = source;
13479         source = object;
13480         object = this;
13481       }
13482     }
13483     if (!methodNames) {
13484       methodNames = baseFunctions(source, keys(source));
13485     }
13486     var chain = true,
13487         index = -1,
13488         isFunc = isFunction(object),
13489         length = methodNames.length;
13490
13491     if (options === false) {
13492       chain = false;
13493     } else if (isObject(options) && 'chain' in options) {
13494       chain = options.chain;
13495     }
13496     while (++index < length) {
13497       var methodName = methodNames[index],
13498           func = source[methodName];
13499
13500       object[methodName] = func;
13501       if (isFunc) {
13502         object.prototype[methodName] = (function(func) {
13503           return function() {
13504             var chainAll = this.__chain__;
13505             if (chain || chainAll) {
13506               var result = object(this.__wrapped__),
13507                   actions = result.__actions__ = arrayCopy(this.__actions__);
13508
13509               actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
13510               result.__chain__ = chainAll;
13511               return result;
13512             }
13513             var args = [this.value()];
13514             push.apply(args, arguments);
13515             return func.apply(object, args);
13516           };
13517         }(func));
13518       }
13519     }
13520     return object;
13521   }
13522
13523   /**
13524    * A no-operation function that returns `undefined` regardless of the
13525    * arguments it receives.
13526    *
13527    * @static
13528    * @memberOf _
13529    * @category Utility
13530    * @example
13531    *
13532    * var object = { 'user': 'fred' };
13533    *
13534    * _.noop(object) === undefined;
13535    * // => true
13536    */
13537   function noop() {
13538     // No operation performed.
13539   }
13540
13541   /**
13542    * Creates a function that returns the property value at `path` on a
13543    * given object.
13544    *
13545    * @static
13546    * @memberOf _
13547    * @category Utility
13548    * @param {Array|string} path The path of the property to get.
13549    * @returns {Function} Returns the new function.
13550    * @example
13551    *
13552    * var objects = [
13553    *   { 'a': { 'b': { 'c': 2 } } },
13554    *   { 'a': { 'b': { 'c': 1 } } }
13555    * ];
13556    *
13557    * _.map(objects, _.property('a.b.c'));
13558    * // => [2, 1]
13559    *
13560    * _.pluck(_.sortBy(objects, _.property(['a', 'b', 'c'])), 'a.b.c');
13561    * // => [1, 2]
13562    */
13563   function property(path) {
13564     return isKey(path) ? baseProperty(path) : basePropertyDeep(path);
13565   }
13566
13567   /*------------------------------------------------------------------------*/
13568
13569   // Ensure wrappers are instances of `baseLodash`.
13570   lodash.prototype = baseLodash.prototype;
13571
13572   LodashWrapper.prototype = baseCreate(baseLodash.prototype);
13573   LodashWrapper.prototype.constructor = LodashWrapper;
13574
13575   LazyWrapper.prototype = baseCreate(baseLodash.prototype);
13576   LazyWrapper.prototype.constructor = LazyWrapper;
13577
13578   // Add functions to the `Set` cache.
13579   SetCache.prototype.push = cachePush;
13580
13581   // Add functions that return wrapped values when chaining.
13582   lodash.assign = assign;
13583   lodash.bind = bind;
13584   lodash.callback = callback;
13585   lodash.chain = chain;
13586   lodash.chunk = chunk;
13587   lodash.compact = compact;
13588   lodash.constant = constant;
13589   lodash.debounce = debounce;
13590   lodash.difference = difference;
13591   lodash.filter = filter;
13592   lodash.flatten = flatten;
13593   lodash.forEach = forEach;
13594   lodash.forOwn = forOwn;
13595   lodash.groupBy = groupBy;
13596   lodash.intersection = intersection;
13597   lodash.keys = keys;
13598   lodash.keysIn = keysIn;
13599   lodash.map = map;
13600   lodash.matches = matches;
13601   lodash.merge = merge;
13602   lodash.mixin = mixin;
13603   lodash.omit = omit;
13604   lodash.pairs = pairs;
13605   lodash.pick = pick;
13606   lodash.pluck = pluck;
13607   lodash.property = property;
13608   lodash.reject = reject;
13609   lodash.restParam = restParam;
13610   lodash.tap = tap;
13611   lodash.throttle = throttle;
13612   lodash.thru = thru;
13613   lodash.toPlainObject = toPlainObject;
13614   lodash.union = union;
13615   lodash.uniq = uniq;
13616   lodash.values = values;
13617   lodash.without = without;
13618
13619   // Add aliases.
13620   lodash.collect = map;
13621   lodash.each = forEach;
13622   lodash.extend = assign;
13623   lodash.iteratee = callback;
13624   lodash.select = filter;
13625   lodash.unique = uniq;
13626
13627   // Add functions to `lodash.prototype`.
13628   mixin(lodash, lodash);
13629
13630   /*------------------------------------------------------------------------*/
13631
13632   // Add functions that return unwrapped values when chaining.
13633   lodash.clone = clone;
13634   lodash.cloneDeep = cloneDeep;
13635   lodash.escapeRegExp = escapeRegExp;
13636   lodash.every = every;
13637   lodash.find = find;
13638   lodash.first = first;
13639   lodash.identity = identity;
13640   lodash.includes = includes;
13641   lodash.indexOf = indexOf;
13642   lodash.isArguments = isArguments;
13643   lodash.isArray = isArray;
13644   lodash.isEmpty = isEmpty;
13645   lodash.isEqual = isEqual;
13646   lodash.isFunction = isFunction;
13647   lodash.isNative = isNative;
13648   lodash.isObject = isObject;
13649   lodash.isPlainObject = isPlainObject;
13650   lodash.isString = isString;
13651   lodash.isTypedArray = isTypedArray;
13652   lodash.last = last;
13653   lodash.noop = noop;
13654   lodash.now = now;
13655   lodash.reduce = reduce;
13656   lodash.some = some;
13657
13658   // Add aliases.
13659   lodash.all = every;
13660   lodash.any = some;
13661   lodash.contains = includes;
13662   lodash.eq = isEqual;
13663   lodash.detect = find;
13664   lodash.foldl = reduce;
13665   lodash.head = first;
13666   lodash.include = includes;
13667   lodash.inject = reduce;
13668
13669   mixin(lodash, (function() {
13670     var source = {};
13671     baseForOwn(lodash, function(func, methodName) {
13672       if (!lodash.prototype[methodName]) {
13673         source[methodName] = func;
13674       }
13675     });
13676     return source;
13677   }()), false);
13678
13679   /*------------------------------------------------------------------------*/
13680
13681   lodash.prototype.sample = function(n) {
13682     if (!this.__chain__ && n == null) {
13683       return sample(this.value());
13684     }
13685     return this.thru(function(value) {
13686       return sample(value, n);
13687     });
13688   };
13689
13690   /*------------------------------------------------------------------------*/
13691
13692   /**
13693    * The semantic version number.
13694    *
13695    * @static
13696    * @memberOf _
13697    * @type string
13698    */
13699   lodash.VERSION = VERSION;
13700
13701   // Assign default placeholders.
13702   bind.placeholder = lodash;
13703
13704   // Add `LazyWrapper` methods that accept an `iteratee` value.
13705   arrayEach(['dropWhile', 'filter', 'map', 'takeWhile'], function(methodName, type) {
13706     var isFilter = type != LAZY_MAP_FLAG,
13707         isDropWhile = type == LAZY_DROP_WHILE_FLAG;
13708
13709     LazyWrapper.prototype[methodName] = function(iteratee, thisArg) {
13710       var filtered = this.__filtered__,
13711           result = (filtered && isDropWhile) ? new LazyWrapper(this) : this.clone(),
13712           iteratees = result.__iteratees__ || (result.__iteratees__ = []);
13713
13714       iteratees.push({
13715         'done': false,
13716         'count': 0,
13717         'index': 0,
13718         'iteratee': getCallback(iteratee, thisArg, 1),
13719         'limit': -1,
13720         'type': type
13721       });
13722
13723       result.__filtered__ = filtered || isFilter;
13724       return result;
13725     };
13726   });
13727
13728   // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
13729   arrayEach(['drop', 'take'], function(methodName, index) {
13730     var whileName = methodName + 'While';
13731
13732     LazyWrapper.prototype[methodName] = function(n) {
13733       var filtered = this.__filtered__,
13734           result = (filtered && !index) ? this.dropWhile() : this.clone();
13735
13736       n = n == null ? 1 : nativeMax(floor(n) || 0, 0);
13737       if (filtered) {
13738         if (index) {
13739           result.__takeCount__ = nativeMin(result.__takeCount__, n);
13740         } else {
13741           last(result.__iteratees__).limit = n;
13742         }
13743       } else {
13744         var views = result.__views__ || (result.__views__ = []);
13745         views.push({ 'size': n, 'type': methodName + (result.__dir__ < 0 ? 'Right' : '') });
13746       }
13747       return result;
13748     };
13749
13750     LazyWrapper.prototype[methodName + 'Right'] = function(n) {
13751       return this.reverse()[methodName](n).reverse();
13752     };
13753
13754     LazyWrapper.prototype[methodName + 'RightWhile'] = function(predicate, thisArg) {
13755       return this.reverse()[whileName](predicate, thisArg).reverse();
13756     };
13757   });
13758
13759   // Add `LazyWrapper` methods for `_.first` and `_.last`.
13760   arrayEach(['first', 'last'], function(methodName, index) {
13761     var takeName = 'take' + (index ? 'Right' : '');
13762
13763     LazyWrapper.prototype[methodName] = function() {
13764       return this[takeName](1).value()[0];
13765     };
13766   });
13767
13768   // Add `LazyWrapper` methods for `_.initial` and `_.rest`.
13769   arrayEach(['initial', 'rest'], function(methodName, index) {
13770     var dropName = 'drop' + (index ? '' : 'Right');
13771
13772     LazyWrapper.prototype[methodName] = function() {
13773       return this[dropName](1);
13774     };
13775   });
13776
13777   // Add `LazyWrapper` methods for `_.pluck` and `_.where`.
13778   arrayEach(['pluck', 'where'], function(methodName, index) {
13779     var operationName = index ? 'filter' : 'map',
13780         createCallback = index ? baseMatches : property;
13781
13782     LazyWrapper.prototype[methodName] = function(value) {
13783       return this[operationName](createCallback(value));
13784     };
13785   });
13786
13787   LazyWrapper.prototype.compact = function() {
13788     return this.filter(identity);
13789   };
13790
13791   LazyWrapper.prototype.reject = function(predicate, thisArg) {
13792     predicate = getCallback(predicate, thisArg, 1);
13793     return this.filter(function(value) {
13794       return !predicate(value);
13795     });
13796   };
13797
13798   LazyWrapper.prototype.slice = function(start, end) {
13799     start = start == null ? 0 : (+start || 0);
13800
13801     var result = this;
13802     if (start < 0) {
13803       result = this.takeRight(-start);
13804     } else if (start) {
13805       result = this.drop(start);
13806     }
13807     if (end !== undefined) {
13808       end = (+end || 0);
13809       result = end < 0 ? result.dropRight(-end) : result.take(end - start);
13810     }
13811     return result;
13812   };
13813
13814   LazyWrapper.prototype.toArray = function() {
13815     return this.drop(0);
13816   };
13817
13818   // Add `LazyWrapper` methods to `lodash.prototype`.
13819   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
13820     var lodashFunc = lodash[methodName];
13821     if (!lodashFunc) {
13822       return;
13823     }
13824     var checkIteratee = /^(?:filter|map|reject)|While$/.test(methodName),
13825         retUnwrapped = /^(?:first|last)$/.test(methodName);
13826
13827     lodash.prototype[methodName] = function() {
13828       var args = arguments,
13829           chainAll = this.__chain__,
13830           value = this.__wrapped__,
13831           isHybrid = !!this.__actions__.length,
13832           isLazy = value instanceof LazyWrapper,
13833           iteratee = args[0],
13834           useLazy = isLazy || isArray(value);
13835
13836       if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
13837         // avoid lazy use if the iteratee has a "length" value other than `1`
13838         isLazy = useLazy = false;
13839       }
13840       var onlyLazy = isLazy && !isHybrid;
13841       if (retUnwrapped && !chainAll) {
13842         return onlyLazy
13843           ? func.call(value)
13844           : lodashFunc.call(lodash, this.value());
13845       }
13846       var interceptor = function(value) {
13847         var otherArgs = [value];
13848         push.apply(otherArgs, args);
13849         return lodashFunc.apply(lodash, otherArgs);
13850       };
13851       if (useLazy) {
13852         var wrapper = onlyLazy ? value : new LazyWrapper(this),
13853             result = func.apply(wrapper, args);
13854
13855         if (!retUnwrapped && (isHybrid || result.__actions__)) {
13856           var actions = result.__actions__ || (result.__actions__ = []);
13857           actions.push({ 'func': thru, 'args': [interceptor], 'thisArg': lodash });
13858         }
13859         return new LodashWrapper(result, chainAll);
13860       }
13861       return this.thru(interceptor);
13862     };
13863   });
13864
13865   // Add `Array` and `String` methods to `lodash.prototype`.
13866   arrayEach(['concat', 'join', 'pop', 'push', 'replace', 'shift', 'sort', 'splice', 'split', 'unshift'], function(methodName) {
13867     var protoFunc = (/^(?:replace|split)$/.test(methodName) ? stringProto : arrayProto)[methodName],
13868         chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
13869         fixObjects = !support.spliceObjects && /^(?:pop|shift|splice)$/.test(methodName),
13870         retUnwrapped = /^(?:join|pop|replace|shift)$/.test(methodName);
13871
13872     // Avoid array-like object bugs with `Array#shift` and `Array#splice` in
13873     // IE < 9, Firefox < 10, and RingoJS.
13874     var func = !fixObjects ? protoFunc : function() {
13875       var result = protoFunc.apply(this, arguments);
13876       if (this.length === 0) {
13877         delete this[0];
13878       }
13879       return result;
13880     };
13881
13882     lodash.prototype[methodName] = function() {
13883       var args = arguments;
13884       if (retUnwrapped && !this.__chain__) {
13885         return func.apply(this.value(), args);
13886       }
13887       return this[chainName](function(value) {
13888         return func.apply(value, args);
13889       });
13890     };
13891   });
13892
13893   // Map minified function names to their real names.
13894   baseForOwn(LazyWrapper.prototype, function(func, methodName) {
13895     var lodashFunc = lodash[methodName];
13896     if (lodashFunc) {
13897       var key = lodashFunc.name,
13898           names = realNames[key] || (realNames[key] = []);
13899
13900       names.push({ 'name': methodName, 'func': lodashFunc });
13901     }
13902   });
13903
13904   realNames[createHybridWrapper(null, BIND_KEY_FLAG).name] = [{ 'name': 'wrapper', 'func': null }];
13905
13906   // Add functions to the lazy wrapper.
13907   LazyWrapper.prototype.clone = lazyClone;
13908   LazyWrapper.prototype.reverse = lazyReverse;
13909   LazyWrapper.prototype.value = lazyValue;
13910
13911   // Add chaining functions to the `lodash` wrapper.
13912   lodash.prototype.chain = wrapperChain;
13913   lodash.prototype.commit = wrapperCommit;
13914   lodash.prototype.plant = wrapperPlant;
13915   lodash.prototype.reverse = wrapperReverse;
13916   lodash.prototype.toString = wrapperToString;
13917   lodash.prototype.run = lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
13918
13919   // Add function aliases to the `lodash` wrapper.
13920   lodash.prototype.collect = lodash.prototype.map;
13921   lodash.prototype.head = lodash.prototype.first;
13922   lodash.prototype.select = lodash.prototype.filter;
13923   lodash.prototype.tail = lodash.prototype.rest;
13924
13925   /*--------------------------------------------------------------------------*/
13926
13927   if (freeExports && freeModule) {
13928     // Export for Node.js or RingoJS.
13929     if (moduleExports) {
13930       (freeModule.exports = lodash)._ = lodash;
13931     }
13932   }
13933   else {
13934     // Export for a browser or Rhino.
13935     root._ = lodash;
13936   }
13937 }.call(this));
13938 (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;
13939 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){
13940 'use strict';
13941
13942 var ohauth = require('ohauth'),
13943     xtend = require('xtend'),
13944     store = require('store');
13945
13946 // # osm-auth
13947 //
13948 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
13949 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
13950 // does not support custom headers, which this uses everywhere.
13951 module.exports = function(o) {
13952
13953     var oauth = {};
13954
13955     // authenticated users will also have a request token secret, but it's
13956     // not used in transactions with the server
13957     oauth.authenticated = function() {
13958         return !!(token('oauth_token') && token('oauth_token_secret'));
13959     };
13960
13961     oauth.logout = function() {
13962         token('oauth_token', '');
13963         token('oauth_token_secret', '');
13964         token('oauth_request_token_secret', '');
13965         return oauth;
13966     };
13967
13968     // TODO: detect lack of click event
13969     oauth.authenticate = function(callback) {
13970         if (oauth.authenticated()) return callback();
13971
13972         oauth.logout();
13973
13974         // ## Getting a request token
13975         var params = timenonce(getAuth(o)),
13976             url = o.url + '/oauth/request_token',
13977             timer;
13978
13979         params.oauth_signature = ohauth.signature(
13980             o.oauth_secret, '',
13981             ohauth.baseString('POST', url, params));
13982
13983         if (!o.singlepage) {
13984             // Create a 600x550 popup window in the center of the screen
13985             var w = 600, h = 550,
13986                 settings = [
13987                     ['width', w], ['height', h],
13988                     ['left', screen.width / 2 - w / 2],
13989                     ['top', screen.height / 2 - h / 2]].map(function(x) {
13990                         return x.join('=');
13991                     }).join(','),
13992                 popup = window.open('about:blank', 'oauth_window', settings);
13993
13994
13995             timer = setInterval(function() {
13996                 if (popup.closed) {
13997                     o.done();
13998                     clearInterval(timer);
13999                     callback('not authenticated', null);
14000                 }
14001             }, 100);
14002         }
14003
14004         // Request a request token. When this is complete, the popup
14005         // window is redirected to OSM's authorization page.
14006         ohauth.xhr('POST', url, params, null, {}, reqTokenDone);
14007         o.loading();
14008
14009         function reqTokenDone(err, xhr) {
14010             o.done();
14011             if (err) {
14012                 if (timer) clearInterval(timer);
14013                 return callback(err);
14014             }
14015             var resp = ohauth.stringQs(xhr.response);
14016             token('oauth_request_token_secret', resp.oauth_token_secret);
14017             var authorize_url = o.url + '/oauth/authorize?' + ohauth.qsString({
14018                 oauth_token: resp.oauth_token,
14019                 oauth_callback: location.href.replace('index.html', '')
14020                     .replace(/#.*/, '') + o.landing
14021             });
14022
14023             if (o.singlepage) {
14024                 location.href = authorize_url;
14025             } else {
14026                 popup.location = authorize_url;
14027             }
14028         }
14029
14030         // Called by a function in a landing page, in the popup window. The
14031         // window closes itself.
14032         window.authComplete = function(token) {
14033             if (timer) clearInterval(timer);
14034             var oauth_token = ohauth.stringQs(token.split('?')[1]);
14035             get_access_token(oauth_token.oauth_token);
14036             delete window.authComplete;
14037         };
14038
14039         // ## Getting an request token
14040         //
14041         // At this point we have an `oauth_token`, brought in from a function
14042         // call on a landing page popup.
14043         function get_access_token(oauth_token) {
14044             var url = o.url + '/oauth/access_token',
14045                 params = timenonce(getAuth(o)),
14046                 request_token_secret = token('oauth_request_token_secret');
14047             params.oauth_token = oauth_token;
14048             params.oauth_signature = ohauth.signature(
14049                 o.oauth_secret,
14050                 request_token_secret,
14051                 ohauth.baseString('POST', url, params));
14052
14053             // ## Getting an access token
14054             //
14055             // The final token required for authentication. At this point
14056             // we have a `request token secret`
14057             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
14058             o.loading();
14059         }
14060
14061         function accessTokenDone(err, xhr) {
14062             o.done();
14063             if (timer) clearInterval(timer);
14064             if (err) return callback(err);
14065             var access_token = ohauth.stringQs(xhr.response);
14066             token('oauth_token', access_token.oauth_token);
14067             token('oauth_token_secret', access_token.oauth_token_secret);
14068             callback(null, oauth);
14069         }
14070     };
14071
14072     oauth.bootstrapToken = function(oauth_token, callback) {
14073         // ## Getting an request token
14074         // At this point we have an `oauth_token`, brought in from a function
14075         // call on a landing page popup.
14076         function get_access_token(oauth_token) {
14077             var url = o.url + '/oauth/access_token',
14078                 params = timenonce(getAuth(o)),
14079                 request_token_secret = token('oauth_request_token_secret');
14080             params.oauth_token = oauth_token;
14081             params.oauth_signature = ohauth.signature(
14082                 o.oauth_secret,
14083                 request_token_secret,
14084                 ohauth.baseString('POST', url, params));
14085
14086             // ## Getting an access token
14087             // The final token required for authentication. At this point
14088             // we have a `request token secret`
14089             ohauth.xhr('POST', url, params, null, {}, accessTokenDone);
14090             o.loading();
14091         }
14092
14093         function accessTokenDone(err, xhr) {
14094             o.done();
14095             if (err) return callback(err);
14096             var access_token = ohauth.stringQs(xhr.response);
14097             token('oauth_token', access_token.oauth_token);
14098             token('oauth_token_secret', access_token.oauth_token_secret);
14099             callback(null, oauth);
14100         }
14101
14102         get_access_token(oauth_token);
14103     };
14104
14105     // # xhr
14106     //
14107     // A single XMLHttpRequest wrapper that does authenticated calls if the
14108     // user has logged in.
14109     oauth.xhr = function(options, callback) {
14110         if (!oauth.authenticated()) {
14111             if (o.auto) return oauth.authenticate(run);
14112             else return callback('not authenticated', null);
14113         } else return run();
14114
14115         function run() {
14116             var params = timenonce(getAuth(o)),
14117                 url = o.url + options.path,
14118                 oauth_token_secret = token('oauth_token_secret');
14119
14120             // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
14121             if ((!options.options || !options.options.header ||
14122                 options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
14123                 options.content) {
14124                 params = xtend(params, ohauth.stringQs(options.content));
14125             }
14126
14127             params.oauth_token = token('oauth_token');
14128             params.oauth_signature = ohauth.signature(
14129                 o.oauth_secret,
14130                 oauth_token_secret,
14131                 ohauth.baseString(options.method, url, params));
14132
14133             ohauth.xhr(options.method,
14134                 url, params, options.content, options.options, done);
14135         }
14136
14137         function done(err, xhr) {
14138             if (err) return callback(err);
14139             else if (xhr.responseXML) return callback(err, xhr.responseXML);
14140             else return callback(err, xhr.response);
14141         }
14142     };
14143
14144     // pre-authorize this object, if we can just get a token and token_secret
14145     // from the start
14146     oauth.preauth = function(c) {
14147         if (!c) return;
14148         if (c.oauth_token) token('oauth_token', c.oauth_token);
14149         if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
14150         return oauth;
14151     };
14152
14153     oauth.options = function(_) {
14154         if (!arguments.length) return o;
14155
14156         o = _;
14157
14158         o.url = o.url || 'http://www.openstreetmap.org';
14159         o.landing = o.landing || 'land.html';
14160
14161         o.singlepage = o.singlepage || false;
14162
14163         // Optional loading and loading-done functions for nice UI feedback.
14164         // by default, no-ops
14165         o.loading = o.loading || function() {};
14166         o.done = o.done || function() {};
14167
14168         return oauth.preauth(o);
14169     };
14170
14171     // 'stamp' an authentication object from `getAuth()`
14172     // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
14173     // and timestamp
14174     function timenonce(o) {
14175         o.oauth_timestamp = ohauth.timestamp();
14176         o.oauth_nonce = ohauth.nonce();
14177         return o;
14178     }
14179
14180     // get/set tokens. These are prefixed with the base URL so that `osm-auth`
14181     // can be used with multiple APIs and the keys in `localStorage`
14182     // will not clash
14183     var token;
14184
14185     if (store.enabled) {
14186         token = function (x, y) {
14187             if (arguments.length === 1) return store.get(o.url + x);
14188             else if (arguments.length === 2) return store.set(o.url + x, y);
14189         };
14190     } else {
14191         var storage = {};
14192         token = function (x, y) {
14193             if (arguments.length === 1) return storage[o.url + x];
14194             else if (arguments.length === 2) return storage[o.url + x] = y;
14195         };
14196     }
14197
14198     // Get an authentication object. If you just add and remove properties
14199     // from a single object, you'll need to use `delete` to make sure that
14200     // it doesn't contain undesired properties for authentication
14201     function getAuth(o) {
14202         return {
14203             oauth_consumer_key: o.oauth_consumer_key,
14204             oauth_signature_method: "HMAC-SHA1"
14205         };
14206     }
14207
14208     // potentially pre-authorize
14209     oauth.options(o);
14210
14211     return oauth;
14212 };
14213
14214 },{"ohauth":2,"store":3,"xtend":4}],3:[function(require,module,exports){
14215 (function(global){;(function(win){
14216         var store = {},
14217                 doc = win.document,
14218                 localStorageName = 'localStorage',
14219                 storage
14220
14221         store.disabled = false
14222         store.set = function(key, value) {}
14223         store.get = function(key) {}
14224         store.remove = function(key) {}
14225         store.clear = function() {}
14226         store.transact = function(key, defaultVal, transactionFn) {
14227                 var val = store.get(key)
14228                 if (transactionFn == null) {
14229                         transactionFn = defaultVal
14230                         defaultVal = null
14231                 }
14232                 if (typeof val == 'undefined') { val = defaultVal || {} }
14233                 transactionFn(val)
14234                 store.set(key, val)
14235         }
14236         store.getAll = function() {}
14237         store.forEach = function() {}
14238
14239         store.serialize = function(value) {
14240                 return JSON.stringify(value)
14241         }
14242         store.deserialize = function(value) {
14243                 if (typeof value != 'string') { return undefined }
14244                 try { return JSON.parse(value) }
14245                 catch(e) { return value || undefined }
14246         }
14247
14248         // Functions to encapsulate questionable FireFox 3.6.13 behavior
14249         // when about.config::dom.storage.enabled === false
14250         // See https://github.com/marcuswestin/store.js/issues#issue/13
14251         function isLocalStorageNameSupported() {
14252                 try { return (localStorageName in win && win[localStorageName]) }
14253                 catch(err) { return false }
14254         }
14255
14256         if (isLocalStorageNameSupported()) {
14257                 storage = win[localStorageName]
14258                 store.set = function(key, val) {
14259                         if (val === undefined) { return store.remove(key) }
14260                         storage.setItem(key, store.serialize(val))
14261                         return val
14262                 }
14263                 store.get = function(key) { return store.deserialize(storage.getItem(key)) }
14264                 store.remove = function(key) { storage.removeItem(key) }
14265                 store.clear = function() { storage.clear() }
14266                 store.getAll = function() {
14267                         var ret = {}
14268                         store.forEach(function(key, val) {
14269                                 ret[key] = val
14270                         })
14271                         return ret
14272                 }
14273                 store.forEach = function(callback) {
14274                         for (var i=0; i<storage.length; i++) {
14275                                 var key = storage.key(i)
14276                                 callback(key, store.get(key))
14277                         }
14278                 }
14279         } else if (doc.documentElement.addBehavior) {
14280                 var storageOwner,
14281                         storageContainer
14282                 // Since #userData storage applies only to specific paths, we need to
14283                 // somehow link our data to a specific path.  We choose /favicon.ico
14284                 // as a pretty safe option, since all browsers already make a request to
14285                 // this URL anyway and being a 404 will not hurt us here.  We wrap an
14286                 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
14287                 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
14288                 // since the iframe access rules appear to allow direct access and
14289                 // manipulation of the document element, even for a 404 page.  This
14290                 // document can be used instead of the current document (which would
14291                 // have been limited to the current path) to perform #userData storage.
14292                 try {
14293                         storageContainer = new ActiveXObject('htmlfile')
14294                         storageContainer.open()
14295                         storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></iframe>')
14296                         storageContainer.close()
14297                         storageOwner = storageContainer.w.frames[0].document
14298                         storage = storageOwner.createElement('div')
14299                 } catch(e) {
14300                         // somehow ActiveXObject instantiation failed (perhaps some special
14301                         // security settings or otherwse), fall back to per-path storage
14302                         storage = doc.createElement('div')
14303                         storageOwner = doc.body
14304                 }
14305                 function withIEStorage(storeFunction) {
14306                         return function() {
14307                                 var args = Array.prototype.slice.call(arguments, 0)
14308                                 args.unshift(storage)
14309                                 // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
14310                                 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
14311                                 storageOwner.appendChild(storage)
14312                                 storage.addBehavior('#default#userData')
14313                                 storage.load(localStorageName)
14314                                 var result = storeFunction.apply(store, args)
14315                                 storageOwner.removeChild(storage)
14316                                 return result
14317                         }
14318                 }
14319
14320                 // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
14321                 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
14322                 function ieKeyFix(key) {
14323                         return key.replace(forbiddenCharsRegex, '___')
14324                 }
14325                 store.set = withIEStorage(function(storage, key, val) {
14326                         key = ieKeyFix(key)
14327                         if (val === undefined) { return store.remove(key) }
14328                         storage.setAttribute(key, store.serialize(val))
14329                         storage.save(localStorageName)
14330                         return val
14331                 })
14332                 store.get = withIEStorage(function(storage, key) {
14333                         key = ieKeyFix(key)
14334                         return store.deserialize(storage.getAttribute(key))
14335                 })
14336                 store.remove = withIEStorage(function(storage, key) {
14337                         key = ieKeyFix(key)
14338                         storage.removeAttribute(key)
14339                         storage.save(localStorageName)
14340                 })
14341                 store.clear = withIEStorage(function(storage) {
14342                         var attributes = storage.XMLDocument.documentElement.attributes
14343                         storage.load(localStorageName)
14344                         for (var i=0, attr; attr=attributes[i]; i++) {
14345                                 storage.removeAttribute(attr.name)
14346                         }
14347                         storage.save(localStorageName)
14348                 })
14349                 store.getAll = function(storage) {
14350                         var ret = {}
14351                         store.forEach(function(key, val) {
14352                                 ret[key] = val
14353                         })
14354                         return ret
14355                 }
14356                 store.forEach = withIEStorage(function(storage, callback) {
14357                         var attributes = storage.XMLDocument.documentElement.attributes
14358                         for (var i=0, attr; attr=attributes[i]; ++i) {
14359                                 callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
14360                         }
14361                 })
14362         }
14363
14364         try {
14365                 var testKey = '__storejs__'
14366                 store.set(testKey, testKey)
14367                 if (store.get(testKey) != testKey) { store.disabled = true }
14368                 store.remove(testKey)
14369         } catch(e) {
14370                 store.disabled = true
14371         }
14372         store.enabled = !store.disabled
14373
14374         if (typeof module != 'undefined' && module.exports) { module.exports = store }
14375         else if (typeof define === 'function' && define.amd) { define(store) }
14376         else { win.store = store }
14377
14378 })(this.window || global);
14379
14380 })(window)
14381 },{}],5:[function(require,module,exports){
14382 module.exports = hasKeys
14383
14384 function hasKeys(source) {
14385     return source !== null &&
14386         (typeof source === "object" ||
14387         typeof source === "function")
14388 }
14389
14390 },{}],4:[function(require,module,exports){
14391 var Keys = require("object-keys")
14392 var hasKeys = require("./has-keys")
14393
14394 module.exports = extend
14395
14396 function extend() {
14397     var target = {}
14398
14399     for (var i = 0; i < arguments.length; i++) {
14400         var source = arguments[i]
14401
14402         if (!hasKeys(source)) {
14403             continue
14404         }
14405
14406         var keys = Keys(source)
14407
14408         for (var j = 0; j < keys.length; j++) {
14409             var name = keys[j]
14410             target[name] = source[name]
14411         }
14412     }
14413
14414     return target
14415 }
14416
14417 },{"./has-keys":5,"object-keys":6}],7:[function(require,module,exports){
14418 (function(global){/**
14419  * jsHashes - A fast and independent hashing library pure JavaScript implemented (ES3 compliant) for both server and client side
14420  *
14421  * @class Hashes
14422  * @author Tomas Aparicio <tomas@rijndael-project.com>
14423  * @license New BSD (see LICENSE file)
14424  * @version 1.0.4
14425  *
14426  * Algorithms specification:
14427  *
14428  * MD5 <http://www.ietf.org/rfc/rfc1321.txt>
14429  * RIPEMD-160 <http://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
14430  * SHA1   <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14431  * SHA256 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14432  * SHA512 <http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf>
14433  * HMAC <http://www.ietf.org/rfc/rfc2104.txt>
14434  *
14435  */
14436 (function(){
14437   var Hashes;
14438
14439   // private helper methods
14440   function utf8Encode(str) {
14441     var  x, y, output = '', i = -1, l;
14442
14443     if (str && str.length) {
14444       l = str.length;
14445       while ((i+=1) < l) {
14446         /* Decode utf-16 surrogate pairs */
14447         x = str.charCodeAt(i);
14448         y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
14449         if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
14450             x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
14451             i += 1;
14452         }
14453         /* Encode output as utf-8 */
14454         if (x <= 0x7F) {
14455             output += String.fromCharCode(x);
14456         } else if (x <= 0x7FF) {
14457             output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
14458                         0x80 | ( x & 0x3F));
14459         } else if (x <= 0xFFFF) {
14460             output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
14461                         0x80 | ((x >>> 6 ) & 0x3F),
14462                         0x80 | ( x & 0x3F));
14463         } else if (x <= 0x1FFFFF) {
14464             output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
14465                         0x80 | ((x >>> 12) & 0x3F),
14466                         0x80 | ((x >>> 6 ) & 0x3F),
14467                         0x80 | ( x & 0x3F));
14468         }
14469       }
14470     }
14471     return output;
14472   }
14473
14474   function utf8Decode(str) {
14475     var i, ac, c1, c2, c3, arr = [], l;
14476     i = ac = c1 = c2 = c3 = 0;
14477
14478     if (str && str.length) {
14479       l = str.length;
14480       str += '';
14481
14482       while (i < l) {
14483           c1 = str.charCodeAt(i);
14484           ac += 1;
14485           if (c1 < 128) {
14486               arr[ac] = String.fromCharCode(c1);
14487               i+=1;
14488           } else if (c1 > 191 && c1 < 224) {
14489               c2 = str.charCodeAt(i + 1);
14490               arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
14491               i += 2;
14492           } else {
14493               c2 = str.charCodeAt(i + 1);
14494               c3 = str.charCodeAt(i + 2);
14495               arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
14496               i += 3;
14497           }
14498       }
14499     }
14500     return arr.join('');
14501   }
14502
14503   /**
14504    * Add integers, wrapping at 2^32. This uses 16-bit operations internally
14505    * to work around bugs in some JS interpreters.
14506    */
14507   function safe_add(x, y) {
14508     var lsw = (x & 0xFFFF) + (y & 0xFFFF),
14509         msw = (x >> 16) + (y >> 16) + (lsw >> 16);
14510     return (msw << 16) | (lsw & 0xFFFF);
14511   }
14512
14513   /**
14514    * Bitwise rotate a 32-bit number to the left.
14515    */
14516   function bit_rol(num, cnt) {
14517     return (num << cnt) | (num >>> (32 - cnt));
14518   }
14519
14520   /**
14521    * Convert a raw string to a hex string
14522    */
14523   function rstr2hex(input, hexcase) {
14524     var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
14525         output = '', x, i = 0, l = input.length;
14526     for (; i < l; i+=1) {
14527       x = input.charCodeAt(i);
14528       output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
14529     }
14530     return output;
14531   }
14532
14533   /**
14534    * Encode a string as utf-16
14535    */
14536   function str2rstr_utf16le(input) {
14537     var i, l = input.length, output = '';
14538     for (i = 0; i < l; i+=1) {
14539       output += String.fromCharCode( input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF);
14540     }
14541     return output;
14542   }
14543
14544   function str2rstr_utf16be(input) {
14545     var i, l = input.length, output = '';
14546     for (i = 0; i < l; i+=1) {
14547       output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF);
14548     }
14549     return output;
14550   }
14551
14552   /**
14553    * Convert an array of big-endian words to a string
14554    */
14555   function binb2rstr(input) {
14556     var i, l = input.length * 32, output = '';
14557     for (i = 0; i < l; i += 8) {
14558         output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
14559     }
14560     return output;
14561   }
14562
14563   /**
14564    * Convert an array of little-endian words to a string
14565    */
14566   function binl2rstr(input) {
14567     var i, l = input.length * 32, output = '';
14568     for (i = 0;i < l; i += 8) {
14569       output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
14570     }
14571     return output;
14572   }
14573
14574   /**
14575    * Convert a raw string to an array of little-endian words
14576    * Characters >255 have their high-byte silently ignored.
14577    */
14578   function rstr2binl(input) {
14579     var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
14580     for (i = 0; i < lo; i+=1) {
14581       output[i] = 0;
14582     }
14583     for (i = 0; i < l; i += 8) {
14584       output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
14585     }
14586     return output;
14587   }
14588
14589   /**
14590    * Convert a raw string to an array of big-endian words
14591    * Characters >255 have their high-byte silently ignored.
14592    */
14593    function rstr2binb(input) {
14594       var i, l = input.length * 8, output = Array(input.length >> 2), lo = output.length;
14595       for (i = 0; i < lo; i+=1) {
14596             output[i] = 0;
14597         }
14598       for (i = 0; i < l; i += 8) {
14599             output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
14600         }
14601       return output;
14602    }
14603
14604   /**
14605    * Convert a raw string to an arbitrary string encoding
14606    */
14607   function rstr2any(input, encoding) {
14608     var divisor = encoding.length,
14609         remainders = Array(),
14610         i, q, x, ld, quotient, dividend, output, full_length;
14611
14612     /* Convert to an array of 16-bit big-endian values, forming the dividend */
14613     dividend = Array(Math.ceil(input.length / 2));
14614     ld = dividend.length;
14615     for (i = 0; i < ld; i+=1) {
14616       dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
14617     }
14618
14619     /**
14620      * Repeatedly perform a long division. The binary array forms the dividend,
14621      * the length of the encoding is the divisor. Once computed, the quotient
14622      * forms the dividend for the next step. We stop when the dividend is zerHashes.
14623      * All remainders are stored for later use.
14624      */
14625     while(dividend.length > 0) {
14626       quotient = Array();
14627       x = 0;
14628       for (i = 0; i < dividend.length; i+=1) {
14629         x = (x << 16) + dividend[i];
14630         q = Math.floor(x / divisor);
14631         x -= q * divisor;
14632         if (quotient.length > 0 || q > 0) {
14633           quotient[quotient.length] = q;
14634         }
14635       }
14636       remainders[remainders.length] = x;
14637       dividend = quotient;
14638     }
14639
14640     /* Convert the remainders to the output string */
14641     output = '';
14642     for (i = remainders.length - 1; i >= 0; i--) {
14643       output += encoding.charAt(remainders[i]);
14644     }
14645
14646     /* Append leading zero equivalents */
14647     full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
14648     for (i = output.length; i < full_length; i+=1) {
14649       output = encoding[0] + output;
14650     }
14651     return output;
14652   }
14653
14654   /**
14655    * Convert a raw string to a base-64 string
14656    */
14657   function rstr2b64(input, b64pad) {
14658     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
14659         output = '',
14660         len = input.length, i, j, triplet;
14661     b64pad= b64pad || '=';
14662     for (i = 0; i < len; i += 3) {
14663       triplet = (input.charCodeAt(i) << 16)
14664             | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
14665             | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
14666       for (j = 0; j < 4; j+=1) {
14667         if (i * 8 + j * 6 > input.length * 8) {
14668           output += b64pad;
14669         } else {
14670           output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
14671         }
14672        }
14673     }
14674     return output;
14675   }
14676
14677   Hashes = {
14678   /**
14679    * @property {String} version
14680    * @readonly
14681    */
14682   VERSION : '1.0.3',
14683   /**
14684    * @member Hashes
14685    * @class Base64
14686    * @constructor
14687    */
14688   Base64 : function () {
14689     // private properties
14690     var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
14691         pad = '=', // default pad according with the RFC standard
14692         url = false, // URL encoding support @todo
14693         utf8 = true; // by default enable UTF-8 support encoding
14694
14695     // public method for encoding
14696     this.encode = function (input) {
14697       var i, j, triplet,
14698           output = '',
14699           len = input.length;
14700
14701       pad = pad || '=';
14702       input = (utf8) ? utf8Encode(input) : input;
14703
14704       for (i = 0; i < len; i += 3) {
14705         triplet = (input.charCodeAt(i) << 16)
14706               | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
14707               | (i + 2 < len ? input.charCodeAt(i+2) : 0);
14708         for (j = 0; j < 4; j+=1) {
14709           if (i * 8 + j * 6 > len * 8) {
14710               output += pad;
14711           } else {
14712               output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
14713           }
14714         }
14715       }
14716       return output;
14717     };
14718
14719     // public method for decoding
14720     this.decode = function (input) {
14721       // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
14722       var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
14723         dec = '',
14724         arr = [];
14725       if (!input) { return input; }
14726
14727       i = ac = 0;
14728       input = input.replace(new RegExp('\\'+pad,'gi'),''); // use '='
14729       //input += '';
14730
14731       do { // unpack four hexets into three octets using index points in b64
14732         h1 = tab.indexOf(input.charAt(i+=1));
14733         h2 = tab.indexOf(input.charAt(i+=1));
14734         h3 = tab.indexOf(input.charAt(i+=1));
14735         h4 = tab.indexOf(input.charAt(i+=1));
14736
14737         bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
14738
14739         o1 = bits >> 16 & 0xff;
14740         o2 = bits >> 8 & 0xff;
14741         o3 = bits & 0xff;
14742         ac += 1;
14743
14744         if (h3 === 64) {
14745           arr[ac] = String.fromCharCode(o1);
14746         } else if (h4 === 64) {
14747           arr[ac] = String.fromCharCode(o1, o2);
14748         } else {
14749           arr[ac] = String.fromCharCode(o1, o2, o3);
14750         }
14751       } while (i < input.length);
14752
14753       dec = arr.join('');
14754       dec = (utf8) ? utf8Decode(dec) : dec;
14755
14756       return dec;
14757     };
14758
14759     // set custom pad string
14760     this.setPad = function (str) {
14761         pad = str || pad;
14762         return this;
14763     };
14764     // set custom tab string characters
14765     this.setTab = function (str) {
14766         tab = str || tab;
14767         return this;
14768     };
14769     this.setUTF8 = function (bool) {
14770         if (typeof bool === 'boolean') {
14771           utf8 = bool;
14772         }
14773         return this;
14774     };
14775   },
14776
14777   /**
14778    * CRC-32 calculation
14779    * @member Hashes
14780    * @method CRC32
14781    * @static
14782    * @param {String} str Input String
14783    * @return {String}
14784    */
14785   CRC32 : function (str) {
14786     var crc = 0, x = 0, y = 0, table, i, iTop;
14787     str = utf8Encode(str);
14788
14789     table = [
14790         '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
14791         '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
14792         '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
14793         '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
14794         'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
14795         '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
14796         'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
14797         '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
14798         'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
14799         '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
14800         'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
14801         '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
14802         'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
14803         '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
14804         '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
14805         '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
14806         '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
14807         'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
14808         '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
14809         'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
14810         '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
14811         'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
14812         '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
14813         'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
14814         '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
14815         'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
14816     ].join('');
14817
14818     crc = crc ^ (-1);
14819     for (i = 0, iTop = str.length; i < iTop; i+=1 ) {
14820         y = ( crc ^ str.charCodeAt( i ) ) & 0xFF;
14821         x = '0x' + table.substr( y * 9, 8 );
14822         crc = ( crc >>> 8 ) ^ x;
14823     }
14824     // always return a positive number (that's what >>> 0 does)
14825     return (crc ^ (-1)) >>> 0;
14826   },
14827   /**
14828    * @member Hashes
14829    * @class MD5
14830    * @constructor
14831    * @param {Object} [config]
14832    *
14833    * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
14834    * Digest Algorithm, as defined in RFC 1321.
14835    * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
14836    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
14837    * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
14838    */
14839   MD5 : function (options) {
14840     /**
14841      * Private config properties. You may need to tweak these to be compatible with
14842      * the server-side, but the defaults work in most cases.
14843      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
14844      */
14845     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
14846         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
14847         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
14848
14849     // privileged (public) methods
14850     this.hex = function (s) {
14851       return rstr2hex(rstr(s, utf8), hexcase);
14852     };
14853     this.b64 = function (s) {
14854       return rstr2b64(rstr(s), b64pad);
14855     };
14856     this.any = function(s, e) {
14857       return rstr2any(rstr(s, utf8), e);
14858     };
14859     this.hex_hmac = function (k, d) {
14860       return rstr2hex(rstr_hmac(k, d), hexcase);
14861     };
14862     this.b64_hmac = function (k, d) {
14863       return rstr2b64(rstr_hmac(k,d), b64pad);
14864     };
14865     this.any_hmac = function (k, d, e) {
14866       return rstr2any(rstr_hmac(k, d), e);
14867     };
14868     /**
14869      * Perform a simple self-test to see if the VM is working
14870      * @return {String} Hexadecimal hash sample
14871      */
14872     this.vm_test = function () {
14873       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
14874     };
14875     /**
14876      * Enable/disable uppercase hexadecimal returned string
14877      * @param {Boolean}
14878      * @return {Object} this
14879      */
14880     this.setUpperCase = function (a) {
14881       if (typeof a === 'boolean' ) {
14882         hexcase = a;
14883       }
14884       return this;
14885     };
14886     /**
14887      * Defines a base64 pad string
14888      * @param {String} Pad
14889      * @return {Object} this
14890      */
14891     this.setPad = function (a) {
14892       b64pad = a || b64pad;
14893       return this;
14894     };
14895     /**
14896      * Defines a base64 pad string
14897      * @param {Boolean}
14898      * @return {Object} [this]
14899      */
14900     this.setUTF8 = function (a) {
14901       if (typeof a === 'boolean') {
14902         utf8 = a;
14903       }
14904       return this;
14905     };
14906
14907     // private methods
14908
14909     /**
14910      * Calculate the MD5 of a raw string
14911      */
14912     function rstr(s) {
14913       s = (utf8) ? utf8Encode(s): s;
14914       return binl2rstr(binl(rstr2binl(s), s.length * 8));
14915     }
14916
14917     /**
14918      * Calculate the HMAC-MD5, of a key and some data (raw strings)
14919      */
14920     function rstr_hmac(key, data) {
14921       var bkey, ipad, opad, hash, i;
14922
14923       key = (utf8) ? utf8Encode(key) : key;
14924       data = (utf8) ? utf8Encode(data) : data;
14925       bkey = rstr2binl(key);
14926       if (bkey.length > 16) {
14927         bkey = binl(bkey, key.length * 8);
14928       }
14929
14930       ipad = Array(16), opad = Array(16);
14931       for (i = 0; i < 16; i+=1) {
14932           ipad[i] = bkey[i] ^ 0x36363636;
14933           opad[i] = bkey[i] ^ 0x5C5C5C5C;
14934       }
14935       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
14936       return binl2rstr(binl(opad.concat(hash), 512 + 128));
14937     }
14938
14939     /**
14940      * Calculate the MD5 of an array of little-endian words, and a bit length.
14941      */
14942     function binl(x, len) {
14943       var i, olda, oldb, oldc, oldd,
14944           a =  1732584193,
14945           b = -271733879,
14946           c = -1732584194,
14947           d =  271733878;
14948
14949       /* append padding */
14950       x[len >> 5] |= 0x80 << ((len) % 32);
14951       x[(((len + 64) >>> 9) << 4) + 14] = len;
14952
14953       for (i = 0; i < x.length; i += 16) {
14954         olda = a;
14955         oldb = b;
14956         oldc = c;
14957         oldd = d;
14958
14959         a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
14960         d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
14961         c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
14962         b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
14963         a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
14964         d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
14965         c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
14966         b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
14967         a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
14968         d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
14969         c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
14970         b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
14971         a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
14972         d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
14973         c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
14974         b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
14975
14976         a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
14977         d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
14978         c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
14979         b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
14980         a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
14981         d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
14982         c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
14983         b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
14984         a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
14985         d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
14986         c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
14987         b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
14988         a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
14989         d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
14990         c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
14991         b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
14992
14993         a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
14994         d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
14995         c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
14996         b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
14997         a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
14998         d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
14999         c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
15000         b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
15001         a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
15002         d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
15003         c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
15004         b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
15005         a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
15006         d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
15007         c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
15008         b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
15009
15010         a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
15011         d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
15012         c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
15013         b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
15014         a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
15015         d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
15016         c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
15017         b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
15018         a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
15019         d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
15020         c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
15021         b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
15022         a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
15023         d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
15024         c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
15025         b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
15026
15027         a = safe_add(a, olda);
15028         b = safe_add(b, oldb);
15029         c = safe_add(c, oldc);
15030         d = safe_add(d, oldd);
15031       }
15032       return Array(a, b, c, d);
15033     }
15034
15035     /**
15036      * These functions implement the four basic operations the algorithm uses.
15037      */
15038     function md5_cmn(q, a, b, x, s, t) {
15039       return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
15040     }
15041     function md5_ff(a, b, c, d, x, s, t) {
15042       return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
15043     }
15044     function md5_gg(a, b, c, d, x, s, t) {
15045       return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
15046     }
15047     function md5_hh(a, b, c, d, x, s, t) {
15048       return md5_cmn(b ^ c ^ d, a, b, x, s, t);
15049     }
15050     function md5_ii(a, b, c, d, x, s, t) {
15051       return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
15052     }
15053   },
15054   /**
15055    * @member Hashes
15056    * @class Hashes.SHA1
15057    * @param {Object} [config]
15058    * @constructor
15059    *
15060    * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
15061    * Version 2.2 Copyright Paul Johnston 2000 - 2009.
15062    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15063    * See http://pajhome.org.uk/crypt/md5 for details.
15064    */
15065   SHA1 : function (options) {
15066    /**
15067      * Private config properties. You may need to tweak these to be compatible with
15068      * the server-side, but the defaults work in most cases.
15069      * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
15070      */
15071     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
15072         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
15073         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
15074
15075     // public methods
15076     this.hex = function (s) {
15077         return rstr2hex(rstr(s, utf8), hexcase);
15078     };
15079     this.b64 = function (s) {
15080         return rstr2b64(rstr(s, utf8), b64pad);
15081     };
15082     this.any = function (s, e) {
15083         return rstr2any(rstr(s, utf8), e);
15084     };
15085     this.hex_hmac = function (k, d) {
15086         return rstr2hex(rstr_hmac(k, d));
15087     };
15088     this.b64_hmac = function (k, d) {
15089         return rstr2b64(rstr_hmac(k, d), b64pad);
15090     };
15091     this.any_hmac = function (k, d, e) {
15092         return rstr2any(rstr_hmac(k, d), e);
15093     };
15094     /**
15095      * Perform a simple self-test to see if the VM is working
15096      * @return {String} Hexadecimal hash sample
15097      * @public
15098      */
15099     this.vm_test = function () {
15100       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15101     };
15102     /**
15103      * @description Enable/disable uppercase hexadecimal returned string
15104      * @param {boolean}
15105      * @return {Object} this
15106      * @public
15107      */
15108     this.setUpperCase = function (a) {
15109         if (typeof a === 'boolean') {
15110         hexcase = a;
15111       }
15112         return this;
15113     };
15114     /**
15115      * @description Defines a base64 pad string
15116      * @param {string} Pad
15117      * @return {Object} this
15118      * @public
15119      */
15120     this.setPad = function (a) {
15121       b64pad = a || b64pad;
15122         return this;
15123     };
15124     /**
15125      * @description Defines a base64 pad string
15126      * @param {boolean}
15127      * @return {Object} this
15128      * @public
15129      */
15130     this.setUTF8 = function (a) {
15131         if (typeof a === 'boolean') {
15132         utf8 = a;
15133       }
15134         return this;
15135     };
15136
15137     // private methods
15138
15139     /**
15140          * Calculate the SHA-512 of a raw string
15141          */
15142         function rstr(s) {
15143       s = (utf8) ? utf8Encode(s) : s;
15144       return binb2rstr(binb(rstr2binb(s), s.length * 8));
15145         }
15146
15147     /**
15148      * Calculate the HMAC-SHA1 of a key and some data (raw strings)
15149      */
15150     function rstr_hmac(key, data) {
15151         var bkey, ipad, opad, i, hash;
15152         key = (utf8) ? utf8Encode(key) : key;
15153         data = (utf8) ? utf8Encode(data) : data;
15154         bkey = rstr2binb(key);
15155
15156         if (bkey.length > 16) {
15157         bkey = binb(bkey, key.length * 8);
15158       }
15159         ipad = Array(16), opad = Array(16);
15160         for (i = 0; i < 16; i+=1) {
15161                 ipad[i] = bkey[i] ^ 0x36363636;
15162                 opad[i] = bkey[i] ^ 0x5C5C5C5C;
15163         }
15164         hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
15165         return binb2rstr(binb(opad.concat(hash), 512 + 160));
15166     }
15167
15168     /**
15169      * Calculate the SHA-1 of an array of big-endian words, and a bit length
15170      */
15171     function binb(x, len) {
15172       var i, j, t, olda, oldb, oldc, oldd, olde,
15173           w = Array(80),
15174           a =  1732584193,
15175           b = -271733879,
15176           c = -1732584194,
15177           d =  271733878,
15178           e = -1009589776;
15179
15180       /* append padding */
15181       x[len >> 5] |= 0x80 << (24 - len % 32);
15182       x[((len + 64 >> 9) << 4) + 15] = len;
15183
15184       for (i = 0; i < x.length; i += 16) {
15185         olda = a,
15186         oldb = b;
15187         oldc = c;
15188         oldd = d;
15189         olde = e;
15190
15191         for (j = 0; j < 80; j+=1)       {
15192           if (j < 16) {
15193             w[j] = x[i + j];
15194           } else {
15195             w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
15196           }
15197           t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
15198                                            safe_add(safe_add(e, w[j]), sha1_kt(j)));
15199           e = d;
15200           d = c;
15201           c = bit_rol(b, 30);
15202           b = a;
15203           a = t;
15204         }
15205
15206         a = safe_add(a, olda);
15207         b = safe_add(b, oldb);
15208         c = safe_add(c, oldc);
15209         d = safe_add(d, oldd);
15210         e = safe_add(e, olde);
15211       }
15212       return Array(a, b, c, d, e);
15213     }
15214
15215     /**
15216      * Perform the appropriate triplet combination function for the current
15217      * iteration
15218      */
15219     function sha1_ft(t, b, c, d) {
15220       if (t < 20) { return (b & c) | ((~b) & d); }
15221       if (t < 40) { return b ^ c ^ d; }
15222       if (t < 60) { return (b & c) | (b & d) | (c & d); }
15223       return b ^ c ^ d;
15224     }
15225
15226     /**
15227      * Determine the appropriate additive constant for the current iteration
15228      */
15229     function sha1_kt(t) {
15230       return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
15231                  (t < 60) ? -1894007588 : -899497514;
15232     }
15233   },
15234   /**
15235    * @class Hashes.SHA256
15236    * @param {config}
15237    *
15238    * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
15239    * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
15240    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15241    * See http://pajhome.org.uk/crypt/md5 for details.
15242    * Also http://anmar.eu.org/projects/jssha2/
15243    */
15244   SHA256 : function (options) {
15245     /**
15246      * Private properties configuration variables. You may need to tweak these to be compatible with
15247      * the server-side, but the defaults work in most cases.
15248      * @see this.setUpperCase() method
15249      * @see this.setPad() method
15250      */
15251     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase  */
15252               b64pad = (options && typeof options.pad === 'string') ? options.pda : '=', /* base-64 pad character. Default '=' for strict RFC compliance   */
15253               utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15254               sha256_K;
15255
15256     /* privileged (public) methods */
15257     this.hex = function (s) {
15258       return rstr2hex(rstr(s, utf8));
15259     };
15260     this.b64 = function (s) {
15261       return rstr2b64(rstr(s, utf8), b64pad);
15262     };
15263     this.any = function (s, e) {
15264       return rstr2any(rstr(s, utf8), e);
15265     };
15266     this.hex_hmac = function (k, d) {
15267       return rstr2hex(rstr_hmac(k, d));
15268     };
15269     this.b64_hmac = function (k, d) {
15270       return rstr2b64(rstr_hmac(k, d), b64pad);
15271     };
15272     this.any_hmac = function (k, d, e) {
15273       return rstr2any(rstr_hmac(k, d), e);
15274     };
15275     /**
15276      * Perform a simple self-test to see if the VM is working
15277      * @return {String} Hexadecimal hash sample
15278      * @public
15279      */
15280     this.vm_test = function () {
15281       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15282     };
15283     /**
15284      * Enable/disable uppercase hexadecimal returned string
15285      * @param {boolean}
15286      * @return {Object} this
15287      * @public
15288      */
15289     this.setUpperCase = function (a) {
15290       if (typeof a === 'boolean') {
15291         hexcase = a;
15292       }
15293       return this;
15294     };
15295     /**
15296      * @description Defines a base64 pad string
15297      * @param {string} Pad
15298      * @return {Object} this
15299      * @public
15300      */
15301     this.setPad = function (a) {
15302       b64pad = a || b64pad;
15303       return this;
15304     };
15305     /**
15306      * Defines a base64 pad string
15307      * @param {boolean}
15308      * @return {Object} this
15309      * @public
15310      */
15311     this.setUTF8 = function (a) {
15312       if (typeof a === 'boolean') {
15313         utf8 = a;
15314       }
15315       return this;
15316     };
15317
15318     // private methods
15319
15320     /**
15321      * Calculate the SHA-512 of a raw string
15322      */
15323     function rstr(s, utf8) {
15324       s = (utf8) ? utf8Encode(s) : s;
15325       return binb2rstr(binb(rstr2binb(s), s.length * 8));
15326     }
15327
15328     /**
15329      * Calculate the HMAC-sha256 of a key and some data (raw strings)
15330      */
15331     function rstr_hmac(key, data) {
15332       key = (utf8) ? utf8Encode(key) : key;
15333       data = (utf8) ? utf8Encode(data) : data;
15334       var hash, i = 0,
15335           bkey = rstr2binb(key),
15336           ipad = Array(16),
15337           opad = Array(16);
15338
15339       if (bkey.length > 16) { bkey = binb(bkey, key.length * 8); }
15340
15341       for (; i < 16; i+=1) {
15342         ipad[i] = bkey[i] ^ 0x36363636;
15343         opad[i] = bkey[i] ^ 0x5C5C5C5C;
15344       }
15345
15346       hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
15347       return binb2rstr(binb(opad.concat(hash), 512 + 256));
15348     }
15349
15350     /*
15351      * Main sha256 function, with its support functions
15352      */
15353     function sha256_S (X, n) {return ( X >>> n ) | (X << (32 - n));}
15354     function sha256_R (X, n) {return ( X >>> n );}
15355     function sha256_Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
15356     function sha256_Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
15357     function sha256_Sigma0256(x) {return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));}
15358     function sha256_Sigma1256(x) {return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));}
15359     function sha256_Gamma0256(x) {return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));}
15360     function sha256_Gamma1256(x) {return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));}
15361     function sha256_Sigma0512(x) {return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39));}
15362     function sha256_Sigma1512(x) {return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41));}
15363     function sha256_Gamma0512(x) {return (sha256_S(x, 1)  ^ sha256_S(x, 8) ^ sha256_R(x, 7));}
15364     function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));}
15365
15366     sha256_K = [
15367       1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,
15368       -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
15369       1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
15370       264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
15371       -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
15372       113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
15373       1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885,
15374       -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
15375       430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
15376       1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,
15377       -1866530822, -1538233109, -1090935817, -965641998
15378     ];
15379
15380     function binb(m, l) {
15381       var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
15382                  1359893119, -1694144372, 528734635, 1541459225];
15383       var W = new Array(64);
15384       var a, b, c, d, e, f, g, h;
15385       var i, j, T1, T2;
15386
15387       /* append padding */
15388       m[l >> 5] |= 0x80 << (24 - l % 32);
15389       m[((l + 64 >> 9) << 4) + 15] = l;
15390
15391       for (i = 0; i < m.length; i += 16)
15392       {
15393       a = HASH[0];
15394       b = HASH[1];
15395       c = HASH[2];
15396       d = HASH[3];
15397       e = HASH[4];
15398       f = HASH[5];
15399       g = HASH[6];
15400       h = HASH[7];
15401
15402       for (j = 0; j < 64; j+=1)
15403       {
15404         if (j < 16) {
15405           W[j] = m[j + i];
15406         } else {
15407           W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
15408                           sha256_Gamma0256(W[j - 15])), W[j - 16]);
15409         }
15410
15411         T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
15412                                   sha256_K[j]), W[j]);
15413         T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
15414         h = g;
15415         g = f;
15416         f = e;
15417         e = safe_add(d, T1);
15418         d = c;
15419         c = b;
15420         b = a;
15421         a = safe_add(T1, T2);
15422       }
15423
15424       HASH[0] = safe_add(a, HASH[0]);
15425       HASH[1] = safe_add(b, HASH[1]);
15426       HASH[2] = safe_add(c, HASH[2]);
15427       HASH[3] = safe_add(d, HASH[3]);
15428       HASH[4] = safe_add(e, HASH[4]);
15429       HASH[5] = safe_add(f, HASH[5]);
15430       HASH[6] = safe_add(g, HASH[6]);
15431       HASH[7] = safe_add(h, HASH[7]);
15432       }
15433       return HASH;
15434     }
15435
15436   },
15437
15438   /**
15439    * @class Hashes.SHA512
15440    * @param {config}
15441    *
15442    * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
15443    * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
15444    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15445    * See http://pajhome.org.uk/crypt/md5 for details.
15446    */
15447   SHA512 : function (options) {
15448     /**
15449      * Private properties configuration variables. You may need to tweak these to be compatible with
15450      * the server-side, but the defaults work in most cases.
15451      * @see this.setUpperCase() method
15452      * @see this.setPad() method
15453      */
15454     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false , /* hexadecimal output case format. false - lowercase; true - uppercase  */
15455         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
15456         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15457         sha512_k;
15458
15459     /* privileged (public) methods */
15460     this.hex = function (s) {
15461       return rstr2hex(rstr(s));
15462     };
15463     this.b64 = function (s) {
15464       return rstr2b64(rstr(s), b64pad);
15465     };
15466     this.any = function (s, e) {
15467       return rstr2any(rstr(s), e);
15468     };
15469     this.hex_hmac = function (k, d) {
15470       return rstr2hex(rstr_hmac(k, d));
15471     };
15472     this.b64_hmac = function (k, d) {
15473       return rstr2b64(rstr_hmac(k, d), b64pad);
15474     };
15475     this.any_hmac = function (k, d, e) {
15476       return rstr2any(rstr_hmac(k, d), e);
15477     };
15478     /**
15479      * Perform a simple self-test to see if the VM is working
15480      * @return {String} Hexadecimal hash sample
15481      * @public
15482      */
15483     this.vm_test = function () {
15484       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15485     };
15486     /**
15487      * @description Enable/disable uppercase hexadecimal returned string
15488      * @param {boolean}
15489      * @return {Object} this
15490      * @public
15491      */
15492     this.setUpperCase = function (a) {
15493       if (typeof a === 'boolean') {
15494         hexcase = a;
15495       }
15496       return this;
15497     };
15498     /**
15499      * @description Defines a base64 pad string
15500      * @param {string} Pad
15501      * @return {Object} this
15502      * @public
15503      */
15504     this.setPad = function (a) {
15505       b64pad = a || b64pad;
15506       return this;
15507     };
15508     /**
15509      * @description Defines a base64 pad string
15510      * @param {boolean}
15511      * @return {Object} this
15512      * @public
15513      */
15514     this.setUTF8 = function (a) {
15515       if (typeof a === 'boolean') {
15516         utf8 = a;
15517       }
15518       return this;
15519     };
15520
15521     /* private methods */
15522
15523     /**
15524      * Calculate the SHA-512 of a raw string
15525      */
15526     function rstr(s) {
15527       s = (utf8) ? utf8Encode(s) : s;
15528       return binb2rstr(binb(rstr2binb(s), s.length * 8));
15529     }
15530     /*
15531      * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
15532      */
15533     function rstr_hmac(key, data) {
15534       key = (utf8) ? utf8Encode(key) : key;
15535       data = (utf8) ? utf8Encode(data) : data;
15536
15537       var hash, i = 0,
15538           bkey = rstr2binb(key),
15539           ipad = Array(32), opad = Array(32);
15540
15541       if (bkey.length > 32) { bkey = binb(bkey, key.length * 8); }
15542
15543       for (; i < 32; i+=1) {
15544         ipad[i] = bkey[i] ^ 0x36363636;
15545         opad[i] = bkey[i] ^ 0x5C5C5C5C;
15546       }
15547
15548       hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
15549       return binb2rstr(binb(opad.concat(hash), 1024 + 512));
15550     }
15551
15552     /**
15553      * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
15554      */
15555     function binb(x, len) {
15556       var j, i, l,
15557           W = new Array(80),
15558           hash = new Array(16),
15559           //Initial hash values
15560           H = [
15561             new int64(0x6a09e667, -205731576),
15562             new int64(-1150833019, -2067093701),
15563             new int64(0x3c6ef372, -23791573),
15564             new int64(-1521486534, 0x5f1d36f1),
15565             new int64(0x510e527f, -1377402159),
15566             new int64(-1694144372, 0x2b3e6c1f),
15567             new int64(0x1f83d9ab, -79577749),
15568             new int64(0x5be0cd19, 0x137e2179)
15569           ],
15570           T1 = new int64(0, 0),
15571           T2 = new int64(0, 0),
15572           a = new int64(0,0),
15573           b = new int64(0,0),
15574           c = new int64(0,0),
15575           d = new int64(0,0),
15576           e = new int64(0,0),
15577           f = new int64(0,0),
15578           g = new int64(0,0),
15579           h = new int64(0,0),
15580           //Temporary variables not specified by the document
15581           s0 = new int64(0, 0),
15582           s1 = new int64(0, 0),
15583           Ch = new int64(0, 0),
15584           Maj = new int64(0, 0),
15585           r1 = new int64(0, 0),
15586           r2 = new int64(0, 0),
15587           r3 = new int64(0, 0);
15588
15589       if (sha512_k === undefined) {
15590           //SHA512 constants
15591           sha512_k = [
15592             new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
15593             new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
15594             new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
15595             new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
15596             new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
15597             new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
15598             new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
15599             new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
15600             new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
15601             new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
15602             new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
15603             new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
15604             new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
15605             new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
15606             new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
15607             new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
15608             new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
15609             new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
15610             new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
15611             new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
15612             new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
15613             new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
15614             new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
15615             new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
15616             new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
15617             new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
15618             new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
15619             new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
15620             new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
15621             new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
15622             new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
15623             new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
15624             new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
15625             new int64(-354779690, -840897762), new int64(-176337025, -294727304),
15626             new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
15627             new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
15628             new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
15629             new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
15630             new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
15631             new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
15632           ];
15633       }
15634
15635       for (i=0; i<80; i+=1) {
15636         W[i] = new int64(0, 0);
15637       }
15638
15639       // append padding to the source string. The format is described in the FIPS.
15640       x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
15641       x[((len + 128 >> 10)<< 5) + 31] = len;
15642       l = x.length;
15643       for (i = 0; i<l; i+=32) { //32 dwords is the block size
15644         int64copy(a, H[0]);
15645         int64copy(b, H[1]);
15646         int64copy(c, H[2]);
15647         int64copy(d, H[3]);
15648         int64copy(e, H[4]);
15649         int64copy(f, H[5]);
15650         int64copy(g, H[6]);
15651         int64copy(h, H[7]);
15652
15653         for (j=0; j<16; j+=1) {
15654           W[j].h = x[i + 2*j];
15655           W[j].l = x[i + 2*j + 1];
15656         }
15657
15658         for (j=16; j<80; j+=1) {
15659           //sigma1
15660           int64rrot(r1, W[j-2], 19);
15661           int64revrrot(r2, W[j-2], 29);
15662           int64shr(r3, W[j-2], 6);
15663           s1.l = r1.l ^ r2.l ^ r3.l;
15664           s1.h = r1.h ^ r2.h ^ r3.h;
15665           //sigma0
15666           int64rrot(r1, W[j-15], 1);
15667           int64rrot(r2, W[j-15], 8);
15668           int64shr(r3, W[j-15], 7);
15669           s0.l = r1.l ^ r2.l ^ r3.l;
15670           s0.h = r1.h ^ r2.h ^ r3.h;
15671
15672           int64add4(W[j], s1, W[j-7], s0, W[j-16]);
15673         }
15674
15675         for (j = 0; j < 80; j+=1) {
15676           //Ch
15677           Ch.l = (e.l & f.l) ^ (~e.l & g.l);
15678           Ch.h = (e.h & f.h) ^ (~e.h & g.h);
15679
15680           //Sigma1
15681           int64rrot(r1, e, 14);
15682           int64rrot(r2, e, 18);
15683           int64revrrot(r3, e, 9);
15684           s1.l = r1.l ^ r2.l ^ r3.l;
15685           s1.h = r1.h ^ r2.h ^ r3.h;
15686
15687           //Sigma0
15688           int64rrot(r1, a, 28);
15689           int64revrrot(r2, a, 2);
15690           int64revrrot(r3, a, 7);
15691           s0.l = r1.l ^ r2.l ^ r3.l;
15692           s0.h = r1.h ^ r2.h ^ r3.h;
15693
15694           //Maj
15695           Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
15696           Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
15697
15698           int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
15699           int64add(T2, s0, Maj);
15700
15701           int64copy(h, g);
15702           int64copy(g, f);
15703           int64copy(f, e);
15704           int64add(e, d, T1);
15705           int64copy(d, c);
15706           int64copy(c, b);
15707           int64copy(b, a);
15708           int64add(a, T1, T2);
15709         }
15710         int64add(H[0], H[0], a);
15711         int64add(H[1], H[1], b);
15712         int64add(H[2], H[2], c);
15713         int64add(H[3], H[3], d);
15714         int64add(H[4], H[4], e);
15715         int64add(H[5], H[5], f);
15716         int64add(H[6], H[6], g);
15717         int64add(H[7], H[7], h);
15718       }
15719
15720       //represent the hash as an array of 32-bit dwords
15721       for (i=0; i<8; i+=1) {
15722         hash[2*i] = H[i].h;
15723         hash[2*i + 1] = H[i].l;
15724       }
15725       return hash;
15726     }
15727
15728     //A constructor for 64-bit numbers
15729     function int64(h, l) {
15730       this.h = h;
15731       this.l = l;
15732       //this.toString = int64toString;
15733     }
15734
15735     //Copies src into dst, assuming both are 64-bit numbers
15736     function int64copy(dst, src) {
15737       dst.h = src.h;
15738       dst.l = src.l;
15739     }
15740
15741     //Right-rotates a 64-bit number by shift
15742     //Won't handle cases of shift>=32
15743     //The function revrrot() is for that
15744     function int64rrot(dst, x, shift) {
15745       dst.l = (x.l >>> shift) | (x.h << (32-shift));
15746       dst.h = (x.h >>> shift) | (x.l << (32-shift));
15747     }
15748
15749     //Reverses the dwords of the source and then rotates right by shift.
15750     //This is equivalent to rotation by 32+shift
15751     function int64revrrot(dst, x, shift) {
15752       dst.l = (x.h >>> shift) | (x.l << (32-shift));
15753       dst.h = (x.l >>> shift) | (x.h << (32-shift));
15754     }
15755
15756     //Bitwise-shifts right a 64-bit number by shift
15757     //Won't handle shift>=32, but it's never needed in SHA512
15758     function int64shr(dst, x, shift) {
15759       dst.l = (x.l >>> shift) | (x.h << (32-shift));
15760       dst.h = (x.h >>> shift);
15761     }
15762
15763     //Adds two 64-bit numbers
15764     //Like the original implementation, does not rely on 32-bit operations
15765     function int64add(dst, x, y) {
15766        var w0 = (x.l & 0xffff) + (y.l & 0xffff);
15767        var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
15768        var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
15769        var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
15770        dst.l = (w0 & 0xffff) | (w1 << 16);
15771        dst.h = (w2 & 0xffff) | (w3 << 16);
15772     }
15773
15774     //Same, except with 4 addends. Works faster than adding them one by one.
15775     function int64add4(dst, a, b, c, d) {
15776        var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
15777        var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
15778        var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
15779        var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
15780        dst.l = (w0 & 0xffff) | (w1 << 16);
15781        dst.h = (w2 & 0xffff) | (w3 << 16);
15782     }
15783
15784     //Same, except with 5 addends
15785     function int64add5(dst, a, b, c, d, e) {
15786       var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
15787           w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
15788           w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
15789           w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
15790        dst.l = (w0 & 0xffff) | (w1 << 16);
15791        dst.h = (w2 & 0xffff) | (w3 << 16);
15792     }
15793   },
15794   /**
15795    * @class Hashes.RMD160
15796    * @constructor
15797    * @param {Object} [config]
15798    *
15799    * A JavaScript implementation of the RIPEMD-160 Algorithm
15800    * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
15801    * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15802    * See http://pajhome.org.uk/crypt/md5 for details.
15803    * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
15804    */
15805   RMD160 : function (options) {
15806     /**
15807      * Private properties configuration variables. You may need to tweak these to be compatible with
15808      * the server-side, but the defaults work in most cases.
15809      * @see this.setUpperCase() method
15810      * @see this.setPad() method
15811      */
15812     var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,   /* hexadecimal output case format. false - lowercase; true - uppercase  */
15813         b64pad = (options && typeof options.pad === 'string') ? options.pda : '=',  /* base-64 pad character. Default '=' for strict RFC compliance   */
15814         utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, /* enable/disable utf8 encoding */
15815         rmd160_r1 = [
15816            0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
15817            7,  4, 13,  1, 10,  6, 15,  3, 12,  0,  9,  5,  2, 14, 11,  8,
15818            3, 10, 14,  4,  9, 15,  8,  1,  2,  7,  0,  6, 13, 11,  5, 12,
15819            1,  9, 11, 10,  0,  8, 12,  4, 13,  3,  7, 15, 14,  5,  6,  2,
15820            4,  0,  5,  9,  7, 12,  2, 10, 14,  1,  3,  8, 11,  6, 15, 13
15821         ],
15822         rmd160_r2 = [
15823            5, 14,  7,  0,  9,  2, 11,  4, 13,  6, 15,  8,  1, 10,  3, 12,
15824            6, 11,  3,  7,  0, 13,  5, 10, 14, 15,  8, 12,  4,  9,  1,  2,
15825           15,  5,  1,  3,  7, 14,  6,  9, 11,  8, 12,  2, 10,  0,  4, 13,
15826            8,  6,  4,  1,  3, 11, 15,  0,  5, 12,  2, 13,  9,  7, 10, 14,
15827           12, 15, 10,  4,  1,  5,  8,  7,  6,  2, 13, 14,  0,  3,  9, 11
15828         ],
15829         rmd160_s1 = [
15830           11, 14, 15, 12,  5,  8,  7,  9, 11, 13, 14, 15,  6,  7,  9,  8,
15831            7,  6,  8, 13, 11,  9,  7, 15,  7, 12, 15,  9, 11,  7, 13, 12,
15832           11, 13,  6,  7, 14,  9, 13, 15, 14,  8, 13,  6,  5, 12,  7,  5,
15833           11, 12, 14, 15, 14, 15,  9,  8,  9, 14,  5,  6,  8,  6,  5, 12,
15834            9, 15,  5, 11,  6,  8, 13, 12,  5, 12, 13, 14, 11,  8,  5,  6
15835         ],
15836         rmd160_s2 = [
15837            8,  9,  9, 11, 13, 15, 15,  5,  7,  7,  8, 11, 14, 14, 12,  6,
15838            9, 13, 15,  7, 12,  8,  9, 11,  7,  7, 12,  7,  6, 15, 13, 11,
15839            9,  7, 15, 11,  8,  6,  6, 14, 12, 13,  5, 14, 13, 13,  7,  5,
15840           15,  5,  8, 11, 14, 14,  6, 14,  6,  9, 12,  9, 12,  5, 15,  8,
15841            8,  5, 12,  9, 12,  5, 14,  6,  8, 13,  6,  5, 15, 13, 11, 11
15842         ];
15843
15844     /* privileged (public) methods */
15845     this.hex = function (s) {
15846       return rstr2hex(rstr(s, utf8));
15847     };
15848     this.b64 = function (s) {
15849       return rstr2b64(rstr(s, utf8), b64pad);
15850     };
15851     this.any = function (s, e) {
15852       return rstr2any(rstr(s, utf8), e);
15853     };
15854     this.hex_hmac = function (k, d) {
15855       return rstr2hex(rstr_hmac(k, d));
15856     };
15857     this.b64_hmac = function (k, d) {
15858       return rstr2b64(rstr_hmac(k, d), b64pad);
15859     };
15860     this.any_hmac = function (k, d, e) {
15861       return rstr2any(rstr_hmac(k, d), e);
15862     };
15863     /**
15864      * Perform a simple self-test to see if the VM is working
15865      * @return {String} Hexadecimal hash sample
15866      * @public
15867      */
15868     this.vm_test = function () {
15869       return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
15870     };
15871     /**
15872      * @description Enable/disable uppercase hexadecimal returned string
15873      * @param {boolean}
15874      * @return {Object} this
15875      * @public
15876      */
15877     this.setUpperCase = function (a) {
15878       if (typeof a === 'boolean' ) { hexcase = a; }
15879       return this;
15880     };
15881     /**
15882      * @description Defines a base64 pad string
15883      * @param {string} Pad
15884      * @return {Object} this
15885      * @public
15886      */
15887     this.setPad = function (a) {
15888       if (typeof a !== 'undefined' ) { b64pad = a; }
15889       return this;
15890     };
15891     /**
15892      * @description Defines a base64 pad string
15893      * @param {boolean}
15894      * @return {Object} this
15895      * @public
15896      */
15897     this.setUTF8 = function (a) {
15898       if (typeof a === 'boolean') { utf8 = a; }
15899       return this;
15900     };
15901
15902     /* private methods */
15903
15904     /**
15905      * Calculate the rmd160 of a raw string
15906      */
15907     function rstr(s) {
15908       s = (utf8) ? utf8Encode(s) : s;
15909       return binl2rstr(binl(rstr2binl(s), s.length * 8));
15910     }
15911
15912     /**
15913      * Calculate the HMAC-rmd160 of a key and some data (raw strings)
15914      */
15915     function rstr_hmac(key, data) {
15916       key = (utf8) ? utf8Encode(key) : key;
15917       data = (utf8) ? utf8Encode(data) : data;
15918       var i, hash,
15919           bkey = rstr2binl(key),
15920           ipad = Array(16), opad = Array(16);
15921
15922       if (bkey.length > 16) {
15923         bkey = binl(bkey, key.length * 8);
15924       }
15925
15926       for (i = 0; i < 16; i+=1) {
15927         ipad[i] = bkey[i] ^ 0x36363636;
15928         opad[i] = bkey[i] ^ 0x5C5C5C5C;
15929       }
15930       hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
15931       return binl2rstr(binl(opad.concat(hash), 512 + 160));
15932     }
15933
15934     /**
15935      * Convert an array of little-endian words to a string
15936      */
15937     function binl2rstr(input) {
15938       var i, output = '', l = input.length * 32;
15939       for (i = 0; i < l; i += 8) {
15940         output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
15941       }
15942       return output;
15943     }
15944
15945     /**
15946      * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
15947      */
15948     function binl(x, len) {
15949       var T, j, i, l,
15950           h0 = 0x67452301,
15951           h1 = 0xefcdab89,
15952           h2 = 0x98badcfe,
15953           h3 = 0x10325476,
15954           h4 = 0xc3d2e1f0,
15955           A1, B1, C1, D1, E1,
15956           A2, B2, C2, D2, E2;
15957
15958       /* append padding */
15959       x[len >> 5] |= 0x80 << (len % 32);
15960       x[(((len + 64) >>> 9) << 4) + 14] = len;
15961       l = x.length;
15962
15963       for (i = 0; i < l; i+=16) {
15964         A1 = A2 = h0; B1 = B2 = h1; C1 = C2 = h2; D1 = D2 = h3; E1 = E2 = h4;
15965         for (j = 0; j <= 79; j+=1) {
15966           T = safe_add(A1, rmd160_f(j, B1, C1, D1));
15967           T = safe_add(T, x[i + rmd160_r1[j]]);
15968           T = safe_add(T, rmd160_K1(j));
15969           T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
15970           A1 = E1; E1 = D1; D1 = bit_rol(C1, 10); C1 = B1; B1 = T;
15971           T = safe_add(A2, rmd160_f(79-j, B2, C2, D2));
15972           T = safe_add(T, x[i + rmd160_r2[j]]);
15973           T = safe_add(T, rmd160_K2(j));
15974           T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
15975           A2 = E2; E2 = D2; D2 = bit_rol(C2, 10); C2 = B2; B2 = T;
15976         }
15977
15978         T = safe_add(h1, safe_add(C1, D2));
15979         h1 = safe_add(h2, safe_add(D1, E2));
15980         h2 = safe_add(h3, safe_add(E1, A2));
15981         h3 = safe_add(h4, safe_add(A1, B2));
15982         h4 = safe_add(h0, safe_add(B1, C2));
15983         h0 = T;
15984       }
15985       return [h0, h1, h2, h3, h4];
15986     }
15987
15988     // specific algorithm methods
15989     function rmd160_f(j, x, y, z) {
15990       return ( 0 <= j && j <= 15) ? (x ^ y ^ z) :
15991          (16 <= j && j <= 31) ? (x & y) | (~x & z) :
15992          (32 <= j && j <= 47) ? (x | ~y) ^ z :
15993          (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
15994          (64 <= j && j <= 79) ? x ^ (y | ~z) :
15995          'rmd160_f: j out of range';
15996     }
15997
15998     function rmd160_K1(j) {
15999       return ( 0 <= j && j <= 15) ? 0x00000000 :
16000          (16 <= j && j <= 31) ? 0x5a827999 :
16001          (32 <= j && j <= 47) ? 0x6ed9eba1 :
16002          (48 <= j && j <= 63) ? 0x8f1bbcdc :
16003          (64 <= j && j <= 79) ? 0xa953fd4e :
16004          'rmd160_K1: j out of range';
16005     }
16006
16007     function rmd160_K2(j){
16008       return ( 0 <= j && j <= 15) ? 0x50a28be6 :
16009          (16 <= j && j <= 31) ? 0x5c4dd124 :
16010          (32 <= j && j <= 47) ? 0x6d703ef3 :
16011          (48 <= j && j <= 63) ? 0x7a6d76e9 :
16012          (64 <= j && j <= 79) ? 0x00000000 :
16013          'rmd160_K2: j out of range';
16014     }
16015   }
16016 };
16017
16018   // exposes Hashes
16019   (function( window, undefined ) {
16020     var freeExports = false;
16021     if (typeof exports === 'object' ) {
16022       freeExports = exports;
16023       if (exports && typeof global === 'object' && global && global === global.global ) { window = global; }
16024     }
16025
16026     if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
16027       // define as an anonymous module, so, through path mapping, it can be aliased
16028       define(function () { return Hashes; });
16029     }
16030     else if ( freeExports ) {
16031       // in Node.js or RingoJS v0.8.0+
16032       if ( typeof module === 'object' && module && module.exports === freeExports ) {
16033         module.exports = Hashes;
16034       }
16035       // in Narwhal or RingoJS v0.7.0-
16036       else {
16037         freeExports.Hashes = Hashes;
16038       }
16039     }
16040     else {
16041       // in a browser or Rhino
16042       window.Hashes = Hashes;
16043     }
16044   }( this ));
16045 }()); // IIFE
16046
16047 })(window)
16048 },{}],2:[function(require,module,exports){
16049 'use strict';
16050
16051 var hashes = require('jshashes'),
16052     xtend = require('xtend'),
16053     sha1 = new hashes.SHA1();
16054
16055 var ohauth = {};
16056
16057 ohauth.qsString = function(obj) {
16058     return Object.keys(obj).sort().map(function(key) {
16059         return ohauth.percentEncode(key) + '=' +
16060             ohauth.percentEncode(obj[key]);
16061     }).join('&');
16062 };
16063
16064 ohauth.stringQs = function(str) {
16065     return str.split('&').reduce(function(obj, pair){
16066         var parts = pair.split('=');
16067         obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
16068             '' : decodeURIComponent(parts[1]);
16069         return obj;
16070     }, {});
16071 };
16072
16073 ohauth.rawxhr = function(method, url, data, headers, callback) {
16074     var xhr = new XMLHttpRequest(),
16075         twoHundred = /^20\d$/;
16076     xhr.onreadystatechange = function() {
16077         if (4 == xhr.readyState && 0 !== xhr.status) {
16078             if (twoHundred.test(xhr.status)) callback(null, xhr);
16079             else return callback(xhr, null);
16080         }
16081     };
16082     xhr.onerror = function(e) { return callback(e, null); };
16083     xhr.open(method, url, true);
16084     for (var h in headers) xhr.setRequestHeader(h, headers[h]);
16085     xhr.send(data);
16086 };
16087
16088 ohauth.xhr = function(method, url, auth, data, options, callback) {
16089     var headers = (options && options.header) || {
16090         'Content-Type': 'application/x-www-form-urlencoded'
16091     };
16092     headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
16093     ohauth.rawxhr(method, url, data, headers, callback);
16094 };
16095
16096 ohauth.nonce = function() {
16097     for (var o = ''; o.length < 6;) {
16098         o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
16099     }
16100     return o;
16101 };
16102
16103 ohauth.authHeader = function(obj) {
16104     return Object.keys(obj).sort().map(function(key) {
16105         return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
16106     }).join(', ');
16107 };
16108
16109 ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
16110
16111 ohauth.percentEncode = function(s) {
16112     return encodeURIComponent(s)
16113         .replace(/\!/g, '%21').replace(/\'/g, '%27')
16114         .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
16115 };
16116
16117 ohauth.baseString = function(method, url, params) {
16118     if (params.oauth_signature) delete params.oauth_signature;
16119     return [
16120         method,
16121         ohauth.percentEncode(url),
16122         ohauth.percentEncode(ohauth.qsString(params))].join('&');
16123 };
16124
16125 ohauth.signature = function(oauth_secret, token_secret, baseString) {
16126     return sha1.b64_hmac(
16127         ohauth.percentEncode(oauth_secret) + '&' +
16128         ohauth.percentEncode(token_secret),
16129         baseString);
16130 };
16131
16132 /**
16133  * Takes an options object for configuration (consumer_key,
16134  * consumer_secret, version, signature_method, token) and returns a
16135  * function that generates the Authorization header for given data.
16136  *
16137  * The returned function takes these parameters:
16138  * - method: GET/POST/...
16139  * - uri: full URI with protocol, port, path and query string
16140  * - extra_params: any extra parameters (that are passed in the POST data),
16141  *   can be an object or a from-urlencoded string.
16142  *
16143  * Returned function returns full OAuth header with "OAuth" string in it.
16144  */
16145
16146 ohauth.headerGenerator = function(options) {
16147     options = options || {};
16148     var consumer_key = options.consumer_key || '',
16149         consumer_secret = options.consumer_secret || '',
16150         signature_method = options.signature_method || 'HMAC-SHA1',
16151         version = options.version || '1.0',
16152         token = options.token || '';
16153
16154     return function(method, uri, extra_params) {
16155         method = method.toUpperCase();
16156         if (typeof extra_params === 'string' && extra_params.length > 0) {
16157             extra_params = ohauth.stringQs(extra_params);
16158         }
16159
16160         var uri_parts = uri.split('?', 2),
16161         base_uri = uri_parts[0];
16162
16163         var query_params = uri_parts.length === 2 ?
16164             ohauth.stringQs(uri_parts[1]) : {};
16165
16166         var oauth_params = {
16167             oauth_consumer_key: consumer_key,
16168             oauth_signature_method: signature_method,
16169             oauth_version: version,
16170             oauth_timestamp: ohauth.timestamp(),
16171             oauth_nonce: ohauth.nonce()
16172         };
16173
16174         if (token) oauth_params.oauth_token = token;
16175
16176         var all_params = xtend({}, oauth_params, query_params, extra_params),
16177             base_str = ohauth.baseString(method, base_uri, all_params);
16178
16179         oauth_params.oauth_signature = ohauth.signature(consumer_secret, token, base_str);
16180
16181         return 'OAuth ' + ohauth.authHeader(oauth_params);
16182     };
16183 };
16184
16185 module.exports = ohauth;
16186
16187 },{"jshashes":7,"xtend":4}],6:[function(require,module,exports){
16188 module.exports = Object.keys || require('./shim');
16189
16190
16191 },{"./shim":8}],8:[function(require,module,exports){
16192 (function () {
16193         "use strict";
16194
16195         // modified from https://github.com/kriskowal/es5-shim
16196         var has = Object.prototype.hasOwnProperty,
16197                 is = require('is'),
16198                 forEach = require('foreach'),
16199                 hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'),
16200                 dontEnums = [
16201                         "toString",
16202                         "toLocaleString",
16203                         "valueOf",
16204                         "hasOwnProperty",
16205                         "isPrototypeOf",
16206                         "propertyIsEnumerable",
16207                         "constructor"
16208                 ],
16209                 keysShim;
16210
16211         keysShim = function keys(object) {
16212                 if (!is.object(object) && !is.array(object)) {
16213                         throw new TypeError("Object.keys called on a non-object");
16214                 }
16215
16216                 var name, theKeys = [];
16217                 for (name in object) {
16218                         if (has.call(object, name)) {
16219                                 theKeys.push(name);
16220                         }
16221                 }
16222
16223                 if (hasDontEnumBug) {
16224                         forEach(dontEnums, function (dontEnum) {
16225                                 if (has.call(object, dontEnum)) {
16226                                         theKeys.push(dontEnum);
16227                                 }
16228                         });
16229                 }
16230                 return theKeys;
16231         };
16232
16233         module.exports = keysShim;
16234 }());
16235
16236
16237 },{"is":9,"foreach":10}],9:[function(require,module,exports){
16238
16239 /**!
16240  * is
16241  * the definitive JavaScript type testing library
16242  *
16243  * @copyright 2013 Enrico Marino
16244  * @license MIT
16245  */
16246
16247 var objProto = Object.prototype;
16248 var owns = objProto.hasOwnProperty;
16249 var toString = objProto.toString;
16250 var isActualNaN = function (value) {
16251   return value !== value;
16252 };
16253 var NON_HOST_TYPES = {
16254   "boolean": 1,
16255   "number": 1,
16256   "string": 1,
16257   "undefined": 1
16258 };
16259
16260 /**
16261  * Expose `is`
16262  */
16263
16264 var is = module.exports = {};
16265
16266 /**
16267  * Test general.
16268  */
16269
16270 /**
16271  * is.type
16272  * Test if `value` is a type of `type`.
16273  *
16274  * @param {Mixed} value value to test
16275  * @param {String} type type
16276  * @return {Boolean} true if `value` is a type of `type`, false otherwise
16277  * @api public
16278  */
16279
16280 is.a =
16281 is.type = function (value, type) {
16282   return typeof value === type;
16283 };
16284
16285 /**
16286  * is.defined
16287  * Test if `value` is defined.
16288  *
16289  * @param {Mixed} value value to test
16290  * @return {Boolean} true if 'value' is defined, false otherwise
16291  * @api public
16292  */
16293
16294 is.defined = function (value) {
16295   return value !== undefined;
16296 };
16297
16298 /**
16299  * is.empty
16300  * Test if `value` is empty.
16301  *
16302  * @param {Mixed} value value to test
16303  * @return {Boolean} true if `value` is empty, false otherwise
16304  * @api public
16305  */
16306
16307 is.empty = function (value) {
16308   var type = toString.call(value);
16309   var key;
16310
16311   if ('[object Array]' === type || '[object Arguments]' === type) {
16312     return value.length === 0;
16313   }
16314
16315   if ('[object Object]' === type) {
16316     for (key in value) if (owns.call(value, key)) return false;
16317     return true;
16318   }
16319
16320   if ('[object String]' === type) {
16321     return '' === value;
16322   }
16323
16324   return false;
16325 };
16326
16327 /**
16328  * is.equal
16329  * Test if `value` is equal to `other`.
16330  *
16331  * @param {Mixed} value value to test
16332  * @param {Mixed} other value to compare with
16333  * @return {Boolean} true if `value` is equal to `other`, false otherwise
16334  */
16335
16336 is.equal = function (value, other) {
16337   var type = toString.call(value)
16338   var key;
16339
16340   if (type !== toString.call(other)) {
16341     return false;
16342   }
16343
16344   if ('[object Object]' === type) {
16345     for (key in value) {
16346       if (!is.equal(value[key], other[key])) {
16347         return false;
16348       }
16349     }
16350     return true;
16351   }
16352
16353   if ('[object Array]' === type) {
16354     key = value.length;
16355     if (key !== other.length) {
16356       return false;
16357     }
16358     while (--key) {
16359       if (!is.equal(value[key], other[key])) {
16360         return false;
16361       }
16362     }
16363     return true;
16364   }
16365
16366   if ('[object Function]' === type) {
16367     return value.prototype === other.prototype;
16368   }
16369
16370   if ('[object Date]' === type) {
16371     return value.getTime() === other.getTime();
16372   }
16373
16374   return value === other;
16375 };
16376
16377 /**
16378  * is.hosted
16379  * Test if `value` is hosted by `host`.
16380  *
16381  * @param {Mixed} value to test
16382  * @param {Mixed} host host to test with
16383  * @return {Boolean} true if `value` is hosted by `host`, false otherwise
16384  * @api public
16385  */
16386
16387 is.hosted = function (value, host) {
16388   var type = typeof host[value];
16389   return type === 'object' ? !!host[value] : !NON_HOST_TYPES[type];
16390 };
16391
16392 /**
16393  * is.instance
16394  * Test if `value` is an instance of `constructor`.
16395  *
16396  * @param {Mixed} value value to test
16397  * @return {Boolean} true if `value` is an instance of `constructor`
16398  * @api public
16399  */
16400
16401 is.instance = is['instanceof'] = function (value, constructor) {
16402   return value instanceof constructor;
16403 };
16404
16405 /**
16406  * is.null
16407  * Test if `value` is null.
16408  *
16409  * @param {Mixed} value value to test
16410  * @return {Boolean} true if `value` is null, false otherwise
16411  * @api public
16412  */
16413
16414 is['null'] = function (value) {
16415   return value === null;
16416 };
16417
16418 /**
16419  * is.undefined
16420  * Test if `value` is undefined.
16421  *
16422  * @param {Mixed} value value to test
16423  * @return {Boolean} true if `value` is undefined, false otherwise
16424  * @api public
16425  */
16426
16427 is.undefined = function (value) {
16428   return value === undefined;
16429 };
16430
16431 /**
16432  * Test arguments.
16433  */
16434
16435 /**
16436  * is.arguments
16437  * Test if `value` is an arguments object.
16438  *
16439  * @param {Mixed} value value to test
16440  * @return {Boolean} true if `value` is an arguments object, false otherwise
16441  * @api public
16442  */
16443
16444 is.arguments = function (value) {
16445   var isStandardArguments = '[object Arguments]' === toString.call(value);
16446   var isOldArguments = !is.array(value) && is.arraylike(value) && is.object(value) && is.fn(value.callee);
16447   return isStandardArguments || isOldArguments;
16448 };
16449
16450 /**
16451  * Test array.
16452  */
16453
16454 /**
16455  * is.array
16456  * Test if 'value' is an array.
16457  *
16458  * @param {Mixed} value value to test
16459  * @return {Boolean} true if `value` is an array, false otherwise
16460  * @api public
16461  */
16462
16463 is.array = function (value) {
16464   return '[object Array]' === toString.call(value);
16465 };
16466
16467 /**
16468  * is.arguments.empty
16469  * Test if `value` is an empty arguments object.
16470  *
16471  * @param {Mixed} value value to test
16472  * @return {Boolean} true if `value` is an empty arguments object, false otherwise
16473  * @api public
16474  */
16475 is.arguments.empty = function (value) {
16476   return is.arguments(value) && value.length === 0;
16477 };
16478
16479 /**
16480  * is.array.empty
16481  * Test if `value` is an empty array.
16482  *
16483  * @param {Mixed} value value to test
16484  * @return {Boolean} true if `value` is an empty array, false otherwise
16485  * @api public
16486  */
16487 is.array.empty = function (value) {
16488   return is.array(value) && value.length === 0;
16489 };
16490
16491 /**
16492  * is.arraylike
16493  * Test if `value` is an arraylike object.
16494  *
16495  * @param {Mixed} value value to test
16496  * @return {Boolean} true if `value` is an arguments object, false otherwise
16497  * @api public
16498  */
16499
16500 is.arraylike = function (value) {
16501   return !!value && !is.boolean(value)
16502     && owns.call(value, 'length')
16503     && isFinite(value.length)
16504     && is.number(value.length)
16505     && value.length >= 0;
16506 };
16507
16508 /**
16509  * Test boolean.
16510  */
16511
16512 /**
16513  * is.boolean
16514  * Test if `value` is a boolean.
16515  *
16516  * @param {Mixed} value value to test
16517  * @return {Boolean} true if `value` is a boolean, false otherwise
16518  * @api public
16519  */
16520
16521 is.boolean = function (value) {
16522   return '[object Boolean]' === toString.call(value);
16523 };
16524
16525 /**
16526  * is.false
16527  * Test if `value` is false.
16528  *
16529  * @param {Mixed} value value to test
16530  * @return {Boolean} true if `value` is false, false otherwise
16531  * @api public
16532  */
16533
16534 is['false'] = function (value) {
16535   return is.boolean(value) && (value === false || value.valueOf() === false);
16536 };
16537
16538 /**
16539  * is.true
16540  * Test if `value` is true.
16541  *
16542  * @param {Mixed} value value to test
16543  * @return {Boolean} true if `value` is true, false otherwise
16544  * @api public
16545  */
16546
16547 is['true'] = function (value) {
16548   return is.boolean(value) && (value === true || value.valueOf() === true);
16549 };
16550
16551 /**
16552  * Test date.
16553  */
16554
16555 /**
16556  * is.date
16557  * Test if `value` is a date.
16558  *
16559  * @param {Mixed} value value to test
16560  * @return {Boolean} true if `value` is a date, false otherwise
16561  * @api public
16562  */
16563
16564 is.date = function (value) {
16565   return '[object Date]' === toString.call(value);
16566 };
16567
16568 /**
16569  * Test element.
16570  */
16571
16572 /**
16573  * is.element
16574  * Test if `value` is an html element.
16575  *
16576  * @param {Mixed} value value to test
16577  * @return {Boolean} true if `value` is an HTML Element, false otherwise
16578  * @api public
16579  */
16580
16581 is.element = function (value) {
16582   return value !== undefined
16583     && typeof HTMLElement !== 'undefined'
16584     && value instanceof HTMLElement
16585     && value.nodeType === 1;
16586 };
16587
16588 /**
16589  * Test error.
16590  */
16591
16592 /**
16593  * is.error
16594  * Test if `value` is an error object.
16595  *
16596  * @param {Mixed} value value to test
16597  * @return {Boolean} true if `value` is an error object, false otherwise
16598  * @api public
16599  */
16600
16601 is.error = function (value) {
16602   return '[object Error]' === toString.call(value);
16603 };
16604
16605 /**
16606  * Test function.
16607  */
16608
16609 /**
16610  * is.fn / is.function (deprecated)
16611  * Test if `value` is a function.
16612  *
16613  * @param {Mixed} value value to test
16614  * @return {Boolean} true if `value` is a function, false otherwise
16615  * @api public
16616  */
16617
16618 is.fn = is['function'] = function (value) {
16619   var isAlert = typeof window !== 'undefined' && value === window.alert;
16620   return isAlert || '[object Function]' === toString.call(value);
16621 };
16622
16623 /**
16624  * Test number.
16625  */
16626
16627 /**
16628  * is.number
16629  * Test if `value` is a number.
16630  *
16631  * @param {Mixed} value value to test
16632  * @return {Boolean} true if `value` is a number, false otherwise
16633  * @api public
16634  */
16635
16636 is.number = function (value) {
16637   return '[object Number]' === toString.call(value);
16638 };
16639
16640 /**
16641  * is.infinite
16642  * Test if `value` is positive or negative infinity.
16643  *
16644  * @param {Mixed} value value to test
16645  * @return {Boolean} true if `value` is positive or negative Infinity, false otherwise
16646  * @api public
16647  */
16648 is.infinite = function (value) {
16649   return value === Infinity || value === -Infinity;
16650 };
16651
16652 /**
16653  * is.decimal
16654  * Test if `value` is a decimal number.
16655  *
16656  * @param {Mixed} value value to test
16657  * @return {Boolean} true if `value` is a decimal number, false otherwise
16658  * @api public
16659  */
16660
16661 is.decimal = function (value) {
16662   return is.number(value) && !isActualNaN(value) && value % 1 !== 0;
16663 };
16664
16665 /**
16666  * is.divisibleBy
16667  * Test if `value` is divisible by `n`.
16668  *
16669  * @param {Number} value value to test
16670  * @param {Number} n dividend
16671  * @return {Boolean} true if `value` is divisible by `n`, false otherwise
16672  * @api public
16673  */
16674
16675 is.divisibleBy = function (value, n) {
16676   var isDividendInfinite = is.infinite(value);
16677   var isDivisorInfinite = is.infinite(n);
16678   var isNonZeroNumber = is.number(value) && !isActualNaN(value) && is.number(n) && !isActualNaN(n) && n !== 0;
16679   return isDividendInfinite || isDivisorInfinite || (isNonZeroNumber && value % n === 0);
16680 };
16681
16682 /**
16683  * is.int
16684  * Test if `value` is an integer.
16685  *
16686  * @param value to test
16687  * @return {Boolean} true if `value` is an integer, false otherwise
16688  * @api public
16689  */
16690
16691 is.int = function (value) {
16692   return is.number(value) && !isActualNaN(value) && value % 1 === 0;
16693 };
16694
16695 /**
16696  * is.maximum
16697  * Test if `value` is greater than 'others' values.
16698  *
16699  * @param {Number} value value to test
16700  * @param {Array} others values to compare with
16701  * @return {Boolean} true if `value` is greater than `others` values
16702  * @api public
16703  */
16704
16705 is.maximum = function (value, others) {
16706   if (isActualNaN(value)) {
16707     throw new TypeError('NaN is not a valid value');
16708   } else if (!is.arraylike(others)) {
16709     throw new TypeError('second argument must be array-like');
16710   }
16711   var len = others.length;
16712
16713   while (--len >= 0) {
16714     if (value < others[len]) {
16715       return false;
16716     }
16717   }
16718
16719   return true;
16720 };
16721
16722 /**
16723  * is.minimum
16724  * Test if `value` is less than `others` values.
16725  *
16726  * @param {Number} value value to test
16727  * @param {Array} others values to compare with
16728  * @return {Boolean} true if `value` is less than `others` values
16729  * @api public
16730  */
16731
16732 is.minimum = function (value, others) {
16733   if (isActualNaN(value)) {
16734     throw new TypeError('NaN is not a valid value');
16735   } else if (!is.arraylike(others)) {
16736     throw new TypeError('second argument must be array-like');
16737   }
16738   var len = others.length;
16739
16740   while (--len >= 0) {
16741     if (value > others[len]) {
16742       return false;
16743     }
16744   }
16745
16746   return true;
16747 };
16748
16749 /**
16750  * is.nan
16751  * Test if `value` is not a number.
16752  *
16753  * @param {Mixed} value value to test
16754  * @return {Boolean} true if `value` is not a number, false otherwise
16755  * @api public
16756  */
16757
16758 is.nan = function (value) {
16759   return !is.number(value) || value !== value;
16760 };
16761
16762 /**
16763  * is.even
16764  * Test if `value` is an even number.
16765  *
16766  * @param {Number} value value to test
16767  * @return {Boolean} true if `value` is an even number, false otherwise
16768  * @api public
16769  */
16770
16771 is.even = function (value) {
16772   return is.infinite(value) || (is.number(value) && value === value && value % 2 === 0);
16773 };
16774
16775 /**
16776  * is.odd
16777  * Test if `value` is an odd number.
16778  *
16779  * @param {Number} value value to test
16780  * @return {Boolean} true if `value` is an odd number, false otherwise
16781  * @api public
16782  */
16783
16784 is.odd = function (value) {
16785   return is.infinite(value) || (is.number(value) && value === value && value % 2 !== 0);
16786 };
16787
16788 /**
16789  * is.ge
16790  * Test if `value` is greater than or equal to `other`.
16791  *
16792  * @param {Number} value value to test
16793  * @param {Number} other value to compare with
16794  * @return {Boolean}
16795  * @api public
16796  */
16797
16798 is.ge = function (value, other) {
16799   if (isActualNaN(value) || isActualNaN(other)) {
16800     throw new TypeError('NaN is not a valid value');
16801   }
16802   return !is.infinite(value) && !is.infinite(other) && value >= other;
16803 };
16804
16805 /**
16806  * is.gt
16807  * Test if `value` is greater than `other`.
16808  *
16809  * @param {Number} value value to test
16810  * @param {Number} other value to compare with
16811  * @return {Boolean}
16812  * @api public
16813  */
16814
16815 is.gt = function (value, other) {
16816   if (isActualNaN(value) || isActualNaN(other)) {
16817     throw new TypeError('NaN is not a valid value');
16818   }
16819   return !is.infinite(value) && !is.infinite(other) && value > other;
16820 };
16821
16822 /**
16823  * is.le
16824  * Test if `value` is less than or equal to `other`.
16825  *
16826  * @param {Number} value value to test
16827  * @param {Number} other value to compare with
16828  * @return {Boolean} if 'value' is less than or equal to 'other'
16829  * @api public
16830  */
16831
16832 is.le = function (value, other) {
16833   if (isActualNaN(value) || isActualNaN(other)) {
16834     throw new TypeError('NaN is not a valid value');
16835   }
16836   return !is.infinite(value) && !is.infinite(other) && value <= other;
16837 };
16838
16839 /**
16840  * is.lt
16841  * Test if `value` is less than `other`.
16842  *
16843  * @param {Number} value value to test
16844  * @param {Number} other value to compare with
16845  * @return {Boolean} if `value` is less than `other`
16846  * @api public
16847  */
16848
16849 is.lt = function (value, other) {
16850   if (isActualNaN(value) || isActualNaN(other)) {
16851     throw new TypeError('NaN is not a valid value');
16852   }
16853   return !is.infinite(value) && !is.infinite(other) && value < other;
16854 };
16855
16856 /**
16857  * is.within
16858  * Test if `value` is within `start` and `finish`.
16859  *
16860  * @param {Number} value value to test
16861  * @param {Number} start lower bound
16862  * @param {Number} finish upper bound
16863  * @return {Boolean} true if 'value' is is within 'start' and 'finish'
16864  * @api public
16865  */
16866 is.within = function (value, start, finish) {
16867   if (isActualNaN(value) || isActualNaN(start) || isActualNaN(finish)) {
16868     throw new TypeError('NaN is not a valid value');
16869   } else if (!is.number(value) || !is.number(start) || !is.number(finish)) {
16870     throw new TypeError('all arguments must be numbers');
16871   }
16872   var isAnyInfinite = is.infinite(value) || is.infinite(start) || is.infinite(finish);
16873   return isAnyInfinite || (value >= start && value <= finish);
16874 };
16875
16876 /**
16877  * Test object.
16878  */
16879
16880 /**
16881  * is.object
16882  * Test if `value` is an object.
16883  *
16884  * @param {Mixed} value value to test
16885  * @return {Boolean} true if `value` is an object, false otherwise
16886  * @api public
16887  */
16888
16889 is.object = function (value) {
16890   return value && '[object Object]' === toString.call(value);
16891 };
16892
16893 /**
16894  * is.hash
16895  * Test if `value` is a hash - a plain object literal.
16896  *
16897  * @param {Mixed} value value to test
16898  * @return {Boolean} true if `value` is a hash, false otherwise
16899  * @api public
16900  */
16901
16902 is.hash = function (value) {
16903   return is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval;
16904 };
16905
16906 /**
16907  * Test regexp.
16908  */
16909
16910 /**
16911  * is.regexp
16912  * Test if `value` is a regular expression.
16913  *
16914  * @param {Mixed} value value to test
16915  * @return {Boolean} true if `value` is a regexp, false otherwise
16916  * @api public
16917  */
16918
16919 is.regexp = function (value) {
16920   return '[object RegExp]' === toString.call(value);
16921 };
16922
16923 /**
16924  * Test string.
16925  */
16926
16927 /**
16928  * is.string
16929  * Test if `value` is a string.
16930  *
16931  * @param {Mixed} value value to test
16932  * @return {Boolean} true if 'value' is a string, false otherwise
16933  * @api public
16934  */
16935
16936 is.string = function (value) {
16937   return '[object String]' === toString.call(value);
16938 };
16939
16940
16941 },{}],10:[function(require,module,exports){
16942
16943 var hasOwn = Object.prototype.hasOwnProperty;
16944 var toString = Object.prototype.toString;
16945
16946 module.exports = function forEach (obj, fn, ctx) {
16947     if (toString.call(fn) !== '[object Function]') {
16948         throw new TypeError('iterator must be a function');
16949     }
16950     var l = obj.length;
16951     if (l === +l) {
16952         for (var i = 0; i < l; i++) {
16953             fn.call(ctx, obj[i], i, obj);
16954         }
16955     } else {
16956         for (var k in obj) {
16957             if (hasOwn.call(obj, k)) {
16958                 fn.call(ctx, obj[k], k, obj);
16959             }
16960         }
16961     }
16962 };
16963
16964
16965 },{}]},{},[1])(1)
16966 });
16967 ;
16968 /*
16969  (c) 2015, Vladimir Agafonkin
16970  RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
16971  https://github.com/mourner/rbush
16972 */
16973
16974 (function () {
16975 'use strict';
16976
16977 function rbush(maxEntries, format) {
16978
16979     // jshint newcap: false, validthis: true
16980     if (!(this instanceof rbush)) return new rbush(maxEntries, format);
16981
16982     // max entries in a node is 9 by default; min node fill is 40% for best performance
16983     this._maxEntries = Math.max(4, maxEntries || 9);
16984     this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
16985
16986     if (format) {
16987         this._initFormat(format);
16988     }
16989
16990     this.clear();
16991 }
16992
16993 rbush.prototype = {
16994
16995     all: function () {
16996         return this._all(this.data, []);
16997     },
16998
16999     search: function (bbox) {
17000
17001         var node = this.data,
17002             result = [],
17003             toBBox = this.toBBox;
17004
17005         if (!intersects(bbox, node.bbox)) return result;
17006
17007         var nodesToSearch = [],
17008             i, len, child, childBBox;
17009
17010         while (node) {
17011             for (i = 0, len = node.children.length; i < len; i++) {
17012
17013                 child = node.children[i];
17014                 childBBox = node.leaf ? toBBox(child) : child.bbox;
17015
17016                 if (intersects(bbox, childBBox)) {
17017                     if (node.leaf) result.push(child);
17018                     else if (contains(bbox, childBBox)) this._all(child, result);
17019                     else nodesToSearch.push(child);
17020                 }
17021             }
17022             node = nodesToSearch.pop();
17023         }
17024
17025         return result;
17026     },
17027
17028     collides: function (bbox) {
17029
17030         var node = this.data,
17031             toBBox = this.toBBox;
17032
17033         if (!intersects(bbox, node.bbox)) return false;
17034
17035         var nodesToSearch = [],
17036             i, len, child, childBBox;
17037
17038         while (node) {
17039             for (i = 0, len = node.children.length; i < len; i++) {
17040
17041                 child = node.children[i];
17042                 childBBox = node.leaf ? toBBox(child) : child.bbox;
17043
17044                 if (intersects(bbox, childBBox)) {
17045                     if (node.leaf || contains(bbox, childBBox)) return true;
17046                     nodesToSearch.push(child);
17047                 }
17048             }
17049             node = nodesToSearch.pop();
17050         }
17051
17052         return false;
17053     },
17054
17055     load: function (data) {
17056         if (!(data && data.length)) return this;
17057
17058         if (data.length < this._minEntries) {
17059             for (var i = 0, len = data.length; i < len; i++) {
17060                 this.insert(data[i]);
17061             }
17062             return this;
17063         }
17064
17065         // recursively build the tree with the given data from stratch using OMT algorithm
17066         var node = this._build(data.slice(), 0, data.length - 1, 0);
17067
17068         if (!this.data.children.length) {
17069             // save as is if tree is empty
17070             this.data = node;
17071
17072         } else if (this.data.height === node.height) {
17073             // split root if trees have the same height
17074             this._splitRoot(this.data, node);
17075
17076         } else {
17077             if (this.data.height < node.height) {
17078                 // swap trees if inserted one is bigger
17079                 var tmpNode = this.data;
17080                 this.data = node;
17081                 node = tmpNode;
17082             }
17083
17084             // insert the small tree into the large tree at appropriate level
17085             this._insert(node, this.data.height - node.height - 1, true);
17086         }
17087
17088         return this;
17089     },
17090
17091     insert: function (item) {
17092         if (item) this._insert(item, this.data.height - 1);
17093         return this;
17094     },
17095
17096     clear: function () {
17097         this.data = {
17098             children: [],
17099             height: 1,
17100             bbox: empty(),
17101             leaf: true
17102         };
17103         return this;
17104     },
17105
17106     remove: function (item) {
17107         if (!item) return this;
17108
17109         var node = this.data,
17110             bbox = this.toBBox(item),
17111             path = [],
17112             indexes = [],
17113             i, parent, index, goingUp;
17114
17115         // depth-first iterative tree traversal
17116         while (node || path.length) {
17117
17118             if (!node) { // go up
17119                 node = path.pop();
17120                 parent = path[path.length - 1];
17121                 i = indexes.pop();
17122                 goingUp = true;
17123             }
17124
17125             if (node.leaf) { // check current node
17126                 index = node.children.indexOf(item);
17127
17128                 if (index !== -1) {
17129                     // item found, remove the item and condense tree upwards
17130                     node.children.splice(index, 1);
17131                     path.push(node);
17132                     this._condense(path);
17133                     return this;
17134                 }
17135             }
17136
17137             if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
17138                 path.push(node);
17139                 indexes.push(i);
17140                 i = 0;
17141                 parent = node;
17142                 node = node.children[0];
17143
17144             } else if (parent) { // go right
17145                 i++;
17146                 node = parent.children[i];
17147                 goingUp = false;
17148
17149             } else node = null; // nothing found
17150         }
17151
17152         return this;
17153     },
17154
17155     toBBox: function (item) { return item; },
17156
17157     compareMinX: function (a, b) { return a[0] - b[0]; },
17158     compareMinY: function (a, b) { return a[1] - b[1]; },
17159
17160     toJSON: function () { return this.data; },
17161
17162     fromJSON: function (data) {
17163         this.data = data;
17164         return this;
17165     },
17166
17167     _all: function (node, result) {
17168         var nodesToSearch = [];
17169         while (node) {
17170             if (node.leaf) result.push.apply(result, node.children);
17171             else nodesToSearch.push.apply(nodesToSearch, node.children);
17172
17173             node = nodesToSearch.pop();
17174         }
17175         return result;
17176     },
17177
17178     _build: function (items, left, right, height) {
17179
17180         var N = right - left + 1,
17181             M = this._maxEntries,
17182             node;
17183
17184         if (N <= M) {
17185             // reached leaf level; return leaf
17186             node = {
17187                 children: items.slice(left, right + 1),
17188                 height: 1,
17189                 bbox: null,
17190                 leaf: true
17191             };
17192             calcBBox(node, this.toBBox);
17193             return node;
17194         }
17195
17196         if (!height) {
17197             // target height of the bulk-loaded tree
17198             height = Math.ceil(Math.log(N) / Math.log(M));
17199
17200             // target number of root entries to maximize storage utilization
17201             M = Math.ceil(N / Math.pow(M, height - 1));
17202         }
17203
17204         node = {
17205             children: [],
17206             height: height,
17207             bbox: null,
17208             leaf: false
17209         };
17210
17211         // split the items into M mostly square tiles
17212
17213         var N2 = Math.ceil(N / M),
17214             N1 = N2 * Math.ceil(Math.sqrt(M)),
17215             i, j, right2, right3;
17216
17217         multiSelect(items, left, right, N1, this.compareMinX);
17218
17219         for (i = left; i <= right; i += N1) {
17220
17221             right2 = Math.min(i + N1 - 1, right);
17222
17223             multiSelect(items, i, right2, N2, this.compareMinY);
17224
17225             for (j = i; j <= right2; j += N2) {
17226
17227                 right3 = Math.min(j + N2 - 1, right2);
17228
17229                 // pack each entry recursively
17230                 node.children.push(this._build(items, j, right3, height - 1));
17231             }
17232         }
17233
17234         calcBBox(node, this.toBBox);
17235
17236         return node;
17237     },
17238
17239     _chooseSubtree: function (bbox, node, level, path) {
17240
17241         var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
17242
17243         while (true) {
17244             path.push(node);
17245
17246             if (node.leaf || path.length - 1 === level) break;
17247
17248             minArea = minEnlargement = Infinity;
17249
17250             for (i = 0, len = node.children.length; i < len; i++) {
17251                 child = node.children[i];
17252                 area = bboxArea(child.bbox);
17253                 enlargement = enlargedArea(bbox, child.bbox) - area;
17254
17255                 // choose entry with the least area enlargement
17256                 if (enlargement < minEnlargement) {
17257                     minEnlargement = enlargement;
17258                     minArea = area < minArea ? area : minArea;
17259                     targetNode = child;
17260
17261                 } else if (enlargement === minEnlargement) {
17262                     // otherwise choose one with the smallest area
17263                     if (area < minArea) {
17264                         minArea = area;
17265                         targetNode = child;
17266                     }
17267                 }
17268             }
17269
17270             node = targetNode;
17271         }
17272
17273         return node;
17274     },
17275
17276     _insert: function (item, level, isNode) {
17277
17278         var toBBox = this.toBBox,
17279             bbox = isNode ? item.bbox : toBBox(item),
17280             insertPath = [];
17281
17282         // find the best node for accommodating the item, saving all nodes along the path too
17283         var node = this._chooseSubtree(bbox, this.data, level, insertPath);
17284
17285         // put the item into the node
17286         node.children.push(item);
17287         extend(node.bbox, bbox);
17288
17289         // split on node overflow; propagate upwards if necessary
17290         while (level >= 0) {
17291             if (insertPath[level].children.length > this._maxEntries) {
17292                 this._split(insertPath, level);
17293                 level--;
17294             } else break;
17295         }
17296
17297         // adjust bboxes along the insertion path
17298         this._adjustParentBBoxes(bbox, insertPath, level);
17299     },
17300
17301     // split overflowed node into two
17302     _split: function (insertPath, level) {
17303
17304         var node = insertPath[level],
17305             M = node.children.length,
17306             m = this._minEntries;
17307
17308         this._chooseSplitAxis(node, m, M);
17309
17310         var splitIndex = this._chooseSplitIndex(node, m, M);
17311
17312         var newNode = {
17313             children: node.children.splice(splitIndex, node.children.length - splitIndex),
17314             height: node.height,
17315             bbox: null,
17316             leaf: false
17317         };
17318
17319         if (node.leaf) newNode.leaf = true;
17320
17321         calcBBox(node, this.toBBox);
17322         calcBBox(newNode, this.toBBox);
17323
17324         if (level) insertPath[level - 1].children.push(newNode);
17325         else this._splitRoot(node, newNode);
17326     },
17327
17328     _splitRoot: function (node, newNode) {
17329         // split root node
17330         this.data = {
17331             children: [node, newNode],
17332             height: node.height + 1,
17333             bbox: null,
17334             leaf: false
17335         };
17336         calcBBox(this.data, this.toBBox);
17337     },
17338
17339     _chooseSplitIndex: function (node, m, M) {
17340
17341         var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
17342
17343         minOverlap = minArea = Infinity;
17344
17345         for (i = m; i <= M - m; i++) {
17346             bbox1 = distBBox(node, 0, i, this.toBBox);
17347             bbox2 = distBBox(node, i, M, this.toBBox);
17348
17349             overlap = intersectionArea(bbox1, bbox2);
17350             area = bboxArea(bbox1) + bboxArea(bbox2);
17351
17352             // choose distribution with minimum overlap
17353             if (overlap < minOverlap) {
17354                 minOverlap = overlap;
17355                 index = i;
17356
17357                 minArea = area < minArea ? area : minArea;
17358
17359             } else if (overlap === minOverlap) {
17360                 // otherwise choose distribution with minimum area
17361                 if (area < minArea) {
17362                     minArea = area;
17363                     index = i;
17364                 }
17365             }
17366         }
17367
17368         return index;
17369     },
17370
17371     // sorts node children by the best axis for split
17372     _chooseSplitAxis: function (node, m, M) {
17373
17374         var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
17375             compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
17376             xMargin = this._allDistMargin(node, m, M, compareMinX),
17377             yMargin = this._allDistMargin(node, m, M, compareMinY);
17378
17379         // if total distributions margin value is minimal for x, sort by minX,
17380         // otherwise it's already sorted by minY
17381         if (xMargin < yMargin) node.children.sort(compareMinX);
17382     },
17383
17384     // total margin of all possible split distributions where each node is at least m full
17385     _allDistMargin: function (node, m, M, compare) {
17386
17387         node.children.sort(compare);
17388
17389         var toBBox = this.toBBox,
17390             leftBBox = distBBox(node, 0, m, toBBox),
17391             rightBBox = distBBox(node, M - m, M, toBBox),
17392             margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
17393             i, child;
17394
17395         for (i = m; i < M - m; i++) {
17396             child = node.children[i];
17397             extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
17398             margin += bboxMargin(leftBBox);
17399         }
17400
17401         for (i = M - m - 1; i >= m; i--) {
17402             child = node.children[i];
17403             extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
17404             margin += bboxMargin(rightBBox);
17405         }
17406
17407         return margin;
17408     },
17409
17410     _adjustParentBBoxes: function (bbox, path, level) {
17411         // adjust bboxes along the given tree path
17412         for (var i = level; i >= 0; i--) {
17413             extend(path[i].bbox, bbox);
17414         }
17415     },
17416
17417     _condense: function (path) {
17418         // go through the path, removing empty nodes and updating bboxes
17419         for (var i = path.length - 1, siblings; i >= 0; i--) {
17420             if (path[i].children.length === 0) {
17421                 if (i > 0) {
17422                     siblings = path[i - 1].children;
17423                     siblings.splice(siblings.indexOf(path[i]), 1);
17424
17425                 } else this.clear();
17426
17427             } else calcBBox(path[i], this.toBBox);
17428         }
17429     },
17430
17431     _initFormat: function (format) {
17432         // data format (minX, minY, maxX, maxY accessors)
17433
17434         // uses eval-type function compilation instead of just accepting a toBBox function
17435         // because the algorithms are very sensitive to sorting functions performance,
17436         // so they should be dead simple and without inner calls
17437
17438         // jshint evil: true
17439
17440         var compareArr = ['return a', ' - b', ';'];
17441
17442         this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
17443         this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
17444
17445         this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
17446     }
17447 };
17448
17449
17450 // calculate node's bbox from bboxes of its children
17451 function calcBBox(node, toBBox) {
17452     node.bbox = distBBox(node, 0, node.children.length, toBBox);
17453 }
17454
17455 // min bounding rectangle of node children from k to p-1
17456 function distBBox(node, k, p, toBBox) {
17457     var bbox = empty();
17458
17459     for (var i = k, child; i < p; i++) {
17460         child = node.children[i];
17461         extend(bbox, node.leaf ? toBBox(child) : child.bbox);
17462     }
17463
17464     return bbox;
17465 }
17466
17467 function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
17468
17469 function extend(a, b) {
17470     a[0] = Math.min(a[0], b[0]);
17471     a[1] = Math.min(a[1], b[1]);
17472     a[2] = Math.max(a[2], b[2]);
17473     a[3] = Math.max(a[3], b[3]);
17474     return a;
17475 }
17476
17477 function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
17478 function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
17479
17480 function bboxArea(a)   { return (a[2] - a[0]) * (a[3] - a[1]); }
17481 function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
17482
17483 function enlargedArea(a, b) {
17484     return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
17485            (Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
17486 }
17487
17488 function intersectionArea(a, b) {
17489     var minX = Math.max(a[0], b[0]),
17490         minY = Math.max(a[1], b[1]),
17491         maxX = Math.min(a[2], b[2]),
17492         maxY = Math.min(a[3], b[3]);
17493
17494     return Math.max(0, maxX - minX) *
17495            Math.max(0, maxY - minY);
17496 }
17497
17498 function contains(a, b) {
17499     return a[0] <= b[0] &&
17500            a[1] <= b[1] &&
17501            b[2] <= a[2] &&
17502            b[3] <= a[3];
17503 }
17504
17505 function intersects(a, b) {
17506     return b[0] <= a[2] &&
17507            b[1] <= a[3] &&
17508            b[2] >= a[0] &&
17509            b[3] >= a[1];
17510 }
17511
17512 // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
17513 // combines selection algorithm with binary divide & conquer approach
17514
17515 function multiSelect(arr, left, right, n, compare) {
17516     var stack = [left, right],
17517         mid;
17518
17519     while (stack.length) {
17520         right = stack.pop();
17521         left = stack.pop();
17522
17523         if (right - left <= n) continue;
17524
17525         mid = left + Math.ceil((right - left) / n / 2) * n;
17526         select(arr, left, right, mid, compare);
17527
17528         stack.push(left, mid, mid, right);
17529     }
17530 }
17531
17532 // Floyd-Rivest selection algorithm:
17533 // sort an array between left and right (inclusive) so that the smallest k elements come first (unordered)
17534 function select(arr, left, right, k, compare) {
17535     var n, i, z, s, sd, newLeft, newRight, t, j;
17536
17537     while (right > left) {
17538         if (right - left > 600) {
17539             n = right - left + 1;
17540             i = k - left + 1;
17541             z = Math.log(n);
17542             s = 0.5 * Math.exp(2 * z / 3);
17543             sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1);
17544             newLeft = Math.max(left, Math.floor(k - i * s / n + sd));
17545             newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd));
17546             select(arr, newLeft, newRight, k, compare);
17547         }
17548
17549         t = arr[k];
17550         i = left;
17551         j = right;
17552
17553         swap(arr, left, k);
17554         if (compare(arr[right], t) > 0) swap(arr, left, right);
17555
17556         while (i < j) {
17557             swap(arr, i, j);
17558             i++;
17559             j--;
17560             while (compare(arr[i], t) < 0) i++;
17561             while (compare(arr[j], t) > 0) j--;
17562         }
17563
17564         if (compare(arr[left], t) === 0) swap(arr, left, j);
17565         else {
17566             j++;
17567             swap(arr, j, right);
17568         }
17569
17570         if (j <= k) left = j + 1;
17571         if (k <= j) right = j - 1;
17572     }
17573 }
17574
17575 function swap(arr, i, j) {
17576     var tmp = arr[i];
17577     arr[i] = arr[j];
17578     arr[j] = tmp;
17579 }
17580
17581
17582 // export as AMD/CommonJS module or global variable
17583 if (typeof define === 'function' && define.amd) define('rbush', function () { return rbush; });
17584 else if (typeof module !== 'undefined') module.exports = rbush;
17585 else if (typeof self !== 'undefined') self.rbush = rbush;
17586 else window.rbush = rbush;
17587
17588 })();
17589 (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;
17590 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){
17591 module.exports = element;
17592 module.exports.pair = pair;
17593 module.exports.format = format;
17594 module.exports.formatPair = formatPair;
17595
17596 function element(x, dims) {
17597     return search(x, dims).val;
17598 }
17599
17600 function formatPair(x) {
17601     return format(x.lat, 'lat') + ' ' + format(x.lon, 'lon');
17602 }
17603
17604 // Is 0 North or South?
17605 function format(x, dim) {
17606     var dirs = {
17607             lat: ['N', 'S'],
17608             lon: ['E', 'W']
17609         }[dim] || '',
17610         dir = dirs[x >= 0 ? 0 : 1],
17611         abs = Math.abs(x),
17612         whole = Math.floor(abs),
17613         fraction = abs - whole,
17614         fractionMinutes = fraction * 60,
17615         minutes = Math.floor(fractionMinutes),
17616         seconds = Math.floor((fractionMinutes - minutes) * 60);
17617
17618     return whole + '° ' +
17619         (minutes ? minutes + "' " : '') +
17620         (seconds ? seconds + '" ' : '') + dir;
17621 }
17622
17623 function search(x, dims, r) {
17624     if (!dims) dims = 'NSEW';
17625     if (typeof x !== 'string') return { val: null, regex: r };
17626     r = r || /[\s\,]*([\-|\—|\―]?[0-9.]+)°? *(?:([0-9.]+)['’′‘] *)?(?:([0-9.]+)(?:''|"|”|″) *)?([NSEW])?/gi;
17627     var m = r.exec(x);
17628     if (!m) return { val: null, regex: r };
17629     else if (m[4] && dims.indexOf(m[4]) === -1) return { val: null, regex: r };
17630     else return {
17631         val: (((m[1]) ? parseFloat(m[1]) : 0) +
17632             ((m[2] ? parseFloat(m[2]) / 60 : 0)) +
17633             ((m[3] ? parseFloat(m[3]) / 3600 : 0))) *
17634             ((m[4] && m[4] === 'S' || m[4] === 'W') ? -1 : 1),
17635         regex: r,
17636         raw: m[0],
17637         dim: m[4]
17638     };
17639 }
17640
17641 function pair(x, dims) {
17642     x = x.trim();
17643     var one = search(x, dims);
17644     if (one.val === null) return null;
17645     var two = search(x, dims, one.regex);
17646     if (two.val === null) return null;
17647     // null if one/two are not contiguous.
17648     if (one.raw + two.raw !== x) return null;
17649     if (one.dim) return swapdim(one.val, two.val, one.dim);
17650     else return [one.val, two.val];
17651 }
17652
17653 function swapdim(a, b, dim) {
17654     if (dim == 'N' || dim == 'S') return [a, b];
17655     if (dim == 'W' || dim == 'E') return [b, a];
17656 }
17657
17658 },{}]},{},[1])
17659 (1)
17660 });
17661 ;toGeoJSON = (function() {
17662     'use strict';
17663
17664     var removeSpace = (/\s*/g),
17665         trimSpace = (/^\s*|\s*$/g),
17666         splitSpace = (/\s+/);
17667     // generate a short, numeric hash of a string
17668     function okhash(x) {
17669         if (!x || !x.length) return 0;
17670         for (var i = 0, h = 0; i < x.length; i++) {
17671             h = ((h << 5) - h) + x.charCodeAt(i) | 0;
17672         } return h;
17673     }
17674     // all Y children of X
17675     function get(x, y) { return x.getElementsByTagName(y); }
17676     function attr(x, y) { return x.getAttribute(y); }
17677     function attrf(x, y) { return parseFloat(attr(x, y)); }
17678     // one Y child of X, if any, otherwise null
17679     function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
17680     // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
17681     function norm(el) { if (el.normalize) { el.normalize(); } return el; }
17682     // cast array x into numbers
17683     function numarray(x) {
17684         for (var j = 0, o = []; j < x.length; j++) o[j] = parseFloat(x[j]);
17685         return o;
17686     }
17687     function clean(x) {
17688         var o = {};
17689         for (var i in x) if (x[i]) o[i] = x[i];
17690         return o;
17691     }
17692     // get the content of a text node, if any
17693     function nodeVal(x) { if (x) {norm(x);} return x && x.firstChild && x.firstChild.nodeValue; }
17694     // get one coordinate from a coordinate array, if any
17695     function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
17696     // get all coordinates from a coordinate array as [[],[]]
17697     function coord(v) {
17698         var coords = v.replace(trimSpace, '').split(splitSpace),
17699             o = [];
17700         for (var i = 0; i < coords.length; i++) {
17701             o.push(coord1(coords[i]));
17702         }
17703         return o;
17704     }
17705     function coordPair(x) { return [attrf(x, 'lon'), attrf(x, 'lat')]; }
17706
17707     // create a new feature collection parent object
17708     function fc() {
17709         return {
17710             type: 'FeatureCollection',
17711             features: []
17712         };
17713     }
17714
17715     var styleSupport = false;
17716     if (typeof XMLSerializer !== 'undefined') {
17717         var serializer = new XMLSerializer();
17718         styleSupport = true;
17719     }
17720     function xml2str(str) { return serializer.serializeToString(str); }
17721
17722     var t = {
17723         kml: function(doc, o) {
17724             o = o || {};
17725
17726             var gj = fc(),
17727                 // styleindex keeps track of hashed styles in order to match features
17728                 styleIndex = {},
17729                 // atomic geospatial types supported by KML - MultiGeometry is
17730                 // handled separately
17731                 geotypes = ['Polygon', 'LineString', 'Point', 'Track'],
17732                 // all root placemarks in the file
17733                 placemarks = get(doc, 'Placemark'),
17734                 styles = get(doc, 'Style');
17735
17736             if (styleSupport) for (var k = 0; k < styles.length; k++) {
17737                 styleIndex['#' + attr(styles[k], 'id')] = okhash(xml2str(styles[k])).toString(16);
17738             }
17739             for (var j = 0; j < placemarks.length; j++) {
17740                 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
17741             }
17742             function gxCoord(v) { return numarray(v.split(' ')); }
17743             function gxCoords(root) {
17744                 var elems = get(root, 'coord', 'gx'), coords = [];
17745                 for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
17746                 return coords;
17747             }
17748             function getGeometry(root) {
17749                 var geomNode, geomNodes, i, j, k, geoms = [];
17750                 if (get1(root, 'MultiGeometry')) return getGeometry(get1(root, 'MultiGeometry'));
17751                 if (get1(root, 'MultiTrack')) return getGeometry(get1(root, 'MultiTrack'));
17752                 for (i = 0; i < geotypes.length; i++) {
17753                     geomNodes = get(root, geotypes[i]);
17754                     if (geomNodes) {
17755                         for (j = 0; j < geomNodes.length; j++) {
17756                             geomNode = geomNodes[j];
17757                             if (geotypes[i] == 'Point') {
17758                                 geoms.push({
17759                                     type: 'Point',
17760                                     coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
17761                                 });
17762                             } else if (geotypes[i] == 'LineString') {
17763                                 geoms.push({
17764                                     type: 'LineString',
17765                                     coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
17766                                 });
17767                             } else if (geotypes[i] == 'Polygon') {
17768                                 var rings = get(geomNode, 'LinearRing'),
17769                                     coords = [];
17770                                 for (k = 0; k < rings.length; k++) {
17771                                     coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
17772                                 }
17773                                 geoms.push({
17774                                     type: 'Polygon',
17775                                     coordinates: coords
17776                                 });
17777                             } else if (geotypes[i] == 'Track') {
17778                                 geoms.push({
17779                                     type: 'LineString',
17780                                     coordinates: gxCoords(geomNode)
17781                                 });
17782                             }
17783                         }
17784                     }
17785                 }
17786                 return geoms;
17787             }
17788             function getPlacemark(root) {
17789                 var geoms = getGeometry(root), i, properties = {},
17790                     name = nodeVal(get1(root, 'name')),
17791                     styleUrl = nodeVal(get1(root, 'styleUrl')),
17792                     description = nodeVal(get1(root, 'description')),
17793                     extendedData = get1(root, 'ExtendedData');
17794
17795                 if (!geoms.length) return [];
17796                 if (name) properties.name = name;
17797                 if (styleUrl && styleIndex[styleUrl]) {
17798                     properties.styleUrl = styleUrl;
17799                     properties.styleHash = styleIndex[styleUrl];
17800                 }
17801                 if (description) properties.description = description;
17802                 if (extendedData) {
17803                     var datas = get(extendedData, 'Data'),
17804                         simpleDatas = get(extendedData, 'SimpleData');
17805
17806                     for (i = 0; i < datas.length; i++) {
17807                         properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
17808                     }
17809                     for (i = 0; i < simpleDatas.length; i++) {
17810                         properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
17811                     }
17812                 }
17813                 return [{
17814                     type: 'Feature',
17815                     geometry: (geoms.length === 1) ? geoms[0] : {
17816                         type: 'GeometryCollection',
17817                         geometries: geoms
17818                     },
17819                     properties: properties
17820                 }];
17821             }
17822             return gj;
17823         },
17824         gpx: function(doc, o) {
17825             var i,
17826                 tracks = get(doc, 'trk'),
17827                 routes = get(doc, 'rte'),
17828                 waypoints = get(doc, 'wpt'),
17829                 // a feature collection
17830                 gj = fc();
17831             for (i = 0; i < tracks.length; i++) {
17832                 gj.features.push(getLinestring(tracks[i], 'trkpt'));
17833             }
17834             for (i = 0; i < routes.length; i++) {
17835                 gj.features.push(getLinestring(routes[i], 'rtept'));
17836             }
17837             for (i = 0; i < waypoints.length; i++) {
17838                 gj.features.push(getPoint(waypoints[i]));
17839             }
17840             function getLinestring(node, pointname) {
17841                 var j, pts = get(node, pointname), line = [];
17842                 for (j = 0; j < pts.length; j++) {
17843                     line.push(coordPair(pts[j]));
17844                 }
17845                 return {
17846                     type: 'Feature',
17847                     properties: getProperties(node),
17848                     geometry: {
17849                         type: 'LineString',
17850                         coordinates: line
17851                     }
17852                 };
17853             }
17854             function getPoint(node) {
17855                 var prop = getProperties(node);
17856                 prop.ele = nodeVal(get1(node, 'ele'));
17857                 prop.sym = nodeVal(get1(node, 'sym'));
17858                 return {
17859                     type: 'Feature',
17860                     properties: prop,
17861                     geometry: {
17862                         type: 'Point',
17863                         coordinates: coordPair(node)
17864                     }
17865                 };
17866             }
17867             function getProperties(node) {
17868                 var meta = ['name', 'desc', 'author', 'copyright', 'link',
17869                             'time', 'keywords'],
17870                     prop = {},
17871                     k;
17872                 for (k = 0; k < meta.length; k++) {
17873                     prop[meta[k]] = nodeVal(get1(node, meta[k]));
17874                 }
17875                 return clean(prop);
17876             }
17877             return gj;
17878         }
17879     };
17880     return t;
17881 })();
17882
17883 if (typeof module !== 'undefined') module.exports = toGeoJSON;
17884 /**
17885  * marked - a markdown parser
17886  * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
17887  * https://github.com/chjj/marked
17888  */
17889
17890 ;(function() {
17891
17892 /**
17893  * Block-Level Grammar
17894  */
17895
17896 var block = {
17897   newline: /^\n+/,
17898   code: /^( {4}[^\n]+\n*)+/,
17899   fences: noop,
17900   hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17901   heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17902   nptable: noop,
17903   lheading: /^([^\n]+)\n *(=|-){3,} *\n*/,
17904   blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
17905   list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17906   html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
17907   def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17908   table: noop,
17909   paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17910   text: /^[^\n]+/
17911 };
17912
17913 block.bullet = /(?:[*+-]|\d+\.)/;
17914 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17915 block.item = replace(block.item, 'gm')
17916   (/bull/g, block.bullet)
17917   ();
17918
17919 block.list = replace(block.list)
17920   (/bull/g, block.bullet)
17921   ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)
17922   ();
17923
17924 block._tag = '(?!(?:'
17925   + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17926   + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17927   + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b';
17928
17929 block.html = replace(block.html)
17930   ('comment', /<!--[\s\S]*?-->/)
17931   ('closed', /<(tag)[\s\S]+?<\/\1>/)
17932   ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17933   (/tag/g, block._tag)
17934   ();
17935
17936 block.paragraph = replace(block.paragraph)
17937   ('hr', block.hr)
17938   ('heading', block.heading)
17939   ('lheading', block.lheading)
17940   ('blockquote', block.blockquote)
17941   ('tag', '<' + block._tag)
17942   ('def', block.def)
17943   ();
17944
17945 /**
17946  * Normal Block Grammar
17947  */
17948
17949 block.normal = merge({}, block);
17950
17951 /**
17952  * GFM Block Grammar
17953  */
17954
17955 block.gfm = merge({}, block.normal, {
17956   fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
17957   paragraph: /^/
17958 });
17959
17960 block.gfm.paragraph = replace(block.paragraph)
17961   ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|')
17962   ();
17963
17964 /**
17965  * GFM + Tables Block Grammar
17966  */
17967
17968 block.tables = merge({}, block.gfm, {
17969   nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17970   table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17971 });
17972
17973 /**
17974  * Block Lexer
17975  */
17976
17977 function Lexer(options) {
17978   this.tokens = [];
17979   this.tokens.links = {};
17980   this.options = options || marked.defaults;
17981   this.rules = block.normal;
17982
17983   if (this.options.gfm) {
17984     if (this.options.tables) {
17985       this.rules = block.tables;
17986     } else {
17987       this.rules = block.gfm;
17988     }
17989   }
17990 }
17991
17992 /**
17993  * Expose Block Rules
17994  */
17995
17996 Lexer.rules = block;
17997
17998 /**
17999  * Static Lex Method
18000  */
18001
18002 Lexer.lex = function(src, options) {
18003   var lexer = new Lexer(options);
18004   return lexer.lex(src);
18005 };
18006
18007 /**
18008  * Preprocessing
18009  */
18010
18011 Lexer.prototype.lex = function(src) {
18012   src = src
18013     .replace(/\r\n|\r/g, '\n')
18014     .replace(/\t/g, '    ')
18015     .replace(/\u00a0/g, ' ')
18016     .replace(/\u2424/g, '\n');
18017
18018   return this.token(src, true);
18019 };
18020
18021 /**
18022  * Lexing
18023  */
18024
18025 Lexer.prototype.token = function(src, top) {
18026   var src = src.replace(/^ +$/gm, '')
18027     , next
18028     , loose
18029     , cap
18030     , bull
18031     , b
18032     , item
18033     , space
18034     , i
18035     , l;
18036
18037   while (src) {
18038     // newline
18039     if (cap = this.rules.newline.exec(src)) {
18040       src = src.substring(cap[0].length);
18041       if (cap[0].length > 1) {
18042         this.tokens.push({
18043           type: 'space'
18044         });
18045       }
18046     }
18047
18048     // code
18049     if (cap = this.rules.code.exec(src)) {
18050       src = src.substring(cap[0].length);
18051       cap = cap[0].replace(/^ {4}/gm, '');
18052       this.tokens.push({
18053         type: 'code',
18054         text: !this.options.pedantic
18055           ? cap.replace(/\n+$/, '')
18056           : cap
18057       });
18058       continue;
18059     }
18060
18061     // fences (gfm)
18062     if (cap = this.rules.fences.exec(src)) {
18063       src = src.substring(cap[0].length);
18064       this.tokens.push({
18065         type: 'code',
18066         lang: cap[2],
18067         text: cap[3]
18068       });
18069       continue;
18070     }
18071
18072     // heading
18073     if (cap = this.rules.heading.exec(src)) {
18074       src = src.substring(cap[0].length);
18075       this.tokens.push({
18076         type: 'heading',
18077         depth: cap[1].length,
18078         text: cap[2]
18079       });
18080       continue;
18081     }
18082
18083     // table no leading pipe (gfm)
18084     if (top && (cap = this.rules.nptable.exec(src))) {
18085       src = src.substring(cap[0].length);
18086
18087       item = {
18088         type: 'table',
18089         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18090         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18091         cells: cap[3].replace(/\n$/, '').split('\n')
18092       };
18093
18094       for (i = 0; i < item.align.length; i++) {
18095         if (/^ *-+: *$/.test(item.align[i])) {
18096           item.align[i] = 'right';
18097         } else if (/^ *:-+: *$/.test(item.align[i])) {
18098           item.align[i] = 'center';
18099         } else if (/^ *:-+ *$/.test(item.align[i])) {
18100           item.align[i] = 'left';
18101         } else {
18102           item.align[i] = null;
18103         }
18104       }
18105
18106       for (i = 0; i < item.cells.length; i++) {
18107         item.cells[i] = item.cells[i].split(/ *\| */);
18108       }
18109
18110       this.tokens.push(item);
18111
18112       continue;
18113     }
18114
18115     // lheading
18116     if (cap = this.rules.lheading.exec(src)) {
18117       src = src.substring(cap[0].length);
18118       this.tokens.push({
18119         type: 'heading',
18120         depth: cap[2] === '=' ? 1 : 2,
18121         text: cap[1]
18122       });
18123       continue;
18124     }
18125
18126     // hr
18127     if (cap = this.rules.hr.exec(src)) {
18128       src = src.substring(cap[0].length);
18129       this.tokens.push({
18130         type: 'hr'
18131       });
18132       continue;
18133     }
18134
18135     // blockquote
18136     if (cap = this.rules.blockquote.exec(src)) {
18137       src = src.substring(cap[0].length);
18138
18139       this.tokens.push({
18140         type: 'blockquote_start'
18141       });
18142
18143       cap = cap[0].replace(/^ *> ?/gm, '');
18144
18145       // Pass `top` to keep the current
18146       // "toplevel" state. This is exactly
18147       // how markdown.pl works.
18148       this.token(cap, top);
18149
18150       this.tokens.push({
18151         type: 'blockquote_end'
18152       });
18153
18154       continue;
18155     }
18156
18157     // list
18158     if (cap = this.rules.list.exec(src)) {
18159       src = src.substring(cap[0].length);
18160       bull = cap[2];
18161
18162       this.tokens.push({
18163         type: 'list_start',
18164         ordered: bull.length > 1
18165       });
18166
18167       // Get each top-level item.
18168       cap = cap[0].match(this.rules.item);
18169
18170       next = false;
18171       l = cap.length;
18172       i = 0;
18173
18174       for (; i < l; i++) {
18175         item = cap[i];
18176
18177         // Remove the list item's bullet
18178         // so it is seen as the next token.
18179         space = item.length;
18180         item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18181
18182         // Outdent whatever the
18183         // list item contains. Hacky.
18184         if (~item.indexOf('\n ')) {
18185           space -= item.length;
18186           item = !this.options.pedantic
18187             ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18188             : item.replace(/^ {1,4}/gm, '');
18189         }
18190
18191         // Determine whether the next list item belongs here.
18192         // Backpedal if it does not belong in this list.
18193         if (this.options.smartLists && i !== l - 1) {
18194           b = block.bullet.exec(cap[i+1])[0];
18195           if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18196             src = cap.slice(i + 1).join('\n') + src;
18197             i = l - 1;
18198           }
18199         }
18200
18201         // Determine whether item is loose or not.
18202         // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18203         // for discount behavior.
18204         loose = next || /\n\n(?!\s*$)/.test(item);
18205         if (i !== l - 1) {
18206           next = item[item.length-1] === '\n';
18207           if (!loose) loose = next;
18208         }
18209
18210         this.tokens.push({
18211           type: loose
18212             ? 'loose_item_start'
18213             : 'list_item_start'
18214         });
18215
18216         // Recurse.
18217         this.token(item, false);
18218
18219         this.tokens.push({
18220           type: 'list_item_end'
18221         });
18222       }
18223
18224       this.tokens.push({
18225         type: 'list_end'
18226       });
18227
18228       continue;
18229     }
18230
18231     // html
18232     if (cap = this.rules.html.exec(src)) {
18233       src = src.substring(cap[0].length);
18234       this.tokens.push({
18235         type: this.options.sanitize
18236           ? 'paragraph'
18237           : 'html',
18238         pre: cap[1] === 'pre' || cap[1] === 'script',
18239         text: cap[0]
18240       });
18241       continue;
18242     }
18243
18244     // def
18245     if (top && (cap = this.rules.def.exec(src))) {
18246       src = src.substring(cap[0].length);
18247       this.tokens.links[cap[1].toLowerCase()] = {
18248         href: cap[2],
18249         title: cap[3]
18250       };
18251       continue;
18252     }
18253
18254     // table (gfm)
18255     if (top && (cap = this.rules.table.exec(src))) {
18256       src = src.substring(cap[0].length);
18257
18258       item = {
18259         type: 'table',
18260         header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18261         align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18262         cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18263       };
18264
18265       for (i = 0; i < item.align.length; i++) {
18266         if (/^ *-+: *$/.test(item.align[i])) {
18267           item.align[i] = 'right';
18268         } else if (/^ *:-+: *$/.test(item.align[i])) {
18269           item.align[i] = 'center';
18270         } else if (/^ *:-+ *$/.test(item.align[i])) {
18271           item.align[i] = 'left';
18272         } else {
18273           item.align[i] = null;
18274         }
18275       }
18276
18277       for (i = 0; i < item.cells.length; i++) {
18278         item.cells[i] = item.cells[i]
18279           .replace(/^ *\| *| *\| *$/g, '')
18280           .split(/ *\| */);
18281       }
18282
18283       this.tokens.push(item);
18284
18285       continue;
18286     }
18287
18288     // top-level paragraph
18289     if (top && (cap = this.rules.paragraph.exec(src))) {
18290       src = src.substring(cap[0].length);
18291       this.tokens.push({
18292         type: 'paragraph',
18293         text: cap[1][cap[1].length-1] === '\n'
18294           ? cap[1].slice(0, -1)
18295           : cap[1]
18296       });
18297       continue;
18298     }
18299
18300     // text
18301     if (cap = this.rules.text.exec(src)) {
18302       // Top-level should never reach here.
18303       src = src.substring(cap[0].length);
18304       this.tokens.push({
18305         type: 'text',
18306         text: cap[0]
18307       });
18308       continue;
18309     }
18310
18311     if (src) {
18312       throw new
18313         Error('Infinite loop on byte: ' + src.charCodeAt(0));
18314     }
18315   }
18316
18317   return this.tokens;
18318 };
18319
18320 /**
18321  * Inline-Level Grammar
18322  */
18323
18324 var inline = {
18325   escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18326   autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18327   url: noop,
18328   tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18329   link: /^!?\[(inside)\]\(href\)/,
18330   reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18331   nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18332   strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18333   em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18334   code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18335   br: /^ {2,}\n(?!\s*$)/,
18336   del: noop,
18337   text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18338 };
18339
18340 inline._inside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
18341 inline._href = /\s*<?([^\s]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18342
18343 inline.link = replace(inline.link)
18344   ('inside', inline._inside)
18345   ('href', inline._href)
18346   ();
18347
18348 inline.reflink = replace(inline.reflink)
18349   ('inside', inline._inside)
18350   ();
18351
18352 /**
18353  * Normal Inline Grammar
18354  */
18355
18356 inline.normal = merge({}, inline);
18357
18358 /**
18359  * Pedantic Inline Grammar
18360  */
18361
18362 inline.pedantic = merge({}, inline.normal, {
18363   strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18364   em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18365 });
18366
18367 /**
18368  * GFM Inline Grammar
18369  */
18370
18371 inline.gfm = merge({}, inline.normal, {
18372   escape: replace(inline.escape)('])', '~|])')(),
18373   url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18374   del: /^~~(?=\S)([\s\S]*?\S)~~/,
18375   text: replace(inline.text)
18376     (']|', '~]|')
18377     ('|', '|https?://|')
18378     ()
18379 });
18380
18381 /**
18382  * GFM + Line Breaks Inline Grammar
18383  */
18384
18385 inline.breaks = merge({}, inline.gfm, {
18386   br: replace(inline.br)('{2,}', '*')(),
18387   text: replace(inline.gfm.text)('{2,}', '*')()
18388 });
18389
18390 /**
18391  * Inline Lexer & Compiler
18392  */
18393
18394 function InlineLexer(links, options) {
18395   this.options = options || marked.defaults;
18396   this.links = links;
18397   this.rules = inline.normal;
18398
18399   if (!this.links) {
18400     throw new
18401       Error('Tokens array requires a `links` property.');
18402   }
18403
18404   if (this.options.gfm) {
18405     if (this.options.breaks) {
18406       this.rules = inline.breaks;
18407     } else {
18408       this.rules = inline.gfm;
18409     }
18410   } else if (this.options.pedantic) {
18411     this.rules = inline.pedantic;
18412   }
18413 }
18414
18415 /**
18416  * Expose Inline Rules
18417  */
18418
18419 InlineLexer.rules = inline;
18420
18421 /**
18422  * Static Lexing/Compiling Method
18423  */
18424
18425 InlineLexer.output = function(src, links, options) {
18426   var inline = new InlineLexer(links, options);
18427   return inline.output(src);
18428 };
18429
18430 /**
18431  * Lexing/Compiling
18432  */
18433
18434 InlineLexer.prototype.output = function(src) {
18435   var out = ''
18436     , link
18437     , text
18438     , href
18439     , cap;
18440
18441   while (src) {
18442     // escape
18443     if (cap = this.rules.escape.exec(src)) {
18444       src = src.substring(cap[0].length);
18445       out += cap[1];
18446       continue;
18447     }
18448
18449     // autolink
18450     if (cap = this.rules.autolink.exec(src)) {
18451       src = src.substring(cap[0].length);
18452       if (cap[2] === '@') {
18453         text = cap[1][6] === ':'
18454           ? this.mangle(cap[1].substring(7))
18455           : this.mangle(cap[1]);
18456         href = this.mangle('mailto:') + text;
18457       } else {
18458         text = escape(cap[1]);
18459         href = text;
18460       }
18461       out += '<a href="'
18462         + href
18463         + '">'
18464         + text
18465         + '</a>';
18466       continue;
18467     }
18468
18469     // url (gfm)
18470     if (cap = this.rules.url.exec(src)) {
18471       src = src.substring(cap[0].length);
18472       text = escape(cap[1]);
18473       href = text;
18474       out += '<a href="'
18475         + href
18476         + '">'
18477         + text
18478         + '</a>';
18479       continue;
18480     }
18481
18482     // tag
18483     if (cap = this.rules.tag.exec(src)) {
18484       src = src.substring(cap[0].length);
18485       out += this.options.sanitize
18486         ? escape(cap[0])
18487         : cap[0];
18488       continue;
18489     }
18490
18491     // link
18492     if (cap = this.rules.link.exec(src)) {
18493       src = src.substring(cap[0].length);
18494       out += this.outputLink(cap, {
18495         href: cap[2],
18496         title: cap[3]
18497       });
18498       continue;
18499     }
18500
18501     // reflink, nolink
18502     if ((cap = this.rules.reflink.exec(src))
18503         || (cap = this.rules.nolink.exec(src))) {
18504       src = src.substring(cap[0].length);
18505       link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18506       link = this.links[link.toLowerCase()];
18507       if (!link || !link.href) {
18508         out += cap[0][0];
18509         src = cap[0].substring(1) + src;
18510         continue;
18511       }
18512       out += this.outputLink(cap, link);
18513       continue;
18514     }
18515
18516     // strong
18517     if (cap = this.rules.strong.exec(src)) {
18518       src = src.substring(cap[0].length);
18519       out += '<strong>'
18520         + this.output(cap[2] || cap[1])
18521         + '</strong>';
18522       continue;
18523     }
18524
18525     // em
18526     if (cap = this.rules.em.exec(src)) {
18527       src = src.substring(cap[0].length);
18528       out += '<em>'
18529         + this.output(cap[2] || cap[1])
18530         + '</em>';
18531       continue;
18532     }
18533
18534     // code
18535     if (cap = this.rules.code.exec(src)) {
18536       src = src.substring(cap[0].length);
18537       out += '<code>'
18538         + escape(cap[2], true)
18539         + '</code>';
18540       continue;
18541     }
18542
18543     // br
18544     if (cap = this.rules.br.exec(src)) {
18545       src = src.substring(cap[0].length);
18546       out += '<br>';
18547       continue;
18548     }
18549
18550     // del (gfm)
18551     if (cap = this.rules.del.exec(src)) {
18552       src = src.substring(cap[0].length);
18553       out += '<del>'
18554         + this.output(cap[1])
18555         + '</del>';
18556       continue;
18557     }
18558
18559     // text
18560     if (cap = this.rules.text.exec(src)) {
18561       src = src.substring(cap[0].length);
18562       out += escape(cap[0]);
18563       continue;
18564     }
18565
18566     if (src) {
18567       throw new
18568         Error('Infinite loop on byte: ' + src.charCodeAt(0));
18569     }
18570   }
18571
18572   return out;
18573 };
18574
18575 /**
18576  * Compile Link
18577  */
18578
18579 InlineLexer.prototype.outputLink = function(cap, link) {
18580   if (cap[0][0] !== '!') {
18581     return '<a href="'
18582       + escape(link.href)
18583       + '"'
18584       + (link.title
18585       ? ' title="'
18586       + escape(link.title)
18587       + '"'
18588       : '')
18589       + '>'
18590       + this.output(cap[1])
18591       + '</a>';
18592   } else {
18593     return '<img src="'
18594       + escape(link.href)
18595       + '" alt="'
18596       + escape(cap[1])
18597       + '"'
18598       + (link.title
18599       ? ' title="'
18600       + escape(link.title)
18601       + '"'
18602       : '')
18603       + '>';
18604   }
18605 };
18606
18607 /**
18608  * Smartypants Transformations
18609  */
18610
18611 InlineLexer.prototype.smartypants = function(text) {
18612   if (!this.options.smartypants) return text;
18613   return text
18614     .replace(/--/g, '—')
18615     .replace(/'([^']*)'/g, '‘$1’')
18616     .replace(/"([^"]*)"/g, '“$1”')
18617     .replace(/\.{3}/g, '…');
18618 };
18619
18620 /**
18621  * Mangle Links
18622  */
18623
18624 InlineLexer.prototype.mangle = function(text) {
18625   var out = ''
18626     , l = text.length
18627     , i = 0
18628     , ch;
18629
18630   for (; i < l; i++) {
18631     ch = text.charCodeAt(i);
18632     if (Math.random() > 0.5) {
18633       ch = 'x' + ch.toString(16);
18634     }
18635     out += '&#' + ch + ';';
18636   }
18637
18638   return out;
18639 };
18640
18641 /**
18642  * Parsing & Compiling
18643  */
18644
18645 function Parser(options) {
18646   this.tokens = [];
18647   this.token = null;
18648   this.options = options || marked.defaults;
18649 }
18650
18651 /**
18652  * Static Parse Method
18653  */
18654
18655 Parser.parse = function(src, options) {
18656   var parser = new Parser(options);
18657   return parser.parse(src);
18658 };
18659
18660 /**
18661  * Parse Loop
18662  */
18663
18664 Parser.prototype.parse = function(src) {
18665   this.inline = new InlineLexer(src.links, this.options);
18666   this.tokens = src.reverse();
18667
18668   var out = '';
18669   while (this.next()) {
18670     out += this.tok();
18671   }
18672
18673   return out;
18674 };
18675
18676 /**
18677  * Next Token
18678  */
18679
18680 Parser.prototype.next = function() {
18681   return this.token = this.tokens.pop();
18682 };
18683
18684 /**
18685  * Preview Next Token
18686  */
18687
18688 Parser.prototype.peek = function() {
18689   return this.tokens[this.tokens.length-1] || 0;
18690 };
18691
18692 /**
18693  * Parse Text Tokens
18694  */
18695
18696 Parser.prototype.parseText = function() {
18697   var body = this.token.text;
18698
18699   while (this.peek().type === 'text') {
18700     body += '\n' + this.next().text;
18701   }
18702
18703   return this.inline.output(body);
18704 };
18705
18706 /**
18707  * Parse Current Token
18708  */
18709
18710 Parser.prototype.tok = function() {
18711   switch (this.token.type) {
18712     case 'space': {
18713       return '';
18714     }
18715     case 'hr': {
18716       return '<hr>\n';
18717     }
18718     case 'heading': {
18719       return '<h'
18720         + this.token.depth
18721         + '>'
18722         + this.inline.output(this.token.text)
18723         + '</h'
18724         + this.token.depth
18725         + '>\n';
18726     }
18727     case 'code': {
18728       if (this.options.highlight) {
18729         var code = this.options.highlight(this.token.text, this.token.lang);
18730         if (code != null && code !== this.token.text) {
18731           this.token.escaped = true;
18732           this.token.text = code;
18733         }
18734       }
18735
18736       if (!this.token.escaped) {
18737         this.token.text = escape(this.token.text, true);
18738       }
18739
18740       return '<pre><code'
18741         + (this.token.lang
18742         ? ' class="'
18743         + this.options.langPrefix
18744         + this.token.lang
18745         + '"'
18746         : '')
18747         + '>'
18748         + this.token.text
18749         + '</code></pre>\n';
18750     }
18751     case 'table': {
18752       var body = ''
18753         , heading
18754         , i
18755         , row
18756         , cell
18757         , j;
18758
18759       // header
18760       body += '<thead>\n<tr>\n';
18761       for (i = 0; i < this.token.header.length; i++) {
18762         heading = this.inline.output(this.token.header[i]);
18763         body += this.token.align[i]
18764           ? '<th align="' + this.token.align[i] + '">' + heading + '</th>\n'
18765           : '<th>' + heading + '</th>\n';
18766       }
18767       body += '</tr>\n</thead>\n';
18768
18769       // body
18770       body += '<tbody>\n'
18771       for (i = 0; i < this.token.cells.length; i++) {
18772         row = this.token.cells[i];
18773         body += '<tr>\n';
18774         for (j = 0; j < row.length; j++) {
18775           cell = this.inline.output(row[j]);
18776           body += this.token.align[j]
18777             ? '<td align="' + this.token.align[j] + '">' + cell + '</td>\n'
18778             : '<td>' + cell + '</td>\n';
18779         }
18780         body += '</tr>\n';
18781       }
18782       body += '</tbody>\n';
18783
18784       return '<table>\n'
18785         + body
18786         + '</table>\n';
18787     }
18788     case 'blockquote_start': {
18789       var body = '';
18790
18791       while (this.next().type !== 'blockquote_end') {
18792         body += this.tok();
18793       }
18794
18795       return '<blockquote>\n'
18796         + body
18797         + '</blockquote>\n';
18798     }
18799     case 'list_start': {
18800       var type = this.token.ordered ? 'ol' : 'ul'
18801         , body = '';
18802
18803       while (this.next().type !== 'list_end') {
18804         body += this.tok();
18805       }
18806
18807       return '<'
18808         + type
18809         + '>\n'
18810         + body
18811         + '</'
18812         + type
18813         + '>\n';
18814     }
18815     case 'list_item_start': {
18816       var body = '';
18817
18818       while (this.next().type !== 'list_item_end') {
18819         body += this.token.type === 'text'
18820           ? this.parseText()
18821           : this.tok();
18822       }
18823
18824       return '<li>'
18825         + body
18826         + '</li>\n';
18827     }
18828     case 'loose_item_start': {
18829       var body = '';
18830
18831       while (this.next().type !== 'list_item_end') {
18832         body += this.tok();
18833       }
18834
18835       return '<li>'
18836         + body
18837         + '</li>\n';
18838     }
18839     case 'html': {
18840       return !this.token.pre && !this.options.pedantic
18841         ? this.inline.output(this.token.text)
18842         : this.token.text;
18843     }
18844     case 'paragraph': {
18845       return '<p>'
18846         + this.inline.output(this.token.text)
18847         + '</p>\n';
18848     }
18849     case 'text': {
18850       return '<p>'
18851         + this.parseText()
18852         + '</p>\n';
18853     }
18854   }
18855 };
18856
18857 /**
18858  * Helpers
18859  */
18860
18861 function escape(html, encode) {
18862   return html
18863     .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18864     .replace(/</g, '&lt;')
18865     .replace(/>/g, '&gt;')
18866     .replace(/"/g, '&quot;')
18867     .replace(/'/g, '&#39;');
18868 }
18869
18870 function replace(regex, opt) {
18871   regex = regex.source;
18872   opt = opt || '';
18873   return function self(name, val) {
18874     if (!name) return new RegExp(regex, opt);
18875     val = val.source || val;
18876     val = val.replace(/(^|[^\[])\^/g, '$1');
18877     regex = regex.replace(name, val);
18878     return self;
18879   };
18880 }
18881
18882 function noop() {}
18883 noop.exec = noop;
18884
18885 function merge(obj) {
18886   var i = 1
18887     , target
18888     , key;
18889
18890   for (; i < arguments.length; i++) {
18891     target = arguments[i];
18892     for (key in target) {
18893       if (Object.prototype.hasOwnProperty.call(target, key)) {
18894         obj[key] = target[key];
18895       }
18896     }
18897   }
18898
18899   return obj;
18900 }
18901
18902 /**
18903  * Marked
18904  */
18905
18906 function marked(src, opt, callback) {
18907   if (callback || typeof opt === 'function') {
18908     if (!callback) {
18909       callback = opt;
18910       opt = null;
18911     }
18912
18913     if (opt) opt = merge({}, marked.defaults, opt);
18914
18915     var tokens = Lexer.lex(tokens, opt)
18916       , highlight = opt.highlight
18917       , pending = 0
18918       , l = tokens.length
18919       , i = 0;
18920
18921     if (!highlight || highlight.length < 3) {
18922       return callback(null, Parser.parse(tokens, opt));
18923     }
18924
18925     var done = function() {
18926       delete opt.highlight;
18927       var out = Parser.parse(tokens, opt);
18928       opt.highlight = highlight;
18929       return callback(null, out);
18930     };
18931
18932     for (; i < l; i++) {
18933       (function(token) {
18934         if (token.type !== 'code') return;
18935         pending++;
18936         return highlight(token.text, token.lang, function(err, code) {
18937           if (code == null || code === token.text) {
18938             return --pending || done();
18939           }
18940           token.text = code;
18941           token.escaped = true;
18942           --pending || done();
18943         });
18944       })(tokens[i]);
18945     }
18946
18947     return;
18948   }
18949   try {
18950     if (opt) opt = merge({}, marked.defaults, opt);
18951     return Parser.parse(Lexer.lex(src, opt), opt);
18952   } catch (e) {
18953     e.message += '\nPlease report this to https://github.com/chjj/marked.';
18954     if ((opt || marked.defaults).silent) {
18955       return '<p>An error occured:</p><pre>'
18956         + escape(e.message + '', true)
18957         + '</pre>';
18958     }
18959     throw e;
18960   }
18961 }
18962
18963 /**
18964  * Options
18965  */
18966
18967 marked.options =
18968 marked.setOptions = function(opt) {
18969   merge(marked.defaults, opt);
18970   return marked;
18971 };
18972
18973 marked.defaults = {
18974   gfm: true,
18975   tables: true,
18976   breaks: false,
18977   pedantic: false,
18978   sanitize: false,
18979   smartLists: false,
18980   silent: false,
18981   highlight: null,
18982   langPrefix: 'lang-'
18983 };
18984
18985 /**
18986  * Expose
18987  */
18988
18989 marked.Parser = Parser;
18990 marked.parser = Parser.parse;
18991
18992 marked.Lexer = Lexer;
18993 marked.lexer = Lexer.lex;
18994
18995 marked.InlineLexer = InlineLexer;
18996 marked.inlineLexer = InlineLexer.output;
18997
18998 marked.parse = marked;
18999
19000 if (typeof exports === 'object') {
19001   module.exports = marked;
19002 } else if (typeof define === 'function' && define.amd) {
19003   define(function() { return marked; });
19004 } else {
19005   this.marked = marked;
19006 }
19007
19008 }).call(function() {
19009   return this || (typeof window !== 'undefined' ? window : global);
19010 }());
19011 (function () {
19012 'use strict';
19013 window.iD = function () {
19014     window.locale.en = iD.data.en;
19015     window.locale.current('en');
19016
19017     var dispatch = d3.dispatch('enter', 'exit'),
19018         context = {};
19019
19020     // https://github.com/openstreetmap/iD/issues/772
19021     // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
19022     var storage;
19023     try { storage = localStorage; } catch (e) {}  // eslint-disable-line no-empty
19024     storage = storage || (function() {
19025         var s = {};
19026         return {
19027             getItem: function(k) { return s[k]; },
19028             setItem: function(k, v) { s[k] = v; },
19029             removeItem: function(k) { delete s[k]; }
19030         };
19031     })();
19032
19033     context.storage = function(k, v) {
19034         try {
19035             if (arguments.length === 1) return storage.getItem(k);
19036             else if (v === null) storage.removeItem(k);
19037             else storage.setItem(k, v);
19038         } catch(e) {
19039             // localstorage quota exceeded
19040             /* eslint-disable no-console */
19041             if (typeof console !== 'undefined') console.error('localStorage quota exceeded');
19042             /* eslint-enable no-console */
19043         }
19044     };
19045
19046
19047     /* Straight accessors. Avoid using these if you can. */
19048     var ui, connection, history;
19049     context.ui = function() { return ui; };
19050     context.connection = function() { return connection; };
19051     context.history = function() { return history; };
19052
19053
19054     /* Connection */
19055     function entitiesLoaded(err, result) {
19056         if (!err) history.merge(result.data, result.extent);
19057     }
19058
19059     context.preauth = function(options) {
19060         connection.switch(options);
19061         return context;
19062     };
19063
19064     context.loadTiles = function(projection, dimensions, callback) {
19065         function done(err, result) {
19066             entitiesLoaded(err, result);
19067             if (callback) callback(err, result);
19068         }
19069         connection.loadTiles(projection, dimensions, done);
19070     };
19071
19072     context.loadEntity = function(id, callback) {
19073         function done(err, result) {
19074             entitiesLoaded(err, result);
19075             if (callback) callback(err, result);
19076         }
19077         connection.loadEntity(id, done);
19078     };
19079
19080     context.zoomToEntity = function(id, zoomTo) {
19081         if (zoomTo !== false) {
19082             this.loadEntity(id, function(err, result) {
19083                 if (err) return;
19084                 var entity = _.find(result.data, function(e) { return e.id === id; });
19085                 if (entity) { map.zoomTo(entity); }
19086             });
19087         }
19088
19089         map.on('drawn.zoomToEntity', function() {
19090             if (!context.hasEntity(id)) return;
19091             map.on('drawn.zoomToEntity', null);
19092             context.on('enter.zoomToEntity', null);
19093             context.enter(iD.modes.Select(context, [id]));
19094         });
19095
19096         context.on('enter.zoomToEntity', function() {
19097             if (mode.id !== 'browse') {
19098                 map.on('drawn.zoomToEntity', null);
19099                 context.on('enter.zoomToEntity', null);
19100             }
19101         });
19102     };
19103
19104     var minEditableZoom = 16;
19105     context.minEditableZoom = function(_) {
19106         if (!arguments.length) return minEditableZoom;
19107         minEditableZoom = _;
19108         connection.tileZoom(_);
19109         return context;
19110     };
19111
19112
19113     /* History */
19114     var inIntro = false;
19115     context.inIntro = function(_) {
19116         if (!arguments.length) return inIntro;
19117         inIntro = _;
19118         return context;
19119     };
19120
19121     context.save = function() {
19122         if (inIntro || (mode && mode.id === 'save') || d3.select('.modal').size()) return;
19123         history.save();
19124         if (history.hasChanges()) return t('save.unsaved_changes');
19125     };
19126
19127     context.flush = function() {
19128         context.debouncedSave.cancel();
19129         connection.flush();
19130         features.reset();
19131         history.reset();
19132         _.each(iD.services, function(service) {
19133             var reset = service().reset;
19134             if (reset) reset(context);
19135         });
19136         return context;
19137     };
19138
19139
19140     /* Graph */
19141     context.hasEntity = function(id) {
19142         return history.graph().hasEntity(id);
19143     };
19144     context.entity = function(id) {
19145         return history.graph().entity(id);
19146     };
19147     context.childNodes = function(way) {
19148         return history.graph().childNodes(way);
19149     };
19150     context.geometry = function(id) {
19151         return context.entity(id).geometry(history.graph());
19152     };
19153
19154
19155     /* Modes */
19156     var mode;
19157     context.mode = function() {
19158         return mode;
19159     };
19160     context.enter = function(newMode) {
19161         if (mode) {
19162             mode.exit();
19163             dispatch.exit(mode);
19164         }
19165
19166         mode = newMode;
19167         mode.enter();
19168         dispatch.enter(mode);
19169     };
19170
19171     context.selectedIDs = function() {
19172         if (mode && mode.selectedIDs) {
19173             return mode.selectedIDs();
19174         } else {
19175             return [];
19176         }
19177     };
19178
19179
19180     /* Behaviors */
19181     context.install = function(behavior) {
19182         context.surface().call(behavior);
19183     };
19184     context.uninstall = function(behavior) {
19185         context.surface().call(behavior.off);
19186     };
19187
19188
19189     /* Copy/Paste */
19190     var copyIDs = [], copyGraph;
19191     context.copyGraph = function() { return copyGraph; };
19192     context.copyIDs = function(_) {
19193         if (!arguments.length) return copyIDs;
19194         copyIDs = _;
19195         copyGraph = history.graph();
19196         return context;
19197     };
19198
19199
19200     /* Background */
19201     var background;
19202     context.background = function() { return background; };
19203
19204
19205     /* Features */
19206     var features;
19207     context.features = function() { return features; };
19208     context.hasHiddenConnections = function(id) {
19209         var graph = history.graph(),
19210             entity = graph.entity(id);
19211         return features.hasHiddenConnections(entity, graph);
19212     };
19213
19214
19215     /* Map */
19216     var map;
19217     context.map = function() { return map; };
19218     context.layers = function() { return map.layers; };
19219     context.surface = function() { return map.surface; };
19220     context.editable = function() { return map.editable(); };
19221
19222     context.surfaceRect = function() {
19223         // Work around a bug in Firefox.
19224         //   http://stackoverflow.com/questions/18153989/
19225         //   https://bugzilla.mozilla.org/show_bug.cgi?id=530985
19226         return context.surface().node().parentNode.getBoundingClientRect();
19227     };
19228
19229
19230     /* Debug */
19231     var debugTile = false, debugCollision = false;
19232     context.debugTile = function(_) {
19233         if (!arguments.length) return debugTile;
19234         debugTile = _;
19235         return context;
19236     };
19237     context.debugCollision = function(_) {
19238         if (!arguments.length) return debugCollision;
19239         debugCollision = _;
19240         return context;
19241     };
19242
19243
19244     /* Presets */
19245     var presets;
19246     context.presets = function(_) {
19247         if (!arguments.length) return presets;
19248         presets.load(_);
19249         iD.areaKeys = presets.areaKeys();
19250         return context;
19251     };
19252
19253
19254     /* Imagery */
19255     context.imagery = function(_) {
19256         background.load(_);
19257         return context;
19258     };
19259
19260
19261     /* Container */
19262     var container, embed;
19263     context.container = function(_) {
19264         if (!arguments.length) return container;
19265         container = _;
19266         container.classed('id-container', true);
19267         return context;
19268     };
19269     context.embed = function(_) {
19270         if (!arguments.length) return embed;
19271         embed = _;
19272         return context;
19273     };
19274
19275
19276     /* Taginfo */
19277     var taginfo;
19278     context.taginfo = function(_) {
19279         if (!arguments.length) return taginfo;
19280         taginfo = _;
19281         return context;
19282     };
19283
19284
19285     /* Assets */
19286     var assetPath = '';
19287     context.assetPath = function(_) {
19288         if (!arguments.length) return assetPath;
19289         assetPath = _;
19290         return context;
19291     };
19292
19293     var assetMap = {};
19294     context.assetMap = function(_) {
19295         if (!arguments.length) return assetMap;
19296         assetMap = _;
19297         return context;
19298     };
19299
19300     context.asset = function(_) {
19301         var filename = assetPath + _;
19302         return assetMap[filename] || filename;
19303     };
19304
19305     context.imagePath = function(_) {
19306         return context.asset('img/' + _);
19307     };
19308
19309     var locale, localePath;
19310     context.locale = function(loc, path) {
19311         locale = loc;
19312         localePath = path;
19313
19314         // Also set iD.detect().locale (unless we detected 'en-us' and openstreetmap wants 'en')..
19315         if (!(loc.toLowerCase() === 'en' && iD.detect().locale.toLowerCase() === 'en-us')) {
19316             iD.detect().locale = loc;
19317         }
19318
19319         return context;
19320     };
19321
19322     context.loadLocale = function(cb) {
19323         if (locale && locale !== 'en' && iD.data.locales.indexOf(locale) !== -1) {
19324             localePath = localePath || context.asset('locales/' + locale + '.json');
19325             d3.json(localePath, function(err, result) {
19326                 window.locale[locale] = result;
19327                 window.locale.current(locale);
19328                 cb();
19329             });
19330         } else {
19331             cb();
19332         }
19333     };
19334
19335
19336     /* Init */
19337
19338     context.projection = iD.geo.RawMercator();
19339
19340     locale = iD.detect().locale;
19341     if (locale && iD.data.locales.indexOf(locale) === -1) {
19342         locale = locale.split('-')[0];
19343     }
19344
19345     history = iD.History(context);
19346     context.graph = history.graph;
19347     context.changes = history.changes;
19348     context.intersects = history.intersects;
19349
19350     // Debounce save, since it's a synchronous localStorage write,
19351     // and history changes can happen frequently (e.g. when dragging).
19352     context.debouncedSave = _.debounce(context.save, 350);
19353     function withDebouncedSave(fn) {
19354         return function() {
19355             var result = fn.apply(history, arguments);
19356             context.debouncedSave();
19357             return result;
19358         };
19359     }
19360
19361     context.perform = withDebouncedSave(history.perform);
19362     context.replace = withDebouncedSave(history.replace);
19363     context.pop = withDebouncedSave(history.pop);
19364     context.overwrite = withDebouncedSave(history.overwrite);
19365     context.undo = withDebouncedSave(history.undo);
19366     context.redo = withDebouncedSave(history.redo);
19367
19368     ui = iD.ui(context);
19369
19370     connection = iD.Connection();
19371
19372     background = iD.Background(context);
19373
19374     features = iD.Features(context);
19375
19376     map = iD.Map(context);
19377     context.mouse = map.mouse;
19378     context.extent = map.extent;
19379     context.pan = map.pan;
19380     context.zoomIn = map.zoomIn;
19381     context.zoomOut = map.zoomOut;
19382     context.zoomInFurther = map.zoomInFurther;
19383     context.zoomOutFurther = map.zoomOutFurther;
19384     context.redrawEnable = map.redrawEnable;
19385
19386     presets = iD.presets();
19387
19388     return d3.rebind(context, dispatch, 'on');
19389 };
19390
19391
19392 iD.version = '1.9.4';
19393
19394 (function() {
19395     var detected = {};
19396
19397     var ua = navigator.userAgent,
19398         m = null;
19399
19400     m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i);   // Edge
19401     if (m !== null) {
19402         detected.browser = m[1];
19403         detected.version = m[2];
19404     }
19405     if (!detected.browser) {
19406         m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i);   // IE11
19407         if (m !== null) {
19408             detected.browser = 'msie';
19409             detected.version = m[1];
19410         }
19411     }
19412     if (!detected.browser) {
19413         m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i);   // Opera 15+
19414         if (m !== null) {
19415             detected.browser = 'Opera';
19416             detected.version = m[2];
19417         }
19418     }
19419     if (!detected.browser) {
19420         m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
19421         if (m !== null) {
19422             detected.browser = m[1];
19423             detected.version = m[2];
19424             m = ua.match(/version\/([\.\d]+)/i);
19425             if (m !== null) detected.version = m[1];
19426         }
19427     }
19428     if (!detected.browser) {
19429         detected.browser = navigator.appName;
19430         detected.version = navigator.appVersion;
19431     }
19432
19433     // keep major.minor version only..
19434     detected.version = detected.version.split(/\W/).slice(0,2).join('.');
19435
19436     if (detected.browser.toLowerCase() === 'msie') {
19437         detected.ie = true;
19438         detected.browser = 'Internet Explorer';
19439         detected.support = parseFloat(detected.version) >= 11;
19440     } else {
19441         detected.ie = false;
19442         detected.support = true;
19443     }
19444
19445     // Added due to incomplete svg style support. See #715
19446     detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 );
19447
19448     detected.locale = (navigator.languages && navigator.languages.length)
19449         ? navigator.languages[0] : (navigator.language || navigator.userLanguage || 'en-US');
19450
19451     detected.filedrop = (window.FileReader && 'ondrop' in window);
19452
19453     function nav(x) {
19454         return navigator.userAgent.indexOf(x) !== -1;
19455     }
19456
19457     if (nav('Win')) {
19458         detected.os = 'win';
19459         detected.platform = 'Windows';
19460     }
19461     else if (nav('Mac')) {
19462         detected.os = 'mac';
19463         detected.platform = 'Macintosh';
19464     }
19465     else if (nav('X11') || nav('Linux')) {
19466         detected.os = 'linux';
19467         detected.platform = 'Linux';
19468     }
19469     else {
19470         detected.os = 'win';
19471         detected.platform = 'Unknown';
19472     }
19473
19474     iD.detect = function() { return detected; };
19475 })();
19476 iD.services = {};
19477 iD.services.mapillary = function() {
19478     var mapillary = {},
19479         dispatch = d3.dispatch('loadedImages', 'loadedSigns'),
19480         apibase = 'https://a.mapillary.com/v2/',
19481         urlImage = 'https://www.mapillary.com/map/im/',
19482         urlThumb = 'https://d1cuyjsrcm0gby.cloudfront.net/',
19483         clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi',
19484         maxResults = 1000,
19485         tileZoom = 14;
19486
19487
19488     function loadSignStyles(context) {
19489         d3.select('head').selectAll('#traffico')
19490             .data([0])
19491             .enter()
19492             .append('link')
19493             .attr('id', 'traffico')
19494             .attr('rel', 'stylesheet')
19495             .attr('href', context.asset('traffico/stylesheets/traffico.css'));
19496     }
19497
19498     function loadSignDefs(context) {
19499         if (!iD.services.mapillary.sign_defs) {
19500             iD.services.mapillary.sign_defs = {};
19501             _.each(['au', 'br', 'ca', 'de', 'us'], function(region) {
19502                 d3.json(context.asset('traffico/string-maps/' + region + '-map.json'), function(err, data) {
19503                     if (err) return;
19504                     if (region === 'de') region = 'eu';
19505                     iD.services.mapillary.sign_defs[region] = data;
19506                 });
19507             });
19508         }
19509     }
19510
19511     function abortRequest(i) {
19512         i.abort();
19513     }
19514
19515     function nearNullIsland(x, y, z) {
19516         if (z >= 7) {
19517             var center = Math.pow(2, z - 1),
19518                 width = Math.pow(2, z - 6),
19519                 min = center - (width / 2),
19520                 max = center + (width / 2) - 1;
19521             return x >= min && x <= max && y >= min && y <= max;
19522         }
19523         return false;
19524     }
19525
19526     function getTiles(projection, dimensions) {
19527         var s = projection.scale() * 2 * Math.PI,
19528             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
19529             ts = 256 * Math.pow(2, z - tileZoom),
19530             origin = [
19531                 s / 2 - projection.translate()[0],
19532                 s / 2 - projection.translate()[1]];
19533
19534         return d3.geo.tile()
19535             .scaleExtent([tileZoom, tileZoom])
19536             .scale(s)
19537             .size(dimensions)
19538             .translate(projection.translate())()
19539             .map(function(tile) {
19540                 var x = tile[0] * ts - origin[0],
19541                     y = tile[1] * ts - origin[1];
19542
19543                 return {
19544                     id: tile.toString(),
19545                     extent: iD.geo.Extent(
19546                         projection.invert([x, y + ts]),
19547                         projection.invert([x + ts, y]))
19548                 };
19549             });
19550     }
19551
19552
19553     function loadTiles(which, url, projection, dimensions) {
19554         var tiles = getTiles(projection, dimensions).filter(function(t) {
19555                 var xyz = t.id.split(',');
19556                 return !nearNullIsland(xyz[0], xyz[1], xyz[2]);
19557             });
19558
19559         _.filter(which.inflight, function(v, k) {
19560             var wanted = _.find(tiles, function(tile) { return k === (tile.id + ',0'); });
19561             if (!wanted) delete which.inflight[k];
19562             return !wanted;
19563         }).map(abortRequest);
19564
19565         tiles.forEach(function(tile) {
19566             loadTilePage(which, url, tile, 0);
19567         });
19568     }
19569
19570     function loadTilePage(which, url, tile, page) {
19571         var cache = iD.services.mapillary.cache[which],
19572             id = tile.id + ',' + String(page),
19573             rect = tile.extent.rectangle();
19574
19575         if (cache.loaded[id] || cache.inflight[id]) return;
19576
19577         cache.inflight[id] = d3.json(url +
19578             iD.util.qsString({
19579                 geojson: 'true',
19580                 limit: maxResults,
19581                 page: page,
19582                 client_id: clientId,
19583                 min_lon: rect[0],
19584                 min_lat: rect[1],
19585                 max_lon: rect[2],
19586                 max_lat: rect[3]
19587             }), function(err, data) {
19588                 cache.loaded[id] = true;
19589                 delete cache.inflight[id];
19590                 if (err || !data.features || !data.features.length) return;
19591
19592                 var features = [],
19593                     feature, loc, d;
19594
19595                 for (var i = 0; i < data.features.length; i++) {
19596                     feature = data.features[i];
19597                     loc = feature.geometry.coordinates;
19598                     d = { key: feature.properties.key, loc: loc };
19599                     if (which === 'images') d.ca = feature.properties.ca;
19600                     if (which === 'signs') d.signs = feature.properties.rects;
19601
19602                     features.push([loc[0], loc[1], loc[0], loc[1], d]);
19603                 }
19604
19605                 cache.rtree.load(features);
19606
19607                 if (which === 'images') dispatch.loadedImages();
19608                 if (which === 'signs') dispatch.loadedSigns();
19609
19610                 if (data.features.length === maxResults) {
19611                     loadTilePage(which, url, tile, ++page);
19612                 }
19613             }
19614         );
19615     }
19616
19617     mapillary.loadImages = function(projection, dimensions) {
19618         var url = apibase + 'search/im/geojson?';
19619         loadTiles('images', url, projection, dimensions);
19620     };
19621
19622     mapillary.loadSigns = function(context, projection, dimensions) {
19623         var url = apibase + 'search/im/geojson/or?';
19624         loadSignStyles(context);
19625         loadSignDefs(context);
19626         loadTiles('signs', url, projection, dimensions);
19627     };
19628
19629
19630     // partition viewport into `psize` x `psize` regions
19631     function partitionViewport(psize, projection, dimensions) {
19632         psize = psize || 16;
19633         var cols = d3.range(0, dimensions[0], psize),
19634             rows = d3.range(0, dimensions[1], psize),
19635             partitions = [];
19636
19637         rows.forEach(function(y) {
19638             cols.forEach(function(x) {
19639                 var min = [x, y + psize],
19640                     max = [x + psize, y];
19641                 partitions.push(
19642                     iD.geo.Extent(projection.invert(min), projection.invert(max)));
19643             });
19644         });
19645
19646         return partitions;
19647     }
19648
19649     // no more than `limit` results per partition.
19650     function searchLimited(psize, limit, projection, dimensions, rtree) {
19651         limit = limit || 3;
19652
19653         var partitions = partitionViewport(psize, projection, dimensions);
19654         return _.flatten(_.compact(_.map(partitions, function(extent) {
19655             return rtree.search(extent.rectangle())
19656                 .slice(0, limit)
19657                 .map(function(d) { return d[4]; });
19658         })));
19659     }
19660
19661     mapillary.images = function(projection, dimensions) {
19662         var psize = 16, limit = 3;
19663         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.images.rtree);
19664     };
19665
19666     mapillary.signs = function(projection, dimensions) {
19667         var psize = 32, limit = 3;
19668         return searchLimited(psize, limit, projection, dimensions, iD.services.mapillary.cache.signs.rtree);
19669     };
19670
19671     mapillary.signsSupported = function() {
19672         var detected = iD.detect();
19673         return (!(detected.ie || detected.browser.toLowerCase() === 'safari'));
19674     };
19675
19676     mapillary.signHTML = function(d) {
19677         if (!iD.services.mapillary.sign_defs) return;
19678
19679         var detectionPackage = d.signs[0].package,
19680             type = d.signs[0].type,
19681             country = detectionPackage.split('_')[1];
19682         return iD.services.mapillary.sign_defs[country][type];
19683     };
19684
19685     mapillary.showThumbnail = function(imageKey, position) {
19686         if (!imageKey) return;
19687
19688         var positionClass = {
19689             'ar': (position !== 'left'),
19690             'al': (position === 'left')
19691         };
19692
19693         var thumbnail = d3.select('#content').selectAll('.mapillary-image')
19694             .data([0]);
19695
19696         // Enter
19697         var enter = thumbnail.enter().append('div')
19698             .attr('class', 'mapillary-image ar');
19699
19700         enter.append('button')
19701             .on('click', function () {
19702                 mapillary.hideThumbnail();
19703             })
19704             .append('div')
19705             .call(iD.svg.Icon('#icon-close'));
19706
19707         enter.append('img');
19708
19709         enter.append('a')
19710             .attr('class', 'link ar')
19711             .attr('target', '_blank')
19712             .call(iD.svg.Icon('#icon-out-link', 'inline'))
19713             .append('span')
19714             .text(t('mapillary.view_on_mapillary'));
19715
19716         // Update
19717         thumbnail.selectAll('img')
19718             .attr('src', urlThumb + imageKey + '/thumb-320.jpg');
19719
19720         var link = thumbnail.selectAll('a')
19721             .attr('href', urlImage + imageKey);
19722
19723         if (position) {
19724             thumbnail.classed(positionClass);
19725             link.classed(positionClass);
19726         }
19727
19728         thumbnail
19729             .transition()
19730             .duration(200)
19731             .style('opacity', 1);
19732     };
19733
19734     mapillary.hideThumbnail = function() {
19735         if (iD.services.mapillary) {
19736             iD.services.mapillary.thumb = null;
19737         }
19738         d3.select('#content').selectAll('.mapillary-image')
19739             .transition()
19740             .duration(200)
19741             .style('opacity', 0)
19742             .remove();
19743     };
19744
19745     mapillary.selectedThumbnail = function(d) {
19746         if (!iD.services.mapillary) return null;
19747         if (!arguments.length) return iD.services.mapillary.thumb;
19748         iD.services.mapillary.thumb = d;
19749     };
19750
19751     mapillary.reset = function() {
19752         var cache = iD.services.mapillary.cache;
19753
19754         if (cache) {
19755             _.forEach(cache.images.inflight, abortRequest);
19756             _.forEach(cache.signs.inflight, abortRequest);
19757         }
19758
19759         iD.services.mapillary.cache = {
19760             images: { inflight: {}, loaded: {}, rtree: rbush() },
19761             signs:  { inflight: {}, loaded: {}, rtree: rbush() }
19762         };
19763
19764         iD.services.mapillary.thumb = null;
19765
19766         return mapillary;
19767     };
19768
19769
19770     if (!iD.services.mapillary.cache) {
19771         mapillary.reset();
19772     }
19773
19774     return d3.rebind(mapillary, dispatch, 'on');
19775 };
19776 iD.services.nominatim  = function() {
19777     var nominatim = {},
19778         endpoint = 'https://nominatim.openstreetmap.org/reverse?';
19779
19780
19781     nominatim.countryCode = function(location, callback) {
19782         var cache = iD.services.nominatim.cache,
19783             countryCodes = cache.search([location[0], location[1], location[0], location[1]]);
19784
19785         if (countryCodes.length > 0)
19786             return callback(null, countryCodes[0][4]);
19787
19788         d3.json(endpoint +
19789             iD.util.qsString({
19790                 format: 'json',
19791                 addressdetails: 1,
19792                 lat: location[1],
19793                 lon: location[0]
19794             }), function(err, result) {
19795                 if (err)
19796                     return callback(err);
19797                 else if (result && result.error)
19798                     return callback(result.error);
19799
19800                 var extent = iD.geo.Extent(location).padByMeters(1000);
19801
19802                 cache.insert(extent.rectangle().concat(result.address.country_code));
19803
19804                 callback(null, result.address.country_code);
19805             });
19806     };
19807
19808     nominatim.reset = function() {
19809         iD.services.nominatim.cache = rbush();
19810         return nominatim;
19811     };
19812
19813
19814     if (!iD.services.nominatim.cache) {
19815         nominatim.reset();
19816     }
19817
19818     return nominatim;
19819 };
19820 iD.services.taginfo = function() {
19821     var taginfo = {},
19822         endpoint = 'https://taginfo.openstreetmap.org/api/4/',
19823         tag_sorts = {
19824             point: 'count_nodes',
19825             vertex: 'count_nodes',
19826             area: 'count_ways',
19827             line: 'count_ways'
19828         },
19829         tag_filters = {
19830             point: 'nodes',
19831             vertex: 'nodes',
19832             area: 'ways',
19833             line: 'ways'
19834         };
19835
19836
19837     function sets(parameters, n, o) {
19838         if (parameters.geometry && o[parameters.geometry]) {
19839             parameters[n] = o[parameters.geometry];
19840         }
19841         return parameters;
19842     }
19843
19844     function setFilter(parameters) {
19845         return sets(parameters, 'filter', tag_filters);
19846     }
19847
19848     function setSort(parameters) {
19849         return sets(parameters, 'sortname', tag_sorts);
19850     }
19851
19852     function clean(parameters) {
19853         return _.omit(parameters, 'geometry', 'debounce');
19854     }
19855
19856     function filterKeys(type) {
19857         var count_type = type ? 'count_' + type : 'count_all';
19858         return function(d) {
19859             return parseFloat(d[count_type]) > 2500 || d.in_wiki;
19860         };
19861     }
19862
19863     function filterMultikeys() {
19864         return function(d) {
19865             return (d.key.match(/:/g) || []).length === 1;  // exactly one ':'
19866         };
19867     }
19868
19869     function filterValues() {
19870         return function(d) {
19871             if (d.value.match(/[A-Z*;,]/) !== null) return false;  // exclude some punctuation, uppercase letters
19872             return parseFloat(d.fraction) > 0.0 || d.in_wiki;
19873         };
19874     }
19875
19876     function valKey(d) {
19877         return {
19878             value: d.key,
19879             title: d.key
19880         };
19881     }
19882
19883     function valKeyDescription(d) {
19884         return {
19885             value: d.value,
19886             title: d.description || d.value
19887         };
19888     }
19889
19890     // sort keys with ':' lower than keys without ':'
19891     function sortKeys(a, b) {
19892         return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
19893             : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
19894             : 0;
19895     }
19896
19897     var debounced = _.debounce(d3.json, 100, true);
19898
19899     function request(url, debounce, callback) {
19900         var cache = iD.services.taginfo.cache;
19901
19902         if (cache[url]) {
19903             callback(null, cache[url]);
19904         } else if (debounce) {
19905             debounced(url, done);
19906         } else {
19907             d3.json(url, done);
19908         }
19909
19910         function done(err, data) {
19911             if (!err) cache[url] = data;
19912             callback(err, data);
19913         }
19914     }
19915
19916     taginfo.keys = function(parameters, callback) {
19917         var debounce = parameters.debounce;
19918         parameters = clean(setSort(parameters));
19919         request(endpoint + 'keys/all?' +
19920             iD.util.qsString(_.extend({
19921                 rp: 10,
19922                 sortname: 'count_all',
19923                 sortorder: 'desc',
19924                 page: 1
19925             }, parameters)), debounce, function(err, d) {
19926                 if (err) return callback(err);
19927                 var f = filterKeys(parameters.filter);
19928                 callback(null, d.data.filter(f).sort(sortKeys).map(valKey));
19929             });
19930     };
19931
19932     taginfo.multikeys = function(parameters, callback) {
19933         var debounce = parameters.debounce;
19934         parameters = clean(setSort(parameters));
19935         request(endpoint + 'keys/all?' +
19936             iD.util.qsString(_.extend({
19937                 rp: 25,
19938                 sortname: 'count_all',
19939                 sortorder: 'desc',
19940                 page: 1
19941             }, parameters)), debounce, function(err, d) {
19942                 if (err) return callback(err);
19943                 var f = filterMultikeys();
19944                 callback(null, d.data.filter(f).map(valKey));
19945             });
19946     };
19947
19948     taginfo.values = function(parameters, callback) {
19949         var debounce = parameters.debounce;
19950         parameters = clean(setSort(setFilter(parameters)));
19951         request(endpoint + 'key/values?' +
19952             iD.util.qsString(_.extend({
19953                 rp: 25,
19954                 sortname: 'count_all',
19955                 sortorder: 'desc',
19956                 page: 1
19957             }, parameters)), debounce, function(err, d) {
19958                 if (err) return callback(err);
19959                 var f = filterValues();
19960                 callback(null, d.data.filter(f).map(valKeyDescription));
19961             });
19962     };
19963
19964     taginfo.docs = function(parameters, callback) {
19965         var debounce = parameters.debounce;
19966         parameters = clean(setSort(parameters));
19967
19968         var path = 'key/wiki_pages?';
19969         if (parameters.value) path = 'tag/wiki_pages?';
19970         else if (parameters.rtype) path = 'relation/wiki_pages?';
19971
19972         request(endpoint + path + iD.util.qsString(parameters), debounce, function(err, d) {
19973             if (err) return callback(err);
19974             callback(null, d.data);
19975         });
19976     };
19977
19978     taginfo.endpoint = function(_) {
19979         if (!arguments.length) return endpoint;
19980         endpoint = _;
19981         return taginfo;
19982     };
19983
19984     taginfo.reset = function() {
19985         iD.services.taginfo.cache = {};
19986         return taginfo;
19987     };
19988
19989
19990     if (!iD.services.taginfo.cache) {
19991         taginfo.reset();
19992     }
19993
19994     return taginfo;
19995 };
19996 iD.services.wikipedia = function() {
19997     var wiki = {},
19998         endpoint = 'https://en.wikipedia.org/w/api.php?';
19999
20000     wiki.search = function(lang, query, callback) {
20001         lang = lang || 'en';
20002         d3.jsonp(endpoint.replace('en', lang) +
20003             iD.util.qsString({
20004                 action: 'query',
20005                 list: 'search',
20006                 srlimit: '10',
20007                 srinfo: 'suggestion',
20008                 format: 'json',
20009                 callback: '{callback}',
20010                 srsearch: query
20011             }), function(data) {
20012                 if (!data.query) return;
20013                 callback(query, data.query.search.map(function(d) {
20014                     return d.title;
20015                 }));
20016             });
20017     };
20018
20019     wiki.suggestions = function(lang, query, callback) {
20020         lang = lang || 'en';
20021         d3.jsonp(endpoint.replace('en', lang) +
20022             iD.util.qsString({
20023                 action: 'opensearch',
20024                 namespace: 0,
20025                 suggest: '',
20026                 format: 'json',
20027                 callback: '{callback}',
20028                 search: query
20029             }), function(d) {
20030                 callback(d[0], d[1]);
20031             });
20032     };
20033
20034     wiki.translations = function(lang, title, callback) {
20035         d3.jsonp(endpoint.replace('en', lang) +
20036             iD.util.qsString({
20037                 action: 'query',
20038                 prop: 'langlinks',
20039                 format: 'json',
20040                 callback: '{callback}',
20041                 lllimit: 500,
20042                 titles: title
20043             }), function(d) {
20044                 var list = d.query.pages[Object.keys(d.query.pages)[0]],
20045                     translations = {};
20046                 if (list && list.langlinks) {
20047                     list.langlinks.forEach(function(d) {
20048                         translations[d.lang] = d['*'];
20049                     });
20050                     callback(translations);
20051                 }
20052             });
20053     };
20054
20055     return wiki;
20056 };
20057 iD.util = {};
20058
20059 iD.util.tagText = function(entity) {
20060     return d3.entries(entity.tags).map(function(e) {
20061         return e.key + '=' + e.value;
20062     }).join(', ');
20063 };
20064
20065 iD.util.entitySelector = function(ids) {
20066     return ids.length ? '.' + ids.join(',.') : 'nothing';
20067 };
20068
20069 iD.util.entityOrMemberSelector = function(ids, graph) {
20070     var s = iD.util.entitySelector(ids);
20071
20072     ids.forEach(function(id) {
20073         var entity = graph.hasEntity(id);
20074         if (entity && entity.type === 'relation') {
20075             entity.members.forEach(function(member) {
20076                 s += ',.' + member.id;
20077             });
20078         }
20079     });
20080
20081     return s;
20082 };
20083
20084 iD.util.displayName = function(entity) {
20085     var localeName = 'name:' + iD.detect().locale.toLowerCase().split('-')[0];
20086     return entity.tags[localeName] || entity.tags.name || entity.tags.ref;
20087 };
20088
20089 iD.util.displayType = function(id) {
20090     return {
20091         n: t('inspector.node'),
20092         w: t('inspector.way'),
20093         r: t('inspector.relation')
20094     }[id.charAt(0)];
20095 };
20096
20097 iD.util.stringQs = function(str) {
20098     return str.split('&').reduce(function(obj, pair){
20099         var parts = pair.split('=');
20100         if (parts.length === 2) {
20101             obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
20102         }
20103         return obj;
20104     }, {});
20105 };
20106
20107 iD.util.qsString = function(obj, noencode) {
20108     function softEncode(s) {
20109       // encode everything except special characters used in certain hash parameters:
20110       // "/" in map states, ":", ",", {" and "}" in background
20111       return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
20112     }
20113     return Object.keys(obj).sort().map(function(key) {
20114         return encodeURIComponent(key) + '=' + (
20115             noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
20116     }).join('&');
20117 };
20118
20119 iD.util.prefixDOMProperty = function(property) {
20120     var prefixes = ['webkit', 'ms', 'moz', 'o'],
20121         i = -1,
20122         n = prefixes.length,
20123         s = document.body;
20124
20125     if (property in s)
20126         return property;
20127
20128     property = property.substr(0, 1).toUpperCase() + property.substr(1);
20129
20130     while (++i < n)
20131         if (prefixes[i] + property in s)
20132             return prefixes[i] + property;
20133
20134     return false;
20135 };
20136
20137 iD.util.prefixCSSProperty = function(property) {
20138     var prefixes = ['webkit', 'ms', 'Moz', 'O'],
20139         i = -1,
20140         n = prefixes.length,
20141         s = document.body.style;
20142
20143     if (property.toLowerCase() in s)
20144         return property.toLowerCase();
20145
20146     while (++i < n)
20147         if (prefixes[i] + property in s)
20148             return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
20149
20150     return false;
20151 };
20152
20153
20154 iD.util.setTransform = function(el, x, y, scale) {
20155     var prop = iD.util.transformProperty = iD.util.transformProperty || iD.util.prefixCSSProperty('Transform'),
20156         translate = iD.detect().opera ?
20157             'translate('   + x + 'px,' + y + 'px)' :
20158             'translate3d(' + x + 'px,' + y + 'px,0)';
20159     return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
20160 };
20161
20162 iD.util.getStyle = function(selector) {
20163     for (var i = 0; i < document.styleSheets.length; i++) {
20164         var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules || [];
20165         for (var k = 0; k < rules.length; k++) {
20166             var selectorText = rules[k].selectorText && rules[k].selectorText.split(', ');
20167             if (_.contains(selectorText, selector)) {
20168                 return rules[k];
20169             }
20170         }
20171     }
20172 };
20173
20174 iD.util.editDistance = function(a, b) {
20175     if (a.length === 0) return b.length;
20176     if (b.length === 0) return a.length;
20177     var matrix = [];
20178     for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
20179     for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
20180     for (i = 1; i <= b.length; i++) {
20181         for (j = 1; j <= a.length; j++) {
20182             if (b.charAt(i-1) === a.charAt(j-1)) {
20183                 matrix[i][j] = matrix[i-1][j-1];
20184             } else {
20185                 matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
20186                     Math.min(matrix[i][j-1] + 1, // insertion
20187                     matrix[i-1][j] + 1)); // deletion
20188             }
20189         }
20190     }
20191     return matrix[b.length][a.length];
20192 };
20193
20194 // a d3.mouse-alike which
20195 // 1. Only works on HTML elements, not SVG
20196 // 2. Does not cause style recalculation
20197 iD.util.fastMouse = function(container) {
20198     var rect = container.getBoundingClientRect(),
20199         rectLeft = rect.left,
20200         rectTop = rect.top,
20201         clientLeft = +container.clientLeft,
20202         clientTop = +container.clientTop;
20203     return function(e) {
20204         return [
20205             e.clientX - rectLeft - clientLeft,
20206             e.clientY - rectTop - clientTop];
20207     };
20208 };
20209
20210 /* eslint-disable no-proto */
20211 iD.util.getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; };
20212 /* eslint-enable no-proto */
20213
20214 iD.util.asyncMap = function(inputs, func, callback) {
20215     var remaining = inputs.length,
20216         results = [],
20217         errors = [];
20218
20219     inputs.forEach(function(d, i) {
20220         func(d, function done(err, data) {
20221             errors[i] = err;
20222             results[i] = data;
20223             remaining--;
20224             if (!remaining) callback(errors, results);
20225         });
20226     });
20227 };
20228
20229 // wraps an index to an interval [0..length-1]
20230 iD.util.wrap = function(index, length) {
20231     if (index < 0)
20232         index += Math.ceil(-index/length)*length;
20233     return index % length;
20234 };
20235 // A per-domain session mutex backed by a cookie and dead man's
20236 // switch. If the session crashes, the mutex will auto-release
20237 // after 5 seconds.
20238
20239 iD.util.SessionMutex = function(name) {
20240     var mutex = {},
20241         intervalID;
20242
20243     function renew() {
20244         var expires = new Date();
20245         expires.setSeconds(expires.getSeconds() + 5);
20246         document.cookie = name + '=1; expires=' + expires.toUTCString();
20247     }
20248
20249     mutex.lock = function() {
20250         if (intervalID) return true;
20251         var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
20252         if (cookie) return false;
20253         renew();
20254         intervalID = window.setInterval(renew, 4000);
20255         return true;
20256     };
20257
20258     mutex.unlock = function() {
20259         if (!intervalID) return;
20260         document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
20261         clearInterval(intervalID);
20262         intervalID = null;
20263     };
20264
20265     mutex.locked = function() {
20266         return !!intervalID;
20267     };
20268
20269     return mutex;
20270 };
20271 iD.util.SuggestNames = function(preset, suggestions) {
20272     preset = preset.id.split('/', 2);
20273     var k = preset[0],
20274         v = preset[1];
20275
20276     return function(value, callback) {
20277         var result = [];
20278         if (value && value.length > 2) {
20279             if (suggestions[k] && suggestions[k][v]) {
20280                 for (var sugg in suggestions[k][v]) {
20281                     var dist = iD.util.editDistance(value, sugg.substring(0, value.length));
20282                     if (dist < 3) {
20283                         result.push({
20284                             title: sugg,
20285                             value: sugg,
20286                             dist: dist
20287                         });
20288                     }
20289                 }
20290             }
20291             result.sort(function(a, b) {
20292                 return a.dist - b.dist;
20293             });
20294         }
20295         result = result.slice(0,3);
20296         callback(result);
20297     };
20298 };
20299 iD.geo = {};
20300
20301 iD.geo.roundCoords = function(c) {
20302     return [Math.floor(c[0]), Math.floor(c[1])];
20303 };
20304
20305 iD.geo.interp = function(p1, p2, t) {
20306     return [p1[0] + (p2[0] - p1[0]) * t,
20307             p1[1] + (p2[1] - p1[1]) * t];
20308 };
20309
20310 // 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product.
20311 // Returns a positive value, if OAB makes a counter-clockwise turn,
20312 // negative for clockwise turn, and zero if the points are collinear.
20313 iD.geo.cross = function(o, a, b) {
20314     return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
20315 };
20316
20317 // http://jsperf.com/id-dist-optimization
20318 iD.geo.euclideanDistance = function(a, b) {
20319     var x = a[0] - b[0], y = a[1] - b[1];
20320     return Math.sqrt((x * x) + (y * y));
20321 };
20322
20323 // using WGS84 polar radius (6356752.314245179 m)
20324 // const = 2 * PI * r / 360
20325 iD.geo.latToMeters = function(dLat) {
20326     return dLat * 110946.257617;
20327 };
20328
20329 // using WGS84 equatorial radius (6378137.0 m)
20330 // const = 2 * PI * r / 360
20331 iD.geo.lonToMeters = function(dLon, atLat) {
20332     return Math.abs(atLat) >= 90 ? 0 :
20333         dLon * 111319.490793 * Math.abs(Math.cos(atLat * (Math.PI/180)));
20334 };
20335
20336 // using WGS84 polar radius (6356752.314245179 m)
20337 // const = 2 * PI * r / 360
20338 iD.geo.metersToLat = function(m) {
20339     return m / 110946.257617;
20340 };
20341
20342 // using WGS84 equatorial radius (6378137.0 m)
20343 // const = 2 * PI * r / 360
20344 iD.geo.metersToLon = function(m, atLat) {
20345     return Math.abs(atLat) >= 90 ? 0 :
20346         m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180)));
20347 };
20348
20349 iD.geo.offsetToMeters = function(offset) {
20350     var equatRadius = 6356752.314245179,
20351         polarRadius = 6378137.0,
20352         tileSize = 256;
20353
20354     return [
20355         offset[0] * 2 * Math.PI * equatRadius / tileSize,
20356         -offset[1] * 2 * Math.PI * polarRadius / tileSize
20357     ];
20358 };
20359
20360 iD.geo.metersToOffset = function(meters) {
20361     var equatRadius = 6356752.314245179,
20362         polarRadius = 6378137.0,
20363         tileSize = 256;
20364
20365     return [
20366         meters[0] * tileSize / (2 * Math.PI * equatRadius),
20367         -meters[1] * tileSize / (2 * Math.PI * polarRadius)
20368     ];
20369 };
20370
20371 // Equirectangular approximation of spherical distances on Earth
20372 iD.geo.sphericalDistance = function(a, b) {
20373     var x = iD.geo.lonToMeters(a[0] - b[0], (a[1] + b[1]) / 2),
20374         y = iD.geo.latToMeters(a[1] - b[1]);
20375     return Math.sqrt((x * x) + (y * y));
20376 };
20377
20378 iD.geo.edgeEqual = function(a, b) {
20379     return (a[0] === b[0] && a[1] === b[1]) ||
20380         (a[0] === b[1] && a[1] === b[0]);
20381 };
20382
20383 // Return the counterclockwise angle in the range (-pi, pi)
20384 // between the positive X axis and the line intersecting a and b.
20385 iD.geo.angle = function(a, b, projection) {
20386     a = projection(a.loc);
20387     b = projection(b.loc);
20388     return Math.atan2(b[1] - a[1], b[0] - a[0]);
20389 };
20390
20391 // Choose the edge with the minimal distance from `point` to its orthogonal
20392 // projection onto that edge, if such a projection exists, or the distance to
20393 // the closest vertex on that edge. Returns an object with the `index` of the
20394 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
20395 iD.geo.chooseEdge = function(nodes, point, projection) {
20396     var dist = iD.geo.euclideanDistance,
20397         points = nodes.map(function(n) { return projection(n.loc); }),
20398         min = Infinity,
20399         idx, loc;
20400
20401     function dot(p, q) {
20402         return p[0] * q[0] + p[1] * q[1];
20403     }
20404
20405     for (var i = 0; i < points.length - 1; i++) {
20406         var o = points[i],
20407             s = [points[i + 1][0] - o[0],
20408                  points[i + 1][1] - o[1]],
20409             v = [point[0] - o[0],
20410                  point[1] - o[1]],
20411             proj = dot(v, s) / dot(s, s),
20412             p;
20413
20414         if (proj < 0) {
20415             p = o;
20416         } else if (proj > 1) {
20417             p = points[i + 1];
20418         } else {
20419             p = [o[0] + proj * s[0], o[1] + proj * s[1]];
20420         }
20421
20422         var d = dist(p, point);
20423         if (d < min) {
20424             min = d;
20425             idx = i + 1;
20426             loc = projection.invert(p);
20427         }
20428     }
20429
20430     return {
20431         index: idx,
20432         distance: min,
20433         loc: loc
20434     };
20435 };
20436
20437 // Return the intersection point of 2 line segments.
20438 // From https://github.com/pgkelley4/line-segments-intersect
20439 // This uses the vector cross product approach described below:
20440 //  http://stackoverflow.com/a/565282/786339
20441 iD.geo.lineIntersection = function(a, b) {
20442     function subtractPoints(point1, point2) {
20443         return [point1[0] - point2[0], point1[1] - point2[1]];
20444     }
20445     function crossProduct(point1, point2) {
20446         return point1[0] * point2[1] - point1[1] * point2[0];
20447     }
20448
20449     var p = [a[0][0], a[0][1]],
20450         p2 = [a[1][0], a[1][1]],
20451         q = [b[0][0], b[0][1]],
20452         q2 = [b[1][0], b[1][1]],
20453         r = subtractPoints(p2, p),
20454         s = subtractPoints(q2, q),
20455         uNumerator = crossProduct(subtractPoints(q, p), r),
20456         denominator = crossProduct(r, s);
20457
20458     if (uNumerator && denominator) {
20459         var u = uNumerator / denominator,
20460             t = crossProduct(subtractPoints(q, p), s) / denominator;
20461
20462         if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
20463             return iD.geo.interp(p, p2, t);
20464         }
20465     }
20466
20467     return null;
20468 };
20469
20470 iD.geo.pathIntersections = function(path1, path2) {
20471     var intersections = [];
20472     for (var i = 0; i < path1.length - 1; i++) {
20473         for (var j = 0; j < path2.length - 1; j++) {
20474             var a = [ path1[i], path1[i+1] ],
20475                 b = [ path2[j], path2[j+1] ],
20476                 hit = iD.geo.lineIntersection(a, b);
20477             if (hit) intersections.push(hit);
20478         }
20479     }
20480     return intersections;
20481 };
20482
20483 // Return whether point is contained in polygon.
20484 //
20485 // `point` should be a 2-item array of coordinates.
20486 // `polygon` should be an array of 2-item arrays of coordinates.
20487 //
20488 // From https://github.com/substack/point-in-polygon.
20489 // ray-casting algorithm based on
20490 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
20491 //
20492 iD.geo.pointInPolygon = function(point, polygon) {
20493     var x = point[0],
20494         y = point[1],
20495         inside = false;
20496
20497     for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
20498         var xi = polygon[i][0], yi = polygon[i][1];
20499         var xj = polygon[j][0], yj = polygon[j][1];
20500
20501         var intersect = ((yi > y) !== (yj > y)) &&
20502             (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
20503         if (intersect) inside = !inside;
20504     }
20505
20506     return inside;
20507 };
20508
20509 iD.geo.polygonContainsPolygon = function(outer, inner) {
20510     return _.every(inner, function(point) {
20511         return iD.geo.pointInPolygon(point, outer);
20512     });
20513 };
20514
20515 iD.geo.polygonIntersectsPolygon = function(outer, inner, checkSegments) {
20516     function testSegments(outer, inner) {
20517         for (var i = 0; i < outer.length - 1; i++) {
20518             for (var j = 0; j < inner.length - 1; j++) {
20519                 var a = [ outer[i], outer[i+1] ],
20520                     b = [ inner[j], inner[j+1] ];
20521                 if (iD.geo.lineIntersection(a, b)) return true;
20522             }
20523         }
20524         return false;
20525     }
20526
20527     function testPoints(outer, inner) {
20528         return _.some(inner, function(point) {
20529             return iD.geo.pointInPolygon(point, outer);
20530         });
20531     }
20532
20533    return testPoints(outer, inner) || (!!checkSegments && testSegments(outer, inner));
20534 };
20535
20536 iD.geo.pathLength = function(path) {
20537     var length = 0,
20538         dx, dy;
20539     for (var i = 0; i < path.length - 1; i++) {
20540         dx = path[i][0] - path[i + 1][0];
20541         dy = path[i][1] - path[i + 1][1];
20542         length += Math.sqrt(dx * dx + dy * dy);
20543     }
20544     return length;
20545 };
20546 iD.geo.Extent = function geoExtent(min, max) {
20547     if (!(this instanceof iD.geo.Extent)) return new iD.geo.Extent(min, max);
20548     if (min instanceof iD.geo.Extent) {
20549         return min;
20550     } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
20551         this[0] = min[0];
20552         this[1] = min[1];
20553     } else {
20554         this[0] = min        || [ Infinity,  Infinity];
20555         this[1] = max || min || [-Infinity, -Infinity];
20556     }
20557 };
20558
20559 iD.geo.Extent.prototype = new Array(2);
20560
20561 _.extend(iD.geo.Extent.prototype, {
20562     equals: function (obj) {
20563         return this[0][0] === obj[0][0] &&
20564             this[0][1] === obj[0][1] &&
20565             this[1][0] === obj[1][0] &&
20566             this[1][1] === obj[1][1];
20567     },
20568
20569     extend: function(obj) {
20570         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20571         return iD.geo.Extent([Math.min(obj[0][0], this[0][0]),
20572                               Math.min(obj[0][1], this[0][1])],
20573                              [Math.max(obj[1][0], this[1][0]),
20574                               Math.max(obj[1][1], this[1][1])]);
20575     },
20576
20577     _extend: function(extent) {
20578         this[0][0] = Math.min(extent[0][0], this[0][0]);
20579         this[0][1] = Math.min(extent[0][1], this[0][1]);
20580         this[1][0] = Math.max(extent[1][0], this[1][0]);
20581         this[1][1] = Math.max(extent[1][1], this[1][1]);
20582     },
20583
20584     area: function() {
20585         return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
20586     },
20587
20588     center: function() {
20589         return [(this[0][0] + this[1][0]) / 2,
20590                 (this[0][1] + this[1][1]) / 2];
20591     },
20592
20593     rectangle: function() {
20594         return [this[0][0], this[0][1], this[1][0], this[1][1]];
20595     },
20596
20597     polygon: function() {
20598         return [
20599             [this[0][0], this[0][1]],
20600             [this[0][0], this[1][1]],
20601             [this[1][0], this[1][1]],
20602             [this[1][0], this[0][1]],
20603             [this[0][0], this[0][1]]
20604         ];
20605     },
20606
20607     contains: function(obj) {
20608         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20609         return obj[0][0] >= this[0][0] &&
20610                obj[0][1] >= this[0][1] &&
20611                obj[1][0] <= this[1][0] &&
20612                obj[1][1] <= this[1][1];
20613     },
20614
20615     intersects: function(obj) {
20616         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20617         return obj[0][0] <= this[1][0] &&
20618                obj[0][1] <= this[1][1] &&
20619                obj[1][0] >= this[0][0] &&
20620                obj[1][1] >= this[0][1];
20621     },
20622
20623     intersection: function(obj) {
20624         if (!this.intersects(obj)) return new iD.geo.Extent();
20625         return new iD.geo.Extent([Math.max(obj[0][0], this[0][0]),
20626                                   Math.max(obj[0][1], this[0][1])],
20627                                  [Math.min(obj[1][0], this[1][0]),
20628                                   Math.min(obj[1][1], this[1][1])]);
20629     },
20630
20631     percentContainedIn: function(obj) {
20632         if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
20633         var a1 = this.intersection(obj).area(),
20634             a2 = this.area();
20635
20636         if (a1 === Infinity || a2 === Infinity || a1 === 0 || a2 === 0) {
20637             return 0;
20638         } else {
20639             return a1 / a2;
20640         }
20641     },
20642
20643     padByMeters: function(meters) {
20644         var dLat = iD.geo.metersToLat(meters),
20645             dLon = iD.geo.metersToLon(meters, this.center()[1]);
20646         return iD.geo.Extent(
20647                 [this[0][0] - dLon, this[0][1] - dLat],
20648                 [this[1][0] + dLon, this[1][1] + dLat]);
20649     },
20650
20651     toParam: function() {
20652         return this.rectangle().join(',');
20653     }
20654
20655 });
20656 iD.geo.Turn = function(turn) {
20657     if (!(this instanceof iD.geo.Turn))
20658         return new iD.geo.Turn(turn);
20659     _.extend(this, turn);
20660 };
20661
20662 iD.geo.Intersection = function(graph, vertexId) {
20663     var vertex = graph.entity(vertexId),
20664         parentWays = graph.parentWays(vertex),
20665         coincident = [],
20666         highways = {};
20667
20668     function addHighway(way, adjacentNodeId) {
20669         if (highways[adjacentNodeId]) {
20670             coincident.push(adjacentNodeId);
20671         } else {
20672             highways[adjacentNodeId] = way;
20673         }
20674     }
20675
20676     // Pre-split ways that would need to be split in
20677     // order to add a restriction. The real split will
20678     // happen when the restriction is added.
20679     parentWays.forEach(function(way) {
20680         if (!way.tags.highway || way.isArea() || way.isDegenerate())
20681             return;
20682
20683         var isFirst = (vertexId === way.first()),
20684             isLast = (vertexId === way.last()),
20685             isAffix = (isFirst || isLast),
20686             isClosingNode = (isFirst && isLast);
20687
20688         if (isAffix && !isClosingNode) {
20689             var index = (isFirst ? 1 : way.nodes.length - 2);
20690             addHighway(way, way.nodes[index]);
20691
20692         } else {
20693             var splitIndex, wayA, wayB, indexA, indexB;
20694             if (isClosingNode) {
20695                 splitIndex = Math.ceil(way.nodes.length / 2);  // split at midpoint
20696                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex)});
20697                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
20698                 indexA = 1;
20699                 indexB = way.nodes.length - 2;
20700             } else {
20701                 splitIndex = _.indexOf(way.nodes, vertex.id, 1);  // split at vertexid
20702                 wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, splitIndex + 1)});
20703                 wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(splitIndex)});
20704                 indexA = splitIndex - 1;
20705                 indexB = splitIndex + 1;
20706             }
20707             graph = graph.replace(wayA).replace(wayB);
20708             addHighway(wayA, way.nodes[indexA]);
20709             addHighway(wayB, way.nodes[indexB]);
20710         }
20711     });
20712
20713     // remove any ways from this intersection that are coincident
20714     // (i.e. any adjacent node used by more than one intersecting way)
20715     coincident.forEach(function (n) {
20716         delete highways[n];
20717     });
20718
20719
20720     var intersection = {
20721         highways: highways,
20722         ways: _.values(highways),
20723         graph: graph
20724     };
20725
20726     intersection.adjacentNodeId = function(fromWayId) {
20727         return _.find(_.keys(highways), function(k) {
20728             return highways[k].id === fromWayId;
20729         });
20730     };
20731
20732     intersection.turns = function(fromNodeId) {
20733         var start = highways[fromNodeId];
20734         if (!start)
20735             return [];
20736
20737         if (start.first() === vertex.id && start.tags.oneway === 'yes')
20738             return [];
20739         if (start.last() === vertex.id && start.tags.oneway === '-1')
20740             return [];
20741
20742         function withRestriction(turn) {
20743             graph.parentRelations(graph.entity(turn.from.way)).forEach(function(relation) {
20744                 if (relation.tags.type !== 'restriction')
20745                     return;
20746
20747                 var f = relation.memberByRole('from'),
20748                     t = relation.memberByRole('to'),
20749                     v = relation.memberByRole('via');
20750
20751                 if (f && f.id === turn.from.way &&
20752                     v && v.id === turn.via.node &&
20753                     t && t.id === turn.to.way) {
20754                     turn.restriction = relation.id;
20755                 } else if (/^only_/.test(relation.tags.restriction) &&
20756                     f && f.id === turn.from.way &&
20757                     v && v.id === turn.via.node &&
20758                     t && t.id !== turn.to.way) {
20759                     turn.restriction = relation.id;
20760                     turn.indirect_restriction = true;
20761                 }
20762             });
20763
20764             return iD.geo.Turn(turn);
20765         }
20766
20767         var from = {
20768                 node: fromNodeId,
20769                 way: start.id.split(/-(a|b)/)[0]
20770             },
20771             via = { node: vertex.id },
20772             turns = [];
20773
20774         _.each(highways, function(end, adjacentNodeId) {
20775             if (end === start)
20776                 return;
20777
20778             // backward
20779             if (end.first() !== vertex.id && end.tags.oneway !== 'yes') {
20780                 turns.push(withRestriction({
20781                     from: from,
20782                     via: via,
20783                     to: {
20784                         node: adjacentNodeId,
20785                         way: end.id.split(/-(a|b)/)[0]
20786                     }
20787                 }));
20788             }
20789
20790             // forward
20791             if (end.last() !== vertex.id && end.tags.oneway !== '-1') {
20792                 turns.push(withRestriction({
20793                     from: from,
20794                     via: via,
20795                     to: {
20796                         node: adjacentNodeId,
20797                         way: end.id.split(/-(a|b)/)[0]
20798                     }
20799                 }));
20800             }
20801
20802         });
20803
20804         // U-turn
20805         if (start.tags.oneway !== 'yes' && start.tags.oneway !== '-1') {
20806             turns.push(withRestriction({
20807                 from: from,
20808                 via: via,
20809                 to: from,
20810                 u: true
20811             }));
20812         }
20813
20814         return turns;
20815     };
20816
20817     return intersection;
20818 };
20819
20820
20821 iD.geo.inferRestriction = function(graph, from, via, to, projection) {
20822     var fromWay = graph.entity(from.way),
20823         fromNode = graph.entity(from.node),
20824         toWay = graph.entity(to.way),
20825         toNode = graph.entity(to.node),
20826         viaNode = graph.entity(via.node),
20827         fromOneWay = (fromWay.tags.oneway === 'yes' && fromWay.last() === via.node) ||
20828             (fromWay.tags.oneway === '-1' && fromWay.first() === via.node),
20829         toOneWay = (toWay.tags.oneway === 'yes' && toWay.first() === via.node) ||
20830             (toWay.tags.oneway === '-1' && toWay.last() === via.node),
20831         angle = iD.geo.angle(viaNode, fromNode, projection) -
20832                 iD.geo.angle(viaNode, toNode, projection);
20833
20834     angle = angle * 180 / Math.PI;
20835
20836     while (angle < 0)
20837         angle += 360;
20838
20839     if (fromNode === toNode)
20840         return 'no_u_turn';
20841     if ((angle < 23 || angle > 336) && fromOneWay && toOneWay)
20842         return 'no_u_turn';
20843     if (angle < 158)
20844         return 'no_right_turn';
20845     if (angle > 202)
20846         return 'no_left_turn';
20847
20848     return 'no_straight_on';
20849 };
20850 // For fixing up rendering of multipolygons with tags on the outer member.
20851 // https://github.com/openstreetmap/iD/issues/613
20852 iD.geo.isSimpleMultipolygonOuterMember = function(entity, graph) {
20853     if (entity.type !== 'way')
20854         return false;
20855
20856     var parents = graph.parentRelations(entity);
20857     if (parents.length !== 1)
20858         return false;
20859
20860     var parent = parents[0];
20861     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
20862         return false;
20863
20864     var members = parent.members, member;
20865     for (var i = 0; i < members.length; i++) {
20866         member = members[i];
20867         if (member.id === entity.id && member.role && member.role !== 'outer')
20868             return false; // Not outer member
20869         if (member.id !== entity.id && (!member.role || member.role === 'outer'))
20870             return false; // Not a simple multipolygon
20871     }
20872
20873     return parent;
20874 };
20875
20876 iD.geo.simpleMultipolygonOuterMember = function(entity, graph) {
20877     if (entity.type !== 'way')
20878         return false;
20879
20880     var parents = graph.parentRelations(entity);
20881     if (parents.length !== 1)
20882         return false;
20883
20884     var parent = parents[0];
20885     if (!parent.isMultipolygon() || Object.keys(parent.tags).length > 1)
20886         return false;
20887
20888     var members = parent.members, member, outerMember;
20889     for (var i = 0; i < members.length; i++) {
20890         member = members[i];
20891         if (!member.role || member.role === 'outer') {
20892             if (outerMember)
20893                 return false; // Not a simple multipolygon
20894             outerMember = member;
20895         }
20896     }
20897
20898     return outerMember && graph.hasEntity(outerMember.id);
20899 };
20900
20901 // Join `array` into sequences of connecting ways.
20902 //
20903 // Segments which share identical start/end nodes will, as much as possible,
20904 // be connected with each other.
20905 //
20906 // The return value is a nested array. Each constituent array contains elements
20907 // of `array` which have been determined to connect. Each consitituent array
20908 // also has a `nodes` property whose value is an ordered array of member nodes,
20909 // with appropriate order reversal and start/end coordinate de-duplication.
20910 //
20911 // Members of `array` must have, at minimum, `type` and `id` properties.
20912 // Thus either an array of `iD.Way`s or a relation member array may be
20913 // used.
20914 //
20915 // If an member has a `tags` property, its tags will be reversed via
20916 // `iD.actions.Reverse` in the output.
20917 //
20918 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
20919 // false) and non-way members are ignored.
20920 //
20921 iD.geo.joinWays = function(array, graph) {
20922     var joined = [], member, current, nodes, first, last, i, how, what;
20923
20924     array = array.filter(function(member) {
20925         return member.type === 'way' && graph.hasEntity(member.id);
20926     });
20927
20928     function resolve(member) {
20929         return graph.childNodes(graph.entity(member.id));
20930     }
20931
20932     function reverse(member) {
20933         return member.tags ? iD.actions.Reverse(member.id, {reverseOneway: true})(graph).entity(member.id) : member;
20934     }
20935
20936     while (array.length) {
20937         member = array.shift();
20938         current = [member];
20939         current.nodes = nodes = resolve(member).slice();
20940         joined.push(current);
20941
20942         while (array.length && _.first(nodes) !== _.last(nodes)) {
20943             first = _.first(nodes);
20944             last  = _.last(nodes);
20945
20946             for (i = 0; i < array.length; i++) {
20947                 member = array[i];
20948                 what = resolve(member);
20949
20950                 if (last === _.first(what)) {
20951                     how  = nodes.push;
20952                     what = what.slice(1);
20953                     break;
20954                 } else if (last === _.last(what)) {
20955                     how  = nodes.push;
20956                     what = what.slice(0, -1).reverse();
20957                     member = reverse(member);
20958                     break;
20959                 } else if (first === _.last(what)) {
20960                     how  = nodes.unshift;
20961                     what = what.slice(0, -1);
20962                     break;
20963                 } else if (first === _.first(what)) {
20964                     how  = nodes.unshift;
20965                     what = what.slice(1).reverse();
20966                     member = reverse(member);
20967                     break;
20968                 } else {
20969                     what = how = null;
20970                 }
20971             }
20972
20973             if (!what)
20974                 break; // No more joinable ways.
20975
20976             how.apply(current, [member]);
20977             how.apply(nodes, what);
20978
20979             array.splice(i, 1);
20980         }
20981     }
20982
20983     return joined;
20984 };
20985 /*
20986     Bypasses features of D3's default projection stream pipeline that are unnecessary:
20987     * Antimeridian clipping
20988     * Spherical rotation
20989     * Resampling
20990 */
20991 iD.geo.RawMercator = function () {
20992     var project = d3.geo.mercator.raw,
20993         k = 512 / Math.PI, // scale
20994         x = 0, y = 0, // translate
20995         clipExtent = [[0, 0], [0, 0]];
20996
20997     function projection(point) {
20998         point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
20999         return [point[0] * k + x, y - point[1] * k];
21000     }
21001
21002     projection.invert = function(point) {
21003         point = project.invert((point[0] - x) / k, (y - point[1]) / k);
21004         return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
21005     };
21006
21007     projection.scale = function(_) {
21008         if (!arguments.length) return k;
21009         k = +_;
21010         return projection;
21011     };
21012
21013     projection.translate = function(_) {
21014         if (!arguments.length) return [x, y];
21015         x = +_[0];
21016         y = +_[1];
21017         return projection;
21018     };
21019
21020     projection.clipExtent = function(_) {
21021         if (!arguments.length) return clipExtent;
21022         clipExtent = _;
21023         return projection;
21024     };
21025
21026     projection.stream = d3.geo.transform({
21027         point: function(x, y) {
21028             x = projection([x, y]);
21029             this.stream.point(x[0], x[1]);
21030         }
21031     }).stream;
21032
21033     return projection;
21034 };
21035 iD.actions = {};
21036 iD.actions.AddEntity = function(way) {
21037     return function(graph) {
21038         return graph.replace(way);
21039     };
21040 };
21041 iD.actions.AddMember = function(relationId, member, memberIndex) {
21042     return function(graph) {
21043         var relation = graph.entity(relationId);
21044
21045         if (isNaN(memberIndex) && member.type === 'way') {
21046             var members = relation.indexedMembers();
21047             members.push(member);
21048
21049             var joined = iD.geo.joinWays(members, graph);
21050             for (var i = 0; i < joined.length; i++) {
21051                 var segment = joined[i];
21052                 for (var j = 0; j < segment.length && segment.length >= 2; j++) {
21053                     if (segment[j] !== member)
21054                         continue;
21055
21056                     if (j === 0) {
21057                         memberIndex = segment[j + 1].index;
21058                     } else if (j === segment.length - 1) {
21059                         memberIndex = segment[j - 1].index + 1;
21060                     } else {
21061                         memberIndex = Math.min(segment[j - 1].index + 1, segment[j + 1].index + 1);
21062                     }
21063                 }
21064             }
21065         }
21066
21067         return graph.replace(relation.addMember(member, memberIndex));
21068     };
21069 };
21070 iD.actions.AddMidpoint = function(midpoint, node) {
21071     return function(graph) {
21072         graph = graph.replace(node.move(midpoint.loc));
21073
21074         var parents = _.intersection(
21075             graph.parentWays(graph.entity(midpoint.edge[0])),
21076             graph.parentWays(graph.entity(midpoint.edge[1])));
21077
21078         parents.forEach(function(way) {
21079             for (var i = 0; i < way.nodes.length - 1; i++) {
21080                 if (iD.geo.edgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
21081                     graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
21082
21083                     // Add only one midpoint on doubled-back segments,
21084                     // turning them into self-intersections.
21085                     return;
21086                 }
21087             }
21088         });
21089
21090         return graph;
21091     };
21092 };
21093 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
21094 iD.actions.AddVertex = function(wayId, nodeId, index) {
21095     return function(graph) {
21096         return graph.replace(graph.entity(wayId).addNode(nodeId, index));
21097     };
21098 };
21099 iD.actions.ChangeMember = function(relationId, member, memberIndex) {
21100     return function(graph) {
21101         return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
21102     };
21103 };
21104 iD.actions.ChangePreset = function(entityId, oldPreset, newPreset) {
21105     return function(graph) {
21106         var entity = graph.entity(entityId),
21107             geometry = entity.geometry(graph),
21108             tags = entity.tags;
21109
21110         if (oldPreset) tags = oldPreset.removeTags(tags, geometry);
21111         if (newPreset) tags = newPreset.applyTags(tags, geometry);
21112
21113         return graph.replace(entity.update({tags: tags}));
21114     };
21115 };
21116 iD.actions.ChangeTags = function(entityId, tags) {
21117     return function(graph) {
21118         var entity = graph.entity(entityId);
21119         return graph.replace(entity.update({tags: tags}));
21120     };
21121 };
21122 iD.actions.Circularize = function(wayId, projection, maxAngle) {
21123     maxAngle = (maxAngle || 20) * Math.PI / 180;
21124
21125     var action = function(graph) {
21126         var way = graph.entity(wayId);
21127
21128         if (!way.isConvex(graph)) {
21129             graph = action.makeConvex(graph);
21130         }
21131
21132         var nodes = _.uniq(graph.childNodes(way)),
21133             keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }),
21134             points = nodes.map(function(n) { return projection(n.loc); }),
21135             keyPoints = keyNodes.map(function(n) { return projection(n.loc); }),
21136             centroid = (points.length === 2) ? iD.geo.interp(points[0], points[1], 0.5) : d3.geom.polygon(points).centroid(),
21137             radius = d3.median(points, function(p) { return iD.geo.euclideanDistance(centroid, p); }),
21138             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
21139             ids;
21140
21141         // we need atleast two key nodes for the algorithm to work
21142         if (!keyNodes.length) {
21143             keyNodes = [nodes[0]];
21144             keyPoints = [points[0]];
21145         }
21146
21147         if (keyNodes.length === 1) {
21148             var index = nodes.indexOf(keyNodes[0]),
21149                 oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
21150
21151             keyNodes.push(nodes[oppositeIndex]);
21152             keyPoints.push(points[oppositeIndex]);
21153         }
21154
21155         // key points and nodes are those connected to the ways,
21156         // they are projected onto the circle, inbetween nodes are moved
21157         // to constant intervals between key nodes, extra inbetween nodes are
21158         // added if necessary.
21159         for (var i = 0; i < keyPoints.length; i++) {
21160             var nextKeyNodeIndex = (i + 1) % keyNodes.length,
21161                 startNode = keyNodes[i],
21162                 endNode = keyNodes[nextKeyNodeIndex],
21163                 startNodeIndex = nodes.indexOf(startNode),
21164                 endNodeIndex = nodes.indexOf(endNode),
21165                 numberNewPoints = -1,
21166                 indexRange = endNodeIndex - startNodeIndex,
21167                 distance, totalAngle, eachAngle, startAngle, endAngle,
21168                 angle, loc, node, j,
21169                 inBetweenNodes = [];
21170
21171             if (indexRange < 0) {
21172                 indexRange += nodes.length;
21173             }
21174
21175             // position this key node
21176             distance = iD.geo.euclideanDistance(centroid, keyPoints[i]);
21177             if (distance === 0) { distance = 1e-4; }
21178             keyPoints[i] = [
21179                 centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
21180                 centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
21181             graph = graph.replace(keyNodes[i].move(projection.invert(keyPoints[i])));
21182
21183             // figure out the between delta angle we want to match to
21184             startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
21185             endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
21186             totalAngle = endAngle - startAngle;
21187
21188             // detects looping around -pi/pi
21189             if (totalAngle * sign > 0) {
21190                 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
21191             }
21192
21193             do {
21194                 numberNewPoints++;
21195                 eachAngle = totalAngle / (indexRange + numberNewPoints);
21196             } while (Math.abs(eachAngle) > maxAngle);
21197
21198             // move existing points
21199             for (j = 1; j < indexRange; j++) {
21200                 angle = startAngle + j * eachAngle;
21201                 loc = projection.invert([
21202                     centroid[0] + Math.cos(angle)*radius,
21203                     centroid[1] + Math.sin(angle)*radius]);
21204
21205                 node = nodes[(j + startNodeIndex) % nodes.length].move(loc);
21206                 graph = graph.replace(node);
21207             }
21208
21209             // add new inbetween nodes if necessary
21210             for (j = 0; j < numberNewPoints; j++) {
21211                 angle = startAngle + (indexRange + j) * eachAngle;
21212                 loc = projection.invert([
21213                     centroid[0] + Math.cos(angle) * radius,
21214                     centroid[1] + Math.sin(angle) * radius]);
21215
21216                 node = iD.Node({loc: loc});
21217                 graph = graph.replace(node);
21218
21219                 nodes.splice(endNodeIndex + j, 0, node);
21220                 inBetweenNodes.push(node.id);
21221             }
21222
21223             // Check for other ways that share these keyNodes..
21224             // If keyNodes are adjacent in both ways,
21225             // we can add inBetween nodes to that shared way too..
21226             if (indexRange === 1 && inBetweenNodes.length) {
21227                 var startIndex1 = way.nodes.lastIndexOf(startNode.id),
21228                     endIndex1 = way.nodes.lastIndexOf(endNode.id),
21229                     wayDirection1 = (endIndex1 - startIndex1);
21230                 if (wayDirection1 < -1) { wayDirection1 = 1; }
21231
21232                 /* eslint-disable no-loop-func */
21233                 _.each(_.without(graph.parentWays(keyNodes[i]), way), function(sharedWay) {
21234                     if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
21235                         var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id),
21236                             endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id),
21237                             wayDirection2 = (endIndex2 - startIndex2),
21238                             insertAt = endIndex2;
21239                         if (wayDirection2 < -1) { wayDirection2 = 1; }
21240
21241                         if (wayDirection1 !== wayDirection2) {
21242                             inBetweenNodes.reverse();
21243                             insertAt = startIndex2;
21244                         }
21245                         for (j = 0; j < inBetweenNodes.length; j++) {
21246                             sharedWay = sharedWay.addNode(inBetweenNodes[j], insertAt + j);
21247                         }
21248                         graph = graph.replace(sharedWay);
21249                     }
21250                 });
21251                 /* eslint-enable no-loop-func */
21252             }
21253
21254         }
21255
21256         // update the way to have all the new nodes
21257         ids = nodes.map(function(n) { return n.id; });
21258         ids.push(ids[0]);
21259
21260         way = way.update({nodes: ids});
21261         graph = graph.replace(way);
21262
21263         return graph;
21264     };
21265
21266     action.makeConvex = function(graph) {
21267         var way = graph.entity(wayId),
21268             nodes = _.uniq(graph.childNodes(way)),
21269             points = nodes.map(function(n) { return projection(n.loc); }),
21270             sign = d3.geom.polygon(points).area() > 0 ? 1 : -1,
21271             hull = d3.geom.hull(points);
21272
21273         // D3 convex hulls go counterclockwise..
21274         if (sign === -1) {
21275             nodes.reverse();
21276             points.reverse();
21277         }
21278
21279         for (var i = 0; i < hull.length - 1; i++) {
21280             var startIndex = points.indexOf(hull[i]),
21281                 endIndex = points.indexOf(hull[i+1]),
21282                 indexRange = (endIndex - startIndex);
21283
21284             if (indexRange < 0) {
21285                 indexRange += nodes.length;
21286             }
21287
21288             // move interior nodes to the surface of the convex hull..
21289             for (var j = 1; j < indexRange; j++) {
21290                 var point = iD.geo.interp(hull[i], hull[i+1], j / indexRange),
21291                     node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
21292                 graph = graph.replace(node);
21293             }
21294         }
21295         return graph;
21296     };
21297
21298     action.disabled = function(graph) {
21299         if (!graph.entity(wayId).isClosed())
21300             return 'not_closed';
21301     };
21302
21303     return action;
21304 };
21305 // Connect the ways at the given nodes.
21306 //
21307 // The last node will survive. All other nodes will be replaced with
21308 // the surviving node in parent ways, and then removed.
21309 //
21310 // Tags and relation memberships of of non-surviving nodes are merged
21311 // to the survivor.
21312 //
21313 // This is the inverse of `iD.actions.Disconnect`.
21314 //
21315 // Reference:
21316 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
21317 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
21318 //
21319 iD.actions.Connect = function(nodeIds) {
21320     return function(graph) {
21321         var survivor = graph.entity(_.last(nodeIds));
21322
21323         for (var i = 0; i < nodeIds.length - 1; i++) {
21324             var node = graph.entity(nodeIds[i]);
21325
21326             /* eslint-disable no-loop-func */
21327             graph.parentWays(node).forEach(function(parent) {
21328                 if (!parent.areAdjacent(node.id, survivor.id)) {
21329                     graph = graph.replace(parent.replaceNode(node.id, survivor.id));
21330                 }
21331             });
21332
21333             graph.parentRelations(node).forEach(function(parent) {
21334                 graph = graph.replace(parent.replaceMember(node, survivor));
21335             });
21336             /* eslint-enable no-loop-func */
21337
21338             survivor = survivor.mergeTags(node.tags);
21339             graph = iD.actions.DeleteNode(node.id)(graph);
21340         }
21341
21342         graph = graph.replace(survivor);
21343
21344         return graph;
21345     };
21346 };
21347 iD.actions.CopyEntities = function(ids, fromGraph) {
21348     var copies = {};
21349
21350     var action = function(graph) {
21351         ids.forEach(function(id) {
21352             fromGraph.entity(id).copy(fromGraph, copies);
21353         });
21354
21355         for (var id in copies) {
21356             graph = graph.replace(copies[id]);
21357         }
21358
21359         return graph;
21360     };
21361
21362     action.copies = function() {
21363         return copies;
21364     };
21365
21366     return action;
21367 };
21368 iD.actions.DeleteMember = function(relationId, memberIndex) {
21369     return function(graph) {
21370         var relation = graph.entity(relationId)
21371             .removeMember(memberIndex);
21372
21373         graph = graph.replace(relation);
21374
21375         if (relation.isDegenerate())
21376             graph = iD.actions.DeleteRelation(relation.id)(graph);
21377
21378         return graph;
21379     };
21380 };
21381 iD.actions.DeleteMultiple = function(ids) {
21382     var actions = {
21383         way: iD.actions.DeleteWay,
21384         node: iD.actions.DeleteNode,
21385         relation: iD.actions.DeleteRelation
21386     };
21387
21388     var action = function(graph) {
21389         ids.forEach(function(id) {
21390             if (graph.hasEntity(id)) { // It may have been deleted aready.
21391                 graph = actions[graph.entity(id).type](id)(graph);
21392             }
21393         });
21394
21395         return graph;
21396     };
21397
21398     action.disabled = function(graph) {
21399         for (var i = 0; i < ids.length; i++) {
21400             var id = ids[i],
21401                 disabled = actions[graph.entity(id).type](id).disabled(graph);
21402             if (disabled) return disabled;
21403         }
21404     };
21405
21406     return action;
21407 };
21408 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
21409 iD.actions.DeleteNode = function(nodeId) {
21410     var action = function(graph) {
21411         var node = graph.entity(nodeId);
21412
21413         graph.parentWays(node)
21414             .forEach(function(parent) {
21415                 parent = parent.removeNode(nodeId);
21416                 graph = graph.replace(parent);
21417
21418                 if (parent.isDegenerate()) {
21419                     graph = iD.actions.DeleteWay(parent.id)(graph);
21420                 }
21421             });
21422
21423         graph.parentRelations(node)
21424             .forEach(function(parent) {
21425                 parent = parent.removeMembersWithID(nodeId);
21426                 graph = graph.replace(parent);
21427
21428                 if (parent.isDegenerate()) {
21429                     graph = iD.actions.DeleteRelation(parent.id)(graph);
21430                 }
21431             });
21432
21433         return graph.remove(node);
21434     };
21435
21436     action.disabled = function() {
21437         return false;
21438     };
21439
21440     return action;
21441 };
21442 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
21443 iD.actions.DeleteRelation = function(relationId) {
21444     function deleteEntity(entity, graph) {
21445         return !graph.parentWays(entity).length &&
21446             !graph.parentRelations(entity).length &&
21447             !entity.hasInterestingTags();
21448     }
21449
21450     var action = function(graph) {
21451         var relation = graph.entity(relationId);
21452
21453         graph.parentRelations(relation)
21454             .forEach(function(parent) {
21455                 parent = parent.removeMembersWithID(relationId);
21456                 graph = graph.replace(parent);
21457
21458                 if (parent.isDegenerate()) {
21459                     graph = iD.actions.DeleteRelation(parent.id)(graph);
21460                 }
21461             });
21462
21463         _.uniq(_.pluck(relation.members, 'id')).forEach(function(memberId) {
21464             graph = graph.replace(relation.removeMembersWithID(memberId));
21465
21466             var entity = graph.entity(memberId);
21467             if (deleteEntity(entity, graph)) {
21468                 graph = iD.actions.DeleteMultiple([memberId])(graph);
21469             }
21470         });
21471
21472         return graph.remove(relation);
21473     };
21474
21475     action.disabled = function(graph) {
21476         if (!graph.entity(relationId).isComplete(graph))
21477             return 'incomplete_relation';
21478     };
21479
21480     return action;
21481 };
21482 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
21483 iD.actions.DeleteWay = function(wayId) {
21484     function deleteNode(node, graph) {
21485         return !graph.parentWays(node).length &&
21486             !graph.parentRelations(node).length &&
21487             !node.hasInterestingTags();
21488     }
21489
21490     var action = function(graph) {
21491         var way = graph.entity(wayId);
21492
21493         graph.parentRelations(way)
21494             .forEach(function(parent) {
21495                 parent = parent.removeMembersWithID(wayId);
21496                 graph = graph.replace(parent);
21497
21498                 if (parent.isDegenerate()) {
21499                     graph = iD.actions.DeleteRelation(parent.id)(graph);
21500                 }
21501             });
21502
21503         _.uniq(way.nodes).forEach(function(nodeId) {
21504             graph = graph.replace(way.removeNode(nodeId));
21505
21506             var node = graph.entity(nodeId);
21507             if (deleteNode(node, graph)) {
21508                 graph = graph.remove(node);
21509             }
21510         });
21511
21512         return graph.remove(way);
21513     };
21514
21515     action.disabled = function(graph) {
21516         var disabled = false;
21517
21518         graph.parentRelations(graph.entity(wayId)).forEach(function(parent) {
21519             var type = parent.tags.type,
21520                 role = parent.memberById(wayId).role || 'outer';
21521             if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
21522                 disabled = 'part_of_relation';
21523             }
21524         });
21525
21526         return disabled;
21527     };
21528
21529     return action;
21530 };
21531 iD.actions.DeprecateTags = function(entityId) {
21532     return function(graph) {
21533         var entity = graph.entity(entityId),
21534             newtags = _.clone(entity.tags),
21535             change = false,
21536             rule;
21537
21538         // This handles deprecated tags with a single condition
21539         for (var i = 0; i < iD.data.deprecated.length; i++) {
21540
21541             rule = iD.data.deprecated[i];
21542             var match = _.pairs(rule.old)[0],
21543                 replacements = rule.replace ? _.pairs(rule.replace) : null;
21544
21545             if (entity.tags[match[0]] && match[1] === '*') {
21546
21547                 var value = entity.tags[match[0]];
21548                 if (replacements && !newtags[replacements[0][0]]) {
21549                     newtags[replacements[0][0]] = value;
21550                 }
21551                 delete newtags[match[0]];
21552                 change = true;
21553
21554             } else if (entity.tags[match[0]] === match[1]) {
21555                 newtags = _.assign({}, rule.replace || {}, _.omit(newtags, match[0]));
21556                 change = true;
21557             }
21558         }
21559
21560         if (change) {
21561             return graph.replace(entity.update({tags: newtags}));
21562         } else {
21563             return graph;
21564         }
21565     };
21566 };
21567 iD.actions.DiscardTags = function(difference) {
21568     return function(graph) {
21569         function discardTags(entity) {
21570             if (!_.isEmpty(entity.tags)) {
21571                 var tags = {};
21572                 _.each(entity.tags, function(v, k) {
21573                     if (v) tags[k] = v;
21574                 });
21575
21576                 graph = graph.replace(entity.update({
21577                     tags: _.omit(tags, iD.data.discarded)
21578                 }));
21579             }
21580         }
21581
21582         difference.modified().forEach(discardTags);
21583         difference.created().forEach(discardTags);
21584
21585         return graph;
21586     };
21587 };
21588 // Disconect the ways at the given node.
21589 //
21590 // Optionally, disconnect only the given ways.
21591 //
21592 // For testing convenience, accepts an ID to assign to the (first) new node.
21593 // Normally, this will be undefined and the way will automatically
21594 // be assigned a new ID.
21595 //
21596 // This is the inverse of `iD.actions.Connect`.
21597 //
21598 // Reference:
21599 //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
21600 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
21601 //
21602 iD.actions.Disconnect = function(nodeId, newNodeId) {
21603     var wayIds;
21604
21605     var action = function(graph) {
21606         var node = graph.entity(nodeId),
21607             connections = action.connections(graph);
21608
21609         connections.forEach(function(connection) {
21610             var way = graph.entity(connection.wayID),
21611                 newNode = iD.Node({id: newNodeId, loc: node.loc, tags: node.tags});
21612
21613             graph = graph.replace(newNode);
21614             if (connection.index === 0 && way.isArea()) {
21615                 // replace shared node with shared node..
21616                 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
21617             } else {
21618                 // replace shared node with multiple new nodes..
21619                 graph = graph.replace(way.updateNode(newNode.id, connection.index));
21620             }
21621         });
21622
21623         return graph;
21624     };
21625
21626     action.connections = function(graph) {
21627         var candidates = [],
21628             keeping = false,
21629             parentWays = graph.parentWays(graph.entity(nodeId));
21630
21631         parentWays.forEach(function(way) {
21632             if (wayIds && wayIds.indexOf(way.id) === -1) {
21633                 keeping = true;
21634                 return;
21635             }
21636             if (way.isArea() && (way.nodes[0] === nodeId)) {
21637                 candidates.push({wayID: way.id, index: 0});
21638             } else {
21639                 way.nodes.forEach(function(waynode, index) {
21640                     if (waynode === nodeId) {
21641                         candidates.push({wayID: way.id, index: index});
21642                     }
21643                 });
21644             }
21645         });
21646
21647         return keeping ? candidates : candidates.slice(1);
21648     };
21649
21650     action.disabled = function(graph) {
21651         var connections = action.connections(graph);
21652         if (connections.length === 0 || (wayIds && wayIds.length !== connections.length))
21653             return 'not_connected';
21654
21655         var parentWays = graph.parentWays(graph.entity(nodeId)),
21656             seenRelationIds = {},
21657             sharedRelation;
21658
21659         parentWays.forEach(function(way) {
21660             if (wayIds && wayIds.indexOf(way.id) === -1)
21661                 return;
21662
21663             var relations = graph.parentRelations(way);
21664             relations.forEach(function(relation) {
21665                 if (relation.id in seenRelationIds) {
21666                     sharedRelation = relation;
21667                 } else {
21668                     seenRelationIds[relation.id] = true;
21669                 }
21670             });
21671         });
21672
21673         if (sharedRelation)
21674             return 'relation';
21675     };
21676
21677     action.limitWays = function(_) {
21678         if (!arguments.length) return wayIds;
21679         wayIds = _;
21680         return action;
21681     };
21682
21683     return action;
21684 };
21685 // Join ways at the end node they share.
21686 //
21687 // This is the inverse of `iD.actions.Split`.
21688 //
21689 // Reference:
21690 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
21691 //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
21692 //
21693 iD.actions.Join = function(ids) {
21694
21695     function groupEntitiesByGeometry(graph) {
21696         var entities = ids.map(function(id) { return graph.entity(id); });
21697         return _.extend({line: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
21698     }
21699
21700     var action = function(graph) {
21701         var ways = ids.map(graph.entity, graph),
21702             survivor = ways[0];
21703
21704         // Prefer to keep an existing way.
21705         for (var i = 0; i < ways.length; i++) {
21706             if (!ways[i].isNew()) {
21707                 survivor = ways[i];
21708                 break;
21709             }
21710         }
21711
21712         var joined = iD.geo.joinWays(ways, graph)[0];
21713
21714         survivor = survivor.update({nodes: _.pluck(joined.nodes, 'id')});
21715         graph = graph.replace(survivor);
21716
21717         joined.forEach(function(way) {
21718             if (way.id === survivor.id)
21719                 return;
21720
21721             graph.parentRelations(way).forEach(function(parent) {
21722                 graph = graph.replace(parent.replaceMember(way, survivor));
21723             });
21724
21725             survivor = survivor.mergeTags(way.tags);
21726
21727             graph = graph.replace(survivor);
21728             graph = iD.actions.DeleteWay(way.id)(graph);
21729         });
21730
21731         return graph;
21732     };
21733
21734     action.disabled = function(graph) {
21735         var geometries = groupEntitiesByGeometry(graph);
21736         if (ids.length < 2 || ids.length !== geometries.line.length)
21737             return 'not_eligible';
21738
21739         var joined = iD.geo.joinWays(ids.map(graph.entity, graph), graph);
21740         if (joined.length > 1)
21741             return 'not_adjacent';
21742
21743         var nodeIds = _.pluck(joined[0].nodes, 'id').slice(1, -1),
21744             relation,
21745             tags = {},
21746             conflicting = false;
21747
21748         joined[0].forEach(function(way) {
21749             var parents = graph.parentRelations(way);
21750             parents.forEach(function(parent) {
21751                 if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; }))
21752                     relation = parent;
21753             });
21754
21755             for (var k in way.tags) {
21756                 if (!(k in tags)) {
21757                     tags[k] = way.tags[k];
21758                 } else if (tags[k] && iD.interestingTag(k) && tags[k] !== way.tags[k]) {
21759                     conflicting = true;
21760                 }
21761             }
21762         });
21763
21764         if (relation)
21765             return 'restriction';
21766
21767         if (conflicting)
21768             return 'conflicting_tags';
21769     };
21770
21771     return action;
21772 };
21773 iD.actions.Merge = function(ids) {
21774     function groupEntitiesByGeometry(graph) {
21775         var entities = ids.map(function(id) { return graph.entity(id); });
21776         return _.extend({point: [], area: [], line: [], relation: []},
21777             _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
21778     }
21779
21780     var action = function(graph) {
21781         var geometries = groupEntitiesByGeometry(graph),
21782             target = geometries.area[0] || geometries.line[0],
21783             points = geometries.point;
21784
21785         points.forEach(function(point) {
21786             target = target.mergeTags(point.tags);
21787
21788             graph.parentRelations(point).forEach(function(parent) {
21789                 graph = graph.replace(parent.replaceMember(point, target));
21790             });
21791
21792             graph = graph.remove(point);
21793         });
21794
21795         graph = graph.replace(target);
21796
21797         return graph;
21798     };
21799
21800     action.disabled = function(graph) {
21801         var geometries = groupEntitiesByGeometry(graph);
21802         if (geometries.point.length === 0 ||
21803             (geometries.area.length + geometries.line.length) !== 1 ||
21804             geometries.relation.length !== 0)
21805             return 'not_eligible';
21806     };
21807
21808     return action;
21809 };
21810 iD.actions.MergePolygon = function(ids, newRelationId) {
21811
21812     function groupEntities(graph) {
21813         var entities = ids.map(function (id) { return graph.entity(id); });
21814         return _.extend({
21815                 closedWay: [],
21816                 multipolygon: [],
21817                 other: []
21818             }, _.groupBy(entities, function(entity) {
21819                 if (entity.type === 'way' && entity.isClosed()) {
21820                     return 'closedWay';
21821                 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
21822                     return 'multipolygon';
21823                 } else {
21824                     return 'other';
21825                 }
21826             }));
21827     }
21828
21829     var action = function(graph) {
21830         var entities = groupEntities(graph);
21831
21832         // An array representing all the polygons that are part of the multipolygon.
21833         //
21834         // Each element is itself an array of objects with an id property, and has a
21835         // locs property which is an array of the locations forming the polygon.
21836         var polygons = entities.multipolygon.reduce(function(polygons, m) {
21837             return polygons.concat(iD.geo.joinWays(m.members, graph));
21838         }, []).concat(entities.closedWay.map(function(d) {
21839             var member = [{id: d.id}];
21840             member.nodes = graph.childNodes(d);
21841             return member;
21842         }));
21843
21844         // contained is an array of arrays of boolean values,
21845         // where contained[j][k] is true iff the jth way is
21846         // contained by the kth way.
21847         var contained = polygons.map(function(w, i) {
21848             return polygons.map(function(d, n) {
21849                 if (i === n) return null;
21850                 return iD.geo.polygonContainsPolygon(
21851                     _.pluck(d.nodes, 'loc'),
21852                     _.pluck(w.nodes, 'loc'));
21853             });
21854         });
21855
21856         // Sort all polygons as either outer or inner ways
21857         var members = [],
21858             outer = true;
21859
21860         while (polygons.length) {
21861             extractUncontained(polygons);
21862             polygons = polygons.filter(isContained);
21863             contained = contained.filter(isContained).map(filterContained);
21864         }
21865
21866         function isContained(d, i) {
21867             return _.any(contained[i]);
21868         }
21869
21870         function filterContained(d) {
21871             return d.filter(isContained);
21872         }
21873
21874         function extractUncontained(polygons) {
21875             polygons.forEach(function(d, i) {
21876                 if (!isContained(d, i)) {
21877                     d.forEach(function(member) {
21878                         members.push({
21879                             type: 'way',
21880                             id: member.id,
21881                             role: outer ? 'outer' : 'inner'
21882                         });
21883                     });
21884                 }
21885             });
21886             outer = !outer;
21887         }
21888
21889         // Move all tags to one relation
21890         var relation = entities.multipolygon[0] ||
21891             iD.Relation({ id: newRelationId, tags: { type: 'multipolygon' }});
21892
21893         entities.multipolygon.slice(1).forEach(function(m) {
21894             relation = relation.mergeTags(m.tags);
21895             graph = graph.remove(m);
21896         });
21897
21898         entities.closedWay.forEach(function(way) {
21899             function isThisOuter(m) {
21900                 return m.id === way.id && m.role !== 'inner';
21901             }
21902             if (members.some(isThisOuter)) {
21903                 relation = relation.mergeTags(way.tags);
21904                 graph = graph.replace(way.update({ tags: {} }));
21905             }
21906         });
21907
21908         return graph.replace(relation.update({
21909             members: members,
21910             tags: _.omit(relation.tags, 'area')
21911         }));
21912     };
21913
21914     action.disabled = function(graph) {
21915         var entities = groupEntities(graph);
21916         if (entities.other.length > 0 ||
21917             entities.closedWay.length + entities.multipolygon.length < 2)
21918             return 'not_eligible';
21919         if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); }))
21920             return 'incomplete_relation';
21921     };
21922
21923     return action;
21924 };
21925 iD.actions.MergeRemoteChanges = function(id, localGraph, remoteGraph, formatUser) {
21926     var option = 'safe',  // 'safe', 'force_local', 'force_remote'
21927         conflicts = [];
21928
21929     function user(d) {
21930         return _.isFunction(formatUser) ? formatUser(d) : d;
21931     }
21932
21933
21934     function mergeLocation(remote, target) {
21935         function pointEqual(a, b) {
21936             var epsilon = 1e-6;
21937             return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon);
21938         }
21939
21940         if (option === 'force_local' || pointEqual(target.loc, remote.loc)) {
21941             return target;
21942         }
21943         if (option === 'force_remote') {
21944             return target.update({loc: remote.loc});
21945         }
21946
21947         conflicts.push(t('merge_remote_changes.conflict.location', { user: user(remote.user) }));
21948         return target;
21949     }
21950
21951
21952     function mergeNodes(base, remote, target) {
21953         if (option === 'force_local' || _.isEqual(target.nodes, remote.nodes)) {
21954             return target;
21955         }
21956         if (option === 'force_remote') {
21957             return target.update({nodes: remote.nodes});
21958         }
21959
21960         var ccount = conflicts.length,
21961             o = base.nodes || [],
21962             a = target.nodes || [],
21963             b = remote.nodes || [],
21964             nodes = [],
21965             hunks = Diff3.diff3_merge(a, o, b, true);
21966
21967         for (var i = 0; i < hunks.length; i++) {
21968             var hunk = hunks[i];
21969             if (hunk.ok) {
21970                 nodes.push.apply(nodes, hunk.ok);
21971             } else {
21972                 // for all conflicts, we can assume c.a !== c.b
21973                 // because `diff3_merge` called with `true` option to exclude false conflicts..
21974                 var c = hunk.conflict;
21975                 if (_.isEqual(c.o, c.a)) {  // only changed remotely
21976                     nodes.push.apply(nodes, c.b);
21977                 } else if (_.isEqual(c.o, c.b)) {  // only changed locally
21978                     nodes.push.apply(nodes, c.a);
21979                 } else {       // changed both locally and remotely
21980                     conflicts.push(t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) }));
21981                     break;
21982                 }
21983             }
21984         }
21985
21986         return (conflicts.length === ccount) ? target.update({nodes: nodes}) : target;
21987     }
21988
21989
21990     function mergeChildren(targetWay, children, updates, graph) {
21991         function isUsed(node, targetWay) {
21992             var parentWays = _.pluck(graph.parentWays(node), 'id');
21993             return node.hasInterestingTags() ||
21994                 _.without(parentWays, targetWay.id).length > 0 ||
21995                 graph.parentRelations(node).length > 0;
21996         }
21997
21998         var ccount = conflicts.length;
21999
22000         for (var i = 0; i < children.length; i++) {
22001             var id = children[i],
22002                 node = graph.hasEntity(id);
22003
22004             // remove unused childNodes..
22005             if (targetWay.nodes.indexOf(id) === -1) {
22006                 if (node && !isUsed(node, targetWay)) {
22007                     updates.removeIds.push(id);
22008                 }
22009                 continue;
22010             }
22011
22012             // restore used childNodes..
22013             var local = localGraph.hasEntity(id),
22014                 remote = remoteGraph.hasEntity(id),
22015                 target;
22016
22017             if (option === 'force_remote' && remote && remote.visible) {
22018                 updates.replacements.push(remote);
22019
22020             } else if (option === 'force_local' && local) {
22021                 target = iD.Entity(local);
22022                 if (remote) {
22023                     target = target.update({ version: remote.version });
22024                 }
22025                 updates.replacements.push(target);
22026
22027             } else if (option === 'safe' && local && remote && local.version !== remote.version) {
22028                 target = iD.Entity(local, { version: remote.version });
22029                 if (remote.visible) {
22030                     target = mergeLocation(remote, target);
22031                 } else {
22032                     conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
22033                 }
22034
22035                 if (conflicts.length !== ccount) break;
22036                 updates.replacements.push(target);
22037             }
22038         }
22039
22040         return targetWay;
22041     }
22042
22043
22044     function updateChildren(updates, graph) {
22045         for (var i = 0; i < updates.replacements.length; i++) {
22046             graph = graph.replace(updates.replacements[i]);
22047         }
22048         if (updates.removeIds.length) {
22049             graph = iD.actions.DeleteMultiple(updates.removeIds)(graph);
22050         }
22051         return graph;
22052     }
22053
22054
22055     function mergeMembers(remote, target) {
22056         if (option === 'force_local' || _.isEqual(target.members, remote.members)) {
22057             return target;
22058         }
22059         if (option === 'force_remote') {
22060             return target.update({members: remote.members});
22061         }
22062
22063         conflicts.push(t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) }));
22064         return target;
22065     }
22066
22067
22068     function mergeTags(base, remote, target) {
22069         function ignoreKey(k) {
22070             return _.contains(iD.data.discarded, k);
22071         }
22072
22073         if (option === 'force_local' || _.isEqual(target.tags, remote.tags)) {
22074             return target;
22075         }
22076         if (option === 'force_remote') {
22077             return target.update({tags: remote.tags});
22078         }
22079
22080         var ccount = conflicts.length,
22081             o = base.tags || {},
22082             a = target.tags || {},
22083             b = remote.tags || {},
22084             keys = _.reject(_.union(_.keys(o), _.keys(a), _.keys(b)), ignoreKey),
22085             tags = _.clone(a),
22086             changed = false;
22087
22088         for (var i = 0; i < keys.length; i++) {
22089             var k = keys[i];
22090
22091             if (o[k] !== b[k] && a[k] !== b[k]) {    // changed remotely..
22092                 if (o[k] !== a[k]) {      // changed locally..
22093                     conflicts.push(t('merge_remote_changes.conflict.tags',
22094                         { tag: k, local: a[k], remote: b[k], user: user(remote.user) }));
22095
22096                 } else {                  // unchanged locally, accept remote change..
22097                     if (b.hasOwnProperty(k)) {
22098                         tags[k] = b[k];
22099                     } else {
22100                         delete tags[k];
22101                     }
22102                     changed = true;
22103                 }
22104             }
22105         }
22106
22107         return (changed && conflicts.length === ccount) ? target.update({tags: tags}) : target;
22108     }
22109
22110
22111     //  `graph.base()` is the common ancestor of the two graphs.
22112     //  `localGraph` contains user's edits up to saving
22113     //  `remoteGraph` contains remote edits to modified nodes
22114     //  `graph` must be a descendent of `localGraph` and may include
22115     //      some conflict resolution actions performed on it.
22116     //
22117     //                  --- ... --- `localGraph` -- ... -- `graph`
22118     //                 /
22119     //  `graph.base()` --- ... --- `remoteGraph`
22120     //
22121     var action = function(graph) {
22122         var updates = { replacements: [], removeIds: [] },
22123             base = graph.base().entities[id],
22124             local = localGraph.entity(id),
22125             remote = remoteGraph.entity(id),
22126             target = iD.Entity(local, { version: remote.version });
22127
22128         // delete/undelete
22129         if (!remote.visible) {
22130             if (option === 'force_remote') {
22131                 return iD.actions.DeleteMultiple([id])(graph);
22132
22133             } else if (option === 'force_local') {
22134                 if (target.type === 'way') {
22135                     target = mergeChildren(target, _.uniq(local.nodes), updates, graph);
22136                     graph = updateChildren(updates, graph);
22137                 }
22138                 return graph.replace(target);
22139
22140             } else {
22141                 conflicts.push(t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
22142                 return graph;  // do nothing
22143             }
22144         }
22145
22146         // merge
22147         if (target.type === 'node') {
22148             target = mergeLocation(remote, target);
22149
22150         } else if (target.type === 'way') {
22151             // pull in any child nodes that may not be present locally..
22152             graph.rebase(remoteGraph.childNodes(remote), [graph], false);
22153             target = mergeNodes(base, remote, target);
22154             target = mergeChildren(target, _.union(local.nodes, remote.nodes), updates, graph);
22155
22156         } else if (target.type === 'relation') {
22157             target = mergeMembers(remote, target);
22158         }
22159
22160         target = mergeTags(base, remote, target);
22161
22162         if (!conflicts.length) {
22163             graph = updateChildren(updates, graph).replace(target);
22164         }
22165
22166         return graph;
22167     };
22168
22169     action.withOption = function(opt) {
22170         option = opt;
22171         return action;
22172     };
22173
22174     action.conflicts = function() {
22175         return conflicts;
22176     };
22177
22178     return action;
22179 };
22180 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
22181 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
22182 iD.actions.Move = function(moveIds, tryDelta, projection, cache) {
22183     var delta = tryDelta;
22184
22185     function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; }
22186     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
22187
22188     function setupCache(graph) {
22189         function canMove(nodeId) {
22190             var parents = _.pluck(graph.parentWays(graph.entity(nodeId)), 'id');
22191             if (parents.length < 3) return true;
22192
22193             // Don't move a vertex where >2 ways meet, unless all parentWays are moving too..
22194             var parentsMoving = _.all(parents, function(id) { return cache.moving[id]; });
22195             if (!parentsMoving) delete cache.moving[nodeId];
22196
22197             return parentsMoving;
22198         }
22199
22200         function cacheEntities(ids) {
22201             _.each(ids, function(id) {
22202                 if (cache.moving[id]) return;
22203                 cache.moving[id] = true;
22204
22205                 var entity = graph.hasEntity(id);
22206                 if (!entity) return;
22207
22208                 if (entity.type === 'node') {
22209                     cache.nodes.push(id);
22210                     cache.startLoc[id] = entity.loc;
22211                 } else if (entity.type === 'way') {
22212                     cache.ways.push(id);
22213                     cacheEntities(entity.nodes);
22214                 } else {
22215                     cacheEntities(_.pluck(entity.members, 'id'));
22216                 }
22217             });
22218         }
22219
22220         function cacheIntersections(ids) {
22221             function isEndpoint(way, id) { return !way.isClosed() && !!way.affix(id); }
22222
22223             _.each(ids, function(id) {
22224                 // consider only intersections with 1 moved and 1 unmoved way.
22225                 _.each(graph.childNodes(graph.entity(id)), function(node) {
22226                     var parents = graph.parentWays(node);
22227                     if (parents.length !== 2) return;
22228
22229                     var moved = graph.entity(id),
22230                         unmoved = _.find(parents, function(way) { return !cache.moving[way.id]; });
22231                     if (!unmoved) return;
22232
22233                     // exclude ways that are overly connected..
22234                     if (_.intersection(moved.nodes, unmoved.nodes).length > 2) return;
22235
22236                     if (moved.isArea() || unmoved.isArea()) return;
22237
22238                     cache.intersection[node.id] = {
22239                         nodeId: node.id,
22240                         movedId: moved.id,
22241                         unmovedId: unmoved.id,
22242                         movedIsEP: isEndpoint(moved, node.id),
22243                         unmovedIsEP: isEndpoint(unmoved, node.id)
22244                     };
22245                 });
22246             });
22247         }
22248
22249
22250         if (!cache) {
22251             cache = {};
22252         }
22253         if (!cache.ok) {
22254             cache.moving = {};
22255             cache.intersection = {};
22256             cache.replacedVertex = {};
22257             cache.startLoc = {};
22258             cache.nodes = [];
22259             cache.ways = [];
22260
22261             cacheEntities(moveIds);
22262             cacheIntersections(cache.ways);
22263             cache.nodes = _.filter(cache.nodes, canMove);
22264
22265             cache.ok = true;
22266         }
22267     }
22268
22269
22270     // Place a vertex where the moved vertex used to be, to preserve way shape..
22271     function replaceMovedVertex(nodeId, wayId, graph, delta) {
22272         var way = graph.entity(wayId),
22273             moved = graph.entity(nodeId),
22274             movedIndex = way.nodes.indexOf(nodeId),
22275             len, prevIndex, nextIndex;
22276
22277         if (way.isClosed()) {
22278             len = way.nodes.length - 1;
22279             prevIndex = (movedIndex + len - 1) % len;
22280             nextIndex = (movedIndex + len + 1) % len;
22281         } else {
22282             len = way.nodes.length;
22283             prevIndex = movedIndex - 1;
22284             nextIndex = movedIndex + 1;
22285         }
22286
22287         var prev = graph.hasEntity(way.nodes[prevIndex]),
22288             next = graph.hasEntity(way.nodes[nextIndex]);
22289
22290         // Don't add orig vertex at endpoint..
22291         if (!prev || !next) return graph;
22292
22293         var key = wayId + '_' + nodeId,
22294             orig = cache.replacedVertex[key];
22295         if (!orig) {
22296             orig = iD.Node();
22297             cache.replacedVertex[key] = orig;
22298             cache.startLoc[orig.id] = cache.startLoc[nodeId];
22299         }
22300
22301         var start, end;
22302         if (delta) {
22303             start = projection(cache.startLoc[nodeId]);
22304             end = projection.invert(vecAdd(start, delta));
22305         } else {
22306             end = cache.startLoc[nodeId];
22307         }
22308         orig = orig.move(end);
22309
22310         var angle = Math.abs(iD.geo.angle(orig, prev, projection) -
22311                 iD.geo.angle(orig, next, projection)) * 180 / Math.PI;
22312
22313         // Don't add orig vertex if it would just make a straight line..
22314         if (angle > 175 && angle < 185) return graph;
22315
22316         // Don't add orig vertex if another point is already nearby (within 10m)
22317         if (iD.geo.sphericalDistance(prev.loc, orig.loc) < 10 ||
22318             iD.geo.sphericalDistance(orig.loc, next.loc) < 10) return graph;
22319
22320         // moving forward or backward along way?
22321         var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection),
22322             p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection),
22323             d1 = iD.geo.pathLength(p1),
22324             d2 = iD.geo.pathLength(p2),
22325             insertAt = (d1 < d2) ? movedIndex : nextIndex;
22326
22327         // moving around closed loop?
22328         if (way.isClosed() && insertAt === 0) insertAt = len;
22329
22330         way = way.addNode(orig.id, insertAt);
22331         return graph.replace(orig).replace(way);
22332     }
22333
22334     // Reorder nodes around intersections that have moved..
22335     function unZorroIntersection(intersection, graph) {
22336         var vertex = graph.entity(intersection.nodeId),
22337             way1 = graph.entity(intersection.movedId),
22338             way2 = graph.entity(intersection.unmovedId),
22339             isEP1 = intersection.movedIsEP,
22340             isEP2 = intersection.unmovedIsEP;
22341
22342         // don't move the vertex if it is the endpoint of both ways.
22343         if (isEP1 && isEP2) return graph;
22344
22345         var nodes1 = _.without(graph.childNodes(way1), vertex),
22346             nodes2 = _.without(graph.childNodes(way2), vertex);
22347
22348         if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
22349         if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
22350
22351         var edge1 = !isEP1 && iD.geo.chooseEdge(nodes1, projection(vertex.loc), projection),
22352             edge2 = !isEP2 && iD.geo.chooseEdge(nodes2, projection(vertex.loc), projection),
22353             loc;
22354
22355         // snap vertex to nearest edge (or some point between them)..
22356         if (!isEP1 && !isEP2) {
22357             var epsilon = 1e-4, maxIter = 10;
22358             for (var i = 0; i < maxIter; i++) {
22359                 loc = iD.geo.interp(edge1.loc, edge2.loc, 0.5);
22360                 edge1 = iD.geo.chooseEdge(nodes1, projection(loc), projection);
22361                 edge2 = iD.geo.chooseEdge(nodes2, projection(loc), projection);
22362                 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
22363             }
22364         } else if (!isEP1) {
22365             loc = edge1.loc;
22366         } else {
22367             loc = edge2.loc;
22368         }
22369
22370         graph = graph.replace(vertex.move(loc));
22371
22372         // if zorro happened, reorder nodes..
22373         if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
22374             way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
22375             graph = graph.replace(way1);
22376         }
22377         if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
22378             way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
22379             graph = graph.replace(way2);
22380         }
22381
22382         return graph;
22383     }
22384
22385
22386     function cleanupIntersections(graph) {
22387         _.each(cache.intersection, function(obj) {
22388             graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, delta);
22389             graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
22390             graph = unZorroIntersection(obj, graph);
22391         });
22392
22393         return graph;
22394     }
22395
22396     // check if moving way endpoint can cross an unmoved way, if so limit delta..
22397     function limitDelta(graph) {
22398         _.each(cache.intersection, function(obj) {
22399             if (!obj.movedIsEP) return;
22400
22401             var node = graph.entity(obj.nodeId),
22402                 start = projection(node.loc),
22403                 end = vecAdd(start, delta),
22404                 movedNodes = graph.childNodes(graph.entity(obj.movedId)),
22405                 movedPath = _.map(_.pluck(movedNodes, 'loc'),
22406                     function(loc) { return vecAdd(projection(loc), delta); }),
22407                 unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId)),
22408                 unmovedPath = _.map(_.pluck(unmovedNodes, 'loc'), projection),
22409                 hits = iD.geo.pathIntersections(movedPath, unmovedPath);
22410
22411             for (var i = 0; i < hits.length; i++) {
22412                 if (_.isEqual(hits[i], end)) continue;
22413                 var edge = iD.geo.chooseEdge(unmovedNodes, end, projection);
22414                 delta = vecSub(projection(edge.loc), start);
22415             }
22416         });
22417     }
22418
22419
22420     var action = function(graph) {
22421         if (delta[0] === 0 && delta[1] === 0) return graph;
22422
22423         setupCache(graph);
22424
22425         if (!_.isEmpty(cache.intersection)) {
22426             limitDelta(graph);
22427         }
22428
22429         _.each(cache.nodes, function(id) {
22430             var node = graph.entity(id),
22431                 start = projection(node.loc),
22432                 end = vecAdd(start, delta);
22433             graph = graph.replace(node.move(projection.invert(end)));
22434         });
22435
22436         if (!_.isEmpty(cache.intersection)) {
22437             graph = cleanupIntersections(graph);
22438         }
22439
22440         return graph;
22441     };
22442
22443     action.disabled = function(graph) {
22444         function incompleteRelation(id) {
22445             var entity = graph.entity(id);
22446             return entity.type === 'relation' && !entity.isComplete(graph);
22447         }
22448
22449         if (_.any(moveIds, incompleteRelation))
22450             return 'incomplete_relation';
22451     };
22452
22453     action.delta = function() {
22454         return delta;
22455     };
22456
22457     return action;
22458 };
22459 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
22460 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
22461 iD.actions.MoveNode = function(nodeId, loc) {
22462     return function(graph) {
22463         return graph.replace(graph.entity(nodeId).move(loc));
22464     };
22465 };
22466 iD.actions.Noop = function() {
22467     return function(graph) {
22468         return graph;
22469     };
22470 };
22471 /*
22472  * Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
22473  */
22474
22475 iD.actions.Orthogonalize = function(wayId, projection) {
22476     var threshold = 12, // degrees within right or straight to alter
22477         lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180),
22478         upperThreshold = Math.cos(threshold * Math.PI / 180);
22479
22480     var action = function(graph) {
22481         var way = graph.entity(wayId),
22482             nodes = graph.childNodes(way),
22483             points = _.uniq(nodes).map(function(n) { return projection(n.loc); }),
22484             corner = {i: 0, dotp: 1},
22485             epsilon = 1e-4,
22486             i, j, score, motions;
22487
22488         if (nodes.length === 4) {
22489             for (i = 0; i < 1000; i++) {
22490                 motions = points.map(calcMotion);
22491                 points[corner.i] = addPoints(points[corner.i],motions[corner.i]);
22492                 score = corner.dotp;
22493                 if (score < epsilon) {
22494                     break;
22495                 }
22496             }
22497
22498             graph = graph.replace(graph.entity(nodes[corner.i].id)
22499                 .move(projection.invert(points[corner.i])));
22500         } else {
22501             var best,
22502                 originalPoints = _.clone(points);
22503             score = Infinity;
22504
22505             for (i = 0; i < 1000; i++) {
22506                 motions = points.map(calcMotion);
22507                 for (j = 0; j < motions.length; j++) {
22508                     points[j] = addPoints(points[j],motions[j]);
22509                 }
22510                 var newScore = squareness(points);
22511                 if (newScore < score) {
22512                     best = _.clone(points);
22513                     score = newScore;
22514                 }
22515                 if (score < epsilon) {
22516                     break;
22517                 }
22518             }
22519
22520             points = best;
22521
22522             for (i = 0; i < points.length; i++) {
22523                 // only move the points that actually moved
22524                 if (originalPoints[i][0] !== points[i][0] || originalPoints[i][1] !== points[i][1]) {
22525                     graph = graph.replace(graph.entity(nodes[i].id)
22526                         .move(projection.invert(points[i])));
22527                 }
22528             }
22529
22530             // remove empty nodes on straight sections
22531             for (i = 0; i < points.length; i++) {
22532                 var node = nodes[i];
22533
22534                 if (graph.parentWays(node).length > 1 ||
22535                     graph.parentRelations(node).length ||
22536                     node.hasInterestingTags()) {
22537
22538                     continue;
22539                 }
22540
22541                 var dotp = normalizedDotProduct(i, points);
22542                 if (dotp < -1 + epsilon) {
22543                     graph = iD.actions.DeleteNode(nodes[i].id)(graph);
22544                 }
22545             }
22546         }
22547
22548         return graph;
22549
22550         function calcMotion(b, i, array) {
22551             var a = array[(i - 1 + array.length) % array.length],
22552                 c = array[(i + 1) % array.length],
22553                 p = subtractPoints(a, b),
22554                 q = subtractPoints(c, b),
22555                 scale, dotp;
22556
22557             scale = 2 * Math.min(iD.geo.euclideanDistance(p, [0, 0]), iD.geo.euclideanDistance(q, [0, 0]));
22558             p = normalizePoint(p, 1.0);
22559             q = normalizePoint(q, 1.0);
22560
22561             dotp = filterDotProduct(p[0] * q[0] + p[1] * q[1]);
22562
22563             // nasty hack to deal with almost-straight segments (angle is closer to 180 than to 90/270).
22564             if (array.length > 3) {
22565                 if (dotp < -0.707106781186547) {
22566                     dotp += 1.0;
22567                 }
22568             } else if (dotp && Math.abs(dotp) < corner.dotp) {
22569                 corner.i = i;
22570                 corner.dotp = Math.abs(dotp);
22571             }
22572
22573             return normalizePoint(addPoints(p, q), 0.1 * dotp * scale);
22574         }
22575     };
22576
22577     function squareness(points) {
22578         return points.reduce(function(sum, val, i, array) {
22579             var dotp = normalizedDotProduct(i, array);
22580
22581             dotp = filterDotProduct(dotp);
22582             return sum + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
22583         }, 0);
22584     }
22585
22586     function normalizedDotProduct(i, points) {
22587         var a = points[(i - 1 + points.length) % points.length],
22588             b = points[i],
22589             c = points[(i + 1) % points.length],
22590             p = subtractPoints(a, b),
22591             q = subtractPoints(c, b);
22592
22593         p = normalizePoint(p, 1.0);
22594         q = normalizePoint(q, 1.0);
22595
22596         return p[0] * q[0] + p[1] * q[1];
22597     }
22598
22599     function subtractPoints(a, b) {
22600         return [a[0] - b[0], a[1] - b[1]];
22601     }
22602
22603     function addPoints(a, b) {
22604         return [a[0] + b[0], a[1] + b[1]];
22605     }
22606
22607     function normalizePoint(point, scale) {
22608         var vector = [0, 0];
22609         var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]);
22610         if (length !== 0) {
22611             vector[0] = point[0] / length;
22612             vector[1] = point[1] / length;
22613         }
22614
22615         vector[0] *= scale;
22616         vector[1] *= scale;
22617
22618         return vector;
22619     }
22620
22621     function filterDotProduct(dotp) {
22622         if (lowerThreshold > Math.abs(dotp) || Math.abs(dotp) > upperThreshold) {
22623             return dotp;
22624         }
22625
22626         return 0;
22627     }
22628
22629     action.disabled = function(graph) {
22630         var way = graph.entity(wayId),
22631             nodes = graph.childNodes(way),
22632             points = _.uniq(nodes).map(function(n) { return projection(n.loc); });
22633
22634         if (squareness(points)) {
22635             return false;
22636         }
22637
22638         return 'not_squarish';
22639     };
22640
22641     return action;
22642 };
22643 // Create a restriction relation for `turn`, which must have the following structure:
22644 //
22645 //     {
22646 //         from: { node: <node ID>, way: <way ID> },
22647 //         via:  { node: <node ID> },
22648 //         to:   { node: <node ID>, way: <way ID> },
22649 //         restriction: <'no_right_turn', 'no_left_turn', etc.>
22650 //     }
22651 //
22652 // This specifies a restriction of type `restriction` when traveling from
22653 // `from.node` in `from.way` toward `to.node` in `to.way` via `via.node`.
22654 // (The action does not check that these entities form a valid intersection.)
22655 //
22656 // If `restriction` is not provided, it is automatically determined by
22657 // iD.geo.inferRestriction.
22658 //
22659 // If necessary, the `from` and `to` ways are split. In these cases, `from.node`
22660 // and `to.node` are used to determine which portion of the split ways become
22661 // members of the restriction.
22662 //
22663 // For testing convenience, accepts an ID to assign to the new relation.
22664 // Normally, this will be undefined and the relation will automatically
22665 // be assigned a new ID.
22666 //
22667 iD.actions.RestrictTurn = function(turn, projection, restrictionId) {
22668     return function(graph) {
22669         var from = graph.entity(turn.from.way),
22670             via  = graph.entity(turn.via.node),
22671             to   = graph.entity(turn.to.way);
22672
22673         function isClosingNode(way, nodeId) {
22674             return nodeId === way.first() && nodeId === way.last();
22675         }
22676
22677         function split(toOrFrom) {
22678             var newID = toOrFrom.newID || iD.Way().id;
22679             graph = iD.actions.Split(via.id, [newID])
22680                 .limitWays([toOrFrom.way])(graph);
22681
22682             var a = graph.entity(newID),
22683                 b = graph.entity(toOrFrom.way);
22684
22685             if (a.nodes.indexOf(toOrFrom.node) !== -1) {
22686                 return [a, b];
22687             } else {
22688                 return [b, a];
22689             }
22690         }
22691
22692         if (!from.affix(via.id) || isClosingNode(from, via.id)) {
22693             if (turn.from.node === turn.to.node) {
22694                 // U-turn
22695                 from = to = split(turn.from)[0];
22696             } else if (turn.from.way === turn.to.way) {
22697                 // Straight-on or circular
22698                 var s = split(turn.from);
22699                 from = s[0];
22700                 to   = s[1];
22701             } else {
22702                 // Other
22703                 from = split(turn.from)[0];
22704             }
22705         }
22706
22707         if (!to.affix(via.id) || isClosingNode(to, via.id)) {
22708             to = split(turn.to)[0];
22709         }
22710
22711         return graph.replace(iD.Relation({
22712             id: restrictionId,
22713             tags: {
22714                 type: 'restriction',
22715                 restriction: turn.restriction ||
22716                     iD.geo.inferRestriction(
22717                         graph,
22718                         turn.from,
22719                         turn.via,
22720                         turn.to,
22721                         projection)
22722             },
22723             members: [
22724                 {id: from.id, type: 'way',  role: 'from'},
22725                 {id: via.id,  type: 'node', role: 'via'},
22726                 {id: to.id,   type: 'way',  role: 'to'}
22727             ]
22728         }));
22729     };
22730 };
22731 /*
22732   Order the nodes of a way in reverse order and reverse any direction dependent tags
22733   other than `oneway`. (We assume that correcting a backwards oneway is the primary
22734   reason for reversing a way.)
22735
22736   The following transforms are performed:
22737
22738     Keys:
22739           *:right=* ⟺ *:left=*
22740         *:forward=* ⟺ *:backward=*
22741        direction=up ⟺ direction=down
22742          incline=up ⟺ incline=down
22743             *=right ⟺ *=left
22744
22745     Relation members:
22746        role=forward ⟺ role=backward
22747          role=north ⟺ role=south
22748           role=east ⟺ role=west
22749
22750    In addition, numeric-valued `incline` tags are negated.
22751
22752    The JOSM implementation was used as a guide, but transformations that were of unclear benefit
22753    or adjusted tags that don't seem to be used in practice were omitted.
22754
22755    References:
22756       http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
22757       http://wiki.openstreetmap.org/wiki/Key:direction#Steps
22758       http://wiki.openstreetmap.org/wiki/Key:incline
22759       http://wiki.openstreetmap.org/wiki/Route#Members
22760       http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
22761  */
22762 iD.actions.Reverse = function(wayId, options) {
22763     var replacements = [
22764             [/:right$/, ':left'], [/:left$/, ':right'],
22765             [/:forward$/, ':backward'], [/:backward$/, ':forward']
22766         ],
22767         numeric = /^([+\-]?)(?=[\d.])/,
22768         roleReversals = {
22769             forward: 'backward',
22770             backward: 'forward',
22771             north: 'south',
22772             south: 'north',
22773             east: 'west',
22774             west: 'east'
22775         };
22776
22777     function reverseKey(key) {
22778         for (var i = 0; i < replacements.length; ++i) {
22779             var replacement = replacements[i];
22780             if (replacement[0].test(key)) {
22781                 return key.replace(replacement[0], replacement[1]);
22782             }
22783         }
22784         return key;
22785     }
22786
22787     function reverseValue(key, value) {
22788         if (key === 'incline' && numeric.test(value)) {
22789             return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
22790         } else if (key === 'incline' || key === 'direction') {
22791             return {up: 'down', down: 'up'}[value] || value;
22792         } else if (options && options.reverseOneway && key === 'oneway') {
22793             return {yes: '-1', '1': '-1', '-1': 'yes'}[value] || value;
22794         } else {
22795             return {left: 'right', right: 'left'}[value] || value;
22796         }
22797     }
22798
22799     return function(graph) {
22800         var way = graph.entity(wayId),
22801             nodes = way.nodes.slice().reverse(),
22802             tags = {}, key, role;
22803
22804         for (key in way.tags) {
22805             tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
22806         }
22807
22808         graph.parentRelations(way).forEach(function(relation) {
22809             relation.members.forEach(function(member, index) {
22810                 if (member.id === way.id && (role = roleReversals[member.role])) {
22811                     relation = relation.updateMember({role: role}, index);
22812                     graph = graph.replace(relation);
22813                 }
22814             });
22815         });
22816
22817         return graph.replace(way.update({nodes: nodes, tags: tags}));
22818     };
22819 };
22820 iD.actions.Revert = function(id) {
22821
22822     var action = function(graph) {
22823         var entity = graph.hasEntity(id),
22824             base = graph.base().entities[id];
22825
22826         if (entity && !base) {    // entity will be removed..
22827             if (entity.type === 'node') {
22828                 graph.parentWays(entity)
22829                     .forEach(function(parent) {
22830                         parent = parent.removeNode(id);
22831                         graph = graph.replace(parent);
22832
22833                         if (parent.isDegenerate()) {
22834                             graph = iD.actions.DeleteWay(parent.id)(graph);
22835                         }
22836                     });
22837             }
22838
22839             graph.parentRelations(entity)
22840                 .forEach(function(parent) {
22841                     parent = parent.removeMembersWithID(id);
22842                     graph = graph.replace(parent);
22843
22844                     if (parent.isDegenerate()) {
22845                         graph = iD.actions.DeleteRelation(parent.id)(graph);
22846                     }
22847                 });
22848         }
22849
22850         return graph.revert(id);
22851     };
22852
22853     return action;
22854 };
22855 iD.actions.RotateWay = function(wayId, pivot, angle, projection) {
22856     return function(graph) {
22857         return graph.update(function(graph) {
22858             var way = graph.entity(wayId);
22859
22860             _.unique(way.nodes).forEach(function(id) {
22861
22862                 var node = graph.entity(id),
22863                     point = projection(node.loc),
22864                     radial = [0,0];
22865
22866                 radial[0] = point[0] - pivot[0];
22867                 radial[1] = point[1] - pivot[1];
22868
22869                 point = [
22870                     radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + pivot[0],
22871                     radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + pivot[1]
22872                 ];
22873
22874                 graph = graph.replace(node.move(projection.invert(point)));
22875
22876             });
22877
22878         });
22879     };
22880 };
22881 // Split a way at the given node.
22882 //
22883 // Optionally, split only the given ways, if multiple ways share
22884 // the given node.
22885 //
22886 // This is the inverse of `iD.actions.Join`.
22887 //
22888 // For testing convenience, accepts an ID to assign to the new way.
22889 // Normally, this will be undefined and the way will automatically
22890 // be assigned a new ID.
22891 //
22892 // Reference:
22893 //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
22894 //
22895 iD.actions.Split = function(nodeId, newWayIds) {
22896     var wayIds;
22897
22898     // if the way is closed, we need to search for a partner node
22899     // to split the way at.
22900     //
22901     // The following looks for a node that is both far away from
22902     // the initial node in terms of way segment length and nearby
22903     // in terms of beeline-distance. This assures that areas get
22904     // split on the most "natural" points (independent of the number
22905     // of nodes).
22906     // For example: bone-shaped areas get split across their waist
22907     // line, circles across the diameter.
22908     function splitArea(nodes, idxA, graph) {
22909         var lengths = new Array(nodes.length),
22910             length,
22911             i,
22912             best = 0,
22913             idxB;
22914
22915         function wrap(index) {
22916             return iD.util.wrap(index, nodes.length);
22917         }
22918
22919         function dist(nA, nB) {
22920             return iD.geo.sphericalDistance(graph.entity(nA).loc, graph.entity(nB).loc);
22921         }
22922
22923         // calculate lengths
22924         length = 0;
22925         for (i = wrap(idxA+1); i !== idxA; i = wrap(i+1)) {
22926             length += dist(nodes[i], nodes[wrap(i-1)]);
22927             lengths[i] = length;
22928         }
22929
22930         length = 0;
22931         for (i = wrap(idxA-1); i !== idxA; i = wrap(i-1)) {
22932             length += dist(nodes[i], nodes[wrap(i+1)]);
22933             if (length < lengths[i])
22934                 lengths[i] = length;
22935         }
22936
22937         // determine best opposite node to split
22938         for (i = 0; i < nodes.length; i++) {
22939             var cost = lengths[i] / dist(nodes[idxA], nodes[i]);
22940             if (cost > best) {
22941                 idxB = i;
22942                 best = cost;
22943             }
22944         }
22945
22946         return idxB;
22947     }
22948
22949     function split(graph, wayA, newWayId) {
22950         var wayB = iD.Way({id: newWayId, tags: wayA.tags}),
22951             nodesA,
22952             nodesB,
22953             isArea = wayA.isArea(),
22954             isOuter = iD.geo.isSimpleMultipolygonOuterMember(wayA, graph);
22955
22956         if (wayA.isClosed()) {
22957             var nodes = wayA.nodes.slice(0, -1),
22958                 idxA = _.indexOf(nodes, nodeId),
22959                 idxB = splitArea(nodes, idxA, graph);
22960
22961             if (idxB < idxA) {
22962                 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
22963                 nodesB = nodes.slice(idxB, idxA + 1);
22964             } else {
22965                 nodesA = nodes.slice(idxA, idxB + 1);
22966                 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
22967             }
22968         } else {
22969             var idx = _.indexOf(wayA.nodes, nodeId, 1);
22970             nodesA = wayA.nodes.slice(0, idx + 1);
22971             nodesB = wayA.nodes.slice(idx);
22972         }
22973
22974         wayA = wayA.update({nodes: nodesA});
22975         wayB = wayB.update({nodes: nodesB});
22976
22977         graph = graph.replace(wayA);
22978         graph = graph.replace(wayB);
22979
22980         graph.parentRelations(wayA).forEach(function(relation) {
22981             if (relation.isRestriction()) {
22982                 var via = relation.memberByRole('via');
22983                 if (via && wayB.contains(via.id)) {
22984                     relation = relation.updateMember({id: wayB.id}, relation.memberById(wayA.id).index);
22985                     graph = graph.replace(relation);
22986                 }
22987             } else {
22988                 if (relation === isOuter) {
22989                     graph = graph.replace(relation.mergeTags(wayA.tags));
22990                     graph = graph.replace(wayA.update({tags: {}}));
22991                     graph = graph.replace(wayB.update({tags: {}}));
22992                 }
22993
22994                 var member = {
22995                     id: wayB.id,
22996                     type: 'way',
22997                     role: relation.memberById(wayA.id).role
22998                 };
22999
23000                 graph = iD.actions.AddMember(relation.id, member)(graph);
23001             }
23002         });
23003
23004         if (!isOuter && isArea) {
23005             var multipolygon = iD.Relation({
23006                 tags: _.extend({}, wayA.tags, {type: 'multipolygon'}),
23007                 members: [
23008                     {id: wayA.id, role: 'outer', type: 'way'},
23009                     {id: wayB.id, role: 'outer', type: 'way'}
23010                 ]});
23011
23012             graph = graph.replace(multipolygon);
23013             graph = graph.replace(wayA.update({tags: {}}));
23014             graph = graph.replace(wayB.update({tags: {}}));
23015         }
23016
23017         return graph;
23018     }
23019
23020     var action = function(graph) {
23021         var candidates = action.ways(graph);
23022         for (var i = 0; i < candidates.length; i++) {
23023             graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
23024         }
23025         return graph;
23026     };
23027
23028     action.ways = function(graph) {
23029         var node = graph.entity(nodeId),
23030             parents = graph.parentWays(node),
23031             hasLines = _.any(parents, function(parent) { return parent.geometry(graph) === 'line'; });
23032
23033         return parents.filter(function(parent) {
23034             if (wayIds && wayIds.indexOf(parent.id) === -1)
23035                 return false;
23036
23037             if (!wayIds && hasLines && parent.geometry(graph) !== 'line')
23038                 return false;
23039
23040             if (parent.isClosed()) {
23041                 return true;
23042             }
23043
23044             for (var i = 1; i < parent.nodes.length - 1; i++) {
23045                 if (parent.nodes[i] === nodeId) {
23046                     return true;
23047                 }
23048             }
23049
23050             return false;
23051         });
23052     };
23053
23054     action.disabled = function(graph) {
23055         var candidates = action.ways(graph);
23056         if (candidates.length === 0 || (wayIds && wayIds.length !== candidates.length))
23057             return 'not_eligible';
23058     };
23059
23060     action.limitWays = function(_) {
23061         if (!arguments.length) return wayIds;
23062         wayIds = _;
23063         return action;
23064     };
23065
23066     return action;
23067 };
23068 /*
23069  * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
23070  */
23071
23072 iD.actions.Straighten = function(wayId, projection) {
23073     function positionAlongWay(n, s, e) {
23074         return ((n[0] - s[0]) * (e[0] - s[0]) + (n[1] - s[1]) * (e[1] - s[1]))/
23075                 (Math.pow(e[0] - s[0], 2) + Math.pow(e[1] - s[1], 2));
23076     }
23077
23078     var action = function(graph) {
23079         var way = graph.entity(wayId),
23080             nodes = graph.childNodes(way),
23081             points = nodes.map(function(n) { return projection(n.loc); }),
23082             startPoint = points[0],
23083             endPoint = points[points.length-1],
23084             toDelete = [],
23085             i;
23086
23087         for (i = 1; i < points.length-1; i++) {
23088             var node = nodes[i],
23089                 point = points[i];
23090
23091             if (graph.parentWays(node).length > 1 ||
23092                 graph.parentRelations(node).length ||
23093                 node.hasInterestingTags()) {
23094
23095                 var u = positionAlongWay(point, startPoint, endPoint),
23096                     p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
23097                     p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]);
23098
23099                 graph = graph.replace(graph.entity(node.id)
23100                     .move(projection.invert([p0, p1])));
23101             } else {
23102                 // safe to delete
23103                 if (toDelete.indexOf(node) === -1) {
23104                     toDelete.push(node);
23105                 }
23106             }
23107         }
23108
23109         for (i = 0; i < toDelete.length; i++) {
23110             graph = iD.actions.DeleteNode(toDelete[i].id)(graph);
23111         }
23112
23113         return graph;
23114     };
23115
23116     action.disabled = function(graph) {
23117         // check way isn't too bendy
23118         var way = graph.entity(wayId),
23119             nodes = graph.childNodes(way),
23120             points = nodes.map(function(n) { return projection(n.loc); }),
23121             startPoint = points[0],
23122             endPoint = points[points.length-1],
23123             threshold = 0.2 * Math.sqrt(Math.pow(startPoint[0] - endPoint[0], 2) + Math.pow(startPoint[1] - endPoint[1], 2)),
23124             i;
23125
23126         if (threshold === 0) {
23127             return 'too_bendy';
23128         }
23129
23130         for (i = 1; i < points.length-1; i++) {
23131             var point = points[i],
23132                 u = positionAlongWay(point, startPoint, endPoint),
23133                 p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
23134                 p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]),
23135                 dist = Math.sqrt(Math.pow(p0 - point[0], 2) + Math.pow(p1 - point[1], 2));
23136
23137             // to bendy if point is off by 20% of total start/end distance in projected space
23138             if (isNaN(dist) || dist > threshold) {
23139                 return 'too_bendy';
23140             }
23141         }
23142     };
23143
23144     return action;
23145 };
23146 // Remove the effects of `turn.restriction` on `turn`, which must have the
23147 // following structure:
23148 //
23149 //     {
23150 //         from: { node: <node ID>, way: <way ID> },
23151 //         via:  { node: <node ID> },
23152 //         to:   { node: <node ID>, way: <way ID> },
23153 //         restriction: <relation ID>
23154 //     }
23155 //
23156 // In the simple case, `restriction` is a reference to a `no_*` restriction
23157 // on the turn itself. In this case, it is simply deleted.
23158 //
23159 // The more complex case is where `restriction` references an `only_*`
23160 // restriction on a different turn in the same intersection. In that case,
23161 // that restriction is also deleted, but at the same time restrictions on
23162 // the turns other than the first two are created.
23163 //
23164 iD.actions.UnrestrictTurn = function(turn) {
23165     return function(graph) {
23166         return iD.actions.DeleteRelation(turn.restriction)(graph);
23167     };
23168 };
23169 iD.behavior = {};
23170 iD.behavior.AddWay = function(context) {
23171     var event = d3.dispatch('start', 'startFromWay', 'startFromNode'),
23172         draw = iD.behavior.Draw(context);
23173
23174     var addWay = function(surface) {
23175         draw.on('click', event.start)
23176             .on('clickWay', event.startFromWay)
23177             .on('clickNode', event.startFromNode)
23178             .on('cancel', addWay.cancel)
23179             .on('finish', addWay.cancel);
23180
23181         context.map()
23182             .dblclickEnable(false);
23183
23184         surface.call(draw);
23185     };
23186
23187     addWay.off = function(surface) {
23188         surface.call(draw.off);
23189     };
23190
23191     addWay.cancel = function() {
23192         window.setTimeout(function() {
23193             context.map().dblclickEnable(true);
23194         }, 1000);
23195
23196         context.enter(iD.modes.Browse(context));
23197     };
23198
23199     addWay.tail = function(text) {
23200         draw.tail(text);
23201         return addWay;
23202     };
23203
23204     return d3.rebind(addWay, event, 'on');
23205 };
23206 iD.behavior.Breathe = function() {
23207     var duration = 800,
23208         selector = '.selected.shadow, .selected .shadow',
23209         selected = d3.select(null),
23210         classed = '',
23211         params = {},
23212         done;
23213
23214     function reset(selection) {
23215         selection
23216             .style('stroke-opacity', null)
23217             .style('stroke-width', null)
23218             .style('fill-opacity', null)
23219             .style('r', null);
23220     }
23221
23222     function setAnimationParams(transition, fromTo) {
23223         transition
23224             .style('stroke-opacity', function(d) { return params[d.id][fromTo].opacity; })
23225             .style('stroke-width', function(d) { return params[d.id][fromTo].width; })
23226             .style('fill-opacity', function(d) { return params[d.id][fromTo].opacity; })
23227             .style('r', function(d) { return params[d.id][fromTo].width; });
23228     }
23229
23230     function calcAnimationParams(selection) {
23231         selection
23232             .call(reset)
23233             .each(function(d) {
23234                 var s = d3.select(this),
23235                     tag = s.node().tagName,
23236                     p = {'from': {}, 'to': {}},
23237                     opacity, width;
23238
23239                 // determine base opacity and width
23240                 if (tag === 'circle') {
23241                     opacity = parseFloat(s.style('fill-opacity') || 0.5);
23242                     width = parseFloat(s.style('r') || 15.5);
23243                 } else {
23244                     opacity = parseFloat(s.style('stroke-opacity') || 0.7);
23245                     width = parseFloat(s.style('stroke-width') || 10);
23246                 }
23247
23248                 // calculate from/to interpolation params..
23249                 p.tag = tag;
23250                 p.from.opacity = opacity * 0.6;
23251                 p.to.opacity = opacity * 1.25;
23252                 p.from.width = width * 0.9;
23253                 p.to.width = width * (tag === 'circle' ? 1.5 : 1.25);
23254                 params[d.id] = p;
23255             });
23256     }
23257
23258     function run(surface, fromTo) {
23259         var toFrom = (fromTo === 'from' ? 'to': 'from'),
23260             currSelected = surface.selectAll(selector),
23261             currClassed = surface.attr('class'),
23262             n = 0;
23263
23264         if (done || currSelected.empty()) {
23265             selected.call(reset);
23266             return;
23267         }
23268
23269         if (!_.isEqual(currSelected, selected) || currClassed !== classed) {
23270             selected.call(reset);
23271             classed = currClassed;
23272             selected = currSelected.call(calcAnimationParams);
23273         }
23274
23275         selected
23276             .transition()
23277             .call(setAnimationParams, fromTo)
23278             .duration(duration)
23279             .each(function() { ++n; })
23280             .each('end', function() {
23281                 if (!--n) {  // call once
23282                     surface.call(run, toFrom);
23283                 }
23284             });
23285     }
23286
23287     var breathe = function(surface) {
23288         done = false;
23289         d3.timer(function() {
23290             if (done) return true;
23291
23292             var currSelected = surface.selectAll(selector);
23293             if (currSelected.empty()) return false;
23294
23295             surface.call(run, 'from');
23296             return true;
23297         }, 200);
23298     };
23299
23300     breathe.off = function() {
23301         done = true;
23302         d3.timer.flush();
23303         selected
23304             .transition()
23305             .call(reset)
23306             .duration(0);
23307     };
23308
23309     return breathe;
23310 };
23311 iD.behavior.Copy = function(context) {
23312     var keybinding = d3.keybinding('copy');
23313
23314     function groupEntities(ids, graph) {
23315         var entities = ids.map(function (id) { return graph.entity(id); });
23316         return _.extend({relation: [], way: [], node: []},
23317             _.groupBy(entities, function(entity) { return entity.type; }));
23318     }
23319
23320     function getDescendants(id, graph, descendants) {
23321         var entity = graph.entity(id),
23322             i, children;
23323
23324         descendants = descendants || {};
23325
23326         if (entity.type === 'relation') {
23327             children = _.pluck(entity.members, 'id');
23328         } else if (entity.type === 'way') {
23329             children = entity.nodes;
23330         } else {
23331             children = [];
23332         }
23333
23334         for (i = 0; i < children.length; i++) {
23335             if (!descendants[children[i]]) {
23336                 descendants[children[i]] = true;
23337                 descendants = getDescendants(children[i], graph, descendants);
23338             }
23339         }
23340
23341         return descendants;
23342     }
23343
23344     function doCopy() {
23345         d3.event.preventDefault();
23346         if (context.inIntro()) return;
23347
23348         var graph = context.graph(),
23349             selected = groupEntities(context.selectedIDs(), graph),
23350             canCopy = [],
23351             skip = {},
23352             i, entity;
23353
23354         for (i = 0; i < selected.relation.length; i++) {
23355             entity = selected.relation[i];
23356             if (!skip[entity.id] && entity.isComplete(graph)) {
23357                 canCopy.push(entity.id);
23358                 skip = getDescendants(entity.id, graph, skip);
23359             }
23360         }
23361         for (i = 0; i < selected.way.length; i++) {
23362             entity = selected.way[i];
23363             if (!skip[entity.id]) {
23364                 canCopy.push(entity.id);
23365                 skip = getDescendants(entity.id, graph, skip);
23366             }
23367         }
23368         for (i = 0; i < selected.node.length; i++) {
23369             entity = selected.node[i];
23370             if (!skip[entity.id]) {
23371                 canCopy.push(entity.id);
23372             }
23373         }
23374
23375         context.copyIDs(canCopy);
23376     }
23377
23378     function copy() {
23379         keybinding.on(iD.ui.cmd('⌘C'), doCopy);
23380         d3.select(document).call(keybinding);
23381         return copy;
23382     }
23383
23384     copy.off = function() {
23385         d3.select(document).call(keybinding.off);
23386     };
23387
23388     return copy;
23389 };
23390 /*
23391     `iD.behavior.drag` is like `d3.behavior.drag`, with the following differences:
23392
23393     * The `origin` function is expected to return an [x, y] tuple rather than an
23394       {x, y} object.
23395     * The events are `start`, `move`, and `end`.
23396       (https://github.com/mbostock/d3/issues/563)
23397     * The `start` event is not dispatched until the first cursor movement occurs.
23398       (https://github.com/mbostock/d3/pull/368)
23399     * The `move` event has a `point` and `delta` [x, y] tuple properties rather
23400       than `x`, `y`, `dx`, and `dy` properties.
23401     * The `end` event is not dispatched if no movement occurs.
23402     * An `off` function is available that unbinds the drag's internal event handlers.
23403     * Delegation is supported via the `delegate` function.
23404
23405  */
23406 iD.behavior.drag = function() {
23407     function d3_eventCancel() {
23408       d3.event.stopPropagation();
23409       d3.event.preventDefault();
23410     }
23411
23412     var event = d3.dispatch('start', 'move', 'end'),
23413         origin = null,
23414         selector = '',
23415         filter = null,
23416         event_, target, surface;
23417
23418     event.of = function(thiz, argumentz) {
23419       return function(e1) {
23420         var e0 = e1.sourceEvent = d3.event;
23421         e1.target = drag;
23422         d3.event = e1;
23423         try {
23424           event[e1.type].apply(thiz, argumentz);
23425         } finally {
23426           d3.event = e0;
23427         }
23428       };
23429     };
23430
23431     var d3_event_userSelectProperty = iD.util.prefixCSSProperty('UserSelect'),
23432         d3_event_userSelectSuppress = d3_event_userSelectProperty ?
23433             function () {
23434                 var selection = d3.selection(),
23435                     select = selection.style(d3_event_userSelectProperty);
23436                 selection.style(d3_event_userSelectProperty, 'none');
23437                 return function () {
23438                     selection.style(d3_event_userSelectProperty, select);
23439                 };
23440             } :
23441             function (type) {
23442                 var w = d3.select(window).on('selectstart.' + type, d3_eventCancel);
23443                 return function () {
23444                     w.on('selectstart.' + type, null);
23445                 };
23446             };
23447
23448     function mousedown() {
23449         target = this;
23450         event_ = event.of(target, arguments);
23451         var eventTarget = d3.event.target,
23452             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
23453             offset,
23454             origin_ = point(),
23455             started = false,
23456             selectEnable = d3_event_userSelectSuppress(touchId !== null ? 'drag-' + touchId : 'drag');
23457
23458         var w = d3.select(window)
23459             .on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', dragmove)
23460             .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', dragend, true);
23461
23462         if (origin) {
23463             offset = origin.apply(target, arguments);
23464             offset = [offset[0] - origin_[0], offset[1] - origin_[1]];
23465         } else {
23466             offset = [0, 0];
23467         }
23468
23469         if (touchId === null) d3.event.stopPropagation();
23470
23471         function point() {
23472             var p = target.parentNode || surface;
23473             return touchId !== null ? d3.touches(p).filter(function(p) {
23474                 return p.identifier === touchId;
23475             })[0] : d3.mouse(p);
23476         }
23477
23478         function dragmove() {
23479
23480             var p = point(),
23481                 dx = p[0] - origin_[0],
23482                 dy = p[1] - origin_[1];
23483             
23484             if (dx === 0 && dy === 0)
23485                 return;
23486
23487             if (!started) {
23488                 started = true;
23489                 event_({
23490                     type: 'start'
23491                 });
23492             }
23493
23494             origin_ = p;
23495             d3_eventCancel();
23496
23497             event_({
23498                 type: 'move',
23499                 point: [p[0] + offset[0],  p[1] + offset[1]],
23500                 delta: [dx, dy]
23501             });
23502         }
23503
23504         function dragend() {
23505             if (started) {
23506                 event_({
23507                     type: 'end'
23508                 });
23509
23510                 d3_eventCancel();
23511                 if (d3.event.target === eventTarget) w.on('click.drag', click, true);
23512             }
23513
23514             w.on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', null)
23515                 .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', null);
23516             selectEnable();
23517         }
23518
23519         function click() {
23520             d3_eventCancel();
23521             w.on('click.drag', null);
23522         }
23523     }
23524
23525     function drag(selection) {
23526         var matchesSelector = iD.util.prefixDOMProperty('matchesSelector'),
23527             delegate = mousedown;
23528
23529         if (selector) {
23530             delegate = function() {
23531                 var root = this,
23532                     target = d3.event.target;
23533                 for (; target && target !== root; target = target.parentNode) {
23534                     if (target[matchesSelector](selector) &&
23535                             (!filter || filter(target.__data__))) {
23536                         return mousedown.call(target, target.__data__);
23537                     }
23538                 }
23539             };
23540         }
23541
23542         selection.on('mousedown.drag' + selector, delegate)
23543             .on('touchstart.drag' + selector, delegate);
23544     }
23545
23546     drag.off = function(selection) {
23547         selection.on('mousedown.drag' + selector, null)
23548             .on('touchstart.drag' + selector, null);
23549     };
23550
23551     drag.delegate = function(_) {
23552         if (!arguments.length) return selector;
23553         selector = _;
23554         return drag;
23555     };
23556
23557     drag.filter = function(_) {
23558         if (!arguments.length) return origin;
23559         filter = _;
23560         return drag;
23561     };
23562
23563     drag.origin = function (_) {
23564         if (!arguments.length) return origin;
23565         origin = _;
23566         return drag;
23567     };
23568
23569     drag.cancel = function() {
23570         d3.select(window)
23571             .on('mousemove.drag', null)
23572             .on('mouseup.drag', null);
23573         return drag;
23574     };
23575
23576     drag.target = function() {
23577         if (!arguments.length) return target;
23578         target = arguments[0];
23579         event_ = event.of(target, Array.prototype.slice.call(arguments, 1));
23580         return drag;
23581     };
23582
23583     drag.surface = function() {
23584         if (!arguments.length) return surface;
23585         surface = arguments[0];
23586         return drag;
23587     };
23588
23589     return d3.rebind(drag, event, 'on');
23590 };
23591 iD.behavior.Draw = function(context) {
23592     var event = d3.dispatch('move', 'click', 'clickWay',
23593             'clickNode', 'undo', 'cancel', 'finish'),
23594         keybinding = d3.keybinding('draw'),
23595         hover = iD.behavior.Hover(context)
23596             .altDisables(true)
23597             .on('hover', context.ui().sidebar.hover),
23598         tail = iD.behavior.Tail(),
23599         edit = iD.behavior.Edit(context),
23600         closeTolerance = 4,
23601         tolerance = 12;
23602
23603     function datum() {
23604         if (d3.event.altKey) return {};
23605         else return d3.event.target.__data__ || {};
23606     }
23607
23608     function mousedown() {
23609
23610         function point() {
23611             var p = context.container().node();
23612             return touchId !== null ? d3.touches(p).filter(function(p) {
23613                 return p.identifier === touchId;
23614             })[0] : d3.mouse(p);
23615         }
23616
23617         var element = d3.select(this),
23618             touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
23619             t1 = +new Date(),
23620             p1 = point();
23621
23622         element.on('mousemove.draw', null);
23623
23624         d3.select(window).on('mouseup.draw', function() {
23625             var t2 = +new Date(),
23626                 p2 = point(),
23627                 dist = iD.geo.euclideanDistance(p1, p2);
23628
23629             element.on('mousemove.draw', mousemove);
23630             d3.select(window).on('mouseup.draw', null);
23631
23632             if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) {
23633                 // Prevent a quick second click
23634                 d3.select(window).on('click.draw-block', function() {
23635                     d3.event.stopPropagation();
23636                 }, true);
23637
23638                 context.map().dblclickEnable(false);
23639
23640                 window.setTimeout(function() {
23641                     context.map().dblclickEnable(true);
23642                     d3.select(window).on('click.draw-block', null);
23643                 }, 500);
23644
23645                 click();
23646             }
23647         });
23648     }
23649
23650     function mousemove() {
23651         event.move(datum());
23652     }
23653
23654     function click() {
23655         var d = datum();
23656         if (d.type === 'way') {
23657             var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection),
23658                 edge = [d.nodes[choice.index - 1], d.nodes[choice.index]];
23659             event.clickWay(choice.loc, edge);
23660
23661         } else if (d.type === 'node') {
23662             event.clickNode(d);
23663
23664         } else {
23665             event.click(context.map().mouseCoordinates());
23666         }
23667     }
23668
23669     function backspace() {
23670         d3.event.preventDefault();
23671         event.undo();
23672     }
23673
23674     function del() {
23675         d3.event.preventDefault();
23676         event.cancel();
23677     }
23678
23679     function ret() {
23680         d3.event.preventDefault();
23681         event.finish();
23682     }
23683
23684     function draw(selection) {
23685         context.install(hover);
23686         context.install(edit);
23687
23688         if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
23689             context.install(tail);
23690         }
23691
23692         keybinding
23693             .on('⌫', backspace)
23694             .on('⌦', del)
23695             .on('⎋', ret)
23696             .on('↩', ret);
23697
23698         selection
23699             .on('mousedown.draw', mousedown)
23700             .on('mousemove.draw', mousemove);
23701
23702         d3.select(document)
23703             .call(keybinding);
23704
23705         return draw;
23706     }
23707
23708     draw.off = function(selection) {
23709         context.ui().sidebar.hover.cancel();
23710         context.uninstall(hover);
23711         context.uninstall(edit);
23712
23713         if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
23714             context.uninstall(tail);
23715             iD.behavior.Draw.usedTails[tail.text()] = true;
23716         }
23717
23718         selection
23719             .on('mousedown.draw', null)
23720             .on('mousemove.draw', null);
23721
23722         d3.select(window)
23723             .on('mouseup.draw', null);
23724
23725         d3.select(document)
23726             .call(keybinding.off);
23727     };
23728
23729     draw.tail = function(_) {
23730         tail.text(_);
23731         return draw;
23732     };
23733
23734     return d3.rebind(draw, event, 'on');
23735 };
23736
23737 iD.behavior.Draw.usedTails = {};
23738 iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
23739     var way = context.entity(wayId),
23740         isArea = context.geometry(wayId) === 'area',
23741         finished = false,
23742         annotation = t((way.isDegenerate() ?
23743             'operations.start.annotation.' :
23744             'operations.continue.annotation.') + context.geometry(wayId)),
23745         draw = iD.behavior.Draw(context);
23746
23747     var startIndex = typeof index === 'undefined' ? way.nodes.length - 1 : 0,
23748         start = iD.Node({loc: context.graph().entity(way.nodes[startIndex]).loc}),
23749         end = iD.Node({loc: context.map().mouseCoordinates()}),
23750         segment = iD.Way({
23751             nodes: typeof index === 'undefined' ? [start.id, end.id] : [end.id, start.id],
23752             tags: _.clone(way.tags)
23753         });
23754
23755     var f = context[way.isDegenerate() ? 'replace' : 'perform'];
23756     if (isArea) {
23757         f(iD.actions.AddEntity(end),
23758             iD.actions.AddVertex(wayId, end.id, index));
23759     } else {
23760         f(iD.actions.AddEntity(start),
23761             iD.actions.AddEntity(end),
23762             iD.actions.AddEntity(segment));
23763     }
23764
23765     function move(datum) {
23766         var loc;
23767
23768         if (datum.type === 'node' && datum.id !== end.id) {
23769             loc = datum.loc;
23770         } else if (datum.type === 'way' && datum.id !== segment.id) {
23771             loc = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc;
23772         } else {
23773             loc = context.map().mouseCoordinates();
23774         }
23775
23776         context.replace(iD.actions.MoveNode(end.id, loc));
23777     }
23778
23779     function undone() {
23780         finished = true;
23781         context.enter(iD.modes.Browse(context));
23782     }
23783
23784     function setActiveElements() {
23785         var active = isArea ? [wayId, end.id] : [segment.id, start.id, end.id];
23786         context.surface().selectAll(iD.util.entitySelector(active))
23787             .classed('active', true);
23788     }
23789
23790     var drawWay = function(surface) {
23791         draw.on('move', move)
23792             .on('click', drawWay.add)
23793             .on('clickWay', drawWay.addWay)
23794             .on('clickNode', drawWay.addNode)
23795             .on('undo', context.undo)
23796             .on('cancel', drawWay.cancel)
23797             .on('finish', drawWay.finish);
23798
23799         context.map()
23800             .dblclickEnable(false)
23801             .on('drawn.draw', setActiveElements);
23802
23803         setActiveElements();
23804
23805         surface.call(draw);
23806
23807         context.history()
23808             .on('undone.draw', undone);
23809     };
23810
23811     drawWay.off = function(surface) {
23812         if (!finished)
23813             context.pop();
23814
23815         context.map()
23816             .on('drawn.draw', null);
23817
23818         surface.call(draw.off)
23819             .selectAll('.active')
23820             .classed('active', false);
23821
23822         context.history()
23823             .on('undone.draw', null);
23824     };
23825
23826     function ReplaceTemporaryNode(newNode) {
23827         return function(graph) {
23828             if (isArea) {
23829                 return graph
23830                     .replace(way.addNode(newNode.id, index))
23831                     .remove(end);
23832
23833             } else {
23834                 return graph
23835                     .replace(graph.entity(wayId).addNode(newNode.id, index))
23836                     .remove(end)
23837                     .remove(segment)
23838                     .remove(start);
23839             }
23840         };
23841     }
23842
23843     // Accept the current position of the temporary node and continue drawing.
23844     drawWay.add = function(loc) {
23845
23846         // prevent duplicate nodes
23847         var last = context.hasEntity(way.nodes[way.nodes.length - (isArea ? 2 : 1)]);
23848         if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return;
23849
23850         var newNode = iD.Node({loc: loc});
23851
23852         context.replace(
23853             iD.actions.AddEntity(newNode),
23854             ReplaceTemporaryNode(newNode),
23855             annotation);
23856
23857         finished = true;
23858         context.enter(mode);
23859     };
23860
23861     // Connect the way to an existing way.
23862     drawWay.addWay = function(loc, edge) {
23863         var previousEdge = startIndex ?
23864             [way.nodes[startIndex], way.nodes[startIndex - 1]] :
23865             [way.nodes[0], way.nodes[1]];
23866
23867         // Avoid creating duplicate segments
23868         if (!isArea && iD.geo.edgeEqual(edge, previousEdge))
23869             return;
23870
23871         var newNode = iD.Node({ loc: loc });
23872
23873         context.perform(
23874             iD.actions.AddMidpoint({ loc: loc, edge: edge}, newNode),
23875             ReplaceTemporaryNode(newNode),
23876             annotation);
23877
23878         finished = true;
23879         context.enter(mode);
23880     };
23881
23882     // Connect the way to an existing node and continue drawing.
23883     drawWay.addNode = function(node) {
23884
23885         // Avoid creating duplicate segments
23886         if (way.areAdjacent(node.id, way.nodes[way.nodes.length - 1])) return;
23887
23888         context.perform(
23889             ReplaceTemporaryNode(node),
23890             annotation);
23891
23892         finished = true;
23893         context.enter(mode);
23894     };
23895
23896     // Finish the draw operation, removing the temporary node. If the way has enough
23897     // nodes to be valid, it's selected. Otherwise, return to browse mode.
23898     drawWay.finish = function() {
23899         context.pop();
23900         finished = true;
23901
23902         window.setTimeout(function() {
23903             context.map().dblclickEnable(true);
23904         }, 1000);
23905
23906         if (context.hasEntity(wayId)) {
23907             context.enter(
23908                 iD.modes.Select(context, [wayId])
23909                     .suppressMenu(true)
23910                     .newFeature(true));
23911         } else {
23912             context.enter(iD.modes.Browse(context));
23913         }
23914     };
23915
23916     // Cancel the draw operation and return to browse, deleting everything drawn.
23917     drawWay.cancel = function() {
23918         context.perform(
23919             d3.functor(baseGraph),
23920             t('operations.cancel_draw.annotation'));
23921
23922         window.setTimeout(function() {
23923             context.map().dblclickEnable(true);
23924         }, 1000);
23925
23926         finished = true;
23927         context.enter(iD.modes.Browse(context));
23928     };
23929
23930     drawWay.tail = function(text) {
23931         draw.tail(text);
23932         return drawWay;
23933     };
23934
23935     return drawWay;
23936 };
23937 iD.behavior.Edit = function(context) {
23938     function edit() {
23939         context.map()
23940             .minzoom(context.minEditableZoom());
23941     }
23942
23943     edit.off = function() {
23944         context.map()
23945             .minzoom(0);
23946     };
23947
23948     return edit;
23949 };
23950 iD.behavior.Hash = function(context) {
23951     var s0 = null, // cached location.hash
23952         lat = 90 - 1e-8; // allowable latitude range
23953
23954     var parser = function(map, s) {
23955         var q = iD.util.stringQs(s);
23956         var args = (q.map || '').split('/').map(Number);
23957         if (args.length < 3 || args.some(isNaN)) {
23958             return true; // replace bogus hash
23959         } else if (s !== formatter(map).slice(1)) {
23960             map.centerZoom([args[1],
23961                 Math.min(lat, Math.max(-lat, args[2]))], args[0]);
23962         }
23963     };
23964
23965     var formatter = function(map) {
23966         var mode = context.mode(),
23967             center = map.center(),
23968             zoom = map.zoom(),
23969             precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
23970             q = _.omit(iD.util.stringQs(location.hash.substring(1)), 'comment'),
23971             newParams = {};
23972
23973         if (mode && mode.id === 'browse') {
23974             delete q.id;
23975         } else {
23976             var selected = context.selectedIDs().filter(function(id) {
23977                 return !context.entity(id).isNew();
23978             });
23979             if (selected.length) {
23980                 newParams.id = selected.join(',');
23981             }
23982         }
23983
23984         newParams.map = zoom.toFixed(2) +
23985                 '/' + center[0].toFixed(precision) +
23986                 '/' + center[1].toFixed(precision);
23987
23988         return '#' + iD.util.qsString(_.assign(q, newParams), true);
23989     };
23990
23991     function update() {
23992         if (context.inIntro()) return;
23993         var s1 = formatter(context.map());
23994         if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map!
23995     }
23996
23997     var throttledUpdate = _.throttle(update, 500);
23998
23999     function hashchange() {
24000         if (location.hash === s0) return; // ignore spurious hashchange events
24001         if (parser(context.map(), (s0 = location.hash).substring(1))) {
24002             update(); // replace bogus hash
24003         }
24004     }
24005
24006     function hash() {
24007         context.map()
24008             .on('move.hash', throttledUpdate);
24009
24010         context
24011             .on('enter.hash', throttledUpdate);
24012
24013         d3.select(window)
24014             .on('hashchange.hash', hashchange);
24015
24016         if (location.hash) {
24017             var q = iD.util.stringQs(location.hash.substring(1));
24018             if (q.id) context.zoomToEntity(q.id.split(',')[0], !q.map);
24019             if (q.comment) context.storage('comment', q.comment);
24020             hashchange();
24021             if (q.map) hash.hadHash = true;
24022         }
24023     }
24024
24025     hash.off = function() {
24026         throttledUpdate.cancel();
24027
24028         context.map()
24029             .on('move.hash', null);
24030
24031         context
24032             .on('enter.hash', null);
24033
24034         d3.select(window)
24035             .on('hashchange.hash', null);
24036
24037         location.hash = '';
24038     };
24039
24040     return hash;
24041 };
24042 /*
24043    The hover behavior adds the `.hover` class on mouseover to all elements to which
24044    the identical datum is bound, and removes it on mouseout.
24045
24046    The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
24047    representation may consist of several elements scattered throughout the DOM hierarchy.
24048    Only one of these elements can have the :hover pseudo-class, but all of them will
24049    have the .hover class.
24050  */
24051 iD.behavior.Hover = function() {
24052     var dispatch = d3.dispatch('hover'),
24053         selection,
24054         altDisables,
24055         target;
24056
24057     function keydown() {
24058         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
24059             dispatch.hover(null);
24060             selection.selectAll('.hover')
24061                 .classed('hover-suppressed', true)
24062                 .classed('hover', false);
24063         }
24064     }
24065
24066     function keyup() {
24067         if (altDisables && d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
24068             dispatch.hover(target ? target.id : null);
24069             selection.selectAll('.hover-suppressed')
24070                 .classed('hover-suppressed', false)
24071                 .classed('hover', true);
24072         }
24073     }
24074
24075     var hover = function(__) {
24076         selection = __;
24077
24078         function enter(d) {
24079             if (d === target) return;
24080
24081             target = d;
24082
24083             selection.selectAll('.hover')
24084                 .classed('hover', false);
24085             selection.selectAll('.hover-suppressed')
24086                 .classed('hover-suppressed', false);
24087
24088             if (target instanceof iD.Entity) {
24089                 var selector = '.' + target.id;
24090
24091                 if (target.type === 'relation') {
24092                     target.members.forEach(function(member) {
24093                         selector += ', .' + member.id;
24094                     });
24095                 }
24096
24097                 var suppressed = altDisables && d3.event && d3.event.altKey;
24098
24099                 selection.selectAll(selector)
24100                     .classed(suppressed ? 'hover-suppressed' : 'hover', true);
24101
24102                 dispatch.hover(target.id);
24103             } else {
24104                 dispatch.hover(null);
24105             }
24106         }
24107
24108         var down;
24109
24110         function mouseover() {
24111             if (down) return;
24112             var target = d3.event.target;
24113             enter(target ? target.__data__ : null);
24114         }
24115
24116         function mouseout() {
24117             if (down) return;
24118             var target = d3.event.relatedTarget;
24119             enter(target ? target.__data__ : null);
24120         }
24121
24122         function mousedown() {
24123             down = true;
24124             d3.select(window)
24125                 .on('mouseup.hover', mouseup);
24126         }
24127
24128         function mouseup() {
24129             down = false;
24130         }
24131
24132         selection
24133             .on('mouseover.hover', mouseover)
24134             .on('mouseout.hover', mouseout)
24135             .on('mousedown.hover', mousedown)
24136             .on('mouseup.hover', mouseup);
24137
24138         d3.select(window)
24139             .on('keydown.hover', keydown)
24140             .on('keyup.hover', keyup);
24141     };
24142
24143     hover.off = function(selection) {
24144         selection.selectAll('.hover')
24145             .classed('hover', false);
24146         selection.selectAll('.hover-suppressed')
24147             .classed('hover-suppressed', false);
24148
24149         selection
24150             .on('mouseover.hover', null)
24151             .on('mouseout.hover', null)
24152             .on('mousedown.hover', null)
24153             .on('mouseup.hover', null);
24154
24155         d3.select(window)
24156             .on('keydown.hover', null)
24157             .on('keyup.hover', null)
24158             .on('mouseup.hover', null);
24159     };
24160
24161     hover.altDisables = function(_) {
24162         if (!arguments.length) return altDisables;
24163         altDisables = _;
24164         return hover;
24165     };
24166
24167     return d3.rebind(hover, dispatch, 'on');
24168 };
24169 iD.behavior.Lasso = function(context) {
24170
24171     var behavior = function(selection) {
24172         var lasso;
24173
24174         function mousedown() {
24175             var button = 0;  // left
24176             if (d3.event.button === button && d3.event.shiftKey === true) {
24177                 lasso = null;
24178
24179                 selection
24180                     .on('mousemove.lasso', mousemove)
24181                     .on('mouseup.lasso', mouseup);
24182
24183                 d3.event.stopPropagation();
24184             }
24185         }
24186
24187         function mousemove() {
24188             if (!lasso) {
24189                 lasso = iD.ui.Lasso(context);
24190                 context.surface().call(lasso);
24191             }
24192
24193             lasso.p(context.mouse());
24194         }
24195
24196         function normalize(a, b) {
24197             return [
24198                 [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
24199                 [Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
24200         }
24201
24202         function lassoed() {
24203             if (!lasso) return [];
24204
24205             var graph = context.graph(),
24206                 bounds = lasso.extent().map(context.projection.invert),
24207                 extent = iD.geo.Extent(normalize(bounds[0], bounds[1]));
24208
24209             return _.pluck(context.intersects(extent).filter(function(entity) {
24210                 return entity.type === 'node' &&
24211                     iD.geo.pointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
24212                     !context.features().isHidden(entity, graph, entity.geometry(graph));
24213             }), 'id');
24214         }
24215
24216         function mouseup() {
24217             selection
24218                 .on('mousemove.lasso', null)
24219                 .on('mouseup.lasso', null);
24220
24221             if (!lasso) return;
24222
24223             var ids = lassoed();
24224             lasso.close();
24225
24226             if (ids.length) {
24227                 context.enter(iD.modes.Select(context, ids));
24228             }
24229         }
24230
24231         selection
24232             .on('mousedown.lasso', mousedown);
24233     };
24234
24235     behavior.off = function(selection) {
24236         selection.on('mousedown.lasso', null);
24237     };
24238
24239     return behavior;
24240 };
24241 iD.behavior.Paste = function(context) {
24242     var keybinding = d3.keybinding('paste');
24243
24244     function omitTag(v, k) {
24245         return (
24246             k === 'phone' ||
24247             k === 'fax' ||
24248             k === 'email' ||
24249             k === 'website' ||
24250             k === 'url' ||
24251             k === 'note' ||
24252             k === 'description' ||
24253             k.indexOf('name') !== -1 ||
24254             k.indexOf('wiki') === 0 ||
24255             k.indexOf('addr:') === 0 ||
24256             k.indexOf('contact:') === 0
24257         );
24258     }
24259
24260     function doPaste() {
24261         d3.event.preventDefault();
24262         if (context.inIntro()) return;
24263
24264         var baseGraph = context.graph(),
24265             mouse = context.mouse(),
24266             projection = context.projection,
24267             viewport = iD.geo.Extent(projection.clipExtent()).polygon();
24268
24269         if (!iD.geo.pointInPolygon(mouse, viewport)) return;
24270
24271         var extent = iD.geo.Extent(),
24272             oldIDs = context.copyIDs(),
24273             oldGraph = context.copyGraph(),
24274             newIDs = [];
24275
24276         if (!oldIDs.length) return;
24277
24278         var action = iD.actions.CopyEntities(oldIDs, oldGraph);
24279         context.perform(action);
24280
24281         var copies = action.copies();
24282         for (var id in copies) {
24283             var oldEntity = oldGraph.entity(id),
24284                 newEntity = copies[id];
24285
24286             extent._extend(oldEntity.extent(oldGraph));
24287             newIDs.push(newEntity.id);
24288             context.perform(iD.actions.ChangeTags(newEntity.id, _.omit(newEntity.tags, omitTag)));
24289         }
24290
24291         // Put pasted objects where mouse pointer is..
24292         var center = projection(extent.center()),
24293             delta = [ mouse[0] - center[0], mouse[1] - center[1] ];
24294
24295         context.perform(iD.actions.Move(newIDs, delta, projection));
24296         context.enter(iD.modes.Move(context, newIDs, baseGraph));
24297     }
24298
24299     function paste() {
24300         keybinding.on(iD.ui.cmd('⌘V'), doPaste);
24301         d3.select(document).call(keybinding);
24302         return paste;
24303     }
24304
24305     paste.off = function() {
24306         d3.select(document).call(keybinding.off);
24307     };
24308
24309     return paste;
24310 };
24311 iD.behavior.Select = function(context) {
24312     function keydown() {
24313         if (d3.event && d3.event.shiftKey) {
24314             context.surface()
24315                 .classed('behavior-multiselect', true);
24316         }
24317     }
24318
24319     function keyup() {
24320         if (!d3.event || !d3.event.shiftKey) {
24321             context.surface()
24322                 .classed('behavior-multiselect', false);
24323         }
24324     }
24325
24326     function click() {
24327         var datum = d3.event.target.__data__,
24328             lasso = d3.select('#surface .lasso').node(),
24329             mode = context.mode();
24330
24331         if (!(datum instanceof iD.Entity)) {
24332             if (!d3.event.shiftKey && !lasso && mode.id !== 'browse')
24333                 context.enter(iD.modes.Browse(context));
24334
24335         } else if (!d3.event.shiftKey && !lasso) {
24336             // Avoid re-entering Select mode with same entity.
24337             if (context.selectedIDs().length !== 1 || context.selectedIDs()[0] !== datum.id) {
24338                 context.enter(iD.modes.Select(context, [datum.id]));
24339             } else {
24340                 mode.suppressMenu(false).reselect();
24341             }
24342         } else if (context.selectedIDs().indexOf(datum.id) >= 0) {
24343             var selectedIDs = _.without(context.selectedIDs(), datum.id);
24344             context.enter(selectedIDs.length ?
24345                 iD.modes.Select(context, selectedIDs) :
24346                 iD.modes.Browse(context));
24347
24348         } else {
24349             context.enter(iD.modes.Select(context, context.selectedIDs().concat([datum.id])));
24350         }
24351     }
24352
24353     var behavior = function(selection) {
24354         d3.select(window)
24355             .on('keydown.select', keydown)
24356             .on('keyup.select', keyup);
24357
24358         selection.on('click.select', click);
24359
24360         keydown();
24361     };
24362
24363     behavior.off = function(selection) {
24364         d3.select(window)
24365             .on('keydown.select', null)
24366             .on('keyup.select', null);
24367
24368         selection.on('click.select', null);
24369
24370         keyup();
24371     };
24372
24373     return behavior;
24374 };
24375 iD.behavior.Tail = function() {
24376     var text,
24377         container,
24378         xmargin = 25,
24379         tooltipSize = [0, 0],
24380         selectionSize = [0, 0];
24381
24382     function tail(selection) {
24383         if (!text) return;
24384
24385         d3.select(window)
24386             .on('resize.tail', function() { selectionSize = selection.dimensions(); });
24387
24388         function show() {
24389             container.style('display', 'block');
24390             tooltipSize = container.dimensions();
24391         }
24392
24393         function mousemove() {
24394             if (container.style('display') === 'none') show();
24395             var xoffset = ((d3.event.clientX + tooltipSize[0] + xmargin) > selectionSize[0]) ?
24396                 -tooltipSize[0] - xmargin : xmargin;
24397             container.classed('left', xoffset > 0);
24398             iD.util.setTransform(container, d3.event.clientX + xoffset, d3.event.clientY);
24399         }
24400
24401         function mouseleave() {
24402             if (d3.event.relatedTarget !== container.node()) {
24403                 container.style('display', 'none');
24404             }
24405         }
24406
24407         function mouseenter() {
24408             if (d3.event.relatedTarget !== container.node()) {
24409                 show();
24410             }
24411         }
24412
24413         container = d3.select(document.body)
24414             .append('div')
24415             .style('display', 'none')
24416             .attr('class', 'tail tooltip-inner');
24417
24418         container.append('div')
24419             .text(text);
24420
24421         selection
24422             .on('mousemove.tail', mousemove)
24423             .on('mouseenter.tail', mouseenter)
24424             .on('mouseleave.tail', mouseleave);
24425
24426         container
24427             .on('mousemove.tail', mousemove);
24428
24429         tooltipSize = container.dimensions();
24430         selectionSize = selection.dimensions();
24431     }
24432
24433     tail.off = function(selection) {
24434         if (!text) return;
24435
24436         container
24437             .on('mousemove.tail', null)
24438             .remove();
24439
24440         selection
24441             .on('mousemove.tail', null)
24442             .on('mouseenter.tail', null)
24443             .on('mouseleave.tail', null);
24444
24445         d3.select(window)
24446             .on('resize.tail', null);
24447     };
24448
24449     tail.text = function(_) {
24450         if (!arguments.length) return text;
24451         text = _;
24452         return tail;
24453     };
24454
24455     return tail;
24456 };
24457 iD.modes = {};
24458 iD.modes.AddArea = function(context) {
24459     var mode = {
24460         id: 'add-area',
24461         button: 'area',
24462         title: t('modes.add_area.title'),
24463         description: t('modes.add_area.description'),
24464         key: '3'
24465     };
24466
24467     var behavior = iD.behavior.AddWay(context)
24468             .tail(t('modes.add_area.tail'))
24469             .on('start', start)
24470             .on('startFromWay', startFromWay)
24471             .on('startFromNode', startFromNode),
24472         defaultTags = {area: 'yes'};
24473
24474     function start(loc) {
24475         var graph = context.graph(),
24476             node = iD.Node({loc: loc}),
24477             way = iD.Way({tags: defaultTags});
24478
24479         context.perform(
24480             iD.actions.AddEntity(node),
24481             iD.actions.AddEntity(way),
24482             iD.actions.AddVertex(way.id, node.id),
24483             iD.actions.AddVertex(way.id, node.id));
24484
24485         context.enter(iD.modes.DrawArea(context, way.id, graph));
24486     }
24487
24488     function startFromWay(loc, edge) {
24489         var graph = context.graph(),
24490             node = iD.Node({loc: loc}),
24491             way = iD.Way({tags: defaultTags});
24492
24493         context.perform(
24494             iD.actions.AddEntity(node),
24495             iD.actions.AddEntity(way),
24496             iD.actions.AddVertex(way.id, node.id),
24497             iD.actions.AddVertex(way.id, node.id),
24498             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24499
24500         context.enter(iD.modes.DrawArea(context, way.id, graph));
24501     }
24502
24503     function startFromNode(node) {
24504         var graph = context.graph(),
24505             way = iD.Way({tags: defaultTags});
24506
24507         context.perform(
24508             iD.actions.AddEntity(way),
24509             iD.actions.AddVertex(way.id, node.id),
24510             iD.actions.AddVertex(way.id, node.id));
24511
24512         context.enter(iD.modes.DrawArea(context, way.id, graph));
24513     }
24514
24515     mode.enter = function() {
24516         context.install(behavior);
24517     };
24518
24519     mode.exit = function() {
24520         context.uninstall(behavior);
24521     };
24522
24523     return mode;
24524 };
24525 iD.modes.AddLine = function(context) {
24526     var mode = {
24527         id: 'add-line',
24528         button: 'line',
24529         title: t('modes.add_line.title'),
24530         description: t('modes.add_line.description'),
24531         key: '2'
24532     };
24533
24534     var behavior = iD.behavior.AddWay(context)
24535         .tail(t('modes.add_line.tail'))
24536         .on('start', start)
24537         .on('startFromWay', startFromWay)
24538         .on('startFromNode', startFromNode);
24539
24540     function start(loc) {
24541         var baseGraph = context.graph(),
24542             node = iD.Node({loc: loc}),
24543             way = iD.Way();
24544
24545         context.perform(
24546             iD.actions.AddEntity(node),
24547             iD.actions.AddEntity(way),
24548             iD.actions.AddVertex(way.id, node.id));
24549
24550         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24551     }
24552
24553     function startFromWay(loc, edge) {
24554         var baseGraph = context.graph(),
24555             node = iD.Node({loc: loc}),
24556             way = iD.Way();
24557
24558         context.perform(
24559             iD.actions.AddEntity(node),
24560             iD.actions.AddEntity(way),
24561             iD.actions.AddVertex(way.id, node.id),
24562             iD.actions.AddMidpoint({ loc: loc, edge: edge }, node));
24563
24564         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24565     }
24566
24567     function startFromNode(node) {
24568         var baseGraph = context.graph(),
24569             way = iD.Way();
24570
24571         context.perform(
24572             iD.actions.AddEntity(way),
24573             iD.actions.AddVertex(way.id, node.id));
24574
24575         context.enter(iD.modes.DrawLine(context, way.id, baseGraph));
24576     }
24577
24578     mode.enter = function() {
24579         context.install(behavior);
24580     };
24581
24582     mode.exit = function() {
24583         context.uninstall(behavior);
24584     };
24585
24586     return mode;
24587 };
24588 iD.modes.AddPoint = function(context) {
24589     var mode = {
24590         id: 'add-point',
24591         button: 'point',
24592         title: t('modes.add_point.title'),
24593         description: t('modes.add_point.description'),
24594         key: '1'
24595     };
24596
24597     var behavior = iD.behavior.Draw(context)
24598         .tail(t('modes.add_point.tail'))
24599         .on('click', add)
24600         .on('clickWay', addWay)
24601         .on('clickNode', addNode)
24602         .on('cancel', cancel)
24603         .on('finish', cancel);
24604
24605     function add(loc) {
24606         var node = iD.Node({loc: loc});
24607
24608         context.perform(
24609             iD.actions.AddEntity(node),
24610             t('operations.add.annotation.point'));
24611
24612         context.enter(
24613             iD.modes.Select(context, [node.id])
24614                 .suppressMenu(true)
24615                 .newFeature(true));
24616     }
24617
24618     function addWay(loc) {
24619         add(loc);
24620     }
24621
24622     function addNode(node) {
24623         add(node.loc);
24624     }
24625
24626     function cancel() {
24627         context.enter(iD.modes.Browse(context));
24628     }
24629
24630     mode.enter = function() {
24631         context.install(behavior);
24632     };
24633
24634     mode.exit = function() {
24635         context.uninstall(behavior);
24636     };
24637
24638     return mode;
24639 };
24640 iD.modes.Browse = function(context) {
24641     var mode = {
24642         button: 'browse',
24643         id: 'browse',
24644         title: t('modes.browse.title'),
24645         description: t('modes.browse.description')
24646     }, sidebar;
24647
24648     var behaviors = [
24649         iD.behavior.Paste(context),
24650         iD.behavior.Hover(context)
24651             .on('hover', context.ui().sidebar.hover),
24652         iD.behavior.Select(context),
24653         iD.behavior.Lasso(context),
24654         iD.modes.DragNode(context).behavior];
24655
24656     mode.enter = function() {
24657         behaviors.forEach(function(behavior) {
24658             context.install(behavior);
24659         });
24660
24661         // Get focus on the body.
24662         if (document.activeElement && document.activeElement.blur) {
24663             document.activeElement.blur();
24664         }
24665
24666         if (sidebar) {
24667             context.ui().sidebar.show(sidebar);
24668         } else {
24669             context.ui().sidebar.select(null);
24670         }
24671     };
24672
24673     mode.exit = function() {
24674         context.ui().sidebar.hover.cancel();
24675         behaviors.forEach(function(behavior) {
24676             context.uninstall(behavior);
24677         });
24678
24679         if (sidebar) {
24680             context.ui().sidebar.hide();
24681         }
24682     };
24683
24684     mode.sidebar = function(_) {
24685         if (!arguments.length) return sidebar;
24686         sidebar = _;
24687         return mode;
24688     };
24689
24690     return mode;
24691 };
24692 iD.modes.DragNode = function(context) {
24693     var mode = {
24694         id: 'drag-node',
24695         button: 'browse'
24696     };
24697
24698     var nudgeInterval,
24699         activeIDs,
24700         wasMidpoint,
24701         cancelled,
24702         selectedIDs = [],
24703         hover = iD.behavior.Hover(context)
24704             .altDisables(true)
24705             .on('hover', context.ui().sidebar.hover),
24706         edit = iD.behavior.Edit(context);
24707
24708     function edge(point, size) {
24709         var pad = [30, 100, 30, 100];
24710         if (point[0] > size[0] - pad[0]) return [-10, 0];
24711         else if (point[0] < pad[2]) return [10, 0];
24712         else if (point[1] > size[1] - pad[1]) return [0, -10];
24713         else if (point[1] < pad[3]) return [0, 10];
24714         return null;
24715     }
24716
24717     function startNudge(nudge) {
24718         if (nudgeInterval) window.clearInterval(nudgeInterval);
24719         nudgeInterval = window.setInterval(function() {
24720             context.pan(nudge);
24721         }, 50);
24722     }
24723
24724     function stopNudge() {
24725         if (nudgeInterval) window.clearInterval(nudgeInterval);
24726         nudgeInterval = null;
24727     }
24728
24729     function moveAnnotation(entity) {
24730         return t('operations.move.annotation.' + entity.geometry(context.graph()));
24731     }
24732
24733     function connectAnnotation(entity) {
24734         return t('operations.connect.annotation.' + entity.geometry(context.graph()));
24735     }
24736
24737     function origin(entity) {
24738         return context.projection(entity.loc);
24739     }
24740
24741     function start(entity) {
24742         cancelled = d3.event.sourceEvent.shiftKey ||
24743             context.features().hasHiddenConnections(entity, context.graph());
24744
24745         if (cancelled) return behavior.cancel();
24746
24747         wasMidpoint = entity.type === 'midpoint';
24748         if (wasMidpoint) {
24749             var midpoint = entity;
24750             entity = iD.Node();
24751             context.perform(iD.actions.AddMidpoint(midpoint, entity));
24752
24753              var vertex = context.surface()
24754                 .selectAll('.' + entity.id);
24755              behavior.target(vertex.node(), entity);
24756
24757         } else {
24758             context.perform(
24759                 iD.actions.Noop());
24760         }
24761
24762         activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
24763         activeIDs.push(entity.id);
24764
24765         context.enter(mode);
24766     }
24767
24768     function datum() {
24769         if (d3.event.sourceEvent.altKey) {
24770             return {};
24771         }
24772
24773         return d3.event.sourceEvent.target.__data__ || {};
24774     }
24775
24776     // via https://gist.github.com/shawnbot/4166283
24777     function childOf(p, c) {
24778         if (p === c) return false;
24779         while (c && c !== p) c = c.parentNode;
24780         return c === p;
24781     }
24782
24783     function move(entity) {
24784         if (cancelled) return;
24785         d3.event.sourceEvent.stopPropagation();
24786
24787         var nudge = childOf(context.container().node(),
24788             d3.event.sourceEvent.toElement) &&
24789             edge(d3.event.point, context.map().dimensions());
24790
24791         if (nudge) startNudge(nudge);
24792         else stopNudge();
24793
24794         var loc = context.projection.invert(d3.event.point);
24795
24796         var d = datum();
24797         if (d.type === 'node' && d.id !== entity.id) {
24798             loc = d.loc;
24799         } else if (d.type === 'way' && !d3.select(d3.event.sourceEvent.target).classed('fill')) {
24800             loc = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection).loc;
24801         }
24802
24803         context.replace(
24804             iD.actions.MoveNode(entity.id, loc),
24805             moveAnnotation(entity));
24806     }
24807
24808     function end(entity) {
24809         if (cancelled) return;
24810
24811         var d = datum();
24812
24813         if (d.type === 'way') {
24814             var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection);
24815             context.replace(
24816                 iD.actions.AddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity),
24817                 connectAnnotation(d));
24818
24819         } else if (d.type === 'node' && d.id !== entity.id) {
24820             context.replace(
24821                 iD.actions.Connect([d.id, entity.id]),
24822                 connectAnnotation(d));
24823
24824         } else if (wasMidpoint) {
24825             context.replace(
24826                 iD.actions.Noop(),
24827                 t('operations.add.annotation.vertex'));
24828
24829         } else {
24830             context.replace(
24831                 iD.actions.Noop(),
24832                 moveAnnotation(entity));
24833         }
24834
24835         var reselection = selectedIDs.filter(function(id) {
24836             return context.graph().hasEntity(id);
24837         });
24838
24839         if (reselection.length) {
24840             context.enter(
24841                 iD.modes.Select(context, reselection)
24842                     .suppressMenu(true));
24843         } else {
24844             context.enter(iD.modes.Browse(context));
24845         }
24846     }
24847
24848     function cancel() {
24849         behavior.cancel();
24850         context.enter(iD.modes.Browse(context));
24851     }
24852
24853     function setActiveElements() {
24854         context.surface().selectAll(iD.util.entitySelector(activeIDs))
24855             .classed('active', true);
24856     }
24857
24858     var behavior = iD.behavior.drag()
24859         .delegate('g.node, g.point, g.midpoint')
24860         .surface(context.surface().node())
24861         .origin(origin)
24862         .on('start', start)
24863         .on('move', move)
24864         .on('end', end);
24865
24866     mode.enter = function() {
24867         context.install(hover);
24868         context.install(edit);
24869
24870         context.history()
24871             .on('undone.drag-node', cancel);
24872
24873         context.map()
24874             .on('drawn.drag-node', setActiveElements);
24875
24876         setActiveElements();
24877     };
24878
24879     mode.exit = function() {
24880         context.ui().sidebar.hover.cancel();
24881         context.uninstall(hover);
24882         context.uninstall(edit);
24883
24884         context.history()
24885             .on('undone.drag-node', null);
24886
24887         context.map()
24888             .on('drawn.drag-node', null);
24889
24890         context.surface()
24891             .selectAll('.active')
24892             .classed('active', false);
24893
24894         stopNudge();
24895     };
24896
24897     mode.selectedIDs = function(_) {
24898         if (!arguments.length) return selectedIDs;
24899         selectedIDs = _;
24900         return mode;
24901     };
24902
24903     mode.behavior = behavior;
24904
24905     return mode;
24906 };
24907 iD.modes.DrawArea = function(context, wayId, baseGraph) {
24908     var mode = {
24909         button: 'area',
24910         id: 'draw-area'
24911     };
24912
24913     var behavior;
24914
24915     mode.enter = function() {
24916         var way = context.entity(wayId),
24917             headId = way.nodes[way.nodes.length - 2],
24918             tailId = way.first();
24919
24920         behavior = iD.behavior.DrawWay(context, wayId, -1, mode, baseGraph)
24921             .tail(t('modes.draw_area.tail'));
24922
24923         var addNode = behavior.addNode;
24924
24925         behavior.addNode = function(node) {
24926             if (node.id === headId || node.id === tailId) {
24927                 behavior.finish();
24928             } else {
24929                 addNode(node);
24930             }
24931         };
24932
24933         context.install(behavior);
24934     };
24935
24936     mode.exit = function() {
24937         context.uninstall(behavior);
24938     };
24939
24940     mode.selectedIDs = function() {
24941         return [wayId];
24942     };
24943
24944     return mode;
24945 };
24946 iD.modes.DrawLine = function(context, wayId, baseGraph, affix) {
24947     var mode = {
24948         button: 'line',
24949         id: 'draw-line'
24950     };
24951
24952     var behavior;
24953
24954     mode.enter = function() {
24955         var way = context.entity(wayId),
24956             index = (affix === 'prefix') ? 0 : undefined,
24957             headId = (affix === 'prefix') ? way.first() : way.last();
24958
24959         behavior = iD.behavior.DrawWay(context, wayId, index, mode, baseGraph)
24960             .tail(t('modes.draw_line.tail'));
24961
24962         var addNode = behavior.addNode;
24963
24964         behavior.addNode = function(node) {
24965             if (node.id === headId) {
24966                 behavior.finish();
24967             } else {
24968                 addNode(node);
24969             }
24970         };
24971
24972         context.install(behavior);
24973     };
24974
24975     mode.exit = function() {
24976         context.uninstall(behavior);
24977     };
24978
24979     mode.selectedIDs = function() {
24980         return [wayId];
24981     };
24982
24983     return mode;
24984 };
24985 iD.modes.Move = function(context, entityIDs, baseGraph) {
24986     var mode = {
24987         id: 'move',
24988         button: 'browse'
24989     };
24990
24991     var keybinding = d3.keybinding('move'),
24992         edit = iD.behavior.Edit(context),
24993         annotation = entityIDs.length === 1 ?
24994             t('operations.move.annotation.' + context.geometry(entityIDs[0])) :
24995             t('operations.move.annotation.multiple'),
24996         cache,
24997         origin,
24998         nudgeInterval;
24999
25000     function vecSub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
25001
25002     function edge(point, size) {
25003         var pad = [30, 100, 30, 100];
25004         if (point[0] > size[0] - pad[0]) return [-10, 0];
25005         else if (point[0] < pad[2]) return [10, 0];
25006         else if (point[1] > size[1] - pad[1]) return [0, -10];
25007         else if (point[1] < pad[3]) return [0, 10];
25008         return null;
25009     }
25010
25011     function startNudge(nudge) {
25012         if (nudgeInterval) window.clearInterval(nudgeInterval);
25013         nudgeInterval = window.setInterval(function() {
25014             context.pan(nudge);
25015
25016             var currMouse = context.mouse(),
25017                 origMouse = context.projection(origin),
25018                 delta = vecSub(vecSub(currMouse, origMouse), nudge),
25019                 action = iD.actions.Move(entityIDs, delta, context.projection, cache);
25020
25021             context.overwrite(action, annotation);
25022
25023         }, 50);
25024     }
25025
25026     function stopNudge() {
25027         if (nudgeInterval) window.clearInterval(nudgeInterval);
25028         nudgeInterval = null;
25029     }
25030
25031     function move() {
25032         var currMouse = context.mouse(),
25033             origMouse = context.projection(origin),
25034             delta = vecSub(currMouse, origMouse),
25035             action = iD.actions.Move(entityIDs, delta, context.projection, cache);
25036
25037         context.overwrite(action, annotation);
25038
25039         var nudge = edge(currMouse, context.map().dimensions());
25040         if (nudge) startNudge(nudge);
25041         else stopNudge();
25042     }
25043
25044     function finish() {
25045         d3.event.stopPropagation();
25046         context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
25047         stopNudge();
25048     }
25049
25050     function cancel() {
25051         if (baseGraph) {
25052             while (context.graph() !== baseGraph) context.pop();
25053             context.enter(iD.modes.Browse(context));
25054         } else {
25055             context.pop();
25056             context.enter(iD.modes.Select(context, entityIDs).suppressMenu(true));
25057         }
25058         stopNudge();
25059     }
25060
25061     function undone() {
25062         context.enter(iD.modes.Browse(context));
25063     }
25064
25065     mode.enter = function() {
25066         origin = context.map().mouseCoordinates();
25067         cache = {};
25068
25069         context.install(edit);
25070
25071         context.perform(
25072             iD.actions.Noop(),
25073             annotation);
25074
25075         context.surface()
25076             .on('mousemove.move', move)
25077             .on('click.move', finish);
25078
25079         context.history()
25080             .on('undone.move', undone);
25081
25082         keybinding
25083             .on('⎋', cancel)
25084             .on('↩', finish);
25085
25086         d3.select(document)
25087             .call(keybinding);
25088     };
25089
25090     mode.exit = function() {
25091         stopNudge();
25092
25093         context.uninstall(edit);
25094
25095         context.surface()
25096             .on('mousemove.move', null)
25097             .on('click.move', null);
25098
25099         context.history()
25100             .on('undone.move', null);
25101
25102         keybinding.off();
25103     };
25104
25105     return mode;
25106 };
25107 iD.modes.RotateWay = function(context, wayId) {
25108     var mode = {
25109         id: 'rotate-way',
25110         button: 'browse'
25111     };
25112
25113     var keybinding = d3.keybinding('rotate-way'),
25114         edit = iD.behavior.Edit(context);
25115
25116     mode.enter = function() {
25117         context.install(edit);
25118
25119         var annotation = t('operations.rotate.annotation.' + context.geometry(wayId)),
25120             way = context.graph().entity(wayId),
25121             nodes = _.uniq(context.graph().childNodes(way)),
25122             points = nodes.map(function(n) { return context.projection(n.loc); }),
25123             pivot = d3.geom.polygon(points).centroid(),
25124             angle;
25125
25126         context.perform(
25127             iD.actions.Noop(),
25128             annotation);
25129
25130         function rotate() {
25131
25132             var mousePoint = context.mouse(),
25133                 newAngle = Math.atan2(mousePoint[1] - pivot[1], mousePoint[0] - pivot[0]);
25134
25135             if (typeof angle === 'undefined') angle = newAngle;
25136
25137             context.replace(
25138                 iD.actions.RotateWay(wayId, pivot, newAngle - angle, context.projection),
25139                 annotation);
25140
25141             angle = newAngle;
25142         }
25143
25144         function finish() {
25145             d3.event.stopPropagation();
25146             context.enter(iD.modes.Select(context, [wayId])
25147                 .suppressMenu(true));
25148         }
25149
25150         function cancel() {
25151             context.pop();
25152             context.enter(iD.modes.Select(context, [wayId])
25153                 .suppressMenu(true));
25154         }
25155
25156         function undone() {
25157             context.enter(iD.modes.Browse(context));
25158         }
25159
25160         context.surface()
25161             .on('mousemove.rotate-way', rotate)
25162             .on('click.rotate-way', finish);
25163
25164         context.history()
25165             .on('undone.rotate-way', undone);
25166
25167         keybinding
25168             .on('⎋', cancel)
25169             .on('↩', finish);
25170
25171         d3.select(document)
25172             .call(keybinding);
25173     };
25174
25175     mode.exit = function() {
25176         context.uninstall(edit);
25177
25178         context.surface()
25179             .on('mousemove.rotate-way', null)
25180             .on('click.rotate-way', null);
25181
25182         context.history()
25183             .on('undone.rotate-way', null);
25184
25185         keybinding.off();
25186     };
25187
25188     return mode;
25189 };
25190 iD.modes.Save = function(context) {
25191     var ui = iD.ui.Commit(context)
25192             .on('cancel', cancel)
25193             .on('save', save);
25194
25195     function cancel() {
25196         context.enter(iD.modes.Browse(context));
25197     }
25198
25199     function save(e, tryAgain) {
25200         function withChildNodes(ids, graph) {
25201             return _.uniq(_.reduce(ids, function(result, id) {
25202                 var e = graph.entity(id);
25203                 if (e.type === 'way') {
25204                     try {
25205                         var cn = graph.childNodes(e);
25206                         result.push.apply(result, _.pluck(_.filter(cn, 'version'), 'id'));
25207                     } catch(err) {
25208                         /* eslint-disable no-console */
25209                         if (typeof console !== 'undefined') console.error(err);
25210                         /* eslint-enable no-console */
25211                     }
25212                 }
25213                 return result;
25214             }, _.clone(ids)));
25215         }
25216
25217         var loading = iD.ui.Loading(context).message(t('save.uploading')).blocking(true),
25218             history = context.history(),
25219             origChanges = history.changes(iD.actions.DiscardTags(history.difference())),
25220             localGraph = context.graph(),
25221             remoteGraph = iD.Graph(history.base(), true),
25222             modified = _.filter(history.difference().summary(), {changeType: 'modified'}),
25223             toCheck = _.pluck(_.pluck(modified, 'entity'), 'id'),
25224             toLoad = withChildNodes(toCheck, localGraph),
25225             conflicts = [],
25226             errors = [];
25227
25228         if (!tryAgain) history.perform(iD.actions.Noop());  // checkpoint
25229         context.container().call(loading);
25230
25231         if (toCheck.length) {
25232             context.connection().loadMultiple(toLoad, loaded);
25233         } else {
25234             finalize();
25235         }
25236
25237
25238         // Reload modified entities into an alternate graph and check for conflicts..
25239         function loaded(err, result) {
25240             if (errors.length) return;
25241
25242             if (err) {
25243                 errors.push({
25244                     msg: err.responseText,
25245                     details: [ t('save.status_code', { code: err.status }) ]
25246                 });
25247                 showErrors();
25248
25249             } else {
25250                 var loadMore = [];
25251                 _.each(result.data, function(entity) {
25252                     remoteGraph.replace(entity);
25253                     toLoad = _.without(toLoad, entity.id);
25254
25255                     // Because loadMultiple doesn't download /full like loadEntity,
25256                     // need to also load children that aren't already being checked..
25257                     if (!entity.visible) return;
25258                     if (entity.type === 'way') {
25259                         loadMore.push.apply(loadMore,
25260                             _.difference(entity.nodes, toCheck, toLoad, loadMore));
25261                     } else if (entity.type === 'relation' && entity.isMultipolygon()) {
25262                         loadMore.push.apply(loadMore,
25263                             _.difference(_.pluck(entity.members, 'id'), toCheck, toLoad, loadMore));
25264                     }
25265                 });
25266
25267                 if (loadMore.length) {
25268                     toLoad.push.apply(toLoad, loadMore);
25269                     context.connection().loadMultiple(loadMore, loaded);
25270                 }
25271
25272                 if (!toLoad.length) {
25273                     checkConflicts();
25274                 }
25275             }
25276         }
25277
25278
25279         function checkConflicts() {
25280             function choice(id, text, action) {
25281                 return { id: id, text: text, action: function() { history.replace(action); } };
25282             }
25283             function formatUser(d) {
25284                 return '<a href="' + context.connection().userURL(d) + '" target="_blank">' + d + '</a>';
25285             }
25286             function entityName(entity) {
25287                 return iD.util.displayName(entity) || (iD.util.displayType(entity.id) + ' ' + entity.id);
25288             }
25289
25290             function compareVersions(local, remote) {
25291                 if (local.version !== remote.version) return false;
25292
25293                 if (local.type === 'way') {
25294                     var children = _.union(local.nodes, remote.nodes);
25295
25296                     for (var i = 0; i < children.length; i++) {
25297                         var a = localGraph.hasEntity(children[i]),
25298                             b = remoteGraph.hasEntity(children[i]);
25299
25300                         if (a && b && a.version !== b.version) return false;
25301                     }
25302                 }
25303
25304                 return true;
25305             }
25306
25307             _.each(toCheck, function(id) {
25308                 var local = localGraph.entity(id),
25309                     remote = remoteGraph.entity(id);
25310
25311                 if (compareVersions(local, remote)) return;
25312
25313                 var action = iD.actions.MergeRemoteChanges,
25314                     merge = action(id, localGraph, remoteGraph, formatUser);
25315
25316                 history.replace(merge);
25317
25318                 var mergeConflicts = merge.conflicts();
25319                 if (!mergeConflicts.length) return;  // merged safely
25320
25321                 var forceLocal = action(id, localGraph, remoteGraph).withOption('force_local'),
25322                     forceRemote = action(id, localGraph, remoteGraph).withOption('force_remote'),
25323                     keepMine = t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore')),
25324                     keepTheirs = t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
25325
25326                 conflicts.push({
25327                     id: id,
25328                     name: entityName(local),
25329                     details: mergeConflicts,
25330                     chosen: 1,
25331                     choices: [
25332                         choice(id, keepMine, forceLocal),
25333                         choice(id, keepTheirs, forceRemote)
25334                     ]
25335                 });
25336             });
25337
25338             finalize();
25339         }
25340
25341
25342         function finalize() {
25343             if (conflicts.length) {
25344                 conflicts.sort(function(a,b) { return b.id.localeCompare(a.id); });
25345                 showConflicts();
25346             } else if (errors.length) {
25347                 showErrors();
25348             } else {
25349                 var changes = history.changes(iD.actions.DiscardTags(history.difference()));
25350                 if (changes.modified.length || changes.created.length || changes.deleted.length) {
25351                     context.connection().putChangeset(
25352                         changes,
25353                         e.comment,
25354                         history.imageryUsed(),
25355                         function(err, changeset_id) {
25356                             if (err) {
25357                                 errors.push({
25358                                     msg: err.responseText,
25359                                     details: [ t('save.status_code', { code: err.status }) ]
25360                                 });
25361                                 showErrors();
25362                             } else {
25363                                 history.clearSaved();
25364                                 success(e, changeset_id);
25365                                 // Add delay to allow for postgres replication #1646 #2678
25366                                 window.setTimeout(function() {
25367                                     loading.close();
25368                                     context.flush();
25369                                 }, 2500);
25370                             }
25371                         });
25372                 } else {        // changes were insignificant or reverted by user
25373                     loading.close();
25374                     context.flush();
25375                     cancel();
25376                 }
25377             }
25378         }
25379
25380
25381         function showConflicts() {
25382             var selection = context.container()
25383                 .select('#sidebar')
25384                 .append('div')
25385                 .attr('class','sidebar-component');
25386
25387             loading.close();
25388
25389             selection.call(iD.ui.Conflicts(context)
25390                 .list(conflicts)
25391                 .on('download', function() {
25392                     var data = JXON.stringify(context.connection().osmChangeJXON('CHANGEME', origChanges)),
25393                         win = window.open('data:text/xml,' + encodeURIComponent(data), '_blank');
25394                     win.focus();
25395                 })
25396                 .on('cancel', function() {
25397                     history.pop();
25398                     selection.remove();
25399                 })
25400                 .on('save', function() {
25401                     for (var i = 0; i < conflicts.length; i++) {
25402                         if (conflicts[i].chosen === 1) {  // user chose "keep theirs"
25403                             var entity = context.hasEntity(conflicts[i].id);
25404                             if (entity && entity.type === 'way') {
25405                                 var children = _.uniq(entity.nodes);
25406                                 for (var j = 0; j < children.length; j++) {
25407                                     history.replace(iD.actions.Revert(children[j]));
25408                                 }
25409                             }
25410                             history.replace(iD.actions.Revert(conflicts[i].id));
25411                         }
25412                     }
25413
25414                     selection.remove();
25415                     save(e, true);
25416                 })
25417             );
25418         }
25419
25420
25421         function showErrors() {
25422             var selection = iD.ui.confirm(context.container());
25423
25424             history.pop();
25425             loading.close();
25426
25427             selection
25428                 .select('.modal-section.header')
25429                 .append('h3')
25430                 .text(t('save.error'));
25431
25432             addErrors(selection, errors);
25433             selection.okButton();
25434         }
25435
25436
25437         function addErrors(selection, data) {
25438             var message = selection
25439                 .select('.modal-section.message-text');
25440
25441             var items = message
25442                 .selectAll('.error-container')
25443                 .data(data);
25444
25445             var enter = items.enter()
25446                 .append('div')
25447                 .attr('class', 'error-container');
25448
25449             enter
25450                 .append('a')
25451                 .attr('class', 'error-description')
25452                 .attr('href', '#')
25453                 .classed('hide-toggle', true)
25454                 .text(function(d) { return d.msg || t('save.unknown_error_details'); })
25455                 .on('click', function() {
25456                     var error = d3.select(this),
25457                         detail = d3.select(this.nextElementSibling),
25458                         exp = error.classed('expanded');
25459
25460                     detail.style('display', exp ? 'none' : 'block');
25461                     error.classed('expanded', !exp);
25462
25463                     d3.event.preventDefault();
25464                 });
25465
25466             var details = enter
25467                 .append('div')
25468                 .attr('class', 'error-detail-container')
25469                 .style('display', 'none');
25470
25471             details
25472                 .append('ul')
25473                 .attr('class', 'error-detail-list')
25474                 .selectAll('li')
25475                 .data(function(d) { return d.details || []; })
25476                 .enter()
25477                 .append('li')
25478                 .attr('class', 'error-detail-item')
25479                 .text(function(d) { return d; });
25480
25481             items.exit()
25482                 .remove();
25483         }
25484
25485     }
25486
25487
25488     function success(e, changeset_id) {
25489         context.enter(iD.modes.Browse(context)
25490             .sidebar(iD.ui.Success(context)
25491                 .changeset({
25492                     id: changeset_id,
25493                     comment: e.comment
25494                 })
25495                 .on('cancel', function() {
25496                     context.ui().sidebar.hide();
25497                 })));
25498     }
25499
25500     var mode = {
25501         id: 'save'
25502     };
25503
25504     mode.enter = function() {
25505         context.connection().authenticate(function(err) {
25506             if (err) {
25507                 cancel();
25508             } else {
25509                 context.ui().sidebar.show(ui);
25510             }
25511         });
25512     };
25513
25514     mode.exit = function() {
25515         context.ui().sidebar.hide();
25516     };
25517
25518     return mode;
25519 };
25520 iD.modes.Select = function(context, selectedIDs) {
25521     var mode = {
25522         id: 'select',
25523         button: 'browse'
25524     };
25525
25526     var keybinding = d3.keybinding('select'),
25527         timeout = null,
25528         behaviors = [
25529             iD.behavior.Copy(context),
25530             iD.behavior.Paste(context),
25531             iD.behavior.Breathe(context),
25532             iD.behavior.Hover(context),
25533             iD.behavior.Select(context),
25534             iD.behavior.Lasso(context),
25535             iD.modes.DragNode(context)
25536                 .selectedIDs(selectedIDs)
25537                 .behavior],
25538         inspector,
25539         radialMenu,
25540         newFeature = false,
25541         suppressMenu = false;
25542
25543     var wrap = context.container()
25544         .select('.inspector-wrap');
25545
25546
25547     function singular() {
25548         if (selectedIDs.length === 1) {
25549             return context.hasEntity(selectedIDs[0]);
25550         }
25551     }
25552
25553     function closeMenu() {
25554         if (radialMenu) {
25555             context.surface().call(radialMenu.close);
25556         }
25557     }
25558
25559     function positionMenu() {
25560         if (suppressMenu || !radialMenu) { return; }
25561
25562         var entity = singular();
25563         if (entity && context.geometry(entity.id) === 'relation') {
25564             suppressMenu = true;
25565         } else if (entity && entity.type === 'node') {
25566             radialMenu.center(context.projection(entity.loc));
25567         } else {
25568             var point = context.mouse(),
25569                 viewport = iD.geo.Extent(context.projection.clipExtent()).polygon();
25570             if (iD.geo.pointInPolygon(point, viewport)) {
25571                 radialMenu.center(point);
25572             } else {
25573                 suppressMenu = true;
25574             }
25575         }
25576     }
25577
25578     function showMenu() {
25579         closeMenu();
25580         if (!suppressMenu && radialMenu) {
25581             context.surface().call(radialMenu);
25582         }
25583     }
25584
25585     function toggleMenu() {
25586         if (d3.select('.radial-menu').empty()) {
25587             showMenu();
25588         } else {
25589             closeMenu();
25590         }
25591     }
25592
25593     mode.selectedIDs = function() {
25594         return selectedIDs;
25595     };
25596
25597     mode.reselect = function() {
25598         var surfaceNode = context.surface().node();
25599         if (surfaceNode.focus) { // FF doesn't support it
25600             surfaceNode.focus();
25601         }
25602
25603         positionMenu();
25604         showMenu();
25605     };
25606
25607     mode.newFeature = function(_) {
25608         if (!arguments.length) return newFeature;
25609         newFeature = _;
25610         return mode;
25611     };
25612
25613     mode.suppressMenu = function(_) {
25614         if (!arguments.length) return suppressMenu;
25615         suppressMenu = _;
25616         return mode;
25617     };
25618
25619     mode.enter = function() {
25620         function update() {
25621             closeMenu();
25622             if (_.any(selectedIDs, function(id) { return !context.hasEntity(id); })) {
25623                 // Exit mode if selected entity gets undone
25624                 context.enter(iD.modes.Browse(context));
25625             }
25626         }
25627
25628         function dblclick() {
25629             var target = d3.select(d3.event.target),
25630                 datum = target.datum();
25631
25632             if (datum instanceof iD.Way && !target.classed('fill')) {
25633                 var choice = iD.geo.chooseEdge(context.childNodes(datum), context.mouse(), context.projection),
25634                     node = iD.Node();
25635
25636                 var prev = datum.nodes[choice.index - 1],
25637                     next = datum.nodes[choice.index];
25638
25639                 context.perform(
25640                     iD.actions.AddMidpoint({loc: choice.loc, edge: [prev, next]}, node),
25641                     t('operations.add.annotation.vertex'));
25642
25643                 d3.event.preventDefault();
25644                 d3.event.stopPropagation();
25645             }
25646         }
25647
25648         function selectElements(drawn) {
25649             var entity = singular();
25650             if (entity && context.geometry(entity.id) === 'relation') {
25651                 suppressMenu = true;
25652                 return;
25653             }
25654
25655             var selection = context.surface()
25656                     .selectAll(iD.util.entityOrMemberSelector(selectedIDs, context.graph()));
25657
25658             if (selection.empty()) {
25659                 if (drawn) {  // Exit mode if selected DOM elements have disappeared..
25660                     context.enter(iD.modes.Browse(context));
25661                 }
25662             } else {
25663                 selection
25664                     .classed('selected', true);
25665             }
25666         }
25667
25668         function esc() {
25669             if (!context.inIntro()) {
25670                 context.enter(iD.modes.Browse(context));
25671             }
25672         }
25673
25674
25675         behaviors.forEach(function(behavior) {
25676             context.install(behavior);
25677         });
25678
25679         var operations = _.without(d3.values(iD.operations), iD.operations.Delete)
25680                 .map(function(o) { return o(selectedIDs, context); })
25681                 .filter(function(o) { return o.available(); });
25682
25683         operations.unshift(iD.operations.Delete(selectedIDs, context));
25684
25685         keybinding
25686             .on('⎋', esc, true)
25687             .on('space', toggleMenu);
25688
25689         operations.forEach(function(operation) {
25690             operation.keys.forEach(function(key) {
25691                 keybinding.on(key, function() {
25692                     if (!(context.inIntro() || operation.disabled())) {
25693                         operation();
25694                     }
25695                 });
25696             });
25697         });
25698
25699         d3.select(document)
25700             .call(keybinding);
25701
25702         radialMenu = iD.ui.RadialMenu(context, operations);
25703
25704         context.ui().sidebar
25705             .select(singular() ? singular().id : null, newFeature);
25706
25707         context.history()
25708             .on('undone.select', update)
25709             .on('redone.select', update);
25710
25711         context.map()
25712             .on('move.select', closeMenu)
25713             .on('drawn.select', selectElements);
25714
25715         selectElements();
25716
25717         var show = d3.event && !suppressMenu;
25718
25719         if (show) {
25720             positionMenu();
25721         }
25722
25723         timeout = window.setTimeout(function() {
25724             if (show) {
25725                 showMenu();
25726             }
25727
25728             context.surface()
25729                 .on('dblclick.select', dblclick);
25730         }, 200);
25731
25732         if (selectedIDs.length > 1) {
25733             var entities = iD.ui.SelectionList(context, selectedIDs);
25734             context.ui().sidebar.show(entities);
25735         }
25736     };
25737
25738     mode.exit = function() {
25739         if (timeout) window.clearTimeout(timeout);
25740
25741         if (inspector) wrap.call(inspector.close);
25742
25743         behaviors.forEach(function(behavior) {
25744             context.uninstall(behavior);
25745         });
25746
25747         keybinding.off();
25748         closeMenu();
25749         radialMenu = undefined;
25750
25751         context.history()
25752             .on('undone.select', null)
25753             .on('redone.select', null);
25754
25755         context.surface()
25756             .on('dblclick.select', null)
25757             .selectAll('.selected')
25758             .classed('selected', false);
25759
25760         context.map().on('drawn.select', null);
25761         context.ui().sidebar.hide();
25762     };
25763
25764     return mode;
25765 };
25766 iD.operations = {};
25767 iD.operations.Circularize = function(selectedIDs, context) {
25768     var entityId = selectedIDs[0],
25769         entity = context.entity(entityId),
25770         extent = entity.extent(context.graph()),
25771         geometry = context.geometry(entityId),
25772         action = iD.actions.Circularize(entityId, context.projection);
25773
25774     var operation = function() {
25775         var annotation = t('operations.circularize.annotation.' + geometry);
25776         context.perform(action, annotation);
25777     };
25778
25779     operation.available = function() {
25780         return selectedIDs.length === 1 &&
25781             entity.type === 'way' &&
25782             _.uniq(entity.nodes).length > 1;
25783     };
25784
25785     operation.disabled = function() {
25786         var reason;
25787         if (extent.percentContainedIn(context.extent()) < 0.8) {
25788             reason = 'too_large';
25789         } else if (context.hasHiddenConnections(entityId)) {
25790             reason = 'connected_to_hidden';
25791         }
25792         return action.disabled(context.graph()) || reason;
25793     };
25794
25795     operation.tooltip = function() {
25796         var disable = operation.disabled();
25797         return disable ?
25798             t('operations.circularize.' + disable) :
25799             t('operations.circularize.description.' + geometry);
25800     };
25801
25802     operation.id = 'circularize';
25803     operation.keys = [t('operations.circularize.key')];
25804     operation.title = t('operations.circularize.title');
25805
25806     return operation;
25807 };
25808 iD.operations.Continue = function(selectedIDs, context) {
25809     var graph = context.graph(),
25810         entities = selectedIDs.map(function(id) { return graph.entity(id); }),
25811         geometries = _.extend({line: [], vertex: []},
25812             _.groupBy(entities, function(entity) { return entity.geometry(graph); })),
25813         vertex = geometries.vertex[0];
25814
25815     function candidateWays() {
25816         return graph.parentWays(vertex).filter(function(parent) {
25817             return parent.geometry(graph) === 'line' &&
25818                 parent.affix(vertex.id) &&
25819                 (geometries.line.length === 0 || geometries.line[0] === parent);
25820         });
25821     }
25822
25823     var operation = function() {
25824         var candidate = candidateWays()[0];
25825         context.enter(iD.modes.DrawLine(
25826             context,
25827             candidate.id,
25828             context.graph(),
25829             candidate.affix(vertex.id)));
25830     };
25831
25832     operation.available = function() {
25833         return geometries.vertex.length === 1 && geometries.line.length <= 1 &&
25834             !context.features().hasHiddenConnections(vertex, context.graph());
25835     };
25836
25837     operation.disabled = function() {
25838         var candidates = candidateWays();
25839         if (candidates.length === 0)
25840             return 'not_eligible';
25841         if (candidates.length > 1)
25842             return 'multiple';
25843     };
25844
25845     operation.tooltip = function() {
25846         var disable = operation.disabled();
25847         return disable ?
25848             t('operations.continue.' + disable) :
25849             t('operations.continue.description');
25850     };
25851
25852     operation.id = 'continue';
25853     operation.keys = [t('operations.continue.key')];
25854     operation.title = t('operations.continue.title');
25855
25856     return operation;
25857 };
25858 iD.operations.Delete = function(selectedIDs, context) {
25859     var action = iD.actions.DeleteMultiple(selectedIDs);
25860
25861     var operation = function() {
25862         var annotation,
25863             nextSelectedID;
25864
25865         if (selectedIDs.length > 1) {
25866             annotation = t('operations.delete.annotation.multiple', {n: selectedIDs.length});
25867
25868         } else {
25869             var id = selectedIDs[0],
25870                 entity = context.entity(id),
25871                 geometry = context.geometry(id),
25872                 parents = context.graph().parentWays(entity),
25873                 parent = parents[0];
25874
25875             annotation = t('operations.delete.annotation.' + geometry);
25876
25877             // Select the next closest node in the way.
25878             if (geometry === 'vertex' && parents.length === 1 && parent.nodes.length > 2) {
25879                 var nodes = parent.nodes,
25880                     i = nodes.indexOf(id);
25881
25882                 if (i === 0) {
25883                     i++;
25884                 } else if (i === nodes.length - 1) {
25885                     i--;
25886                 } else {
25887                     var a = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc),
25888                         b = iD.geo.sphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
25889                     i = a < b ? i - 1 : i + 1;
25890                 }
25891
25892                 nextSelectedID = nodes[i];
25893             }
25894         }
25895
25896         if (nextSelectedID && context.hasEntity(nextSelectedID)) {
25897             context.enter(iD.modes.Select(context, [nextSelectedID]));
25898         } else {
25899             context.enter(iD.modes.Browse(context));
25900         }
25901
25902         context.perform(
25903             action,
25904             annotation);
25905     };
25906
25907     operation.available = function() {
25908         return true;
25909     };
25910
25911     operation.disabled = function() {
25912         var reason;
25913         if (_.any(selectedIDs, context.hasHiddenConnections)) {
25914             reason = 'connected_to_hidden';
25915         }
25916         return action.disabled(context.graph()) || reason;
25917     };
25918
25919     operation.tooltip = function() {
25920         var disable = operation.disabled();
25921         return disable ?
25922             t('operations.delete.' + disable) :
25923             t('operations.delete.description');
25924     };
25925
25926     operation.id = 'delete';
25927     operation.keys = [iD.ui.cmd('⌘⌫'), iD.ui.cmd('⌘⌦')];
25928     operation.title = t('operations.delete.title');
25929
25930     return operation;
25931 };
25932 iD.operations.Disconnect = function(selectedIDs, context) {
25933     var vertices = _.filter(selectedIDs, function vertex(entityId) {
25934         return context.geometry(entityId) === 'vertex';
25935     });
25936
25937     var entityId = vertices[0],
25938         action = iD.actions.Disconnect(entityId);
25939
25940     if (selectedIDs.length > 1) {
25941         action.limitWays(_.without(selectedIDs, entityId));
25942     }
25943
25944     var operation = function() {
25945         context.perform(action, t('operations.disconnect.annotation'));
25946     };
25947
25948     operation.available = function() {
25949         return vertices.length === 1;
25950     };
25951
25952     operation.disabled = function() {
25953         var reason;
25954         if (_.any(selectedIDs, context.hasHiddenConnections)) {
25955             reason = 'connected_to_hidden';
25956         }
25957         return action.disabled(context.graph()) || reason;
25958     };
25959
25960     operation.tooltip = function() {
25961         var disable = operation.disabled();
25962         return disable ?
25963             t('operations.disconnect.' + disable) :
25964             t('operations.disconnect.description');
25965     };
25966
25967     operation.id = 'disconnect';
25968     operation.keys = [t('operations.disconnect.key')];
25969     operation.title = t('operations.disconnect.title');
25970
25971     return operation;
25972 };
25973 iD.operations.Merge = function(selectedIDs, context) {
25974     var join = iD.actions.Join(selectedIDs),
25975         merge = iD.actions.Merge(selectedIDs),
25976         mergePolygon = iD.actions.MergePolygon(selectedIDs);
25977
25978     var operation = function() {
25979         var annotation = t('operations.merge.annotation', {n: selectedIDs.length}),
25980             action;
25981
25982         if (!join.disabled(context.graph())) {
25983             action = join;
25984         } else if (!merge.disabled(context.graph())) {
25985             action = merge;
25986         } else {
25987             action = mergePolygon;
25988         }
25989
25990         context.perform(action, annotation);
25991         context.enter(iD.modes.Select(context, selectedIDs.filter(function(id) { return context.hasEntity(id); }))
25992             .suppressMenu(true));
25993     };
25994
25995     operation.available = function() {
25996         return selectedIDs.length >= 2;
25997     };
25998
25999     operation.disabled = function() {
26000         return join.disabled(context.graph()) &&
26001             merge.disabled(context.graph()) &&
26002             mergePolygon.disabled(context.graph());
26003     };
26004
26005     operation.tooltip = function() {
26006         var j = join.disabled(context.graph()),
26007             m = merge.disabled(context.graph()),
26008             p = mergePolygon.disabled(context.graph());
26009
26010         if (j === 'restriction' && m && p)
26011             return t('operations.merge.restriction', {relation: context.presets().item('type/restriction').name()});
26012
26013         if (p === 'incomplete_relation' && j && m)
26014             return t('operations.merge.incomplete_relation');
26015
26016         if (j && m && p)
26017             return t('operations.merge.' + j);
26018
26019         return t('operations.merge.description');
26020     };
26021
26022     operation.id = 'merge';
26023     operation.keys = [t('operations.merge.key')];
26024     operation.title = t('operations.merge.title');
26025
26026     return operation;
26027 };
26028 iD.operations.Move = function(selectedIDs, context) {
26029     var extent = selectedIDs.reduce(function(extent, id) {
26030             return extent.extend(context.entity(id).extent(context.graph()));
26031         }, iD.geo.Extent());
26032
26033     var operation = function() {
26034         context.enter(iD.modes.Move(context, selectedIDs));
26035     };
26036
26037     operation.available = function() {
26038         return selectedIDs.length > 1 ||
26039             context.entity(selectedIDs[0]).type !== 'node';
26040     };
26041
26042     operation.disabled = function() {
26043         var reason;
26044         if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
26045             reason = 'too_large';
26046         } else if (_.any(selectedIDs, context.hasHiddenConnections)) {
26047             reason = 'connected_to_hidden';
26048         }
26049         return iD.actions.Move(selectedIDs).disabled(context.graph()) || reason;
26050     };
26051
26052     operation.tooltip = function() {
26053         var disable = operation.disabled();
26054         return disable ?
26055             t('operations.move.' + disable) :
26056             t('operations.move.description');
26057     };
26058
26059     operation.id = 'move';
26060     operation.keys = [t('operations.move.key')];
26061     operation.title = t('operations.move.title');
26062
26063     return operation;
26064 };
26065 iD.operations.Orthogonalize = function(selectedIDs, context) {
26066     var entityId = selectedIDs[0],
26067         entity = context.entity(entityId),
26068         extent = entity.extent(context.graph()),
26069         geometry = context.geometry(entityId),
26070         action = iD.actions.Orthogonalize(entityId, context.projection);
26071
26072     var operation = function() {
26073         var annotation = t('operations.orthogonalize.annotation.' + geometry);
26074         context.perform(action, annotation);
26075     };
26076
26077     operation.available = function() {
26078         return selectedIDs.length === 1 &&
26079             entity.type === 'way' &&
26080             entity.isClosed() &&
26081             _.uniq(entity.nodes).length > 2;
26082     };
26083
26084     operation.disabled = function() {
26085         var reason;
26086         if (extent.percentContainedIn(context.extent()) < 0.8) {
26087             reason = 'too_large';
26088         } else if (context.hasHiddenConnections(entityId)) {
26089             reason = 'connected_to_hidden';
26090         }
26091         return action.disabled(context.graph()) || reason;
26092     };
26093
26094     operation.tooltip = function() {
26095         var disable = operation.disabled();
26096         return disable ?
26097             t('operations.orthogonalize.' + disable) :
26098             t('operations.orthogonalize.description.' + geometry);
26099     };
26100
26101     operation.id = 'orthogonalize';
26102     operation.keys = [t('operations.orthogonalize.key')];
26103     operation.title = t('operations.orthogonalize.title');
26104
26105     return operation;
26106 };
26107 iD.operations.Reverse = function(selectedIDs, context) {
26108     var entityId = selectedIDs[0];
26109
26110     var operation = function() {
26111         context.perform(
26112             iD.actions.Reverse(entityId),
26113             t('operations.reverse.annotation'));
26114     };
26115
26116     operation.available = function() {
26117         return selectedIDs.length === 1 &&
26118             context.geometry(entityId) === 'line';
26119     };
26120
26121     operation.disabled = function() {
26122         return false;
26123     };
26124
26125     operation.tooltip = function() {
26126         return t('operations.reverse.description');
26127     };
26128
26129     operation.id = 'reverse';
26130     operation.keys = [t('operations.reverse.key')];
26131     operation.title = t('operations.reverse.title');
26132
26133     return operation;
26134 };
26135 iD.operations.Rotate = function(selectedIDs, context) {
26136     var entityId = selectedIDs[0],
26137         entity = context.entity(entityId),
26138         extent = entity.extent(context.graph()),
26139         geometry = context.geometry(entityId);
26140
26141     var operation = function() {
26142         context.enter(iD.modes.RotateWay(context, entityId));
26143     };
26144
26145     operation.available = function() {
26146         if (selectedIDs.length !== 1 || entity.type !== 'way')
26147             return false;
26148         if (geometry === 'area')
26149             return true;
26150         if (entity.isClosed() &&
26151             context.graph().parentRelations(entity).some(function(r) { return r.isMultipolygon(); }))
26152             return true;
26153         return false;
26154     };
26155
26156     operation.disabled = function() {
26157         if (extent.percentContainedIn(context.extent()) < 0.8) {
26158             return 'too_large';
26159         } else if (context.hasHiddenConnections(entityId)) {
26160             return 'connected_to_hidden';
26161         } else {
26162             return false;
26163         }
26164     };
26165
26166     operation.tooltip = function() {
26167         var disable = operation.disabled();
26168         return disable ?
26169             t('operations.rotate.' + disable) :
26170             t('operations.rotate.description');
26171     };
26172
26173     operation.id = 'rotate';
26174     operation.keys = [t('operations.rotate.key')];
26175     operation.title = t('operations.rotate.title');
26176
26177     return operation;
26178 };
26179 iD.operations.Split = function(selectedIDs, context) {
26180     var vertices = _.filter(selectedIDs, function vertex(entityId) {
26181         return context.geometry(entityId) === 'vertex';
26182     });
26183
26184     var entityId = vertices[0],
26185         action = iD.actions.Split(entityId);
26186
26187     if (selectedIDs.length > 1) {
26188         action.limitWays(_.without(selectedIDs, entityId));
26189     }
26190
26191     var operation = function() {
26192         var annotation;
26193
26194         var ways = action.ways(context.graph());
26195         if (ways.length === 1) {
26196             annotation = t('operations.split.annotation.' + context.geometry(ways[0].id));
26197         } else {
26198             annotation = t('operations.split.annotation.multiple', {n: ways.length});
26199         }
26200
26201         var difference = context.perform(action, annotation);
26202         context.enter(iD.modes.Select(context, difference.extantIDs()));
26203     };
26204
26205     operation.available = function() {
26206         return vertices.length === 1;
26207     };
26208
26209     operation.disabled = function() {
26210         var reason;
26211         if (_.any(selectedIDs, context.hasHiddenConnections)) {
26212             reason = 'connected_to_hidden';
26213         }
26214         return action.disabled(context.graph()) || reason;
26215     };
26216
26217     operation.tooltip = function() {
26218         var disable = operation.disabled();
26219         if (disable) {
26220             return t('operations.split.' + disable);
26221         }
26222
26223         var ways = action.ways(context.graph());
26224         if (ways.length === 1) {
26225             return t('operations.split.description.' + context.geometry(ways[0].id));
26226         } else {
26227             return t('operations.split.description.multiple');
26228         }
26229     };
26230
26231     operation.id = 'split';
26232     operation.keys = [t('operations.split.key')];
26233     operation.title = t('operations.split.title');
26234
26235     return operation;
26236 };
26237 iD.operations.Straighten = function(selectedIDs, context) {
26238     var entityId = selectedIDs[0],
26239         action = iD.actions.Straighten(entityId, context.projection);
26240
26241     function operation() {
26242         var annotation = t('operations.straighten.annotation');
26243         context.perform(action, annotation);
26244     }
26245
26246     operation.available = function() {
26247         var entity = context.entity(entityId);
26248         return selectedIDs.length === 1 &&
26249             entity.type === 'way' &&
26250             !entity.isClosed() &&
26251             _.uniq(entity.nodes).length > 2;
26252     };
26253
26254     operation.disabled = function() {
26255         var reason;
26256         if (context.hasHiddenConnections(entityId)) {
26257             reason = 'connected_to_hidden';
26258         }
26259         return action.disabled(context.graph()) || reason;
26260     };
26261
26262     operation.tooltip = function() {
26263         var disable = operation.disabled();
26264         return disable ?
26265             t('operations.straighten.' + disable) :
26266             t('operations.straighten.description');
26267     };
26268
26269     operation.id = 'straighten';
26270     operation.keys = [t('operations.straighten.key')];
26271     operation.title = t('operations.straighten.title');
26272
26273     return operation;
26274 };
26275 iD.Connection = function(useHttps) {
26276     if (typeof useHttps !== 'boolean') {
26277       useHttps = window.location.protocol === 'https:';
26278     }
26279
26280     var event = d3.dispatch('authenticating', 'authenticated', 'auth', 'loading', 'loaded'),
26281         protocol = useHttps ? 'https:' : 'http:',
26282         url = protocol + '//www.openstreetmap.org',
26283         connection = {},
26284         inflight = {},
26285         loadedTiles = {},
26286         tileZoom = 16,
26287         oauth = osmAuth({
26288             url: protocol + '//www.openstreetmap.org',
26289             oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
26290             oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
26291             loading: authenticating,
26292             done: authenticated
26293         }),
26294         ndStr = 'nd',
26295         tagStr = 'tag',
26296         memberStr = 'member',
26297         nodeStr = 'node',
26298         wayStr = 'way',
26299         relationStr = 'relation',
26300         userDetails,
26301         off;
26302
26303
26304     connection.changesetURL = function(changesetId) {
26305         return url + '/changeset/' + changesetId;
26306     };
26307
26308     connection.changesetsURL = function(center, zoom) {
26309         var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
26310         return url + '/history#map=' +
26311             Math.floor(zoom) + '/' +
26312             center[1].toFixed(precision) + '/' +
26313             center[0].toFixed(precision);
26314     };
26315
26316     connection.entityURL = function(entity) {
26317         return url + '/' + entity.type + '/' + entity.osmId();
26318     };
26319
26320     connection.userURL = function(username) {
26321         return url + '/user/' + username;
26322     };
26323
26324     connection.loadFromURL = function(url, callback) {
26325         function done(err, dom) {
26326             return callback(err, parse(dom));
26327         }
26328         return d3.xml(url).get(done);
26329     };
26330
26331     connection.loadEntity = function(id, callback) {
26332         var type = iD.Entity.id.type(id),
26333             osmID = iD.Entity.id.toOSM(id);
26334
26335         connection.loadFromURL(
26336             url + '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''),
26337             function(err, entities) {
26338                 if (callback) callback(err, {data: entities});
26339             });
26340     };
26341
26342     connection.loadEntityVersion = function(id, version, callback) {
26343         var type = iD.Entity.id.type(id),
26344             osmID = iD.Entity.id.toOSM(id);
26345
26346         connection.loadFromURL(
26347             url + '/api/0.6/' + type + '/' + osmID + '/' + version,
26348             function(err, entities) {
26349                 if (callback) callback(err, {data: entities});
26350             });
26351     };
26352
26353     connection.loadMultiple = function(ids, callback) {
26354         _.each(_.groupBy(_.uniq(ids), iD.Entity.id.type), function(v, k) {
26355             var type = k + 's',
26356                 osmIDs = _.map(v, iD.Entity.id.toOSM);
26357
26358             _.each(_.chunk(osmIDs, 150), function(arr) {
26359                 connection.loadFromURL(
26360                     url + '/api/0.6/' + type + '?' + type + '=' + arr.join(),
26361                     function(err, entities) {
26362                         if (callback) callback(err, {data: entities});
26363                     });
26364             });
26365         });
26366     };
26367
26368     function authenticating() {
26369         event.authenticating();
26370     }
26371
26372     function authenticated() {
26373         event.authenticated();
26374     }
26375
26376     function getLoc(attrs) {
26377         var lon = attrs.lon && attrs.lon.value,
26378             lat = attrs.lat && attrs.lat.value;
26379         return [parseFloat(lon), parseFloat(lat)];
26380     }
26381
26382     function getNodes(obj) {
26383         var elems = obj.getElementsByTagName(ndStr),
26384             nodes = new Array(elems.length);
26385         for (var i = 0, l = elems.length; i < l; i++) {
26386             nodes[i] = 'n' + elems[i].attributes.ref.value;
26387         }
26388         return nodes;
26389     }
26390
26391     function getTags(obj) {
26392         var elems = obj.getElementsByTagName(tagStr),
26393             tags = {};
26394         for (var i = 0, l = elems.length; i < l; i++) {
26395             var attrs = elems[i].attributes;
26396             tags[attrs.k.value] = attrs.v.value;
26397         }
26398         return tags;
26399     }
26400
26401     function getMembers(obj) {
26402         var elems = obj.getElementsByTagName(memberStr),
26403             members = new Array(elems.length);
26404         for (var i = 0, l = elems.length; i < l; i++) {
26405             var attrs = elems[i].attributes;
26406             members[i] = {
26407                 id: attrs.type.value[0] + attrs.ref.value,
26408                 type: attrs.type.value,
26409                 role: attrs.role.value
26410             };
26411         }
26412         return members;
26413     }
26414
26415     function getVisible(attrs) {
26416         return (!attrs.visible || attrs.visible.value !== 'false');
26417     }
26418
26419     var parsers = {
26420         node: function nodeData(obj) {
26421             var attrs = obj.attributes;
26422             return new iD.Node({
26423                 id: iD.Entity.id.fromOSM(nodeStr, attrs.id.value),
26424                 loc: getLoc(attrs),
26425                 version: attrs.version.value,
26426                 user: attrs.user && attrs.user.value,
26427                 tags: getTags(obj),
26428                 visible: getVisible(attrs)
26429             });
26430         },
26431
26432         way: function wayData(obj) {
26433             var attrs = obj.attributes;
26434             return new iD.Way({
26435                 id: iD.Entity.id.fromOSM(wayStr, attrs.id.value),
26436                 version: attrs.version.value,
26437                 user: attrs.user && attrs.user.value,
26438                 tags: getTags(obj),
26439                 nodes: getNodes(obj),
26440                 visible: getVisible(attrs)
26441             });
26442         },
26443
26444         relation: function relationData(obj) {
26445             var attrs = obj.attributes;
26446             return new iD.Relation({
26447                 id: iD.Entity.id.fromOSM(relationStr, attrs.id.value),
26448                 version: attrs.version.value,
26449                 user: attrs.user && attrs.user.value,
26450                 tags: getTags(obj),
26451                 members: getMembers(obj),
26452                 visible: getVisible(attrs)
26453             });
26454         }
26455     };
26456
26457     function parse(dom) {
26458         if (!dom || !dom.childNodes) return;
26459
26460         var root = dom.childNodes[0],
26461             children = root.childNodes,
26462             entities = [];
26463
26464         for (var i = 0, l = children.length; i < l; i++) {
26465             var child = children[i],
26466                 parser = parsers[child.nodeName];
26467             if (parser) {
26468                 entities.push(parser(child));
26469             }
26470         }
26471
26472         return entities;
26473     }
26474
26475     connection.authenticated = function() {
26476         return oauth.authenticated();
26477     };
26478
26479     // Generate Changeset XML. Returns a string.
26480     connection.changesetJXON = function(tags) {
26481         return {
26482             osm: {
26483                 changeset: {
26484                     tag: _.map(tags, function(value, key) {
26485                         return { '@k': key, '@v': value };
26486                     }),
26487                     '@version': 0.6,
26488                     '@generator': 'iD'
26489                 }
26490             }
26491         };
26492     };
26493
26494     // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
26495     // XML. Returns a string.
26496     connection.osmChangeJXON = function(changeset_id, changes) {
26497         function nest(x, order) {
26498             var groups = {};
26499             for (var i = 0; i < x.length; i++) {
26500                 var tagName = Object.keys(x[i])[0];
26501                 if (!groups[tagName]) groups[tagName] = [];
26502                 groups[tagName].push(x[i][tagName]);
26503             }
26504             var ordered = {};
26505             order.forEach(function(o) {
26506                 if (groups[o]) ordered[o] = groups[o];
26507             });
26508             return ordered;
26509         }
26510
26511         function rep(entity) {
26512             return entity.asJXON(changeset_id);
26513         }
26514
26515         return {
26516             osmChange: {
26517                 '@version': 0.6,
26518                 '@generator': 'iD',
26519                 'create': nest(changes.created.map(rep), ['node', 'way', 'relation']),
26520                 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
26521                 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true})
26522             }
26523         };
26524     };
26525
26526     connection.changesetTags = function(comment, imageryUsed) {
26527         var detected = iD.detect(),
26528             tags = {
26529                 created_by: 'iD ' + iD.version,
26530                 imagery_used: imageryUsed.join(';').substr(0, 255),
26531                 host: (window.location.origin + window.location.pathname).substr(0, 255),
26532                 locale: detected.locale
26533             };
26534
26535         if (comment) {
26536             tags.comment = comment.substr(0, 255);
26537         }
26538
26539         return tags;
26540     };
26541
26542     connection.putChangeset = function(changes, comment, imageryUsed, callback) {
26543         oauth.xhr({
26544                 method: 'PUT',
26545                 path: '/api/0.6/changeset/create',
26546                 options: { header: { 'Content-Type': 'text/xml' } },
26547                 content: JXON.stringify(connection.changesetJXON(connection.changesetTags(comment, imageryUsed)))
26548             }, function(err, changeset_id) {
26549                 if (err) return callback(err);
26550                 oauth.xhr({
26551                     method: 'POST',
26552                     path: '/api/0.6/changeset/' + changeset_id + '/upload',
26553                     options: { header: { 'Content-Type': 'text/xml' } },
26554                     content: JXON.stringify(connection.osmChangeJXON(changeset_id, changes))
26555                 }, function(err) {
26556                     if (err) return callback(err);
26557                     // POST was successful, safe to call the callback.
26558                     // Still attempt to close changeset, but ignore response because #2667
26559                     // Add delay to allow for postgres replication #1646 #2678
26560                     window.setTimeout(function() { callback(null, changeset_id); }, 2500);
26561                     oauth.xhr({
26562                         method: 'PUT',
26563                         path: '/api/0.6/changeset/' + changeset_id + '/close',
26564                         options: { header: { 'Content-Type': 'text/xml' } }
26565                     }, d3.functor(true));
26566                 });
26567             });
26568     };
26569
26570     connection.userDetails = function(callback) {
26571         if (userDetails) {
26572             callback(undefined, userDetails);
26573             return;
26574         }
26575
26576         function done(err, user_details) {
26577             if (err) return callback(err);
26578
26579             var u = user_details.getElementsByTagName('user')[0],
26580                 img = u.getElementsByTagName('img'),
26581                 image_url = '';
26582
26583             if (img && img[0] && img[0].getAttribute('href')) {
26584                 image_url = img[0].getAttribute('href');
26585             }
26586
26587             userDetails = {
26588                 display_name: u.attributes.display_name.value,
26589                 image_url: image_url,
26590                 id: u.attributes.id.value
26591             };
26592
26593             callback(undefined, userDetails);
26594         }
26595
26596         oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
26597     };
26598
26599     connection.userChangesets = function(callback) {
26600         connection.userDetails(function(err, user) {
26601             if (err) return callback(err);
26602
26603             function done(changesets) {
26604                 callback(undefined, Array.prototype.map.call(changesets.getElementsByTagName('changeset'),
26605                     function (changeset) {
26606                         return { tags: getTags(changeset) };
26607                     }));
26608             }
26609
26610             d3.xml(url + '/api/0.6/changesets?user=' + user.id).get()
26611                 .on('load', done)
26612                 .on('error', callback);
26613         });
26614     };
26615
26616     connection.status = function(callback) {
26617         function done(capabilities) {
26618             var apiStatus = capabilities.getElementsByTagName('status');
26619             callback(undefined, apiStatus[0].getAttribute('api'));
26620         }
26621         d3.xml(url + '/api/capabilities').get()
26622             .on('load', done)
26623             .on('error', callback);
26624     };
26625
26626     function abortRequest(i) { i.abort(); }
26627
26628     connection.tileZoom = function(_) {
26629         if (!arguments.length) return tileZoom;
26630         tileZoom = _;
26631         return connection;
26632     };
26633
26634     connection.loadTiles = function(projection, dimensions, callback) {
26635
26636         if (off) return;
26637
26638         var s = projection.scale() * 2 * Math.PI,
26639             z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
26640             ts = 256 * Math.pow(2, z - tileZoom),
26641             origin = [
26642                 s / 2 - projection.translate()[0],
26643                 s / 2 - projection.translate()[1]];
26644
26645         var tiles = d3.geo.tile()
26646             .scaleExtent([tileZoom, tileZoom])
26647             .scale(s)
26648             .size(dimensions)
26649             .translate(projection.translate())()
26650             .map(function(tile) {
26651                 var x = tile[0] * ts - origin[0],
26652                     y = tile[1] * ts - origin[1];
26653
26654                 return {
26655                     id: tile.toString(),
26656                     extent: iD.geo.Extent(
26657                         projection.invert([x, y + ts]),
26658                         projection.invert([x + ts, y]))
26659                 };
26660             });
26661
26662         function bboxUrl(tile) {
26663             return url + '/api/0.6/map?bbox=' + tile.extent.toParam();
26664         }
26665
26666         _.filter(inflight, function(v, i) {
26667             var wanted = _.find(tiles, function(tile) {
26668                 return i === tile.id;
26669             });
26670             if (!wanted) delete inflight[i];
26671             return !wanted;
26672         }).map(abortRequest);
26673
26674         tiles.forEach(function(tile) {
26675             var id = tile.id;
26676
26677             if (loadedTiles[id] || inflight[id]) return;
26678
26679             if (_.isEmpty(inflight)) {
26680                 event.loading();
26681             }
26682
26683             inflight[id] = connection.loadFromURL(bboxUrl(tile), function(err, parsed) {
26684                 loadedTiles[id] = true;
26685                 delete inflight[id];
26686
26687                 if (callback) callback(err, _.extend({data: parsed}, tile));
26688
26689                 if (_.isEmpty(inflight)) {
26690                     event.loaded();
26691                 }
26692             });
26693         });
26694     };
26695
26696     connection.switch = function(options) {
26697         url = options.url;
26698         oauth.options(_.extend({
26699             loading: authenticating,
26700             done: authenticated
26701         }, options));
26702         event.auth();
26703         connection.flush();
26704         return connection;
26705     };
26706
26707     connection.toggle = function(_) {
26708         off = !_;
26709         return connection;
26710     };
26711
26712     connection.flush = function() {
26713         userDetails = undefined;
26714         _.forEach(inflight, abortRequest);
26715         loadedTiles = {};
26716         inflight = {};
26717         return connection;
26718     };
26719
26720     connection.loadedTiles = function(_) {
26721         if (!arguments.length) return loadedTiles;
26722         loadedTiles = _;
26723         return connection;
26724     };
26725
26726     connection.logout = function() {
26727         userDetails = undefined;
26728         oauth.logout();
26729         event.auth();
26730         return connection;
26731     };
26732
26733     connection.authenticate = function(callback) {
26734         userDetails = undefined;
26735         function done(err, res) {
26736             event.auth();
26737             if (callback) callback(err, res);
26738         }
26739         return oauth.authenticate(done);
26740     };
26741
26742     return d3.rebind(connection, event, 'on');
26743 };
26744 /*
26745     iD.Difference represents the difference between two graphs.
26746     It knows how to calculate the set of entities that were
26747     created, modified, or deleted, and also contains the logic
26748     for recursively extending a difference to the complete set
26749     of entities that will require a redraw, taking into account
26750     child and parent relationships.
26751  */
26752 iD.Difference = function(base, head) {
26753     var changes = {}, length = 0;
26754
26755     function changed(h, b) {
26756         return h !== b && !_.isEqual(_.omit(h, 'v'), _.omit(b, 'v'));
26757     }
26758
26759     _.each(head.entities, function(h, id) {
26760         var b = base.entities[id];
26761         if (changed(h, b)) {
26762             changes[id] = {base: b, head: h};
26763             length++;
26764         }
26765     });
26766
26767     _.each(base.entities, function(b, id) {
26768         var h = head.entities[id];
26769         if (!changes[id] && changed(h, b)) {
26770             changes[id] = {base: b, head: h};
26771             length++;
26772         }
26773     });
26774
26775     function addParents(parents, result) {
26776         for (var i = 0; i < parents.length; i++) {
26777             var parent = parents[i];
26778
26779             if (parent.id in result)
26780                 continue;
26781
26782             result[parent.id] = parent;
26783             addParents(head.parentRelations(parent), result);
26784         }
26785     }
26786
26787     var difference = {};
26788
26789     difference.length = function() {
26790         return length;
26791     };
26792
26793     difference.changes = function() {
26794         return changes;
26795     };
26796
26797     difference.extantIDs = function() {
26798         var result = [];
26799         _.each(changes, function(change, id) {
26800             if (change.head) result.push(id);
26801         });
26802         return result;
26803     };
26804
26805     difference.modified = function() {
26806         var result = [];
26807         _.each(changes, function(change) {
26808             if (change.base && change.head) result.push(change.head);
26809         });
26810         return result;
26811     };
26812
26813     difference.created = function() {
26814         var result = [];
26815         _.each(changes, function(change) {
26816             if (!change.base && change.head) result.push(change.head);
26817         });
26818         return result;
26819     };
26820
26821     difference.deleted = function() {
26822         var result = [];
26823         _.each(changes, function(change) {
26824             if (change.base && !change.head) result.push(change.base);
26825         });
26826         return result;
26827     };
26828
26829     difference.summary = function() {
26830         var relevant = {};
26831
26832         function addEntity(entity, graph, changeType) {
26833             relevant[entity.id] = {
26834                 entity: entity,
26835                 graph: graph,
26836                 changeType: changeType
26837             };
26838         }
26839
26840         function addParents(entity) {
26841             var parents = head.parentWays(entity);
26842             for (var j = parents.length - 1; j >= 0; j--) {
26843                 var parent = parents[j];
26844                 if (!(parent.id in relevant)) addEntity(parent, head, 'modified');
26845             }
26846         }
26847
26848         _.each(changes, function(change) {
26849             if (change.head && change.head.geometry(head) !== 'vertex') {
26850                 addEntity(change.head, head, change.base ? 'modified' : 'created');
26851
26852             } else if (change.base && change.base.geometry(base) !== 'vertex') {
26853                 addEntity(change.base, base, 'deleted');
26854
26855             } else if (change.base && change.head) { // modified vertex
26856                 var moved    = !_.isEqual(change.base.loc,  change.head.loc),
26857                     retagged = !_.isEqual(change.base.tags, change.head.tags);
26858
26859                 if (moved) {
26860                     addParents(change.head);
26861                 }
26862
26863                 if (retagged || (moved && change.head.hasInterestingTags())) {
26864                     addEntity(change.head, head, 'modified');
26865                 }
26866
26867             } else if (change.head && change.head.hasInterestingTags()) { // created vertex
26868                 addEntity(change.head, head, 'created');
26869
26870             } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
26871                 addEntity(change.base, base, 'deleted');
26872             }
26873         });
26874
26875         return d3.values(relevant);
26876     };
26877
26878     difference.complete = function(extent) {
26879         var result = {}, id, change;
26880
26881         for (id in changes) {
26882             change = changes[id];
26883
26884             var h = change.head,
26885                 b = change.base,
26886                 entity = h || b;
26887
26888             if (extent &&
26889                 (!h || !h.intersects(extent, head)) &&
26890                 (!b || !b.intersects(extent, base)))
26891                 continue;
26892
26893             result[id] = h;
26894
26895             if (entity.type === 'way') {
26896                 var nh = h ? h.nodes : [],
26897                     nb = b ? b.nodes : [],
26898                     diff, i;
26899
26900                 diff = _.difference(nh, nb);
26901                 for (i = 0; i < diff.length; i++) {
26902                     result[diff[i]] = head.hasEntity(diff[i]);
26903                 }
26904
26905                 diff = _.difference(nb, nh);
26906                 for (i = 0; i < diff.length; i++) {
26907                     result[diff[i]] = head.hasEntity(diff[i]);
26908                 }
26909             }
26910
26911             addParents(head.parentWays(entity), result);
26912             addParents(head.parentRelations(entity), result);
26913         }
26914
26915         return result;
26916     };
26917
26918     return difference;
26919 };
26920 iD.Entity = function(attrs) {
26921     // For prototypal inheritance.
26922     if (this instanceof iD.Entity) return;
26923
26924     // Create the appropriate subtype.
26925     if (attrs && attrs.type) {
26926         return iD.Entity[attrs.type].apply(this, arguments);
26927     } else if (attrs && attrs.id) {
26928         return iD.Entity[iD.Entity.id.type(attrs.id)].apply(this, arguments);
26929     }
26930
26931     // Initialize a generic Entity (used only in tests).
26932     return (new iD.Entity()).initialize(arguments);
26933 };
26934
26935 iD.Entity.id = function(type) {
26936     return iD.Entity.id.fromOSM(type, iD.Entity.id.next[type]--);
26937 };
26938
26939 iD.Entity.id.next = {node: -1, way: -1, relation: -1};
26940
26941 iD.Entity.id.fromOSM = function(type, id) {
26942     return type[0] + id;
26943 };
26944
26945 iD.Entity.id.toOSM = function(id) {
26946     return id.slice(1);
26947 };
26948
26949 iD.Entity.id.type = function(id) {
26950     return {'n': 'node', 'w': 'way', 'r': 'relation'}[id[0]];
26951 };
26952
26953 // A function suitable for use as the second argument to d3.selection#data().
26954 iD.Entity.key = function(entity) {
26955     return entity.id + 'v' + (entity.v || 0);
26956 };
26957
26958 iD.Entity.prototype = {
26959     tags: {},
26960
26961     initialize: function(sources) {
26962         for (var i = 0; i < sources.length; ++i) {
26963             var source = sources[i];
26964             for (var prop in source) {
26965                 if (Object.prototype.hasOwnProperty.call(source, prop)) {
26966                     if (source[prop] === undefined) {
26967                         delete this[prop];
26968                     } else {
26969                         this[prop] = source[prop];
26970                     }
26971                 }
26972             }
26973         }
26974
26975         if (!this.id && this.type) {
26976             this.id = iD.Entity.id(this.type);
26977         }
26978         if (!this.hasOwnProperty('visible')) {
26979             this.visible = true;
26980         }
26981
26982         if (iD.debug) {
26983             Object.freeze(this);
26984             Object.freeze(this.tags);
26985
26986             if (this.loc) Object.freeze(this.loc);
26987             if (this.nodes) Object.freeze(this.nodes);
26988             if (this.members) Object.freeze(this.members);
26989         }
26990
26991         return this;
26992     },
26993
26994     copy: function(resolver, copies) {
26995         if (copies[this.id])
26996             return copies[this.id];
26997
26998         var copy = iD.Entity(this, {id: undefined, user: undefined, version: undefined});
26999         copies[this.id] = copy;
27000
27001         return copy;
27002     },
27003
27004     osmId: function() {
27005         return iD.Entity.id.toOSM(this.id);
27006     },
27007
27008     isNew: function() {
27009         return this.osmId() < 0;
27010     },
27011
27012     update: function(attrs) {
27013         return iD.Entity(this, attrs, {v: 1 + (this.v || 0)});
27014     },
27015
27016     mergeTags: function(tags) {
27017         var merged = _.clone(this.tags), changed = false;
27018         for (var k in tags) {
27019             var t1 = merged[k],
27020                 t2 = tags[k];
27021             if (!t1) {
27022                 changed = true;
27023                 merged[k] = t2;
27024             } else if (t1 !== t2) {
27025                 changed = true;
27026                 merged[k] = _.union(t1.split(/;\s*/), t2.split(/;\s*/)).join(';');
27027             }
27028         }
27029         return changed ? this.update({tags: merged}) : this;
27030     },
27031
27032     intersects: function(extent, resolver) {
27033         return this.extent(resolver).intersects(extent);
27034     },
27035
27036     isUsed: function(resolver) {
27037         return _.without(Object.keys(this.tags), 'area').length > 0 ||
27038             resolver.parentRelations(this).length > 0;
27039     },
27040
27041     hasInterestingTags: function() {
27042         return _.keys(this.tags).some(iD.interestingTag);
27043     },
27044
27045     isHighwayIntersection: function() {
27046         return false;
27047     },
27048
27049     deprecatedTags: function() {
27050         var tags = _.pairs(this.tags);
27051         var deprecated = {};
27052
27053         iD.data.deprecated.forEach(function(d) {
27054             var match = _.pairs(d.old)[0];
27055             tags.forEach(function(t) {
27056                 if (t[0] === match[0] &&
27057                     (t[1] === match[1] || match[1] === '*')) {
27058                     deprecated[t[0]] = t[1];
27059                 }
27060             });
27061         });
27062
27063         return deprecated;
27064     }
27065 };
27066 iD.Graph = function(other, mutable) {
27067     if (!(this instanceof iD.Graph)) return new iD.Graph(other, mutable);
27068
27069     if (other instanceof iD.Graph) {
27070         var base = other.base();
27071         this.entities = _.assign(Object.create(base.entities), other.entities);
27072         this._parentWays = _.assign(Object.create(base.parentWays), other._parentWays);
27073         this._parentRels = _.assign(Object.create(base.parentRels), other._parentRels);
27074
27075     } else {
27076         this.entities = Object.create({});
27077         this._parentWays = Object.create({});
27078         this._parentRels = Object.create({});
27079         this.rebase(other || [], [this]);
27080     }
27081
27082     this.transients = {};
27083     this._childNodes = {};
27084     this.frozen = !mutable;
27085 };
27086
27087 iD.Graph.prototype = {
27088     hasEntity: function(id) {
27089         return this.entities[id];
27090     },
27091
27092     entity: function(id) {
27093         var entity = this.entities[id];
27094         if (!entity) {
27095             throw new Error('entity ' + id + ' not found');
27096         }
27097         return entity;
27098     },
27099
27100     transient: function(entity, key, fn) {
27101         var id = entity.id,
27102             transients = this.transients[id] ||
27103             (this.transients[id] = {});
27104
27105         if (transients[key] !== undefined) {
27106             return transients[key];
27107         }
27108
27109         transients[key] = fn.call(entity);
27110
27111         return transients[key];
27112     },
27113
27114     parentWays: function(entity) {
27115         var parents = this._parentWays[entity.id],
27116             result = [];
27117
27118         if (parents) {
27119             for (var i = 0; i < parents.length; i++) {
27120                 result.push(this.entity(parents[i]));
27121             }
27122         }
27123         return result;
27124     },
27125
27126     isPoi: function(entity) {
27127         var parentWays = this._parentWays[entity.id];
27128         return !parentWays || parentWays.length === 0;
27129     },
27130
27131     isShared: function(entity) {
27132         var parentWays = this._parentWays[entity.id];
27133         return parentWays && parentWays.length > 1;
27134     },
27135
27136     parentRelations: function(entity) {
27137         var parents = this._parentRels[entity.id],
27138             result = [];
27139
27140         if (parents) {
27141             for (var i = 0; i < parents.length; i++) {
27142                 result.push(this.entity(parents[i]));
27143             }
27144         }
27145         return result;
27146     },
27147
27148     childNodes: function(entity) {
27149         if (this._childNodes[entity.id]) return this._childNodes[entity.id];
27150         if (!entity.nodes) return [];
27151
27152         var nodes = [];
27153         for (var i = 0; i < entity.nodes.length; i++) {
27154             nodes[i] = this.entity(entity.nodes[i]);
27155         }
27156
27157         if (iD.debug) Object.freeze(nodes);
27158
27159         this._childNodes[entity.id] = nodes;
27160         return this._childNodes[entity.id];
27161     },
27162
27163     base: function() {
27164         return {
27165             'entities': iD.util.getPrototypeOf(this.entities),
27166             'parentWays': iD.util.getPrototypeOf(this._parentWays),
27167             'parentRels': iD.util.getPrototypeOf(this._parentRels)
27168         };
27169     },
27170
27171     // Unlike other graph methods, rebase mutates in place. This is because it
27172     // is used only during the history operation that merges newly downloaded
27173     // data into each state. To external consumers, it should appear as if the
27174     // graph always contained the newly downloaded data.
27175     rebase: function(entities, stack, force) {
27176         var base = this.base(),
27177             i, j, k, id;
27178
27179         for (i = 0; i < entities.length; i++) {
27180             var entity = entities[i];
27181
27182             if (!entity.visible || (!force && base.entities[entity.id]))
27183                 continue;
27184
27185             // Merging data into the base graph
27186             base.entities[entity.id] = entity;
27187             this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
27188
27189             // Restore provisionally-deleted nodes that are discovered to have an extant parent
27190             if (entity.type === 'way') {
27191                 for (j = 0; j < entity.nodes.length; j++) {
27192                     id = entity.nodes[j];
27193                     for (k = 1; k < stack.length; k++) {
27194                         var ents = stack[k].entities;
27195                         if (ents.hasOwnProperty(id) && ents[id] === undefined) {
27196                             delete ents[id];
27197                         }
27198                     }
27199                 }
27200             }
27201         }
27202
27203         for (i = 0; i < stack.length; i++) {
27204             stack[i]._updateRebased();
27205         }
27206     },
27207
27208     _updateRebased: function() {
27209         var base = this.base(),
27210             i, k, child, id, keys;
27211
27212         keys = Object.keys(this._parentWays);
27213         for (i = 0; i < keys.length; i++) {
27214             child = keys[i];
27215             if (base.parentWays[child]) {
27216                 for (k = 0; k < base.parentWays[child].length; k++) {
27217                     id = base.parentWays[child][k];
27218                     if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentWays[child], id)) {
27219                         this._parentWays[child].push(id);
27220                     }
27221                 }
27222             }
27223         }
27224
27225         keys = Object.keys(this._parentRels);
27226         for (i = 0; i < keys.length; i++) {
27227             child = keys[i];
27228             if (base.parentRels[child]) {
27229                 for (k = 0; k < base.parentRels[child].length; k++) {
27230                     id = base.parentRels[child][k];
27231                     if (!this.entities.hasOwnProperty(id) && !_.contains(this._parentRels[child], id)) {
27232                         this._parentRels[child].push(id);
27233                     }
27234                 }
27235             }
27236         }
27237
27238         this.transients = {};
27239
27240         // this._childNodes is not updated, under the assumption that
27241         // ways are always downloaded with their child nodes.
27242     },
27243
27244     // Updates calculated properties (parentWays, parentRels) for the specified change
27245     _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
27246
27247         parentWays = parentWays || this._parentWays;
27248         parentRels = parentRels || this._parentRels;
27249
27250         var type = entity && entity.type || oldentity && oldentity.type,
27251             removed, added, ways, rels, i;
27252
27253
27254         if (type === 'way') {
27255
27256             // Update parentWays
27257             if (oldentity && entity) {
27258                 removed = _.difference(oldentity.nodes, entity.nodes);
27259                 added = _.difference(entity.nodes, oldentity.nodes);
27260             } else if (oldentity) {
27261                 removed = oldentity.nodes;
27262                 added = [];
27263             } else if (entity) {
27264                 removed = [];
27265                 added = entity.nodes;
27266             }
27267             for (i = 0; i < removed.length; i++) {
27268                 parentWays[removed[i]] = _.without(parentWays[removed[i]], oldentity.id);
27269             }
27270             for (i = 0; i < added.length; i++) {
27271                 ways = _.without(parentWays[added[i]], entity.id);
27272                 ways.push(entity.id);
27273                 parentWays[added[i]] = ways;
27274             }
27275
27276         } else if (type === 'relation') {
27277
27278             // Update parentRels
27279             if (oldentity && entity) {
27280                 removed = _.difference(oldentity.members, entity.members);
27281                 added = _.difference(entity.members, oldentity);
27282             } else if (oldentity) {
27283                 removed = oldentity.members;
27284                 added = [];
27285             } else if (entity) {
27286                 removed = [];
27287                 added = entity.members;
27288             }
27289             for (i = 0; i < removed.length; i++) {
27290                 parentRels[removed[i].id] = _.without(parentRels[removed[i].id], oldentity.id);
27291             }
27292             for (i = 0; i < added.length; i++) {
27293                 rels = _.without(parentRels[added[i].id], entity.id);
27294                 rels.push(entity.id);
27295                 parentRels[added[i].id] = rels;
27296             }
27297         }
27298     },
27299
27300     replace: function(entity) {
27301         if (this.entities[entity.id] === entity)
27302             return this;
27303
27304         return this.update(function() {
27305             this._updateCalculated(this.entities[entity.id], entity);
27306             this.entities[entity.id] = entity;
27307         });
27308     },
27309
27310     remove: function(entity) {
27311         return this.update(function() {
27312             this._updateCalculated(entity, undefined);
27313             this.entities[entity.id] = undefined;
27314         });
27315     },
27316
27317     revert: function(id) {
27318         var baseEntity = this.base().entities[id],
27319             headEntity = this.entities[id];
27320
27321         if (headEntity === baseEntity)
27322             return this;
27323
27324         return this.update(function() {
27325             this._updateCalculated(headEntity, baseEntity);
27326             delete this.entities[id];
27327         });
27328     },
27329
27330     update: function() {
27331         var graph = this.frozen ? iD.Graph(this, true) : this;
27332
27333         for (var i = 0; i < arguments.length; i++) {
27334             arguments[i].call(graph, graph);
27335         }
27336
27337         if (this.frozen) graph.frozen = true;
27338
27339         return graph;
27340     },
27341
27342     // Obliterates any existing entities
27343     load: function(entities) {
27344         var base = this.base();
27345         this.entities = Object.create(base.entities);
27346
27347         for (var i in entities) {
27348             this.entities[i] = entities[i];
27349             this._updateCalculated(base.entities[i], this.entities[i]);
27350         }
27351
27352         return this;
27353     }
27354 };
27355 iD.History = function(context) {
27356     var stack, index, tree,
27357         imageryUsed = ['Bing'],
27358         dispatch = d3.dispatch('change', 'undone', 'redone'),
27359         lock = iD.util.SessionMutex('lock');
27360
27361     function perform(actions) {
27362         actions = Array.prototype.slice.call(actions);
27363
27364         var annotation;
27365
27366         if (!_.isFunction(_.last(actions))) {
27367             annotation = actions.pop();
27368         }
27369
27370         var graph = stack[index].graph;
27371         for (var i = 0; i < actions.length; i++) {
27372             graph = actions[i](graph);
27373         }
27374
27375         return {
27376             graph: graph,
27377             annotation: annotation,
27378             imageryUsed: imageryUsed
27379         };
27380     }
27381
27382     function change(previous) {
27383         var difference = iD.Difference(previous, history.graph());
27384         dispatch.change(difference);
27385         return difference;
27386     }
27387
27388     // iD uses namespaced keys so multiple installations do not conflict
27389     function getKey(n) {
27390         return 'iD_' + window.location.origin + '_' + n;
27391     }
27392
27393     var history = {
27394         graph: function() {
27395             return stack[index].graph;
27396         },
27397
27398         base: function() {
27399             return stack[0].graph;
27400         },
27401
27402         merge: function(entities, extent) {
27403             stack[0].graph.rebase(entities, _.pluck(stack, 'graph'), false);
27404             tree.rebase(entities, false);
27405
27406             dispatch.change(undefined, extent);
27407         },
27408
27409         perform: function() {
27410             var previous = stack[index].graph;
27411
27412             stack = stack.slice(0, index + 1);
27413             stack.push(perform(arguments));
27414             index++;
27415
27416             return change(previous);
27417         },
27418
27419         replace: function() {
27420             var previous = stack[index].graph;
27421
27422             // assert(index == stack.length - 1)
27423             stack[index] = perform(arguments);
27424
27425             return change(previous);
27426         },
27427
27428         pop: function() {
27429             var previous = stack[index].graph;
27430
27431             if (index > 0) {
27432                 index--;
27433                 stack.pop();
27434                 return change(previous);
27435             }
27436         },
27437
27438         // Same as calling pop and then perform
27439         overwrite: function() {
27440             var previous = stack[index].graph;
27441
27442             if (index > 0) {
27443                 index--;
27444                 stack.pop();
27445             }
27446             stack = stack.slice(0, index + 1);
27447             stack.push(perform(arguments));
27448             index++;
27449
27450             return change(previous);
27451         },
27452
27453         undo: function() {
27454             var previous = stack[index].graph;
27455
27456             // Pop to the next annotated state.
27457             while (index > 0) {
27458                 index--;
27459                 if (stack[index].annotation) break;
27460             }
27461
27462             dispatch.undone();
27463             return change(previous);
27464         },
27465
27466         redo: function() {
27467             var previous = stack[index].graph;
27468
27469             while (index < stack.length - 1) {
27470                 index++;
27471                 if (stack[index].annotation) break;
27472             }
27473
27474             dispatch.redone();
27475             return change(previous);
27476         },
27477
27478         undoAnnotation: function() {
27479             var i = index;
27480             while (i >= 0) {
27481                 if (stack[i].annotation) return stack[i].annotation;
27482                 i--;
27483             }
27484         },
27485
27486         redoAnnotation: function() {
27487             var i = index + 1;
27488             while (i <= stack.length - 1) {
27489                 if (stack[i].annotation) return stack[i].annotation;
27490                 i++;
27491             }
27492         },
27493
27494         intersects: function(extent) {
27495             return tree.intersects(extent, stack[index].graph);
27496         },
27497
27498         difference: function() {
27499             var base = stack[0].graph,
27500                 head = stack[index].graph;
27501             return iD.Difference(base, head);
27502         },
27503
27504         changes: function(action) {
27505             var base = stack[0].graph,
27506                 head = stack[index].graph;
27507
27508             if (action) {
27509                 head = action(head);
27510             }
27511
27512             var difference = iD.Difference(base, head);
27513
27514             return {
27515                 modified: difference.modified(),
27516                 created: difference.created(),
27517                 deleted: difference.deleted()
27518             };
27519         },
27520
27521         validate: function(changes) {
27522             return _(iD.validations)
27523                 .map(function(fn) { return fn()(changes, stack[index].graph); })
27524                 .flatten()
27525                 .value();
27526         },
27527
27528         hasChanges: function() {
27529             return this.difference().length() > 0;
27530         },
27531
27532         imageryUsed: function(sources) {
27533             if (sources) {
27534                 imageryUsed = sources;
27535                 return history;
27536             } else {
27537                 return _(stack.slice(1, index + 1))
27538                     .pluck('imageryUsed')
27539                     .flatten()
27540                     .unique()
27541                     .without(undefined, 'Custom')
27542                     .value();
27543             }
27544         },
27545
27546         reset: function() {
27547             stack = [{graph: iD.Graph()}];
27548             index = 0;
27549             tree = iD.Tree(stack[0].graph);
27550             dispatch.change();
27551             return history;
27552         },
27553
27554         toJSON: function() {
27555             if (!this.hasChanges()) return;
27556
27557             var allEntities = {},
27558                 baseEntities = {},
27559                 base = stack[0];
27560
27561             var s = stack.map(function(i) {
27562                 var modified = [], deleted = [];
27563
27564                 _.forEach(i.graph.entities, function(entity, id) {
27565                     if (entity) {
27566                         var key = iD.Entity.key(entity);
27567                         allEntities[key] = entity;
27568                         modified.push(key);
27569                     } else {
27570                         deleted.push(id);
27571                     }
27572
27573                     // make sure that the originals of changed or deleted entities get merged
27574                     // into the base of the stack after restoring the data from JSON.
27575                     if (id in base.graph.entities) {
27576                         baseEntities[id] = base.graph.entities[id];
27577                     }
27578                     // get originals of parent entities too
27579                     _.forEach(base.graph._parentWays[id], function(parentId) {
27580                         if (parentId in base.graph.entities) {
27581                             baseEntities[parentId] = base.graph.entities[parentId];
27582                         }
27583                     });
27584                 });
27585
27586                 var x = {};
27587
27588                 if (modified.length) x.modified = modified;
27589                 if (deleted.length) x.deleted = deleted;
27590                 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
27591                 if (i.annotation) x.annotation = i.annotation;
27592
27593                 return x;
27594             });
27595
27596             return JSON.stringify({
27597                 version: 3,
27598                 entities: _.values(allEntities),
27599                 baseEntities: _.values(baseEntities),
27600                 stack: s,
27601                 nextIDs: iD.Entity.id.next,
27602                 index: index
27603             });
27604         },
27605
27606         fromJSON: function(json, loadChildNodes) {
27607             var h = JSON.parse(json),
27608                 loadComplete = true;
27609
27610             iD.Entity.id.next = h.nextIDs;
27611             index = h.index;
27612
27613             if (h.version === 2 || h.version === 3) {
27614                 var allEntities = {};
27615
27616                 h.entities.forEach(function(entity) {
27617                     allEntities[iD.Entity.key(entity)] = iD.Entity(entity);
27618                 });
27619
27620                 if (h.version === 3) {
27621                     // This merges originals for changed entities into the base of
27622                     // the stack even if the current stack doesn't have them (for
27623                     // example when iD has been restarted in a different region)
27624                     var baseEntities = h.baseEntities.map(function(d) { return iD.Entity(d); });
27625                     stack[0].graph.rebase(baseEntities, _.pluck(stack, 'graph'), true);
27626                     tree.rebase(baseEntities, true);
27627
27628                     // When we restore a modified way, we also need to fetch any missing
27629                     // childnodes that would normally have been downloaded with it.. #2142
27630                     if (loadChildNodes) {
27631                         var missing =  _(baseEntities)
27632                                 .filter('type', 'way')
27633                                 .pluck('nodes')
27634                                 .flatten()
27635                                 .uniq()
27636                                 .reject(function(n) { return stack[0].graph.hasEntity(n); })
27637                                 .value();
27638
27639                         if (!_.isEmpty(missing)) {
27640                             loadComplete = false;
27641                             context.redrawEnable(false);
27642
27643                             var loading = iD.ui.Loading(context).blocking(true);
27644                             context.container().call(loading);
27645
27646                             var childNodesLoaded = function(err, result) {
27647                                 if (!err) {
27648                                     var visible = _.groupBy(result.data, 'visible');
27649                                     if (!_.isEmpty(visible.true)) {
27650                                         missing = _.difference(missing, _.pluck(visible.true, 'id'));
27651                                         stack[0].graph.rebase(visible.true, _.pluck(stack, 'graph'), true);
27652                                         tree.rebase(visible.true, true);
27653                                     }
27654
27655                                     // fetch older versions of nodes that were deleted..
27656                                     _.each(visible.false, function(entity) {
27657                                         context.connection()
27658                                             .loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
27659                                     });
27660                                 }
27661
27662                                 if (err || _.isEmpty(missing)) {
27663                                     loading.close();
27664                                     context.redrawEnable(true);
27665                                     dispatch.change();
27666                                 }
27667                             };
27668
27669                             context.connection().loadMultiple(missing, childNodesLoaded);
27670                         }
27671                     }
27672                 }
27673
27674                 stack = h.stack.map(function(d) {
27675                     var entities = {}, entity;
27676
27677                     if (d.modified) {
27678                         d.modified.forEach(function(key) {
27679                             entity = allEntities[key];
27680                             entities[entity.id] = entity;
27681                         });
27682                     }
27683
27684                     if (d.deleted) {
27685                         d.deleted.forEach(function(id) {
27686                             entities[id] = undefined;
27687                         });
27688                     }
27689
27690                     return {
27691                         graph: iD.Graph(stack[0].graph).load(entities),
27692                         annotation: d.annotation,
27693                         imageryUsed: d.imageryUsed
27694                     };
27695                 });
27696
27697             } else { // original version
27698                 stack = h.stack.map(function(d) {
27699                     var entities = {};
27700
27701                     for (var i in d.entities) {
27702                         var entity = d.entities[i];
27703                         entities[i] = entity === 'undefined' ? undefined : iD.Entity(entity);
27704                     }
27705
27706                     d.graph = iD.Graph(stack[0].graph).load(entities);
27707                     return d;
27708                 });
27709             }
27710
27711             if (loadComplete) {
27712                 dispatch.change();
27713             }
27714
27715             return history;
27716         },
27717
27718         save: function() {
27719             if (lock.locked()) context.storage(getKey('saved_history'), history.toJSON() || null);
27720             return history;
27721         },
27722
27723         clearSaved: function() {
27724             context.debouncedSave.cancel();
27725             if (lock.locked()) context.storage(getKey('saved_history'), null);
27726             return history;
27727         },
27728
27729         lock: function() {
27730             return lock.lock();
27731         },
27732
27733         unlock: function() {
27734             lock.unlock();
27735         },
27736
27737         // is iD not open in another window and it detects that
27738         // there's a history stored in localStorage that's recoverable?
27739         restorableChanges: function() {
27740             return lock.locked() && !!context.storage(getKey('saved_history'));
27741         },
27742
27743         // load history from a version stored in localStorage
27744         restore: function() {
27745             if (!lock.locked()) return;
27746
27747             var json = context.storage(getKey('saved_history'));
27748             if (json) history.fromJSON(json, true);
27749         },
27750
27751         _getKey: getKey
27752
27753     };
27754
27755     history.reset();
27756
27757     return d3.rebind(history, dispatch, 'on');
27758 };
27759 iD.Node = iD.Entity.node = function iD_Node() {
27760     if (!(this instanceof iD_Node)) {
27761         return (new iD_Node()).initialize(arguments);
27762     } else if (arguments.length) {
27763         this.initialize(arguments);
27764     }
27765 };
27766
27767 iD.Node.prototype = Object.create(iD.Entity.prototype);
27768
27769 _.extend(iD.Node.prototype, {
27770     type: 'node',
27771
27772     extent: function() {
27773         return new iD.geo.Extent(this.loc);
27774     },
27775
27776     geometry: function(graph) {
27777         return graph.transient(this, 'geometry', function() {
27778             return graph.isPoi(this) ? 'point' : 'vertex';
27779         });
27780     },
27781
27782     move: function(loc) {
27783         return this.update({loc: loc});
27784     },
27785
27786     isIntersection: function(resolver) {
27787         return resolver.transient(this, 'isIntersection', function() {
27788             return resolver.parentWays(this).filter(function(parent) {
27789                 return (parent.tags.highway ||
27790                     parent.tags.waterway ||
27791                     parent.tags.railway ||
27792                     parent.tags.aeroway) &&
27793                     parent.geometry(resolver) === 'line';
27794             }).length > 1;
27795         });
27796     },
27797
27798     isHighwayIntersection: function(resolver) {
27799         return resolver.transient(this, 'isHighwayIntersection', function() {
27800             return resolver.parentWays(this).filter(function(parent) {
27801                 return parent.tags.highway && parent.geometry(resolver) === 'line';
27802             }).length > 1;
27803         });
27804     },
27805
27806     asJXON: function(changeset_id) {
27807         var r = {
27808             node: {
27809                 '@id': this.osmId(),
27810                 '@lon': this.loc[0],
27811                 '@lat': this.loc[1],
27812                 '@version': (this.version || 0),
27813                 tag: _.map(this.tags, function(v, k) {
27814                     return { keyAttributes: { k: k, v: v } };
27815                 })
27816             }
27817         };
27818         if (changeset_id) r.node['@changeset'] = changeset_id;
27819         return r;
27820     },
27821
27822     asGeoJSON: function() {
27823         return {
27824             type: 'Point',
27825             coordinates: this.loc
27826         };
27827     }
27828 });
27829 iD.Relation = iD.Entity.relation = function iD_Relation() {
27830     if (!(this instanceof iD_Relation)) {
27831         return (new iD_Relation()).initialize(arguments);
27832     } else if (arguments.length) {
27833         this.initialize(arguments);
27834     }
27835 };
27836
27837 iD.Relation.prototype = Object.create(iD.Entity.prototype);
27838
27839 iD.Relation.creationOrder = function(a, b) {
27840     var aId = parseInt(iD.Entity.id.toOSM(a.id), 10);
27841     var bId = parseInt(iD.Entity.id.toOSM(b.id), 10);
27842
27843     if (aId < 0 || bId < 0) return aId - bId;
27844     return bId - aId;
27845 };
27846
27847 _.extend(iD.Relation.prototype, {
27848     type: 'relation',
27849     members: [],
27850
27851     copy: function(resolver, copies) {
27852         if (copies[this.id])
27853             return copies[this.id];
27854
27855         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
27856
27857         var members = this.members.map(function(member) {
27858             return _.extend({}, member, {id: resolver.entity(member.id).copy(resolver, copies).id});
27859         });
27860
27861         copy = copy.update({members: members});
27862         copies[this.id] = copy;
27863
27864         return copy;
27865     },
27866
27867     extent: function(resolver, memo) {
27868         return resolver.transient(this, 'extent', function() {
27869             if (memo && memo[this.id]) return iD.geo.Extent();
27870             memo = memo || {};
27871             memo[this.id] = true;
27872
27873             var extent = iD.geo.Extent();
27874             for (var i = 0; i < this.members.length; i++) {
27875                 var member = resolver.hasEntity(this.members[i].id);
27876                 if (member) {
27877                     extent._extend(member.extent(resolver, memo));
27878                 }
27879             }
27880             return extent;
27881         });
27882     },
27883
27884     geometry: function(graph) {
27885         return graph.transient(this, 'geometry', function() {
27886             return this.isMultipolygon() ? 'area' : 'relation';
27887         });
27888     },
27889
27890     isDegenerate: function() {
27891         return this.members.length === 0;
27892     },
27893
27894     // Return an array of members, each extended with an 'index' property whose value
27895     // is the member index.
27896     indexedMembers: function() {
27897         var result = new Array(this.members.length);
27898         for (var i = 0; i < this.members.length; i++) {
27899             result[i] = _.extend({}, this.members[i], {index: i});
27900         }
27901         return result;
27902     },
27903
27904     // Return the first member with the given role. A copy of the member object
27905     // is returned, extended with an 'index' property whose value is the member index.
27906     memberByRole: function(role) {
27907         for (var i = 0; i < this.members.length; i++) {
27908             if (this.members[i].role === role) {
27909                 return _.extend({}, this.members[i], {index: i});
27910             }
27911         }
27912     },
27913
27914     // Return the first member with the given id. A copy of the member object
27915     // is returned, extended with an 'index' property whose value is the member index.
27916     memberById: function(id) {
27917         for (var i = 0; i < this.members.length; i++) {
27918             if (this.members[i].id === id) {
27919                 return _.extend({}, this.members[i], {index: i});
27920             }
27921         }
27922     },
27923
27924     // Return the first member with the given id and role. A copy of the member object
27925     // is returned, extended with an 'index' property whose value is the member index.
27926     memberByIdAndRole: function(id, role) {
27927         for (var i = 0; i < this.members.length; i++) {
27928             if (this.members[i].id === id && this.members[i].role === role) {
27929                 return _.extend({}, this.members[i], {index: i});
27930             }
27931         }
27932     },
27933
27934     addMember: function(member, index) {
27935         var members = this.members.slice();
27936         members.splice(index === undefined ? members.length : index, 0, member);
27937         return this.update({members: members});
27938     },
27939
27940     updateMember: function(member, index) {
27941         var members = this.members.slice();
27942         members.splice(index, 1, _.extend({}, members[index], member));
27943         return this.update({members: members});
27944     },
27945
27946     removeMember: function(index) {
27947         var members = this.members.slice();
27948         members.splice(index, 1);
27949         return this.update({members: members});
27950     },
27951
27952     removeMembersWithID: function(id) {
27953         var members = _.reject(this.members, function(m) { return m.id === id; });
27954         return this.update({members: members});
27955     },
27956
27957     // Wherever a member appears with id `needle.id`, replace it with a member
27958     // with id `replacement.id`, type `replacement.type`, and the original role,
27959     // unless a member already exists with that id and role. Return an updated
27960     // relation.
27961     replaceMember: function(needle, replacement) {
27962         if (!this.memberById(needle.id))
27963             return this;
27964
27965         var members = [];
27966
27967         for (var i = 0; i < this.members.length; i++) {
27968             var member = this.members[i];
27969             if (member.id !== needle.id) {
27970                 members.push(member);
27971             } else if (!this.memberByIdAndRole(replacement.id, member.role)) {
27972                 members.push({id: replacement.id, type: replacement.type, role: member.role});
27973             }
27974         }
27975
27976         return this.update({members: members});
27977     },
27978
27979     asJXON: function(changeset_id) {
27980         var r = {
27981             relation: {
27982                 '@id': this.osmId(),
27983                 '@version': this.version || 0,
27984                 member: _.map(this.members, function(member) {
27985                     return { keyAttributes: { type: member.type, role: member.role, ref: iD.Entity.id.toOSM(member.id) } };
27986                 }),
27987                 tag: _.map(this.tags, function(v, k) {
27988                     return { keyAttributes: { k: k, v: v } };
27989                 })
27990             }
27991         };
27992         if (changeset_id) r.relation['@changeset'] = changeset_id;
27993         return r;
27994     },
27995
27996     asGeoJSON: function(resolver) {
27997         return resolver.transient(this, 'GeoJSON', function () {
27998             if (this.isMultipolygon()) {
27999                 return {
28000                     type: 'MultiPolygon',
28001                     coordinates: this.multipolygon(resolver)
28002                 };
28003             } else {
28004                 return {
28005                     type: 'FeatureCollection',
28006                     properties: this.tags,
28007                     features: this.members.map(function (member) {
28008                         return _.extend({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
28009                     })
28010                 };
28011             }
28012         });
28013     },
28014
28015     area: function(resolver) {
28016         return resolver.transient(this, 'area', function() {
28017             return d3.geo.area(this.asGeoJSON(resolver));
28018         });
28019     },
28020
28021     isMultipolygon: function() {
28022         return this.tags.type === 'multipolygon';
28023     },
28024
28025     isComplete: function(resolver) {
28026         for (var i = 0; i < this.members.length; i++) {
28027             if (!resolver.hasEntity(this.members[i].id)) {
28028                 return false;
28029             }
28030         }
28031         return true;
28032     },
28033
28034     isRestriction: function() {
28035         return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
28036     },
28037
28038     // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
28039     // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
28040     //
28041     // This corresponds to the structure needed for rendering a multipolygon path using a
28042     // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
28043     //
28044     // In the case of invalid geometries, this function will still return a result which
28045     // includes the nodes of all way members, but some Nds may be unclosed and some inner
28046     // rings not matched with the intended outer ring.
28047     //
28048     multipolygon: function(resolver) {
28049         var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
28050             inners = this.members.filter(function(m) { return 'inner' === m.role; });
28051
28052         outers = iD.geo.joinWays(outers, resolver);
28053         inners = iD.geo.joinWays(inners, resolver);
28054
28055         outers = outers.map(function(outer) { return _.pluck(outer.nodes, 'loc'); });
28056         inners = inners.map(function(inner) { return _.pluck(inner.nodes, 'loc'); });
28057
28058         var result = outers.map(function(o) {
28059             // Heuristic for detecting counterclockwise winding order. Assumes
28060             // that OpenStreetMap polygons are not hemisphere-spanning.
28061             return [d3.geo.area({type: 'Polygon', coordinates: [o]}) > 2 * Math.PI ? o.reverse() : o];
28062         });
28063
28064         function findOuter(inner) {
28065             var o, outer;
28066
28067             for (o = 0; o < outers.length; o++) {
28068                 outer = outers[o];
28069                 if (iD.geo.polygonContainsPolygon(outer, inner))
28070                     return o;
28071             }
28072
28073             for (o = 0; o < outers.length; o++) {
28074                 outer = outers[o];
28075                 if (iD.geo.polygonIntersectsPolygon(outer, inner))
28076                     return o;
28077             }
28078         }
28079
28080         for (var i = 0; i < inners.length; i++) {
28081             var inner = inners[i];
28082
28083             if (d3.geo.area({type: 'Polygon', coordinates: [inner]}) < 2 * Math.PI) {
28084                 inner = inner.reverse();
28085             }
28086
28087             var o = findOuter(inners[i]);
28088             if (o !== undefined)
28089                 result[o].push(inners[i]);
28090             else
28091                 result.push([inners[i]]); // Invalid geometry
28092         }
28093
28094         return result;
28095     }
28096 });
28097 iD.oneWayTags = {
28098     'aerialway': {
28099         'chair_lift': true,
28100         'mixed_lift': true,
28101         't-bar': true,
28102         'j-bar': true,
28103         'platter': true,
28104         'rope_tow': true,
28105         'magic_carpet': true,
28106         'yes': true
28107     },
28108     'highway': {
28109         'motorway': true,
28110         'motorway_link': true
28111     },
28112     'junction': {
28113         'roundabout': true
28114     },
28115     'man_made': {
28116         'piste:halfpipe': true
28117     },
28118     'piste:type': {
28119         'downhill': true,
28120         'sled': true,
28121         'yes': true
28122     },
28123     'waterway': {
28124         'river': true,
28125         'stream': true
28126     }
28127 };
28128
28129 iD.pavedTags = {
28130     'surface': {
28131         'paved': true,
28132         'asphalt': true,
28133         'concrete': true
28134     },
28135     'tracktype': {
28136         'grade1': true
28137     }
28138 };
28139
28140 iD.interestingTag = function (key) {
28141     return key !== 'attribution' &&
28142         key !== 'created_by' &&
28143         key !== 'source' &&
28144         key !== 'odbl' &&
28145         key.indexOf('tiger:') !== 0;
28146
28147 };
28148 iD.Tree = function(head) {
28149     var rtree = rbush(),
28150         rectangles = {};
28151
28152     function entityRectangle(entity) {
28153         var rect = entity.extent(head).rectangle();
28154         rect.id = entity.id;
28155         rectangles[entity.id] = rect;
28156         return rect;
28157     }
28158
28159     function updateParents(entity, insertions, memo) {
28160         head.parentWays(entity).forEach(function(way) {
28161             if (rectangles[way.id]) {
28162                 rtree.remove(rectangles[way.id]);
28163                 insertions[way.id] = way;
28164             }
28165             updateParents(way, insertions, memo);
28166         });
28167
28168         head.parentRelations(entity).forEach(function(relation) {
28169             if (memo[entity.id]) return;
28170             memo[entity.id] = true;
28171             if (rectangles[relation.id]) {
28172                 rtree.remove(rectangles[relation.id]);
28173                 insertions[relation.id] = relation;
28174             }
28175             updateParents(relation, insertions, memo);
28176         });
28177     }
28178
28179     var tree = {};
28180
28181     tree.rebase = function(entities, force) {
28182         var insertions = {};
28183
28184         for (var i = 0; i < entities.length; i++) {
28185             var entity = entities[i];
28186
28187             if (!entity.visible)
28188                 continue;
28189
28190             if (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id]) {
28191                 if (!force) {
28192                     continue;
28193                 } else if (rectangles[entity.id]) {
28194                     rtree.remove(rectangles[entity.id]);
28195                 }
28196             }
28197
28198             insertions[entity.id] = entity;
28199             updateParents(entity, insertions, {});
28200         }
28201
28202         rtree.load(_.map(insertions, entityRectangle));
28203
28204         return tree;
28205     };
28206
28207     tree.intersects = function(extent, graph) {
28208         if (graph !== head) {
28209             var diff = iD.Difference(head, graph),
28210                 insertions = {};
28211
28212             head = graph;
28213
28214             diff.deleted().forEach(function(entity) {
28215                 rtree.remove(rectangles[entity.id]);
28216                 delete rectangles[entity.id];
28217             });
28218
28219             diff.modified().forEach(function(entity) {
28220                 rtree.remove(rectangles[entity.id]);
28221                 insertions[entity.id] = entity;
28222                 updateParents(entity, insertions, {});
28223             });
28224
28225             diff.created().forEach(function(entity) {
28226                 insertions[entity.id] = entity;
28227             });
28228
28229             rtree.load(_.map(insertions, entityRectangle));
28230         }
28231
28232         return rtree.search(extent.rectangle()).map(function(rect) {
28233             return head.entity(rect.id);
28234         });
28235     };
28236
28237     return tree;
28238 };
28239 iD.Way = iD.Entity.way = function iD_Way() {
28240     if (!(this instanceof iD_Way)) {
28241         return (new iD_Way()).initialize(arguments);
28242     } else if (arguments.length) {
28243         this.initialize(arguments);
28244     }
28245 };
28246
28247 iD.Way.prototype = Object.create(iD.Entity.prototype);
28248
28249 _.extend(iD.Way.prototype, {
28250     type: 'way',
28251     nodes: [],
28252
28253     copy: function(resolver, copies) {
28254         if (copies[this.id])
28255             return copies[this.id];
28256
28257         var copy = iD.Entity.prototype.copy.call(this, resolver, copies);
28258
28259         var nodes = this.nodes.map(function(id) {
28260             return resolver.entity(id).copy(resolver, copies).id;
28261         });
28262
28263         copy = copy.update({nodes: nodes});
28264         copies[this.id] = copy;
28265
28266         return copy;
28267     },
28268
28269     extent: function(resolver) {
28270         return resolver.transient(this, 'extent', function() {
28271             var extent = iD.geo.Extent();
28272             for (var i = 0; i < this.nodes.length; i++) {
28273                 var node = resolver.hasEntity(this.nodes[i]);
28274                 if (node) {
28275                     extent._extend(node.extent());
28276                 }
28277             }
28278             return extent;
28279         });
28280     },
28281
28282     first: function() {
28283         return this.nodes[0];
28284     },
28285
28286     last: function() {
28287         return this.nodes[this.nodes.length - 1];
28288     },
28289
28290     contains: function(node) {
28291         return this.nodes.indexOf(node) >= 0;
28292     },
28293
28294     affix: function(node) {
28295         if (this.nodes[0] === node) return 'prefix';
28296         if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
28297     },
28298
28299     layer: function() {
28300         // explicit layer tag, clamp between -10, 10..
28301         if (this.tags.layer !== undefined) {
28302             return Math.max(-10, Math.min(+(this.tags.layer), 10));
28303         }
28304
28305         // implied layer tag..
28306         if (this.tags.location === 'overground') return 1;
28307         if (this.tags.location === 'underground') return -1;
28308         if (this.tags.location === 'underwater') return -10;
28309
28310         if (this.tags.power === 'line') return 10;
28311         if (this.tags.power === 'minor_line') return 10;
28312         if (this.tags.aerialway) return 10;
28313         if (this.tags.bridge) return 1;
28314         if (this.tags.cutting) return -1;
28315         if (this.tags.tunnel) return -1;
28316         if (this.tags.waterway) return -1;
28317         if (this.tags.man_made === 'pipeline') return -10;
28318         if (this.tags.boundary) return -10;
28319         return 0;
28320     },
28321
28322     isOneWay: function() {
28323         // explicit oneway tag..
28324         if (['yes', '1', '-1'].indexOf(this.tags.oneway) !== -1) { return true; }
28325         if (['no', '0'].indexOf(this.tags.oneway) !== -1) { return false; }
28326
28327         // implied oneway tag..
28328         for (var key in this.tags) {
28329             if (key in iD.oneWayTags && (this.tags[key] in iD.oneWayTags[key]))
28330                 return true;
28331         }
28332         return false;
28333     },
28334
28335     isClosed: function() {
28336         return this.nodes.length > 0 && this.first() === this.last();
28337     },
28338
28339     isConvex: function(resolver) {
28340         if (!this.isClosed() || this.isDegenerate()) return null;
28341
28342         var nodes = _.uniq(resolver.childNodes(this)),
28343             coords = _.pluck(nodes, 'loc'),
28344             curr = 0, prev = 0;
28345
28346         for (var i = 0; i < coords.length; i++) {
28347             var o = coords[(i+1) % coords.length],
28348                 a = coords[i],
28349                 b = coords[(i+2) % coords.length],
28350                 res = iD.geo.cross(o, a, b);
28351
28352             curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
28353             if (curr === 0) {
28354                 continue;
28355             } else if (prev && curr !== prev) {
28356                 return false;
28357             }
28358             prev = curr;
28359         }
28360         return true;
28361     },
28362
28363     isArea: function() {
28364         if (this.tags.area === 'yes')
28365             return true;
28366         if (!this.isClosed() || this.tags.area === 'no')
28367             return false;
28368         for (var key in this.tags)
28369             if (key in iD.areaKeys && !(this.tags[key] in iD.areaKeys[key]))
28370                 return true;
28371         return false;
28372     },
28373
28374     isDegenerate: function() {
28375         return _.uniq(this.nodes).length < (this.isArea() ? 3 : 2);
28376     },
28377
28378     areAdjacent: function(n1, n2) {
28379         for (var i = 0; i < this.nodes.length; i++) {
28380             if (this.nodes[i] === n1) {
28381                 if (this.nodes[i - 1] === n2) return true;
28382                 if (this.nodes[i + 1] === n2) return true;
28383             }
28384         }
28385         return false;
28386     },
28387
28388     geometry: function(graph) {
28389         return graph.transient(this, 'geometry', function() {
28390             return this.isArea() ? 'area' : 'line';
28391         });
28392     },
28393
28394     addNode: function(id, index) {
28395         var nodes = this.nodes.slice();
28396         nodes.splice(index === undefined ? nodes.length : index, 0, id);
28397         return this.update({nodes: nodes});
28398     },
28399
28400     updateNode: function(id, index) {
28401         var nodes = this.nodes.slice();
28402         nodes.splice(index, 1, id);
28403         return this.update({nodes: nodes});
28404     },
28405
28406     replaceNode: function(needle, replacement) {
28407         if (this.nodes.indexOf(needle) < 0)
28408             return this;
28409
28410         var nodes = this.nodes.slice();
28411         for (var i = 0; i < nodes.length; i++) {
28412             if (nodes[i] === needle) {
28413                 nodes[i] = replacement;
28414             }
28415         }
28416         return this.update({nodes: nodes});
28417     },
28418
28419     removeNode: function(id) {
28420         var nodes = [];
28421
28422         for (var i = 0; i < this.nodes.length; i++) {
28423             var node = this.nodes[i];
28424             if (node !== id && nodes[nodes.length - 1] !== node) {
28425                 nodes.push(node);
28426             }
28427         }
28428
28429         // Preserve circularity
28430         if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] !== nodes[0]) {
28431             nodes.push(nodes[0]);
28432         }
28433
28434         return this.update({nodes: nodes});
28435     },
28436
28437     asJXON: function(changeset_id) {
28438         var r = {
28439             way: {
28440                 '@id': this.osmId(),
28441                 '@version': this.version || 0,
28442                 nd: _.map(this.nodes, function(id) {
28443                     return { keyAttributes: { ref: iD.Entity.id.toOSM(id) } };
28444                 }),
28445                 tag: _.map(this.tags, function(v, k) {
28446                     return { keyAttributes: { k: k, v: v } };
28447                 })
28448             }
28449         };
28450         if (changeset_id) r.way['@changeset'] = changeset_id;
28451         return r;
28452     },
28453
28454     asGeoJSON: function(resolver) {
28455         return resolver.transient(this, 'GeoJSON', function() {
28456             var coordinates = _.pluck(resolver.childNodes(this), 'loc');
28457             if (this.isArea() && this.isClosed()) {
28458                 return {
28459                     type: 'Polygon',
28460                     coordinates: [coordinates]
28461                 };
28462             } else {
28463                 return {
28464                     type: 'LineString',
28465                     coordinates: coordinates
28466                 };
28467             }
28468         });
28469     },
28470
28471     area: function(resolver) {
28472         return resolver.transient(this, 'area', function() {
28473             var nodes = resolver.childNodes(this);
28474
28475             var json = {
28476                 type: 'Polygon',
28477                 coordinates: [_.pluck(nodes, 'loc')]
28478             };
28479
28480             if (!this.isClosed() && nodes.length) {
28481                 json.coordinates[0].push(nodes[0].loc);
28482             }
28483
28484             var area = d3.geo.area(json);
28485
28486             // Heuristic for detecting counterclockwise winding order. Assumes
28487             // that OpenStreetMap polygons are not hemisphere-spanning.
28488             if (area > 2 * Math.PI) {
28489                 json.coordinates[0] = json.coordinates[0].reverse();
28490                 area = d3.geo.area(json);
28491             }
28492
28493             return isNaN(area) ? 0 : area;
28494         });
28495     }
28496 });
28497 iD.Background = function(context) {
28498     var dispatch = d3.dispatch('change'),
28499         baseLayer = iD.TileLayer().projection(context.projection),
28500         overlayLayers = [],
28501         backgroundSources;
28502
28503
28504     function findSource(id) {
28505         return _.find(backgroundSources, function(d) {
28506             return d.id && d.id === id;
28507         });
28508     }
28509
28510
28511     function background(selection) {
28512         var base = selection.selectAll('.layer-background')
28513             .data([0]);
28514
28515         base.enter()
28516             .insert('div', '.layer-data')
28517             .attr('class', 'layer layer-background');
28518
28519         base.call(baseLayer);
28520
28521         var overlays = selection.selectAll('.layer-overlay')
28522             .data(overlayLayers, function(d) { return d.source().name(); });
28523
28524         overlays.enter()
28525             .insert('div', '.layer-data')
28526             .attr('class', 'layer layer-overlay');
28527
28528         overlays.each(function(layer) {
28529             d3.select(this).call(layer);
28530         });
28531
28532         overlays.exit()
28533             .remove();
28534     }
28535
28536
28537     background.updateImagery = function() {
28538         var b = background.baseLayerSource(),
28539             o = overlayLayers.map(function (d) { return d.source().id; }).join(','),
28540             meters = iD.geo.offsetToMeters(b.offset()),
28541             epsilon = 0.01,
28542             x = +meters[0].toFixed(2),
28543             y = +meters[1].toFixed(2),
28544             q = iD.util.stringQs(location.hash.substring(1));
28545
28546         var id = b.id;
28547         if (id === 'custom') {
28548             id = 'custom:' + b.template;
28549         }
28550
28551         if (id) {
28552             q.background = id;
28553         } else {
28554             delete q.background;
28555         }
28556
28557         if (o) {
28558             q.overlays = o;
28559         } else {
28560             delete q.overlays;
28561         }
28562
28563         if (Math.abs(x) > epsilon || Math.abs(y) > epsilon) {
28564             q.offset = x + ',' + y;
28565         } else {
28566             delete q.offset;
28567         }
28568
28569         location.replace('#' + iD.util.qsString(q, true));
28570
28571         var imageryUsed = [b.imageryUsed()];
28572
28573         overlayLayers.forEach(function (d) {
28574             var source = d.source();
28575             if (!source.isLocatorOverlay()) {
28576                 imageryUsed.push(source.imageryUsed());
28577             }
28578         });
28579
28580         var gpx = context.layers().layer('gpx');
28581         if (gpx && gpx.enabled() && gpx.hasGpx()) {
28582             imageryUsed.push('Local GPX');
28583         }
28584
28585         context.history().imageryUsed(imageryUsed);
28586     };
28587
28588     background.sources = function(extent) {
28589         return backgroundSources.filter(function(source) {
28590             return source.intersects(extent);
28591         });
28592     };
28593
28594     background.dimensions = function(_) {
28595         baseLayer.dimensions(_);
28596
28597         overlayLayers.forEach(function(layer) {
28598             layer.dimensions(_);
28599         });
28600     };
28601
28602     background.baseLayerSource = function(d) {
28603         if (!arguments.length) return baseLayer.source();
28604         baseLayer.source(d);
28605         dispatch.change();
28606         background.updateImagery();
28607         return background;
28608     };
28609
28610     background.bing = function() {
28611         background.baseLayerSource(findSource('Bing'));
28612     };
28613
28614     background.showsLayer = function(d) {
28615         return d === baseLayer.source() ||
28616             (d.id === 'custom' && baseLayer.source().id === 'custom') ||
28617             overlayLayers.some(function(l) { return l.source() === d; });
28618     };
28619
28620     background.overlayLayerSources = function() {
28621         return overlayLayers.map(function (l) { return l.source(); });
28622     };
28623
28624     background.toggleOverlayLayer = function(d) {
28625         var layer;
28626
28627         for (var i = 0; i < overlayLayers.length; i++) {
28628             layer = overlayLayers[i];
28629             if (layer.source() === d) {
28630                 overlayLayers.splice(i, 1);
28631                 dispatch.change();
28632                 background.updateImagery();
28633                 return;
28634             }
28635         }
28636
28637         layer = iD.TileLayer()
28638             .source(d)
28639             .projection(context.projection)
28640             .dimensions(baseLayer.dimensions());
28641
28642         overlayLayers.push(layer);
28643         dispatch.change();
28644         background.updateImagery();
28645     };
28646
28647     background.nudge = function(d, zoom) {
28648         baseLayer.source().nudge(d, zoom);
28649         dispatch.change();
28650         background.updateImagery();
28651         return background;
28652     };
28653
28654     background.offset = function(d) {
28655         if (!arguments.length) return baseLayer.source().offset();
28656         baseLayer.source().offset(d);
28657         dispatch.change();
28658         background.updateImagery();
28659         return background;
28660     };
28661
28662     background.load = function(imagery) {
28663         function parseMap(qmap) {
28664             if (!qmap) return false;
28665             var args = qmap.split('/').map(Number);
28666             if (args.length < 3 || args.some(isNaN)) return false;
28667             return iD.geo.Extent([args[1], args[2]]);
28668         }
28669
28670         var q = iD.util.stringQs(location.hash.substring(1)),
28671             chosen = q.background || q.layer,
28672             extent = parseMap(q.map),
28673             best;
28674
28675         backgroundSources = imagery.map(function(source) {
28676             if (source.type === 'bing') {
28677                 return iD.BackgroundSource.Bing(source, dispatch);
28678             } else {
28679                 return iD.BackgroundSource(source);
28680             }
28681         });
28682
28683         backgroundSources.unshift(iD.BackgroundSource.None());
28684
28685         if (!chosen && extent) {
28686             best = _.find(this.sources(extent), function(s) { return s.best(); });
28687         }
28688
28689         if (chosen && chosen.indexOf('custom:') === 0) {
28690             background.baseLayerSource(iD.BackgroundSource.Custom(chosen.replace(/^custom:/, '')));
28691         } else {
28692             background.baseLayerSource(findSource(chosen) || best || findSource('Bing') || backgroundSources[1] || backgroundSources[0]);
28693         }
28694
28695         var locator = _.find(backgroundSources, function(d) {
28696             return d.overlay && d.default;
28697         });
28698
28699         if (locator) {
28700             background.toggleOverlayLayer(locator);
28701         }
28702
28703         var overlays = (q.overlays || '').split(',');
28704         overlays.forEach(function(overlay) {
28705             overlay = findSource(overlay);
28706             if (overlay) {
28707                 background.toggleOverlayLayer(overlay);
28708             }
28709         });
28710
28711         if (q.gpx) {
28712             var gpx = context.layers().layer('gpx');
28713             if (gpx) {
28714                 gpx.url(q.gpx);
28715             }
28716         }
28717
28718         if (q.offset) {
28719             var offset = q.offset.replace(/;/g, ',').split(',').map(function(n) {
28720                 return !isNaN(n) && n;
28721             });
28722
28723             if (offset.length === 2) {
28724                 background.offset(iD.geo.metersToOffset(offset));
28725             }
28726         }
28727     };
28728
28729     return d3.rebind(background, dispatch, 'on');
28730 };
28731 iD.BackgroundSource = function(data) {
28732     var source = _.clone(data),
28733         offset = [0, 0],
28734         name = source.name,
28735         best = !!source.best;
28736
28737     source.scaleExtent = data.scaleExtent || [0, 20];
28738     source.overzoom = data.overzoom !== false;
28739
28740     source.offset = function(_) {
28741         if (!arguments.length) return offset;
28742         offset = _;
28743         return source;
28744     };
28745
28746     source.nudge = function(_, zoomlevel) {
28747         offset[0] += _[0] / Math.pow(2, zoomlevel);
28748         offset[1] += _[1] / Math.pow(2, zoomlevel);
28749         return source;
28750     };
28751
28752     source.name = function() {
28753         return name;
28754     };
28755
28756     source.best = function() {
28757         return best;
28758     };
28759
28760     source.imageryUsed = function() {
28761         return source.id || name;
28762     };
28763
28764     source.url = function(coord) {
28765         return data.template
28766             .replace('{x}', coord[0])
28767             .replace('{y}', coord[1])
28768             // TMS-flipped y coordinate
28769             .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
28770             .replace(/\{z(oom)?\}/, coord[2])
28771             .replace(/\{switch:([^}]+)\}/, function(s, r) {
28772                 var subdomains = r.split(',');
28773                 return subdomains[(coord[0] + coord[1]) % subdomains.length];
28774             })
28775             .replace('{u}', function() {
28776                 var u = '';
28777                 for (var zoom = coord[2]; zoom > 0; zoom--) {
28778                     var b = 0;
28779                     var mask = 1 << (zoom - 1);
28780                     if ((coord[0] & mask) !== 0) b++;
28781                     if ((coord[1] & mask) !== 0) b += 2;
28782                     u += b.toString();
28783                 }
28784                 return u;
28785             });
28786     };
28787
28788     source.intersects = function(extent) {
28789         extent = extent.polygon();
28790         return !data.polygon || data.polygon.some(function(polygon) {
28791             return iD.geo.polygonIntersectsPolygon(polygon, extent, true);
28792         });
28793     };
28794
28795     source.validZoom = function(z) {
28796         return source.scaleExtent[0] <= z &&
28797             (source.overzoom || source.scaleExtent[1] > z);
28798     };
28799
28800     source.isLocatorOverlay = function() {
28801         return name === 'Locator Overlay';
28802     };
28803
28804     source.copyrightNotices = function() {};
28805
28806     return source;
28807 };
28808
28809 iD.BackgroundSource.Bing = function(data, dispatch) {
28810     // http://msdn.microsoft.com/en-us/library/ff701716.aspx
28811     // http://msdn.microsoft.com/en-us/library/ff701701.aspx
28812
28813     data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
28814
28815     var bing = iD.BackgroundSource(data),
28816         key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU', // Same as P2 and JOSM
28817         url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' +
28818             key + '&jsonp={callback}',
28819         providers = [];
28820
28821     d3.jsonp(url, function(json) {
28822         providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
28823             return {
28824                 attribution: provider.attribution,
28825                 areas: provider.coverageAreas.map(function(area) {
28826                     return {
28827                         zoom: [area.zoomMin, area.zoomMax],
28828                         extent: iD.geo.Extent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
28829                     };
28830                 })
28831             };
28832         });
28833         dispatch.change();
28834     });
28835
28836     bing.copyrightNotices = function(zoom, extent) {
28837         zoom = Math.min(zoom, 21);
28838         return providers.filter(function(provider) {
28839             return _.any(provider.areas, function(area) {
28840                 return extent.intersects(area.extent) &&
28841                     area.zoom[0] <= zoom &&
28842                     area.zoom[1] >= zoom;
28843             });
28844         }).map(function(provider) {
28845             return provider.attribution;
28846         }).join(', ');
28847     };
28848
28849     bing.logo = 'bing_maps.png';
28850     bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
28851
28852     return bing;
28853 };
28854
28855 iD.BackgroundSource.None = function() {
28856     var source = iD.BackgroundSource({id: 'none', template: ''});
28857
28858     source.name = function() {
28859         return t('background.none');
28860     };
28861
28862     source.imageryUsed = function() {
28863         return 'None';
28864     };
28865
28866     return source;
28867 };
28868
28869 iD.BackgroundSource.Custom = function(template) {
28870     var source = iD.BackgroundSource({id: 'custom', template: template});
28871
28872     source.name = function() {
28873         return t('background.custom');
28874     };
28875
28876     source.imageryUsed = function() {
28877         return 'Custom (' + template + ')';
28878     };
28879
28880     return source;
28881 };
28882 iD.Features = function(context) {
28883     var traffic_roads = {
28884         'motorway': true,
28885         'motorway_link': true,
28886         'trunk': true,
28887         'trunk_link': true,
28888         'primary': true,
28889         'primary_link': true,
28890         'secondary': true,
28891         'secondary_link': true,
28892         'tertiary': true,
28893         'tertiary_link': true,
28894         'residential': true,
28895         'unclassified': true,
28896         'living_street': true
28897     };
28898
28899     var service_roads = {
28900         'service': true,
28901         'road': true,
28902         'track': true
28903     };
28904
28905     var paths = {
28906         'path': true,
28907         'footway': true,
28908         'cycleway': true,
28909         'bridleway': true,
28910         'steps': true,
28911         'pedestrian': true,
28912         'corridor': true
28913     };
28914
28915     var past_futures = {
28916         'proposed': true,
28917         'construction': true,
28918         'abandoned': true,
28919         'dismantled': true,
28920         'disused': true,
28921         'razed': true,
28922         'demolished': true,
28923         'obliterated': true
28924     };
28925
28926     var dispatch = d3.dispatch('change', 'redraw'),
28927         _cullFactor = 1,
28928         _cache = {},
28929         _features = {},
28930         _stats = {},
28931         _keys = [],
28932         _hidden = [];
28933
28934     function update() {
28935         _hidden = features.hidden();
28936         dispatch.change();
28937         dispatch.redraw();
28938     }
28939
28940     function defineFeature(k, filter, max) {
28941         _keys.push(k);
28942         _features[k] = {
28943             filter: filter,
28944             enabled: true,   // whether the user wants it enabled..
28945             count: 0,
28946             currentMax: (max || Infinity),
28947             defaultMax: (max || Infinity),
28948             enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
28949             disable: function() { this.enabled = false; this.currentMax = 0; },
28950             hidden: function() { return !context.editable() || this.count > this.currentMax * _cullFactor; },
28951             autoHidden: function() { return this.hidden() && this.currentMax > 0; }
28952         };
28953     }
28954
28955
28956     defineFeature('points', function isPoint(entity, resolver, geometry) {
28957         return geometry === 'point';
28958     }, 200);
28959
28960     defineFeature('traffic_roads', function isTrafficRoad(entity) {
28961         return traffic_roads[entity.tags.highway];
28962     });
28963
28964     defineFeature('service_roads', function isServiceRoad(entity) {
28965         return service_roads[entity.tags.highway];
28966     });
28967
28968     defineFeature('paths', function isPath(entity) {
28969         return paths[entity.tags.highway];
28970     });
28971
28972     defineFeature('buildings', function isBuilding(entity) {
28973         return (
28974             !!entity.tags['building:part'] ||
28975             (!!entity.tags.building && entity.tags.building !== 'no') ||
28976             entity.tags.amenity === 'shelter' ||
28977             entity.tags.parking === 'multi-storey' ||
28978             entity.tags.parking === 'sheds' ||
28979             entity.tags.parking === 'carports' ||
28980             entity.tags.parking === 'garage_boxes'
28981         );
28982     }, 250);
28983
28984     defineFeature('landuse', function isLanduse(entity, resolver, geometry) {
28985         return geometry === 'area' &&
28986             !_features.buildings.filter(entity) &&
28987             !_features.water.filter(entity);
28988     });
28989
28990     defineFeature('boundaries', function isBoundary(entity) {
28991         return !!entity.tags.boundary;
28992     });
28993
28994     defineFeature('water', function isWater(entity) {
28995         return (
28996             !!entity.tags.waterway ||
28997             entity.tags.natural === 'water' ||
28998             entity.tags.natural === 'coastline' ||
28999             entity.tags.natural === 'bay' ||
29000             entity.tags.landuse === 'pond' ||
29001             entity.tags.landuse === 'basin' ||
29002             entity.tags.landuse === 'reservoir' ||
29003             entity.tags.landuse === 'salt_pond'
29004         );
29005     });
29006
29007     defineFeature('rail', function isRail(entity) {
29008         return (
29009             !!entity.tags.railway ||
29010             entity.tags.landuse === 'railway'
29011         ) && !(
29012             traffic_roads[entity.tags.highway] ||
29013             service_roads[entity.tags.highway] ||
29014             paths[entity.tags.highway]
29015         );
29016     });
29017
29018     defineFeature('power', function isPower(entity) {
29019         return !!entity.tags.power;
29020     });
29021
29022     // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
29023     defineFeature('past_future', function isPastFuture(entity) {
29024         if (
29025             traffic_roads[entity.tags.highway] ||
29026             service_roads[entity.tags.highway] ||
29027             paths[entity.tags.highway]
29028         ) { return false; }
29029
29030         var strings = Object.keys(entity.tags);
29031
29032         for (var i = 0; i < strings.length; i++) {
29033             var s = strings[i];
29034             if (past_futures[s] || past_futures[entity.tags[s]]) { return true; }
29035         }
29036         return false;
29037     });
29038
29039     // Lines or areas that don't match another feature filter.
29040     // IMPORTANT: The 'others' feature must be the last one defined,
29041     //   so that code in getMatches can skip this test if `hasMatch = true`
29042     defineFeature('others', function isOther(entity, resolver, geometry) {
29043         return (geometry === 'line' || geometry === 'area');
29044     });
29045
29046
29047     function features() {}
29048
29049     features.features = function() {
29050         return _features;
29051     };
29052
29053     features.keys = function() {
29054         return _keys;
29055     };
29056
29057     features.enabled = function(k) {
29058         if (!arguments.length) {
29059             return _.filter(_keys, function(k) { return _features[k].enabled; });
29060         }
29061         return _features[k] && _features[k].enabled;
29062     };
29063
29064     features.disabled = function(k) {
29065         if (!arguments.length) {
29066             return _.reject(_keys, function(k) { return _features[k].enabled; });
29067         }
29068         return _features[k] && !_features[k].enabled;
29069     };
29070
29071     features.hidden = function(k) {
29072         if (!arguments.length) {
29073             return _.filter(_keys, function(k) { return _features[k].hidden(); });
29074         }
29075         return _features[k] && _features[k].hidden();
29076     };
29077
29078     features.autoHidden = function(k) {
29079         if (!arguments.length) {
29080             return _.filter(_keys, function(k) { return _features[k].autoHidden(); });
29081         }
29082         return _features[k] && _features[k].autoHidden();
29083     };
29084
29085     features.enable = function(k) {
29086         if (_features[k] && !_features[k].enabled) {
29087             _features[k].enable();
29088             update();
29089         }
29090     };
29091
29092     features.disable = function(k) {
29093         if (_features[k] && _features[k].enabled) {
29094             _features[k].disable();
29095             update();
29096         }
29097     };
29098
29099     features.toggle = function(k) {
29100         if (_features[k]) {
29101             (function(f) { return f.enabled ? f.disable() : f.enable(); }(_features[k]));
29102             update();
29103         }
29104     };
29105
29106     features.resetStats = function() {
29107         _.each(_features, function(f) { f.count = 0; });
29108         dispatch.change();
29109     };
29110
29111     features.gatherStats = function(d, resolver, dimensions) {
29112         var needsRedraw = false,
29113             type = _.groupBy(d, function(ent) { return ent.type; }),
29114             entities = [].concat(type.relation || [], type.way || [], type.node || []),
29115             currHidden, geometry, matches;
29116
29117         _.each(_features, function(f) { f.count = 0; });
29118
29119         // adjust the threshold for point/building culling based on viewport size..
29120         // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
29121         _cullFactor = dimensions[0] * dimensions[1] / 1000000;
29122
29123         for (var i = 0; i < entities.length; i++) {
29124             geometry = entities[i].geometry(resolver);
29125             if (!(geometry === 'vertex' || geometry === 'relation')) {
29126                 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
29127                 for (var j = 0; j < matches.length; j++) {
29128                     _features[matches[j]].count++;
29129                 }
29130             }
29131         }
29132
29133         currHidden = features.hidden();
29134         if (currHidden !== _hidden) {
29135             _hidden = currHidden;
29136             needsRedraw = true;
29137             dispatch.change();
29138         }
29139
29140         return needsRedraw;
29141     };
29142
29143     features.stats = function() {
29144         _.each(_keys, function(k) { _stats[k] = _features[k].count; });
29145         return _stats;
29146     };
29147
29148     features.clear = function(d) {
29149         for (var i = 0; i < d.length; i++) {
29150             features.clearEntity(d[i]);
29151         }
29152     };
29153
29154     features.clearEntity = function(entity) {
29155         delete _cache[iD.Entity.key(entity)];
29156     };
29157
29158     features.reset = function() {
29159         _cache = {};
29160     };
29161
29162     features.getMatches = function(entity, resolver, geometry) {
29163         if (geometry === 'vertex' || geometry === 'relation') return {};
29164
29165         var ent = iD.Entity.key(entity);
29166         if (!_cache[ent]) {
29167             _cache[ent] = {};
29168         }
29169
29170         if (!_cache[ent].matches) {
29171             var matches = {},
29172                 hasMatch = false;
29173
29174             for (var i = 0; i < _keys.length; i++) {
29175                 if (_keys[i] === 'others') {
29176                     if (hasMatch) continue;
29177
29178                     // Multipolygon members:
29179                     // If an entity...
29180                     //   1. is a way that hasn't matched other "interesting" feature rules,
29181                     //   2. and it belongs to a single parent multipolygon relation
29182                     // ...then match whatever feature rules the parent multipolygon has matched.
29183                     // see #2548, #2887
29184                     //
29185                     // IMPORTANT:
29186                     // For this to work, getMatches must be called on relations before ways.
29187                     //
29188                     if (entity.type === 'way') {
29189                         var parents = features.getParents(entity, resolver, geometry);
29190                         if (parents.length === 1 && parents[0].isMultipolygon()) {
29191                             var pkey = iD.Entity.key(parents[0]);
29192                             if (_cache[pkey] && _cache[pkey].matches) {
29193                                 matches = _.clone(_cache[pkey].matches);
29194                                 continue;
29195                             }
29196                         }
29197                     }
29198                 }
29199
29200                 if (_features[_keys[i]].filter(entity, resolver, geometry)) {
29201                     matches[_keys[i]] = hasMatch = true;
29202                 }
29203             }
29204             _cache[ent].matches = matches;
29205         }
29206
29207         return _cache[ent].matches;
29208     };
29209
29210     features.getParents = function(entity, resolver, geometry) {
29211         if (geometry === 'point') return [];
29212
29213         var ent = iD.Entity.key(entity);
29214         if (!_cache[ent]) {
29215             _cache[ent] = {};
29216         }
29217
29218         if (!_cache[ent].parents) {
29219             var parents = [];
29220             if (geometry === 'vertex') {
29221                 parents = resolver.parentWays(entity);
29222             } else {   // 'line', 'area', 'relation'
29223                 parents = resolver.parentRelations(entity);
29224             }
29225             _cache[ent].parents = parents;
29226         }
29227         return _cache[ent].parents;
29228     };
29229
29230     features.isHiddenFeature = function(entity, resolver, geometry) {
29231         if (!_hidden.length) return false;
29232         if (!entity.version) return false;
29233
29234         var matches = features.getMatches(entity, resolver, geometry);
29235
29236         for (var i = 0; i < _hidden.length; i++) {
29237             if (matches[_hidden[i]]) return true;
29238         }
29239         return false;
29240     };
29241
29242     features.isHiddenChild = function(entity, resolver, geometry) {
29243         if (!_hidden.length) return false;
29244         if (!entity.version || geometry === 'point') return false;
29245
29246         var parents = features.getParents(entity, resolver, geometry);
29247         if (!parents.length) return false;
29248
29249         for (var i = 0; i < parents.length; i++) {
29250             if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
29251                 return false;
29252             }
29253         }
29254         return true;
29255     };
29256
29257     features.hasHiddenConnections = function(entity, resolver) {
29258         if (!_hidden.length) return false;
29259         var childNodes, connections;
29260
29261         if (entity.type === 'midpoint') {
29262             childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
29263             connections = [];
29264         } else {
29265             childNodes = entity.nodes ? resolver.childNodes(entity) : [];
29266             connections = features.getParents(entity, resolver, entity.geometry(resolver));
29267         }
29268
29269         // gather ways connected to child nodes..
29270         connections = _.reduce(childNodes, function(result, e) {
29271             return resolver.isShared(e) ? _.union(result, resolver.parentWays(e)) : result;
29272         }, connections);
29273
29274         return connections.length ? _.any(connections, function(e) {
29275             return features.isHidden(e, resolver, e.geometry(resolver));
29276         }) : false;
29277     };
29278
29279     features.isHidden = function(entity, resolver, geometry) {
29280         if (!_hidden.length) return false;
29281         if (!entity.version) return false;
29282
29283         var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
29284         return fn(entity, resolver, geometry);
29285     };
29286
29287     features.filter = function(d, resolver) {
29288         if (!_hidden.length) return d;
29289
29290         var result = [];
29291         for (var i = 0; i < d.length; i++) {
29292             var entity = d[i];
29293             if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
29294                 result.push(entity);
29295             }
29296         }
29297         return result;
29298     };
29299
29300     return d3.rebind(features, dispatch, 'on');
29301 };
29302 iD.Map = function(context) {
29303     var dimensions = [1, 1],
29304         dispatch = d3.dispatch('move', 'drawn'),
29305         projection = context.projection,
29306         zoom = d3.behavior.zoom()
29307             .translate(projection.translate())
29308             .scale(projection.scale() * 2 * Math.PI)
29309             .scaleExtent([1024, 256 * Math.pow(2, 24)])
29310             .on('zoom', zoomPan),
29311         dblclickEnabled = true,
29312         redrawEnabled = true,
29313         transformStart,
29314         transformed = false,
29315         minzoom = 0,
29316         drawLayers = iD.svg.Layers(projection, context),
29317         drawPoints = iD.svg.Points(projection, context),
29318         drawVertices = iD.svg.Vertices(projection, context),
29319         drawLines = iD.svg.Lines(projection),
29320         drawAreas = iD.svg.Areas(projection),
29321         drawMidpoints = iD.svg.Midpoints(projection, context),
29322         drawLabels = iD.svg.Labels(projection, context),
29323         supersurface,
29324         wrapper,
29325         surface,
29326         mouse,
29327         mousemove;
29328
29329     function map(selection) {
29330         context.history()
29331             .on('change.map', redraw);
29332         context.background()
29333             .on('change.map', redraw);
29334         context.features()
29335             .on('redraw.map', redraw);
29336         drawLayers
29337             .on('change.map', function() {
29338                 context.background().updateImagery();
29339                 redraw();
29340             });
29341
29342         selection
29343             .on('dblclick.map', dblClick)
29344             .call(zoom);
29345
29346         supersurface = selection.append('div')
29347             .attr('id', 'supersurface')
29348             .call(iD.util.setTransform, 0, 0);
29349
29350         // Need a wrapper div because Opera can't cope with an absolutely positioned
29351         // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
29352         wrapper = supersurface
29353             .append('div')
29354             .attr('class', 'layer layer-data');
29355
29356         map.surface = surface = wrapper
29357             .call(drawLayers)
29358             .selectAll('.surface')
29359             .attr('id', 'surface');
29360
29361         surface
29362             .on('mousedown.zoom', function() {
29363                 if (d3.event.button === 2) {
29364                     d3.event.stopPropagation();
29365                 }
29366             }, true)
29367             .on('mouseup.zoom', function() {
29368                 if (resetTransform()) redraw();
29369             })
29370             .on('mousemove.map', function() {
29371                 mousemove = d3.event;
29372             })
29373             .on('mouseover.vertices', function() {
29374                 if (map.editable() && !transformed) {
29375                     var hover = d3.event.target.__data__;
29376                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29377                     dispatch.drawn({full: false});
29378                 }
29379             })
29380             .on('mouseout.vertices', function() {
29381                 if (map.editable() && !transformed) {
29382                     var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__;
29383                     surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom());
29384                     dispatch.drawn({full: false});
29385                 }
29386             });
29387
29388
29389         supersurface
29390             .call(context.background());
29391
29392
29393         context.on('enter.map', function() {
29394             if (map.editable() && !transformed) {
29395                 var all = context.intersects(map.extent()),
29396                     filter = d3.functor(true),
29397                     graph = context.graph();
29398
29399                 all = context.features().filter(all, graph);
29400                 surface
29401                     .call(drawVertices, graph, all, filter, map.extent(), map.zoom())
29402                     .call(drawMidpoints, graph, all, filter, map.trimmedExtent());
29403                 dispatch.drawn({full: false});
29404             }
29405         });
29406
29407         map.dimensions(selection.dimensions());
29408
29409         drawLabels.supersurface(supersurface);
29410     }
29411
29412     function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }
29413
29414     function drawVector(difference, extent) {
29415         var graph = context.graph(),
29416             features = context.features(),
29417             all = context.intersects(map.extent()),
29418             data, filter;
29419
29420         if (difference) {
29421             var complete = difference.complete(map.extent());
29422             data = _.compact(_.values(complete));
29423             filter = function(d) { return d.id in complete; };
29424             features.clear(data);
29425
29426         } else {
29427             // force a full redraw if gatherStats detects that a feature
29428             // should be auto-hidden (e.g. points or buildings)..
29429             if (features.gatherStats(all, graph, dimensions)) {
29430                 extent = undefined;
29431             }
29432
29433             if (extent) {
29434                 data = context.intersects(map.extent().intersection(extent));
29435                 var set = d3.set(_.pluck(data, 'id'));
29436                 filter = function(d) { return set.has(d.id); };
29437
29438             } else {
29439                 data = all;
29440                 filter = d3.functor(true);
29441             }
29442         }
29443
29444         data = features.filter(data, graph);
29445
29446         surface
29447             .call(drawVertices, graph, data, filter, map.extent(), map.zoom())
29448             .call(drawLines, graph, data, filter)
29449             .call(drawAreas, graph, data, filter)
29450             .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
29451             .call(drawLabels, graph, data, filter, dimensions, !difference && !extent)
29452             .call(drawPoints, graph, data, filter);
29453
29454         dispatch.drawn({full: true});
29455     }
29456
29457     function editOff() {
29458         context.features().resetStats();
29459         surface.selectAll('.layer-osm *').remove();
29460         dispatch.drawn({full: true});
29461     }
29462
29463     function dblClick() {
29464         if (!dblclickEnabled) {
29465             d3.event.preventDefault();
29466             d3.event.stopImmediatePropagation();
29467         }
29468     }
29469
29470     function zoomPan() {
29471         if (Math.log(d3.event.scale) / Math.LN2 - 8 < minzoom) {
29472             surface.interrupt();
29473             iD.ui.flash(context.container())
29474                 .select('.content')
29475                 .text(t('cannot_zoom'));
29476             setZoom(context.minEditableZoom(), true);
29477             queueRedraw();
29478             dispatch.move(map);
29479             return;
29480         }
29481
29482         projection
29483             .translate(d3.event.translate)
29484             .scale(d3.event.scale / (2 * Math.PI));
29485
29486         var scale = d3.event.scale / transformStart[0],
29487             tX = (d3.event.translate[0] / scale - transformStart[1][0]) * scale,
29488             tY = (d3.event.translate[1] / scale - transformStart[1][1]) * scale;
29489
29490         transformed = true;
29491         iD.util.setTransform(supersurface, tX, tY, scale);
29492         queueRedraw();
29493
29494         dispatch.move(map);
29495     }
29496
29497     function resetTransform() {
29498         if (!transformed) return false;
29499
29500         surface.selectAll('.radial-menu').interrupt().remove();
29501         iD.util.setTransform(supersurface, 0, 0);
29502         transformed = false;
29503         return true;
29504     }
29505
29506     function redraw(difference, extent) {
29507         if (!surface || !redrawEnabled) return;
29508
29509         clearTimeout(timeoutId);
29510
29511         // If we are in the middle of a zoom/pan, we can't do differenced redraws.
29512         // It would result in artifacts where differenced entities are redrawn with
29513         // one transform and unchanged entities with another.
29514         if (resetTransform()) {
29515             difference = extent = undefined;
29516         }
29517
29518         var zoom = String(~~map.zoom());
29519         if (surface.attr('data-zoom') !== zoom) {
29520             surface.attr('data-zoom', zoom)
29521                 .classed('low-zoom', zoom <= 16);
29522         }
29523
29524         if (!difference) {
29525             supersurface.call(context.background());
29526         }
29527
29528         // OSM
29529         if (map.editable()) {
29530             context.loadTiles(projection, dimensions);
29531             drawVector(difference, extent);
29532         } else {
29533             editOff();
29534         }
29535
29536         wrapper
29537             .call(drawLayers);
29538
29539         transformStart = [
29540             projection.scale() * 2 * Math.PI,
29541             projection.translate().slice()];
29542
29543         return map;
29544     }
29545
29546     var timeoutId;
29547     function queueRedraw() {
29548         clearTimeout(timeoutId);
29549         timeoutId = setTimeout(function() { redraw(); }, 300);
29550     }
29551
29552     function pointLocation(p) {
29553         var translate = projection.translate(),
29554             scale = projection.scale() * 2 * Math.PI;
29555         return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
29556     }
29557
29558     function locationPoint(l) {
29559         var translate = projection.translate(),
29560             scale = projection.scale() * 2 * Math.PI;
29561         return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
29562     }
29563
29564     map.mouse = function() {
29565         var e = mousemove || d3.event, s;
29566         while ((s = e.sourceEvent)) e = s;
29567         return mouse(e);
29568     };
29569
29570     map.mouseCoordinates = function() {
29571         return projection.invert(map.mouse());
29572     };
29573
29574     map.dblclickEnable = function(_) {
29575         if (!arguments.length) return dblclickEnabled;
29576         dblclickEnabled = _;
29577         return map;
29578     };
29579
29580     map.redrawEnable = function(_) {
29581         if (!arguments.length) return redrawEnabled;
29582         redrawEnabled = _;
29583         return map;
29584     };
29585
29586     function interpolateZoom(_) {
29587         var k = projection.scale(),
29588             t = projection.translate();
29589
29590         surface.node().__chart__ = {
29591             x: t[0],
29592             y: t[1],
29593             k: k * 2 * Math.PI
29594         };
29595
29596         setZoom(_);
29597         projection.scale(k).translate(t);  // undo setZoom projection changes
29598
29599         zoom.event(surface.transition());
29600     }
29601
29602     function setZoom(_, force) {
29603         if (_ === map.zoom() && !force)
29604             return false;
29605         var scale = 256 * Math.pow(2, _),
29606             center = pxCenter(),
29607             l = pointLocation(center);
29608         scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
29609         projection.scale(scale / (2 * Math.PI));
29610         zoom.scale(scale);
29611         var t = projection.translate();
29612         l = locationPoint(l);
29613         t[0] += center[0] - l[0];
29614         t[1] += center[1] - l[1];
29615         projection.translate(t);
29616         zoom.translate(projection.translate());
29617         return true;
29618     }
29619
29620     function setCenter(_) {
29621         var c = map.center();
29622         if (_[0] === c[0] && _[1] === c[1])
29623             return false;
29624         var t = projection.translate(),
29625             pxC = pxCenter(),
29626             ll = projection(_);
29627         projection.translate([
29628             t[0] - ll[0] + pxC[0],
29629             t[1] - ll[1] + pxC[1]]);
29630         zoom.translate(projection.translate());
29631         return true;
29632     }
29633
29634     map.pan = function(d) {
29635         var t = projection.translate();
29636         t[0] += d[0];
29637         t[1] += d[1];
29638         projection.translate(t);
29639         zoom.translate(projection.translate());
29640         dispatch.move(map);
29641         return redraw();
29642     };
29643
29644     map.dimensions = function(_) {
29645         if (!arguments.length) return dimensions;
29646         var center = map.center();
29647         dimensions = _;
29648         drawLayers.dimensions(dimensions);
29649         context.background().dimensions(dimensions);
29650         projection.clipExtent([[0, 0], dimensions]);
29651         mouse = iD.util.fastMouse(supersurface.node());
29652         setCenter(center);
29653         return redraw();
29654     };
29655
29656     function zoomIn(integer) {
29657       interpolateZoom(~~map.zoom() + integer);
29658     }
29659
29660     function zoomOut(integer) {
29661       interpolateZoom(~~map.zoom() - integer);
29662     }
29663
29664     map.zoomIn = function() { zoomIn(1); };
29665     map.zoomInFurther = function() { zoomIn(4); };
29666
29667     map.zoomOut = function() { zoomOut(1); };
29668     map.zoomOutFurther = function() { zoomOut(4); };
29669
29670     map.center = function(loc) {
29671         if (!arguments.length) {
29672             return projection.invert(pxCenter());
29673         }
29674
29675         if (setCenter(loc)) {
29676             dispatch.move(map);
29677         }
29678
29679         return redraw();
29680     };
29681
29682     map.zoom = function(z) {
29683         if (!arguments.length) {
29684             return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
29685         }
29686
29687         if (z < minzoom) {
29688             surface.interrupt();
29689             iD.ui.flash(context.container())
29690                 .select('.content')
29691                 .text(t('cannot_zoom'));
29692             z = context.minEditableZoom();
29693         }
29694
29695         if (setZoom(z)) {
29696             dispatch.move(map);
29697         }
29698
29699         return redraw();
29700     };
29701
29702     map.zoomTo = function(entity, zoomLimits) {
29703         var extent = entity.extent(context.graph());
29704         if (!isFinite(extent.area())) return;
29705
29706         var zoom = map.trimmedExtentZoom(extent);
29707         zoomLimits = zoomLimits || [context.minEditableZoom(), 20];
29708         map.centerZoom(extent.center(), Math.min(Math.max(zoom, zoomLimits[0]), zoomLimits[1]));
29709     };
29710
29711     map.centerZoom = function(loc, z) {
29712         var centered = setCenter(loc),
29713             zoomed   = setZoom(z);
29714
29715         if (centered || zoomed) {
29716             dispatch.move(map);
29717         }
29718
29719         return redraw();
29720     };
29721
29722     map.centerEase = function(loc) {
29723         var from = map.center().slice(),
29724             t = 0,
29725             stop;
29726
29727         surface.one('mousedown.ease', function() {
29728             stop = true;
29729         });
29730
29731         d3.timer(function() {
29732             if (stop) return true;
29733             map.center(iD.geo.interp(from, loc, (t += 1) / 10));
29734             return t === 10;
29735         }, 20);
29736         return map;
29737     };
29738
29739     map.extent = function(_) {
29740         if (!arguments.length) {
29741             return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
29742                                  projection.invert([dimensions[0], 0]));
29743         } else {
29744             var extent = iD.geo.Extent(_);
29745             map.centerZoom(extent.center(), map.extentZoom(extent));
29746         }
29747     };
29748
29749     map.trimmedExtent = function(_) {
29750         if (!arguments.length) {
29751             var headerY = 60, footerY = 30, pad = 10;
29752             return new iD.geo.Extent(projection.invert([pad, dimensions[1] - footerY - pad]),
29753                     projection.invert([dimensions[0] - pad, headerY + pad]));
29754         } else {
29755             var extent = iD.geo.Extent(_);
29756             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
29757         }
29758     };
29759
29760     function calcZoom(extent, dim) {
29761         var tl = projection([extent[0][0], extent[1][1]]),
29762             br = projection([extent[1][0], extent[0][1]]);
29763
29764         // Calculate maximum zoom that fits extent
29765         var hFactor = (br[0] - tl[0]) / dim[0],
29766             vFactor = (br[1] - tl[1]) / dim[1],
29767             hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
29768             vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
29769             newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
29770
29771         return newZoom;
29772     }
29773
29774     map.extentZoom = function(_) {
29775         return calcZoom(iD.geo.Extent(_), dimensions);
29776     };
29777
29778     map.trimmedExtentZoom = function(_) {
29779         var trimY = 120, trimX = 40,
29780             trimmed = [dimensions[0] - trimX, dimensions[1] - trimY];
29781         return calcZoom(iD.geo.Extent(_), trimmed);
29782     };
29783
29784     map.editable = function() {
29785         return map.zoom() >= context.minEditableZoom();
29786     };
29787
29788     map.minzoom = function(_) {
29789         if (!arguments.length) return minzoom;
29790         minzoom = _;
29791         return map;
29792     };
29793
29794     map.layers = drawLayers;
29795
29796     return d3.rebind(map, dispatch, 'on');
29797 };
29798 iD.TileLayer = function() {
29799     var tileSize = 256,
29800         tile = d3.geo.tile(),
29801         projection,
29802         cache = {},
29803         tileOrigin,
29804         z,
29805         transformProp = iD.util.prefixCSSProperty('Transform'),
29806         source = d3.functor('');
29807
29808
29809     // blacklist overlay tiles around Null Island..
29810     function nearNullIsland(x, y, z) {
29811         if (z >= 7) {
29812             var center = Math.pow(2, z - 1),
29813                 width = Math.pow(2, z - 6),
29814                 min = center - (width / 2),
29815                 max = center + (width / 2) - 1;
29816             return x >= min && x <= max && y >= min && y <= max;
29817         }
29818         return false;
29819     }
29820
29821     function tileSizeAtZoom(d, z) {
29822         var epsilon = 0.002;
29823         return ((tileSize * Math.pow(2, z - d[2])) / tileSize) + epsilon;
29824     }
29825
29826     function atZoom(t, distance) {
29827         var power = Math.pow(2, distance);
29828         return [
29829             Math.floor(t[0] * power),
29830             Math.floor(t[1] * power),
29831             t[2] + distance];
29832     }
29833
29834     function lookUp(d) {
29835         for (var up = -1; up > -d[2]; up--) {
29836             var tile = atZoom(d, up);
29837             if (cache[source.url(tile)] !== false) {
29838                 return tile;
29839             }
29840         }
29841     }
29842
29843     function uniqueBy(a, n) {
29844         var o = [], seen = {};
29845         for (var i = 0; i < a.length; i++) {
29846             if (seen[a[i][n]] === undefined) {
29847                 o.push(a[i]);
29848                 seen[a[i][n]] = true;
29849             }
29850         }
29851         return o;
29852     }
29853
29854     function addSource(d) {
29855         d.push(source.url(d));
29856         return d;
29857     }
29858
29859     // Update tiles based on current state of `projection`.
29860     function background(selection) {
29861         tile.scale(projection.scale() * 2 * Math.PI)
29862             .translate(projection.translate());
29863
29864         tileOrigin = [
29865             projection.scale() * Math.PI - projection.translate()[0],
29866             projection.scale() * Math.PI - projection.translate()[1]];
29867
29868         z = Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.log(2) - 8, 0);
29869
29870         render(selection);
29871     }
29872
29873     // Derive the tiles onscreen, remove those offscreen and position them.
29874     // Important that this part not depend on `projection` because it's
29875     // rentered when tiles load/error (see #644).
29876     function render(selection) {
29877         var requests = [];
29878
29879         if (source.validZoom(z)) {
29880             tile().forEach(function(d) {
29881                 addSource(d);
29882                 if (d[3] === '') return;
29883                 if (typeof d[3] !== 'string') return; // Workaround for chrome crash https://github.com/openstreetmap/iD/issues/2295
29884                 requests.push(d);
29885                 if (cache[d[3]] === false && lookUp(d)) {
29886                     requests.push(addSource(lookUp(d)));
29887                 }
29888             });
29889
29890             requests = uniqueBy(requests, 3).filter(function(r) {
29891                 if (!!source.overlay && nearNullIsland(r[0], r[1], r[2])) {
29892                     return false;
29893                 }
29894                 // don't re-request tiles which have failed in the past
29895                 return cache[r[3]] !== false;
29896             });
29897         }
29898
29899         var pixelOffset = [
29900             source.offset()[0] * Math.pow(2, z),
29901             source.offset()[1] * Math.pow(2, z)
29902         ];
29903
29904         function load(d) {
29905             cache[d[3]] = true;
29906             d3.select(this)
29907                 .on('error', null)
29908                 .on('load', null)
29909                 .classed('tile-loaded', true);
29910             render(selection);
29911         }
29912
29913         function error(d) {
29914             cache[d[3]] = false;
29915             d3.select(this)
29916                 .on('error', null)
29917                 .on('load', null)
29918                 .remove();
29919             render(selection);
29920         }
29921
29922         function imageTransform(d) {
29923             var _ts = tileSize * Math.pow(2, z - d[2]);
29924             var scale = tileSizeAtZoom(d, z);
29925             return 'translate(' +
29926                 ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0]) + 'px,' +
29927                 ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1]) + 'px)' +
29928                 'scale(' + scale + ',' + scale + ')';
29929         }
29930
29931         var image = selection
29932             .selectAll('img')
29933             .data(requests, function(d) { return d[3]; });
29934
29935         image.exit()
29936             .style(transformProp, imageTransform)
29937             .classed('tile-removing', true)
29938             .each(function() {
29939                 var tile = d3.select(this);
29940                 window.setTimeout(function() {
29941                     if (tile.classed('tile-removing')) {
29942                         tile.remove();
29943                     }
29944                 }, 300);
29945             });
29946
29947         image.enter().append('img')
29948             .attr('class', 'tile')
29949             .attr('src', function(d) { return d[3]; })
29950             .on('error', error)
29951             .on('load', load);
29952
29953         image
29954             .style(transformProp, imageTransform)
29955             .classed('tile-removing', false);
29956     }
29957
29958     background.projection = function(_) {
29959         if (!arguments.length) return projection;
29960         projection = _;
29961         return background;
29962     };
29963
29964     background.dimensions = function(_) {
29965         if (!arguments.length) return tile.size();
29966         tile.size(_);
29967         return background;
29968     };
29969
29970     background.source = function(_) {
29971         if (!arguments.length) return source;
29972         source = _;
29973         cache = {};
29974         tile.scaleExtent(source.scaleExtent);
29975         return background;
29976     };
29977
29978     return background;
29979 };
29980 iD.svg = {
29981     PointTransform: function(projection) {
29982         return function(entity) {
29983             // http://jsperf.com/short-array-join
29984             var pt = projection(entity.loc);
29985             return 'translate(' + pt[0] + ',' + pt[1] + ')';
29986         };
29987     },
29988
29989     Path: function(projection, graph, polygon) {
29990         var cache = {},
29991             clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
29992             project = projection.stream,
29993             path = d3.geo.path()
29994                 .projection({stream: function(output) { return polygon ? project(output) : project(clip(output)); }});
29995
29996         return function(entity) {
29997             if (entity.id in cache) {
29998                 return cache[entity.id];
29999             } else {
30000                 return cache[entity.id] = path(entity.asGeoJSON(graph));
30001             }
30002         };
30003     },
30004
30005     OneWaySegments: function(projection, graph, dt) {
30006         return function(entity) {
30007             var a,
30008                 b,
30009                 i = 0,
30010                 offset = dt,
30011                 segments = [],
30012                 clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
30013                 coordinates = graph.childNodes(entity).map(function(n) {
30014                     return n.loc;
30015                 });
30016
30017             if (entity.tags.oneway === '-1') coordinates.reverse();
30018
30019             d3.geo.stream({
30020                 type: 'LineString',
30021                 coordinates: coordinates
30022             }, projection.stream(clip({
30023                 lineStart: function() {},
30024                 lineEnd: function() {
30025                     a = null;
30026                 },
30027                 point: function(x, y) {
30028                     b = [x, y];
30029
30030                     if (a) {
30031                         var span = iD.geo.euclideanDistance(a, b) - offset;
30032
30033                         if (span >= 0) {
30034                             var angle = Math.atan2(b[1] - a[1], b[0] - a[0]),
30035                                 dx = dt * Math.cos(angle),
30036                                 dy = dt * Math.sin(angle),
30037                                 p = [a[0] + offset * Math.cos(angle),
30038                                      a[1] + offset * Math.sin(angle)];
30039
30040                             var segment = 'M' + a[0] + ',' + a[1] +
30041                                           'L' + p[0] + ',' + p[1];
30042
30043                             for (span -= dt; span >= 0; span -= dt) {
30044                                 p[0] += dx;
30045                                 p[1] += dy;
30046                                 segment += 'L' + p[0] + ',' + p[1];
30047                             }
30048
30049                             segment += 'L' + b[0] + ',' + b[1];
30050                             segments.push({id: entity.id, index: i, d: segment});
30051                         }
30052
30053                         offset = -span;
30054                         i++;
30055                     }
30056
30057                     a = b;
30058                 }
30059             })));
30060
30061             return segments;
30062         };
30063     },
30064
30065     RelationMemberTags: function(graph) {
30066         return function(entity) {
30067             var tags = entity.tags;
30068             graph.parentRelations(entity).forEach(function(relation) {
30069                 var type = relation.tags.type;
30070                 if (type === 'multipolygon' || type === 'boundary') {
30071                     tags = _.extend({}, relation.tags, tags);
30072                 }
30073             });
30074             return tags;
30075         };
30076     }
30077 };
30078 iD.svg.Areas = function(projection) {
30079     // Patterns only work in Firefox when set directly on element.
30080     // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
30081     var patterns = {
30082         wetland: 'wetland',
30083         beach: 'beach',
30084         scrub: 'scrub',
30085         construction: 'construction',
30086         military: 'construction',
30087         cemetery: 'cemetery',
30088         grave_yard: 'cemetery',
30089         meadow: 'meadow',
30090         farm: 'farmland',
30091         farmland: 'farmland',
30092         orchard: 'orchard'
30093     };
30094
30095     var patternKeys = ['landuse', 'natural', 'amenity'];
30096
30097     function setPattern(d) {
30098         for (var i = 0; i < patternKeys.length; i++) {
30099             if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) {
30100                 this.style.fill = this.style.stroke = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")';
30101                 return;
30102             }
30103         }
30104         this.style.fill = this.style.stroke = '';
30105     }
30106
30107     return function drawAreas(surface, graph, entities, filter) {
30108         var path = iD.svg.Path(projection, graph, true),
30109             areas = {},
30110             multipolygon;
30111
30112         for (var i = 0; i < entities.length; i++) {
30113             var entity = entities[i];
30114             if (entity.geometry(graph) !== 'area') continue;
30115
30116             multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph);
30117             if (multipolygon) {
30118                 areas[multipolygon.id] = {
30119                     entity: multipolygon.mergeTags(entity.tags),
30120                     area: Math.abs(entity.area(graph))
30121                 };
30122             } else if (!areas[entity.id]) {
30123                 areas[entity.id] = {
30124                     entity: entity,
30125                     area: Math.abs(entity.area(graph))
30126                 };
30127             }
30128         }
30129
30130         areas = d3.values(areas).filter(function hasPath(a) { return path(a.entity); });
30131         areas.sort(function areaSort(a, b) { return b.area - a.area; });
30132         areas = _.pluck(areas, 'entity');
30133
30134         var strokes = areas.filter(function(area) {
30135             return area.type === 'way';
30136         });
30137
30138         var data = {
30139             clip: areas,
30140             shadow: strokes,
30141             stroke: strokes,
30142             fill: areas
30143         };
30144
30145         var clipPaths = surface.selectAll('defs').selectAll('.clipPath')
30146            .filter(filter)
30147            .data(data.clip, iD.Entity.key);
30148
30149         clipPaths.enter()
30150            .append('clipPath')
30151            .attr('class', 'clipPath')
30152            .attr('id', function(entity) { return entity.id + '-clippath'; })
30153            .append('path');
30154
30155         clipPaths.selectAll('path')
30156            .attr('d', path);
30157
30158         clipPaths.exit()
30159            .remove();
30160
30161         var areagroup = surface
30162             .selectAll('.layer-areas')
30163             .selectAll('g.areagroup')
30164             .data(['fill', 'shadow', 'stroke']);
30165
30166         areagroup.enter()
30167             .append('g')
30168             .attr('class', function(d) { return 'layer areagroup area-' + d; });
30169
30170         var paths = areagroup
30171             .selectAll('path')
30172             .filter(filter)
30173             .data(function(layer) { return data[layer]; }, iD.Entity.key);
30174
30175         // Remove exiting areas first, so they aren't included in the `fills`
30176         // array used for sorting below (https://github.com/openstreetmap/iD/issues/1903).
30177         paths.exit()
30178             .remove();
30179
30180         var fills = surface.selectAll('.area-fill path.area')[0];
30181
30182         var bisect = d3.bisector(function(node) {
30183             return -node.__data__.area(graph);
30184         }).left;
30185
30186         function sortedByArea(entity) {
30187             if (this.__data__ === 'fill') {
30188                 return fills[bisect(fills, -entity.area(graph))];
30189             }
30190         }
30191
30192         paths.enter()
30193             .insert('path', sortedByArea)
30194             .each(function(entity) {
30195                 var layer = this.parentNode.__data__;
30196
30197                 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
30198
30199                 if (layer === 'fill') {
30200                     this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)');
30201                     setPattern.apply(this, arguments);
30202                 }
30203             })
30204             .call(iD.svg.TagClasses());
30205
30206         paths
30207             .attr('d', path);
30208     };
30209 };
30210 /*
30211     A standalone SVG element that contains only a `defs` sub-element. To be
30212     used once globally, since defs IDs must be unique within a document.
30213 */
30214 iD.svg.Defs = function(context) {
30215
30216     function SVGSpriteDefinition(id, href) {
30217         return function(defs) {
30218             d3.xml(href, 'image/svg+xml', function(err, svg) {
30219                 if (err) return;
30220                 defs.node().appendChild(
30221                     d3.select(svg.documentElement).attr('id', id).node()
30222                 );
30223             });
30224         };
30225     }
30226
30227     return function drawDefs(selection) {
30228         var defs = selection.append('defs');
30229
30230         // marker
30231         defs.append('marker')
30232             .attr({
30233                 id: 'oneway-marker',
30234                 viewBox: '0 0 10 10',
30235                 refY: 2.5,
30236                 refX: 5,
30237                 markerWidth: 2,
30238                 markerHeight: 2,
30239                 markerUnits: 'strokeWidth',
30240                 orient: 'auto'
30241             })
30242             .append('path')
30243             .attr('class', 'oneway')
30244             .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')
30245             .attr('stroke', 'none')
30246             .attr('fill', '#000')
30247             .attr('opacity', '0.5');
30248
30249         // patterns
30250         var patterns = defs.selectAll('pattern')
30251             .data([
30252                 // pattern name, pattern image name
30253                 ['wetland', 'wetland'],
30254                 ['construction', 'construction'],
30255                 ['cemetery', 'cemetery'],
30256                 ['orchard', 'orchard'],
30257                 ['farmland', 'farmland'],
30258                 ['beach', 'dots'],
30259                 ['scrub', 'dots'],
30260                 ['meadow', 'dots']
30261             ])
30262             .enter()
30263             .append('pattern')
30264             .attr({
30265                 id: function (d) {
30266                     return 'pattern-' + d[0];
30267                 },
30268                 width: 32,
30269                 height: 32,
30270                 patternUnits: 'userSpaceOnUse'
30271             });
30272
30273         patterns.append('rect')
30274             .attr({
30275                 x: 0,
30276                 y: 0,
30277                 width: 32,
30278                 height: 32,
30279                 'class': function (d) {
30280                     return 'pattern-color-' + d[0];
30281                 }
30282             });
30283
30284         patterns.append('image')
30285             .attr({
30286                 x: 0,
30287                 y: 0,
30288                 width: 32,
30289                 height: 32
30290             })
30291             .attr('xlink:href', function (d) {
30292                 return context.imagePath('pattern/' + d[1] + '.png');
30293             });
30294
30295         // clip paths
30296         defs.selectAll()
30297             .data([12, 18, 20, 32, 45])
30298             .enter().append('clipPath')
30299             .attr('id', function (d) {
30300                 return 'clip-square-' + d;
30301             })
30302             .append('rect')
30303             .attr('x', 0)
30304             .attr('y', 0)
30305             .attr('width', function (d) {
30306                 return d;
30307             })
30308             .attr('height', function (d) {
30309                 return d;
30310             });
30311
30312         defs.call(SVGSpriteDefinition(
30313             'iD-sprite',
30314             context.imagePath('iD-sprite.svg')));
30315
30316         defs.call(SVGSpriteDefinition(
30317             'maki-sprite',
30318             context.imagePath('maki-sprite.svg')));
30319     };
30320 };
30321 iD.svg.Gpx = function(projection, context, dispatch) {
30322     var showLabels = true,
30323         layer;
30324
30325     function init() {
30326         if (iD.svg.Gpx.initialized) return;  // run once
30327
30328         iD.svg.Gpx.geojson = {};
30329         iD.svg.Gpx.enabled = true;
30330
30331         function over() {
30332             d3.event.stopPropagation();
30333             d3.event.preventDefault();
30334             d3.event.dataTransfer.dropEffect = 'copy';
30335         }
30336
30337         d3.select('body')
30338             .attr('dropzone', 'copy')
30339             .on('drop.localgpx', function() {
30340                 d3.event.stopPropagation();
30341                 d3.event.preventDefault();
30342                 if (!iD.detect().filedrop) return;
30343                 drawGpx.files(d3.event.dataTransfer.files);
30344             })
30345             .on('dragenter.localgpx', over)
30346             .on('dragexit.localgpx', over)
30347             .on('dragover.localgpx', over);
30348
30349         iD.svg.Gpx.initialized = true;
30350     }
30351
30352
30353     function drawGpx(surface) {
30354         var geojson = iD.svg.Gpx.geojson,
30355             enabled = iD.svg.Gpx.enabled;
30356
30357         layer = surface.selectAll('.layer-gpx')
30358             .data(enabled ? [0] : []);
30359
30360         layer.enter()
30361             .append('g')
30362             .attr('class', 'layer-gpx');
30363
30364         layer.exit()
30365             .remove();
30366
30367
30368         var paths = layer
30369             .selectAll('path')
30370             .data([geojson]);
30371
30372         paths.enter()
30373             .append('path')
30374             .attr('class', 'gpx');
30375
30376         paths.exit()
30377             .remove();
30378
30379         var path = d3.geo.path()
30380             .projection(projection);
30381
30382         paths
30383             .attr('d', path);
30384
30385
30386         var labels = layer.selectAll('text')
30387             .data(showLabels && geojson.features ? geojson.features : []);
30388
30389         labels.enter()
30390             .append('text')
30391             .attr('class', 'gpx');
30392
30393         labels.exit()
30394             .remove();
30395
30396         labels
30397             .text(function(d) {
30398                 return d.properties.desc || d.properties.name;
30399             })
30400             .attr('x', function(d) {
30401                 var centroid = path.centroid(d);
30402                 return centroid[0] + 7;
30403             })
30404             .attr('y', function(d) {
30405                 var centroid = path.centroid(d);
30406                 return centroid[1];
30407             });
30408
30409     }
30410
30411     function toDom(x) {
30412         return (new DOMParser()).parseFromString(x, 'text/xml');
30413     }
30414
30415     drawGpx.showLabels = function(_) {
30416         if (!arguments.length) return showLabels;
30417         showLabels = _;
30418         return this;
30419     };
30420
30421     drawGpx.enabled = function(_) {
30422         if (!arguments.length) return iD.svg.Gpx.enabled;
30423         iD.svg.Gpx.enabled = _;
30424         dispatch.change();
30425         return this;
30426     };
30427
30428     drawGpx.hasGpx = function() {
30429         var geojson = iD.svg.Gpx.geojson;
30430         return (!(_.isEmpty(geojson) || _.isEmpty(geojson.features)));
30431     };
30432
30433     drawGpx.geojson = function(gj) {
30434         if (!arguments.length) return iD.svg.Gpx.geojson;
30435         if (_.isEmpty(gj) || _.isEmpty(gj.features)) return this;
30436         iD.svg.Gpx.geojson = gj;
30437         dispatch.change();
30438         return this;
30439     };
30440
30441     drawGpx.url = function(url) {
30442         d3.text(url, function(err, data) {
30443             if (!err) {
30444                 drawGpx.geojson(toGeoJSON.gpx(toDom(data)));
30445             }
30446         });
30447         return this;
30448     };
30449
30450     drawGpx.files = function(fileList) {
30451         if (!fileList.length) return this;
30452         var f = fileList[0],
30453             reader = new FileReader();
30454
30455         reader.onload = function(e) {
30456             drawGpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom();
30457         };
30458
30459         reader.readAsText(f);
30460         return this;
30461     };
30462
30463     drawGpx.fitZoom = function() {
30464         if (!this.hasGpx()) return this;
30465         var geojson = iD.svg.Gpx.geojson;
30466
30467         var map = context.map(),
30468             viewport = map.trimmedExtent().polygon(),
30469             coords = _.reduce(geojson.features, function(coords, feature) {
30470                 var c = feature.geometry.coordinates;
30471                 return _.union(coords, feature.geometry.type === 'Point' ? [c] : c);
30472             }, []);
30473
30474         if (!iD.geo.polygonIntersectsPolygon(viewport, coords, true)) {
30475             var extent = iD.geo.Extent(d3.geo.bounds(geojson));
30476             map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
30477         }
30478
30479         return this;
30480     };
30481
30482     init();
30483     return drawGpx;
30484 };
30485 iD.svg.Icon = function(name, svgklass, useklass) {
30486     return function drawIcon(selection) {
30487         selection.selectAll('svg')
30488             .data([0])
30489             .enter()
30490             .append('svg')
30491             .attr('class', 'icon ' + (svgklass || ''))
30492             .append('use')
30493             .attr('xlink:href', name)
30494             .attr('class', useklass);
30495     };
30496 };
30497 iD.svg.Labels = function(projection, context) {
30498     var path = d3.geo.path().projection(projection);
30499
30500     // Replace with dict and iterate over entities tags instead?
30501     var label_stack = [
30502         ['line', 'aeroway'],
30503         ['line', 'highway'],
30504         ['line', 'railway'],
30505         ['line', 'waterway'],
30506         ['area', 'aeroway'],
30507         ['area', 'amenity'],
30508         ['area', 'building'],
30509         ['area', 'historic'],
30510         ['area', 'leisure'],
30511         ['area', 'man_made'],
30512         ['area', 'natural'],
30513         ['area', 'shop'],
30514         ['area', 'tourism'],
30515         ['point', 'aeroway'],
30516         ['point', 'amenity'],
30517         ['point', 'building'],
30518         ['point', 'historic'],
30519         ['point', 'leisure'],
30520         ['point', 'man_made'],
30521         ['point', 'natural'],
30522         ['point', 'shop'],
30523         ['point', 'tourism'],
30524         ['line', 'name'],
30525         ['area', 'name'],
30526         ['point', 'name']
30527     ];
30528
30529     var default_size = 12;
30530
30531     var font_sizes = label_stack.map(function(d) {
30532         var style = iD.util.getStyle('text.' + d[0] + '.tag-' + d[1]),
30533             m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30534         if (m) return parseInt(m[1], 10);
30535
30536         style = iD.util.getStyle('text.' + d[0]);
30537         m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
30538         if (m) return parseInt(m[1], 10);
30539
30540         return default_size;
30541     });
30542
30543     var iconSize = 18;
30544
30545     var pointOffsets = [
30546         [15, -11, 'start'], // right
30547         [10, -11, 'start'], // unused right now
30548         [-15, -11, 'end']
30549     ];
30550
30551     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25,
30552         75, 20, 80, 15, 95, 10, 90, 5, 95];
30553
30554
30555     var noIcons = ['building', 'landuse', 'natural'];
30556     function blacklisted(preset) {
30557         return _.any(noIcons, function(s) {
30558             return preset.id.indexOf(s) >= 0;
30559         });
30560     }
30561
30562     function get(array, prop) {
30563         return function(d, i) { return array[i][prop]; };
30564     }
30565
30566     var textWidthCache = {};
30567
30568     function textWidth(text, size, elem) {
30569         var c = textWidthCache[size];
30570         if (!c) c = textWidthCache[size] = {};
30571
30572         if (c[text]) {
30573             return c[text];
30574
30575         } else if (elem) {
30576             c[text] = elem.getComputedTextLength();
30577             return c[text];
30578
30579         } else {
30580             var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
30581             if (str === null) {
30582                 return size / 3 * 2 * text.length;
30583             } else {
30584                 return size / 3 * (2 * text.length + str.length);
30585             }
30586         }
30587     }
30588
30589     function drawLineLabels(group, entities, filter, classes, labels) {
30590         var texts = group.selectAll('text.' + classes)
30591             .filter(filter)
30592             .data(entities, iD.Entity.key);
30593
30594         texts.enter()
30595             .append('text')
30596             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
30597             .append('textPath')
30598             .attr('class', 'textpath');
30599
30600
30601         texts.selectAll('.textpath')
30602             .filter(filter)
30603             .data(entities, iD.Entity.key)
30604             .attr({
30605                 'startOffset': '50%',
30606                 'xlink:href': function(d) { return '#labelpath-' + d.id; }
30607             })
30608             .text(iD.util.displayName);
30609
30610         texts.exit().remove();
30611     }
30612
30613     function drawLinePaths(group, entities, filter, classes, labels) {
30614         var halos = group.selectAll('path')
30615             .filter(filter)
30616             .data(entities, iD.Entity.key);
30617
30618         halos.enter()
30619             .append('path')
30620             .style('stroke-width', get(labels, 'font-size'))
30621             .attr('id', function(d) { return 'labelpath-' + d.id; })
30622             .attr('class', classes);
30623
30624         halos.attr('d', get(labels, 'lineString'));
30625
30626         halos.exit().remove();
30627     }
30628
30629     function drawPointLabels(group, entities, filter, classes, labels) {
30630         var texts = group.selectAll('text.' + classes)
30631             .filter(filter)
30632             .data(entities, iD.Entity.key);
30633
30634         texts.enter()
30635             .append('text')
30636             .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; });
30637
30638         texts.attr('x', get(labels, 'x'))
30639             .attr('y', get(labels, 'y'))
30640             .style('text-anchor', get(labels, 'textAnchor'))
30641             .text(iD.util.displayName)
30642             .each(function(d, i) { textWidth(iD.util.displayName(d), labels[i].height, this); });
30643
30644         texts.exit().remove();
30645         return texts;
30646     }
30647
30648     function drawAreaLabels(group, entities, filter, classes, labels) {
30649         entities = entities.filter(hasText);
30650         labels = labels.filter(hasText);
30651         return drawPointLabels(group, entities, filter, classes, labels);
30652
30653         function hasText(d, i) {
30654             return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
30655         }
30656     }
30657
30658     function drawAreaIcons(group, entities, filter, classes, labels) {
30659         var icons = group.selectAll('use')
30660             .filter(filter)
30661             .data(entities, iD.Entity.key);
30662
30663         icons.enter()
30664             .append('use')
30665             .attr('class', 'icon areaicon')
30666             .attr('width', '18px')
30667             .attr('height', '18px');
30668
30669         icons.attr('transform', get(labels, 'transform'))
30670             .attr('xlink:href', function(d) {
30671                 var icon = context.presets().match(d, context.graph()).icon;
30672                 return '#' + icon + (icon === 'hairdresser' ? '-24': '-18');    // workaround: maki hairdresser-18 broken?
30673             });
30674
30675
30676         icons.exit().remove();
30677     }
30678
30679     function reverse(p) {
30680         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
30681         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
30682     }
30683
30684     function lineString(nodes) {
30685         return 'M' + nodes.join('L');
30686     }
30687
30688     function subpath(nodes, from, to) {
30689         function segmentLength(i) {
30690             var dx = nodes[i][0] - nodes[i + 1][0];
30691             var dy = nodes[i][1] - nodes[i + 1][1];
30692             return Math.sqrt(dx * dx + dy * dy);
30693         }
30694
30695         var sofar = 0,
30696             start, end, i0, i1;
30697         for (var i = 0; i < nodes.length - 1; i++) {
30698             var current = segmentLength(i);
30699             var portion;
30700             if (!start && sofar + current >= from) {
30701                 portion = (from - sofar) / current;
30702                 start = [
30703                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30704                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30705                 ];
30706                 i0 = i + 1;
30707             }
30708             if (!end && sofar + current >= to) {
30709                 portion = (to - sofar) / current;
30710                 end = [
30711                     nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
30712                     nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
30713                 ];
30714                 i1 = i + 1;
30715             }
30716             sofar += current;
30717
30718         }
30719         var ret = nodes.slice(i0, i1);
30720         ret.unshift(start);
30721         ret.push(end);
30722         return ret;
30723
30724     }
30725
30726     function hideOnMouseover() {
30727         var layers = d3.select(this)
30728             .selectAll('.layer-label, .layer-halo');
30729
30730         layers.selectAll('.proximate')
30731             .classed('proximate', false);
30732
30733         var mouse = context.mouse(),
30734             pad = 50,
30735             rect = [mouse[0] - pad, mouse[1] - pad, mouse[0] + pad, mouse[1] + pad],
30736             ids = _.pluck(rtree.search(rect), 'id');
30737
30738         if (!ids.length) return;
30739         layers.selectAll('.' + ids.join(', .'))
30740             .classed('proximate', true);
30741     }
30742
30743     var rtree = rbush(),
30744         rectangles = {};
30745
30746     function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
30747         var hidePoints = !surface.selectAll('.node.point').node();
30748
30749         var labelable = [], i, k, entity;
30750         for (i = 0; i < label_stack.length; i++) labelable.push([]);
30751
30752         if (fullRedraw) {
30753             rtree.clear();
30754             rectangles = {};
30755         } else {
30756             for (i = 0; i < entities.length; i++) {
30757                 rtree.remove(rectangles[entities[i].id]);
30758             }
30759         }
30760
30761         // Split entities into groups specified by label_stack
30762         for (i = 0; i < entities.length; i++) {
30763             entity = entities[i];
30764             var geometry = entity.geometry(graph);
30765
30766             if (geometry === 'vertex')
30767                 continue;
30768             if (hidePoints && geometry === 'point')
30769                 continue;
30770
30771             var preset = geometry === 'area' && context.presets().match(entity, graph),
30772                 icon = preset && !blacklisted(preset) && preset.icon;
30773
30774             if (!icon && !iD.util.displayName(entity))
30775                 continue;
30776
30777             for (k = 0; k < label_stack.length; k++) {
30778                 if (geometry === label_stack[k][0] && entity.tags[label_stack[k][1]]) {
30779                     labelable[k].push(entity);
30780                     break;
30781                 }
30782             }
30783         }
30784
30785         var positions = {
30786             point: [],
30787             line: [],
30788             area: []
30789         };
30790
30791         var labelled = {
30792             point: [],
30793             line: [],
30794             area: []
30795         };
30796
30797         // Try and find a valid label for labellable entities
30798         for (k = 0; k < labelable.length; k++) {
30799             var font_size = font_sizes[k];
30800             for (i = 0; i < labelable[k].length; i++) {
30801                 entity = labelable[k][i];
30802                 var name = iD.util.displayName(entity),
30803                     width = name && textWidth(name, font_size),
30804                     p;
30805                 if (entity.geometry(graph) === 'point') {
30806                     p = getPointLabel(entity, width, font_size);
30807                 } else if (entity.geometry(graph) === 'line') {
30808                     p = getLineLabel(entity, width, font_size);
30809                 } else if (entity.geometry(graph) === 'area') {
30810                     p = getAreaLabel(entity, width, font_size);
30811                 }
30812                 if (p) {
30813                     p.classes = entity.geometry(graph) + ' tag-' + label_stack[k][1];
30814                     positions[entity.geometry(graph)].push(p);
30815                     labelled[entity.geometry(graph)].push(entity);
30816                 }
30817             }
30818         }
30819
30820         function getPointLabel(entity, width, height) {
30821             var coord = projection(entity.loc),
30822                 m = 5,  // margin
30823                 offset = pointOffsets[0],
30824                 p = {
30825                     height: height,
30826                     width: width,
30827                     x: coord[0] + offset[0],
30828                     y: coord[1] + offset[1],
30829                     textAnchor: offset[2]
30830                 };
30831             var rect = [p.x - m, p.y - m, p.x + width + m, p.y + height + m];
30832             if (tryInsert(rect, entity.id)) return p;
30833         }
30834
30835
30836         function getLineLabel(entity, width, height) {
30837             var nodes = _.pluck(graph.childNodes(entity), 'loc').map(projection),
30838                 length = iD.geo.pathLength(nodes);
30839             if (length < width + 20) return;
30840
30841             for (var i = 0; i < lineOffsets.length; i++) {
30842                 var offset = lineOffsets[i],
30843                     middle = offset / 100 * length,
30844                     start = middle - width/2;
30845                 if (start < 0 || start + width > length) continue;
30846                 var sub = subpath(nodes, start, start + width),
30847                     rev = reverse(sub),
30848                     rect = [
30849                         Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
30850                         Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
30851                         Math.max(sub[0][0], sub[sub.length - 1][0]) + 20,
30852                         Math.max(sub[0][1], sub[sub.length - 1][1]) + 30
30853                     ];
30854                 if (rev) sub = sub.reverse();
30855                 if (tryInsert(rect, entity.id)) return {
30856                     'font-size': height + 2,
30857                     lineString: lineString(sub),
30858                     startOffset: offset + '%'
30859                 };
30860             }
30861         }
30862
30863         function getAreaLabel(entity, width, height) {
30864             var centroid = path.centroid(entity.asGeoJSON(graph, true)),
30865                 extent = entity.extent(graph),
30866                 entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
30867                 rect;
30868
30869             if (isNaN(centroid[0]) || entitywidth < 20) return;
30870
30871             var iconX = centroid[0] - (iconSize/2),
30872                 iconY = centroid[1] - (iconSize/2),
30873                 textOffset = iconSize + 5;
30874
30875             var p = {
30876                 transform: 'translate(' + iconX + ',' + iconY + ')'
30877             };
30878
30879             if (width && entitywidth >= width + 20) {
30880                 p.x = centroid[0];
30881                 p.y = centroid[1] + textOffset;
30882                 p.textAnchor = 'middle';
30883                 p.height = height;
30884                 rect = [p.x - width/2, p.y, p.x + width/2, p.y + height + textOffset];
30885             } else {
30886                 rect = [iconX, iconY, iconX + iconSize, iconY + iconSize];
30887             }
30888
30889             if (tryInsert(rect, entity.id)) return p;
30890
30891         }
30892
30893         function tryInsert(rect, id) {
30894             // Check that label is visible
30895             if (rect[0] < 0 || rect[1] < 0 || rect[2] > dimensions[0] ||
30896                 rect[3] > dimensions[1]) return false;
30897             var v = rtree.search(rect).length === 0;
30898             if (v) {
30899                 rect.id = id;
30900                 rtree.insert(rect);
30901                 rectangles[id] = rect;
30902             }
30903             return v;
30904         }
30905
30906         var label = surface.selectAll('.layer-label'),
30907             halo = surface.selectAll('.layer-halo');
30908
30909         // points
30910         drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
30911         drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
30912
30913         // lines
30914         drawLinePaths(halo, labelled.line, filter, '', positions.line);
30915         drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
30916         drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
30917
30918         // areas
30919         drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
30920         drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
30921         drawAreaIcons(label, labelled.area, filter, 'arealabel-icon', positions.area);
30922
30923         // debug
30924         var showDebug = context.debugCollision();
30925         var debug = label.selectAll('.layer-label-debug')
30926             .data(showDebug ? [true] : []);
30927
30928         debug.enter()
30929             .append('g')
30930             .attr('class', 'layer-label-debug');
30931
30932         debug.exit()
30933             .remove();
30934
30935         if (showDebug) {
30936             var gj = rtree.all().map(function(d) {
30937                 return { type: 'Polygon', coordinates: [[
30938                     [d[0], d[1]],
30939                     [d[2], d[1]],
30940                     [d[2], d[3]],
30941                     [d[0], d[3]],
30942                     [d[0], d[1]]
30943                 ]]};
30944             });
30945
30946             var debugboxes = debug.selectAll('.bbox').data(gj);
30947
30948             debugboxes.enter()
30949                 .append('path')
30950                 .attr('class', 'bbox');
30951
30952             debugboxes.exit()
30953                 .remove();
30954
30955             debugboxes
30956                 .attr('d', d3.geo.path().projection(null));
30957         }
30958     }
30959
30960     drawLabels.supersurface = function(supersurface) {
30961         supersurface
30962             .on('mousemove.hidelabels', hideOnMouseover)
30963             .on('mousedown.hidelabels', function () {
30964                 supersurface.on('mousemove.hidelabels', null);
30965             })
30966             .on('mouseup.hidelabels', function () {
30967                 supersurface.on('mousemove.hidelabels', hideOnMouseover);
30968             });
30969     };
30970
30971     return drawLabels;
30972 };
30973 iD.svg.Layers = function(projection, context) {
30974     var dispatch = d3.dispatch('change'),
30975         svg = d3.select(null),
30976         layers = [
30977             { id: 'osm', layer: iD.svg.Osm(projection, context, dispatch) },
30978             { id: 'gpx', layer: iD.svg.Gpx(projection, context, dispatch) },
30979             { id: 'mapillary-images', layer: iD.svg.MapillaryImages(projection, context, dispatch) },
30980             { id: 'mapillary-signs',  layer: iD.svg.MapillarySigns(projection, context, dispatch) }
30981         ];
30982
30983
30984     function drawLayers(selection) {
30985         svg = selection.selectAll('.surface')
30986             .data([0]);
30987
30988         svg.enter()
30989             .append('svg')
30990             .attr('class', 'surface')
30991             .append('defs');
30992
30993         var groups = svg.selectAll('.data-layer')
30994             .data(layers);
30995
30996         groups.enter()
30997             .append('g')
30998             .attr('class', function(d) { return 'data-layer data-layer-' + d.id; });
30999
31000         groups
31001             .each(function(d) { d3.select(this).call(d.layer); });
31002
31003         groups.exit()
31004             .remove();
31005     }
31006
31007     drawLayers.all = function() {
31008         return layers;
31009     };
31010
31011     drawLayers.layer = function(id) {
31012         var obj = _.find(layers, 'id', id);
31013         return obj && obj.layer;
31014     };
31015
31016     drawLayers.only = function(what) {
31017         var arr = [].concat(what);
31018         drawLayers.remove(_.difference(_.pluck(layers, 'id'), arr));
31019         return this;
31020     };
31021
31022     drawLayers.remove = function(what) {
31023         var arr = [].concat(what);
31024         arr.forEach(function(id) {
31025             layers = _.reject(layers, 'id', id);
31026         });
31027         dispatch.change();
31028         return this;
31029     };
31030
31031     drawLayers.add = function(what) {
31032         var arr = [].concat(what);
31033         arr.forEach(function(obj) {
31034             if ('id' in obj && 'layer' in obj) {
31035                 layers.push(obj);
31036             }
31037         });
31038         dispatch.change();
31039         return this;
31040     };
31041
31042     drawLayers.dimensions = function(_) {
31043         if (!arguments.length) return svg.dimensions();
31044         svg.dimensions(_);
31045         layers.forEach(function(obj) {
31046             if (obj.layer.dimensions) {
31047                 obj.layer.dimensions(_);
31048             }
31049         });
31050         return this;
31051     };
31052
31053
31054     return d3.rebind(drawLayers, dispatch, 'on');
31055 };
31056 iD.svg.Lines = function(projection) {
31057
31058     var highway_stack = {
31059         motorway: 0,
31060         motorway_link: 1,
31061         trunk: 2,
31062         trunk_link: 3,
31063         primary: 4,
31064         primary_link: 5,
31065         secondary: 6,
31066         tertiary: 7,
31067         unclassified: 8,
31068         residential: 9,
31069         service: 10,
31070         footway: 11
31071     };
31072
31073     function waystack(a, b) {
31074         var as = 0, bs = 0;
31075
31076         if (a.tags.highway) { as -= highway_stack[a.tags.highway]; }
31077         if (b.tags.highway) { bs -= highway_stack[b.tags.highway]; }
31078         return as - bs;
31079     }
31080
31081     return function drawLines(surface, graph, entities, filter) {
31082         var ways = [], pathdata = {}, onewaydata = {},
31083             getPath = iD.svg.Path(projection, graph);
31084
31085         for (var i = 0; i < entities.length; i++) {
31086             var entity = entities[i],
31087                 outer = iD.geo.simpleMultipolygonOuterMember(entity, graph);
31088             if (outer) {
31089                 ways.push(entity.mergeTags(outer.tags));
31090             } else if (entity.geometry(graph) === 'line') {
31091                 ways.push(entity);
31092             }
31093         }
31094
31095         ways = ways.filter(getPath);
31096
31097         pathdata = _.groupBy(ways, function(way) { return way.layer(); });
31098
31099         _.forOwn(pathdata, function(v, k) {
31100             onewaydata[k] = _(v)
31101                 .filter(function(d) { return d.isOneWay(); })
31102                 .map(iD.svg.OneWaySegments(projection, graph, 35))
31103                 .flatten()
31104                 .valueOf();
31105         });
31106
31107         var layergroup = surface
31108             .selectAll('.layer-lines')
31109             .selectAll('g.layergroup')
31110             .data(d3.range(-10, 11));
31111
31112         layergroup.enter()
31113             .append('g')
31114             .attr('class', function(d) { return 'layer layergroup layer' + String(d); });
31115
31116
31117         var linegroup = layergroup
31118             .selectAll('g.linegroup')
31119             .data(['shadow', 'casing', 'stroke']);
31120
31121         linegroup.enter()
31122             .append('g')
31123             .attr('class', function(d) { return 'layer linegroup line-' + d; });
31124
31125
31126         var lines = linegroup
31127             .selectAll('path')
31128             .filter(filter)
31129             .data(
31130                 function() { return pathdata[this.parentNode.parentNode.__data__] || []; },
31131                 iD.Entity.key
31132             );
31133
31134         // Optimization: call simple TagClasses only on enter selection. This
31135         // works because iD.Entity.key is defined to include the entity v attribute.
31136         lines.enter()
31137             .append('path')
31138             .attr('class', function(d) { return 'way line ' + this.parentNode.__data__ + ' ' + d.id; })
31139             .call(iD.svg.TagClasses());
31140
31141         lines
31142             .sort(waystack)
31143             .attr('d', getPath)
31144             .call(iD.svg.TagClasses().tags(iD.svg.RelationMemberTags(graph)));
31145
31146         lines.exit()
31147             .remove();
31148
31149
31150         var onewaygroup = layergroup
31151             .selectAll('g.onewaygroup')
31152             .data(['oneway']);
31153
31154         onewaygroup.enter()
31155             .append('g')
31156             .attr('class', 'layer onewaygroup');
31157
31158
31159         var oneways = onewaygroup
31160             .selectAll('path')
31161             .filter(filter)
31162             .data(
31163                 function() { return onewaydata[this.parentNode.parentNode.__data__] || []; },
31164                 function(d) { return [d.id, d.index]; }
31165             );
31166
31167         oneways.enter()
31168             .append('path')
31169             .attr('class', 'oneway')
31170             .attr('marker-mid', 'url(#oneway-marker)');
31171
31172         oneways
31173             .attr('d', function(d) { return d.d; });
31174
31175         if (iD.detect().ie) {
31176             oneways.each(function() { this.parentNode.insertBefore(this, this); });
31177         }
31178
31179         oneways.exit()
31180             .remove();
31181
31182     };
31183 };
31184 iD.svg.MapillaryImages = function(projection, context, dispatch) {
31185     var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
31186         minZoom = 12,
31187         layer = d3.select(null),
31188         _mapillary;
31189
31190
31191     function init() {
31192         if (iD.svg.MapillaryImages.initialized) return;  // run once
31193         iD.svg.MapillaryImages.enabled = false;
31194         iD.svg.MapillaryImages.initialized = true;
31195     }
31196
31197     function getMapillary() {
31198         if (iD.services.mapillary && !_mapillary) {
31199             _mapillary = iD.services.mapillary().on('loadedImages', debouncedRedraw);
31200         } else if (!iD.services.mapillary && _mapillary) {
31201             _mapillary = null;
31202         }
31203         return _mapillary;
31204     }
31205
31206     function showThumbnail(image) {
31207         var mapillary = getMapillary();
31208         if (!mapillary) return;
31209
31210         var thumb = mapillary.selectedThumbnail(),
31211             posX = projection(image.loc)[0],
31212             width = layer.dimensions()[0],
31213             position = (posX < width / 2) ? 'right' : 'left';
31214
31215         if (thumb) {
31216             d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31217                 .classed('selected', function(d) { return d.key === thumb.key; });
31218         }
31219
31220         mapillary.showThumbnail(image.key, position);
31221     }
31222
31223     function hideThumbnail() {
31224         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31225             .classed('selected', false);
31226
31227         var mapillary = getMapillary();
31228         if (mapillary) {
31229             mapillary.hideThumbnail();
31230         }
31231     }
31232
31233     function showLayer() {
31234         editOn();
31235         layer
31236             .style('opacity', 0)
31237             .transition()
31238             .duration(500)
31239             .style('opacity', 1)
31240             .each('end', debouncedRedraw);
31241     }
31242
31243     function hideLayer() {
31244         debouncedRedraw.cancel();
31245         hideThumbnail();
31246         layer
31247             .transition()
31248             .duration(500)
31249             .style('opacity', 0)
31250             .each('end', editOff);
31251     }
31252
31253     function editOn() {
31254         layer.style('display', 'block');
31255     }
31256
31257     function editOff() {
31258         layer.selectAll('.viewfield-group').remove();
31259         layer.style('display', 'none');
31260     }
31261
31262     function transform(d) {
31263         var t = iD.svg.PointTransform(projection)(d);
31264         if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
31265         return t;
31266     }
31267
31268     function update() {
31269         var mapillary = getMapillary(),
31270             data = (mapillary ? mapillary.images(projection, layer.dimensions()) : []);
31271
31272         var markers = layer.selectAll('.viewfield-group')
31273             .data(data, function(d) { return d.key; });
31274
31275         // Enter
31276         var enter = markers.enter()
31277             .append('g')
31278             .attr('class', 'viewfield-group');
31279
31280         enter.append('path')
31281             .attr('class', 'viewfield')
31282             .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
31283             .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');
31284
31285         enter.append('circle')
31286             .attr('dx', '0')
31287             .attr('dy', '0')
31288             .attr('r', '6');
31289
31290         // Exit
31291         markers.exit()
31292             .remove();
31293
31294         // Update
31295         markers
31296             .attr('transform', transform);
31297     }
31298
31299     function drawImages(selection) {
31300         var enabled = iD.svg.MapillaryImages.enabled,
31301             mapillary = getMapillary();
31302
31303         layer = selection.selectAll('.layer-mapillary-images')
31304             .data(mapillary ? [0] : []);
31305
31306         layer.enter()
31307             .append('g')
31308             .attr('class', 'layer-mapillary-images')
31309             .style('display', enabled ? 'block' : 'none')
31310             .on('click', function() {   // deselect/select
31311                 var mapillary = getMapillary();
31312                 if (!mapillary) return;
31313                 var d = d3.event.target.__data__,
31314                     thumb = mapillary.selectedThumbnail();
31315                 if (thumb && thumb.key === d.key) {
31316                     hideThumbnail();
31317                 } else {
31318                     mapillary.selectedThumbnail(d);
31319                     context.map().centerEase(d.loc);
31320                     showThumbnail(d);
31321                 }
31322             })
31323             .on('mouseover', function() {
31324                 var mapillary = getMapillary();
31325                 if (!mapillary) return;
31326                 showThumbnail(d3.event.target.__data__);
31327             })
31328             .on('mouseout', function() {
31329                 var mapillary = getMapillary();
31330                 if (!mapillary) return;
31331                 var thumb = mapillary.selectedThumbnail();
31332                 if (thumb) {
31333                     showThumbnail(thumb);
31334                 } else {
31335                     hideThumbnail();
31336                 }
31337             });
31338
31339         layer.exit()
31340             .remove();
31341
31342         if (enabled) {
31343             if (mapillary && ~~context.map().zoom() >= minZoom) {
31344                 editOn();
31345                 update();
31346                 mapillary.loadImages(projection, layer.dimensions());
31347             } else {
31348                 editOff();
31349             }
31350         }
31351     }
31352
31353     drawImages.enabled = function(_) {
31354         if (!arguments.length) return iD.svg.MapillaryImages.enabled;
31355         iD.svg.MapillaryImages.enabled = _;
31356         if (iD.svg.MapillaryImages.enabled) {
31357             showLayer();
31358         } else {
31359             hideLayer();
31360         }
31361         dispatch.change();
31362         return this;
31363     };
31364
31365     drawImages.supported = function() {
31366         return !!getMapillary();
31367     };
31368
31369     drawImages.dimensions = function(_) {
31370         if (!arguments.length) return layer.dimensions();
31371         layer.dimensions(_);
31372         return this;
31373     };
31374
31375     init();
31376     return drawImages;
31377 };
31378 iD.svg.MapillarySigns = function(projection, context, dispatch) {
31379     var debouncedRedraw = _.debounce(function () { dispatch.change(); }, 1000),
31380         minZoom = 12,
31381         layer = d3.select(null),
31382         _mapillary;
31383
31384
31385     function init() {
31386         if (iD.svg.MapillarySigns.initialized) return;  // run once
31387         iD.svg.MapillarySigns.enabled = false;
31388         iD.svg.MapillarySigns.initialized = true;
31389     }
31390
31391     function getMapillary() {
31392         if (iD.services.mapillary && !_mapillary) {
31393             _mapillary = iD.services.mapillary().on('loadedSigns', debouncedRedraw);
31394         } else if (!iD.services.mapillary && _mapillary) {
31395             _mapillary = null;
31396         }
31397         return _mapillary;
31398     }
31399
31400     function showThumbnail(image) {
31401         var mapillary = getMapillary();
31402         if (!mapillary) return;
31403
31404         var thumb = mapillary.selectedThumbnail(),
31405             posX = projection(image.loc)[0],
31406             width = layer.dimensions()[0],
31407             position = (posX < width / 2) ? 'right' : 'left';
31408
31409         if (thumb) {
31410             d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31411                 .classed('selected', function(d) { return d.key === thumb.key; });
31412         }
31413
31414         mapillary.showThumbnail(image.key, position);
31415     }
31416
31417     function hideThumbnail() {
31418         d3.selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
31419             .classed('selected', false);
31420
31421         var mapillary = getMapillary();
31422         if (mapillary) {
31423             mapillary.hideThumbnail();
31424         }
31425     }
31426
31427     function showLayer() {
31428         editOn();
31429         debouncedRedraw();
31430     }
31431
31432     function hideLayer() {
31433         debouncedRedraw.cancel();
31434         hideThumbnail();
31435         editOff();
31436     }
31437
31438     function editOn() {
31439         layer.style('display', 'block');
31440     }
31441
31442     function editOff() {
31443         layer.selectAll('.icon-sign').remove();
31444         layer.style('display', 'none');
31445     }
31446
31447     function update() {
31448         var mapillary = getMapillary(),
31449             data = (mapillary ? mapillary.signs(projection, layer.dimensions()) : []);
31450
31451         var signs = layer.selectAll('.icon-sign')
31452             .data(data, function(d) { return d.key; });
31453
31454         // Enter
31455         var enter = signs.enter()
31456             .append('foreignObject')
31457             .attr('class', 'icon-sign')
31458             .attr('width', '32px')      // for Firefox
31459             .attr('height', '32px');    // for Firefox
31460
31461         enter
31462             .append('xhtml:body')
31463             .html(mapillary.signHTML);
31464
31465         enter
31466             .on('click', function(d) {   // deselect/select
31467                 var mapillary = getMapillary();
31468                 if (!mapillary) return;
31469                 var thumb = mapillary.selectedThumbnail();
31470                 if (thumb && thumb.key === d.key) {
31471                     hideThumbnail();
31472                 } else {
31473                     mapillary.selectedThumbnail(d);
31474                     context.map().centerEase(d.loc);
31475                     showThumbnail(d);
31476                 }
31477             })
31478             .on('mouseover', showThumbnail)
31479             .on('mouseout', function() {
31480                 var mapillary = getMapillary();
31481                 if (!mapillary) return;
31482                 var thumb = mapillary.selectedThumbnail();
31483                 if (thumb) {
31484                     showThumbnail(thumb);
31485                 } else {
31486                     hideThumbnail();
31487                 }
31488             });
31489
31490         // Exit
31491         signs.exit()
31492             .remove();
31493
31494         // Update
31495         signs
31496             .attr('transform', iD.svg.PointTransform(projection));
31497     }
31498
31499     function drawSigns(selection) {
31500         var enabled = iD.svg.MapillarySigns.enabled,
31501             mapillary = getMapillary();
31502
31503         layer = selection.selectAll('.layer-mapillary-signs')
31504             .data(mapillary ? [0] : []);
31505
31506         layer.enter()
31507             .append('g')
31508             .attr('class', 'layer-mapillary-signs')
31509             .style('display', enabled ? 'block' : 'none')
31510             .attr('transform', 'translate(-16, -16)');  // center signs on loc
31511
31512         layer.exit()
31513             .remove();
31514
31515         if (enabled) {
31516             if (mapillary && ~~context.map().zoom() >= minZoom) {
31517                 editOn();
31518                 update();
31519                 mapillary.loadSigns(context, projection, layer.dimensions());
31520             } else {
31521                 editOff();
31522             }
31523         }
31524     }
31525
31526     drawSigns.enabled = function(_) {
31527         if (!arguments.length) return iD.svg.MapillarySigns.enabled;
31528         iD.svg.MapillarySigns.enabled = _;
31529         if (iD.svg.MapillarySigns.enabled) {
31530             showLayer();
31531         } else {
31532             hideLayer();
31533         }
31534         dispatch.change();
31535         return this;
31536     };
31537
31538     drawSigns.supported = function() {
31539         var mapillary = getMapillary();
31540         return (mapillary && mapillary.signsSupported());
31541     };
31542
31543     drawSigns.dimensions = function(_) {
31544         if (!arguments.length) return layer.dimensions();
31545         layer.dimensions(_);
31546         return this;
31547     };
31548
31549     init();
31550     return drawSigns;
31551 };
31552 iD.svg.Midpoints = function(projection, context) {
31553     return function drawMidpoints(surface, graph, entities, filter, extent) {
31554         var poly = extent.polygon(),
31555             midpoints = {};
31556
31557         for (var i = 0; i < entities.length; i++) {
31558             var entity = entities[i];
31559
31560             if (entity.type !== 'way')
31561                 continue;
31562             if (!filter(entity))
31563                 continue;
31564             if (context.selectedIDs().indexOf(entity.id) < 0)
31565                 continue;
31566
31567             var nodes = graph.childNodes(entity);
31568             for (var j = 0; j < nodes.length - 1; j++) {
31569
31570                 var a = nodes[j],
31571                     b = nodes[j + 1],
31572                     id = [a.id, b.id].sort().join('-');
31573
31574                 if (midpoints[id]) {
31575                     midpoints[id].parents.push(entity);
31576                 } else {
31577                     if (iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
31578                         var point = iD.geo.interp(a.loc, b.loc, 0.5),
31579                             loc = null;
31580
31581                         if (extent.intersects(point)) {
31582                             loc = point;
31583                         } else {
31584                             for (var k = 0; k < 4; k++) {
31585                                 point = iD.geo.lineIntersection([a.loc, b.loc], [poly[k], poly[k+1]]);
31586                                 if (point &&
31587                                     iD.geo.euclideanDistance(projection(a.loc), projection(point)) > 20 &&
31588                                     iD.geo.euclideanDistance(projection(b.loc), projection(point)) > 20)
31589                                 {
31590                                     loc = point;
31591                                     break;
31592                                 }
31593                             }
31594                         }
31595
31596                         if (loc) {
31597                             midpoints[id] = {
31598                                 type: 'midpoint',
31599                                 id: id,
31600                                 loc: loc,
31601                                 edge: [a.id, b.id],
31602                                 parents: [entity]
31603                             };
31604                         }
31605                     }
31606                 }
31607             }
31608         }
31609
31610         function midpointFilter(d) {
31611             if (midpoints[d.id])
31612                 return true;
31613
31614             for (var i = 0; i < d.parents.length; i++)
31615                 if (filter(d.parents[i]))
31616                     return true;
31617
31618             return false;
31619         }
31620
31621         var groups = surface.selectAll('.layer-hit').selectAll('g.midpoint')
31622             .filter(midpointFilter)
31623             .data(_.values(midpoints), function(d) { return d.id; });
31624
31625         var enter = groups.enter()
31626             .insert('g', ':first-child')
31627             .attr('class', 'midpoint');
31628
31629         enter.append('polygon')
31630             .attr('points', '-6,8 10,0 -6,-8')
31631             .attr('class', 'shadow');
31632
31633         enter.append('polygon')
31634             .attr('points', '-3,4 5,0 -3,-4')
31635             .attr('class', 'fill');
31636
31637         groups
31638             .attr('transform', function(d) {
31639                 var translate = iD.svg.PointTransform(projection),
31640                     a = context.entity(d.edge[0]),
31641                     b = context.entity(d.edge[1]),
31642                     angle = Math.round(iD.geo.angle(a, b, projection) * (180 / Math.PI));
31643                 return translate(d) + ' rotate(' + angle + ')';
31644             })
31645             .call(iD.svg.TagClasses().tags(
31646                 function(d) { return d.parents[0].tags; }
31647             ));
31648
31649         // Propagate data bindings.
31650         groups.select('polygon.shadow');
31651         groups.select('polygon.fill');
31652
31653         groups.exit()
31654             .remove();
31655     };
31656 };
31657 iD.svg.Osm = function() {
31658     return function drawOsm(selection) {
31659         var layers = selection.selectAll('.layer-osm')
31660             .data(['areas', 'lines', 'hit', 'halo', 'label']);
31661
31662         layers.enter().append('g')
31663             .attr('class', function(d) { return 'layer-osm layer-' + d; });
31664     };
31665 };
31666 iD.svg.Points = function(projection, context) {
31667     function markerPath(selection, klass) {
31668         selection
31669             .attr('class', klass)
31670             .attr('transform', 'translate(-8, -23)')
31671             .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');
31672     }
31673
31674     function sortY(a, b) {
31675         return b.loc[1] - a.loc[1];
31676     }
31677
31678     return function drawPoints(surface, graph, entities, filter) {
31679         var wireframe = surface.classed('fill-wireframe'),
31680             points = wireframe ? [] : _.filter(entities, function(e) {
31681                 return e.geometry(graph) === 'point';
31682             });
31683
31684         points.sort(sortY);
31685
31686         var groups = surface.selectAll('.layer-hit').selectAll('g.point')
31687             .filter(filter)
31688             .data(points, iD.Entity.key);
31689
31690         var group = groups.enter()
31691             .append('g')
31692             .attr('class', function(d) { return 'node point ' + d.id; })
31693             .order();
31694
31695         group.append('path')
31696             .call(markerPath, 'shadow');
31697
31698         group.append('path')
31699             .call(markerPath, 'stroke');
31700
31701         group.append('use')
31702             .attr('transform', 'translate(-6, -20)')
31703             .attr('class', 'icon')
31704             .attr('width', '12px')
31705             .attr('height', '12px');
31706
31707         groups.attr('transform', iD.svg.PointTransform(projection))
31708             .call(iD.svg.TagClasses());
31709
31710         // Selecting the following implicitly
31711         // sets the data (point entity) on the element
31712         groups.select('.shadow');
31713         groups.select('.stroke');
31714         groups.select('.icon')
31715             .attr('xlink:href', function(entity) {
31716                 var preset = context.presets().match(entity, graph);
31717                 return preset.icon ? '#' + preset.icon + '-12' : '';
31718             });
31719
31720         groups.exit()
31721             .remove();
31722     };
31723 };
31724 iD.svg.TagClasses = function() {
31725     var primaries = [
31726             'building', 'highway', 'railway', 'waterway', 'aeroway',
31727             'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse',
31728             'leisure', 'place'
31729         ],
31730         statuses = [
31731             'proposed', 'construction', 'disused', 'abandoned', 'dismantled',
31732             'razed', 'demolished', 'obliterated'
31733         ],
31734         secondaries = [
31735             'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
31736             'surface', 'tracktype', 'crossing'
31737         ],
31738         tagClassRe = /^tag-/,
31739         tags = function(entity) { return entity.tags; };
31740
31741
31742     var tagClasses = function(selection) {
31743         selection.each(function tagClassesEach(entity) {
31744             var value = this.className,
31745                 classes, primary, status;
31746
31747             if (value.baseVal !== undefined) value = value.baseVal;
31748
31749             classes = value.trim().split(/\s+/).filter(function(name) {
31750                 return name.length && !tagClassRe.test(name);
31751             }).join(' ');
31752
31753             var t = tags(entity), i, k, v;
31754
31755             // pick at most one primary classification tag..
31756             for (i = 0; i < primaries.length; i++) {
31757                 k = primaries[i];
31758                 v = t[k];
31759                 if (!v || v === 'no') continue;
31760
31761                 primary = k;
31762                 if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
31763                     status = v;
31764                     classes += ' tag-' + k;
31765                 } else {
31766                     classes += ' tag-' + k + ' tag-' + k + '-' + v;
31767                 }
31768
31769                 break;
31770             }
31771
31772             // add at most one status tag, only if relates to primary tag..
31773             if (!status) {
31774                 for (i = 0; i < statuses.length; i++) {
31775                     k = statuses[i];
31776                     v = t[k];
31777                     if (!v || v === 'no') continue;
31778
31779                     if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
31780                         status = k;
31781                     }
31782                     else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
31783                         status = k;
31784                     } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
31785                         status = k;
31786                         primary = v;
31787                         classes += ' tag-' + v;
31788                     }  // else ignore e.g.  `highway=path + abandoned=railway`
31789
31790                     if (status) break;
31791                 }
31792             }
31793
31794             if (status) {
31795                 classes += ' tag-status tag-status-' + status;
31796             }
31797
31798             // add any secondary (structure) tags
31799             for (i = 0; i < secondaries.length; i++) {
31800                 k = secondaries[i];
31801                 v = t[k];
31802                 if (!v || v === 'no') continue;
31803                 classes += ' tag-' + k + ' tag-' + k + '-' + v;
31804             }
31805
31806             // For highways, look for surface tagging..
31807             if (primary === 'highway') {
31808                 var paved = (t.highway !== 'track');
31809                 for (k in t) {
31810                     v = t[k];
31811                     if (k in iD.pavedTags) {
31812                         paved = !!iD.pavedTags[k][v];
31813                         break;
31814                     }
31815                 }
31816                 if (!paved) {
31817                     classes += ' tag-unpaved';
31818                 }
31819             }
31820
31821             classes = classes.trim();
31822
31823             if (classes !== value) {
31824                 d3.select(this).attr('class', classes);
31825             }
31826         });
31827     };
31828
31829     tagClasses.tags = function(_) {
31830         if (!arguments.length) return tags;
31831         tags = _;
31832         return tagClasses;
31833     };
31834
31835     return tagClasses;
31836 };
31837 iD.svg.Turns = function(projection) {
31838     return function drawTurns(surface, graph, turns) {
31839         function key(turn) {
31840             return [turn.from.node + turn.via.node + turn.to.node].join('-');
31841         }
31842
31843         function icon(turn) {
31844             var u = turn.u ? '-u' : '';
31845             if (!turn.restriction)
31846                 return '#turn-yes' + u;
31847             var restriction = graph.entity(turn.restriction).tags.restriction;
31848             return '#turn-' +
31849                 (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u;
31850         }
31851
31852         var groups = surface.selectAll('.layer-hit').selectAll('g.turn')
31853             .data(turns, key);
31854
31855         // Enter
31856         var enter = groups.enter().append('g')
31857             .attr('class', 'turn');
31858
31859         var nEnter = enter.filter(function (turn) { return !turn.u; });
31860
31861         nEnter.append('rect')
31862             .attr('transform', 'translate(-22, -12)')
31863             .attr('width', '44')
31864             .attr('height', '24');
31865
31866         nEnter.append('use')
31867             .attr('transform', 'translate(-22, -12)')
31868             .attr('width', '44')
31869             .attr('height', '24');
31870
31871
31872         var uEnter = enter.filter(function (turn) { return turn.u; });
31873
31874         uEnter.append('circle')
31875             .attr('r', '16');
31876
31877         uEnter.append('use')
31878             .attr('transform', 'translate(-16, -16)')
31879             .attr('width', '32')
31880             .attr('height', '32');
31881
31882
31883         // Update
31884         groups
31885             .attr('transform', function (turn) {
31886                 var v = graph.entity(turn.via.node),
31887                     t = graph.entity(turn.to.node),
31888                     a = iD.geo.angle(v, t, projection),
31889                     p = projection(v.loc),
31890                     r = turn.u ? 0 : 60;
31891
31892                 return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' +
31893                     'rotate(' + a * 180 / Math.PI + ')';
31894             });
31895
31896         groups.select('use')
31897             .attr('xlink:href', icon);
31898
31899         groups.select('rect');
31900         groups.select('circle');
31901
31902
31903         // Exit
31904         groups.exit()
31905             .remove();
31906
31907         return this;
31908     };
31909 };
31910 iD.svg.Vertices = function(projection, context) {
31911     var radiuses = {
31912         //       z16-, z17, z18+, tagged
31913         shadow: [6,    7.5,   7.5,  11.5],
31914         stroke: [2.5,  3.5,   3.5,  7],
31915         fill:   [1,    1.5,   1.5,  1.5]
31916     };
31917
31918     var hover;
31919
31920     function siblingAndChildVertices(ids, graph, extent) {
31921         var vertices = {};
31922
31923         function addChildVertices(entity) {
31924             if (!context.features().isHiddenFeature(entity, graph, entity.geometry(graph))) {
31925                 var i;
31926                 if (entity.type === 'way') {
31927                     for (i = 0; i < entity.nodes.length; i++) {
31928                         addChildVertices(graph.entity(entity.nodes[i]));
31929                     }
31930                 } else if (entity.type === 'relation') {
31931                     for (i = 0; i < entity.members.length; i++) {
31932                         var member = context.hasEntity(entity.members[i].id);
31933                         if (member) {
31934                             addChildVertices(member);
31935                         }
31936                     }
31937                 } else if (entity.intersects(extent, graph)) {
31938                     vertices[entity.id] = entity;
31939                 }
31940             }
31941         }
31942
31943         ids.forEach(function(id) {
31944             var entity = context.hasEntity(id);
31945             if (entity && entity.type === 'node') {
31946                 vertices[entity.id] = entity;
31947                 context.graph().parentWays(entity).forEach(function(entity) {
31948                     addChildVertices(entity);
31949                 });
31950             } else if (entity) {
31951                 addChildVertices(entity);
31952             }
31953         });
31954
31955         return vertices;
31956     }
31957
31958     function draw(selection, vertices, klass, graph, zoom) {
31959         var icons = {},
31960             z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
31961
31962         var groups = selection
31963             .data(vertices, iD.Entity.key);
31964
31965         function icon(entity) {
31966             if (entity.id in icons) return icons[entity.id];
31967             icons[entity.id] =
31968                 entity.hasInterestingTags() &&
31969                 context.presets().match(entity, graph).icon;
31970             return icons[entity.id];
31971         }
31972
31973         function setClass(klass) {
31974             return function(entity) {
31975                 this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id);
31976             };
31977         }
31978
31979         function setAttributes(selection) {
31980             ['shadow','stroke','fill'].forEach(function(klass) {
31981                 var rads = radiuses[klass];
31982                 selection.selectAll('.' + klass)
31983                     .each(function(entity) {
31984                         var i = z && icon(entity),
31985                             c = i ? 0.5 : 0,
31986                             r = rads[i ? 3 : z];
31987                         this.setAttribute('cx', c);
31988                         this.setAttribute('cy', -c);
31989                         this.setAttribute('r', r);
31990                         if (i && klass === 'fill') {
31991                             this.setAttribute('visibility', 'hidden');
31992                         } else {
31993                             this.removeAttribute('visibility');
31994                         }
31995                     });
31996             });
31997
31998             selection.selectAll('use')
31999                 .each(function() {
32000                     if (z) {
32001                         this.removeAttribute('visibility');
32002                     } else {
32003                         this.setAttribute('visibility', 'hidden');
32004                     }
32005                 });
32006         }
32007
32008         var enter = groups.enter()
32009             .append('g')
32010             .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; });
32011
32012         enter.append('circle')
32013             .each(setClass('shadow'));
32014
32015         enter.append('circle')
32016             .each(setClass('stroke'));
32017
32018         // Vertices with icons get a `use`.
32019         enter.filter(function(d) { return icon(d); })
32020             .append('use')
32021             .attr('transform', 'translate(-6, -6)')
32022             .attr('xlink:href', function(d) { return '#' + icon(d) + '-12'; })
32023             .attr('width', '12px')
32024             .attr('height', '12px')
32025             .each(setClass('icon'));
32026
32027         // Vertices with tags get a fill.
32028         enter.filter(function(d) { return d.hasInterestingTags(); })
32029             .append('circle')
32030             .each(setClass('fill'));
32031
32032         groups
32033             .attr('transform', iD.svg.PointTransform(projection))
32034             .classed('shared', function(entity) { return graph.isShared(entity); })
32035             .call(setAttributes);
32036
32037         groups.exit()
32038             .remove();
32039     }
32040
32041     function drawVertices(surface, graph, entities, filter, extent, zoom) {
32042         var selected = siblingAndChildVertices(context.selectedIDs(), graph, extent),
32043             wireframe = surface.classed('fill-wireframe'),
32044             vertices = [];
32045
32046         for (var i = 0; i < entities.length; i++) {
32047             var entity = entities[i],
32048                 geometry = entity.geometry(graph);
32049
32050             if (wireframe && geometry === 'point') {
32051                 vertices.push(entity);
32052                 continue;
32053             }
32054
32055             if (geometry !== 'vertex')
32056                 continue;
32057
32058             if (entity.id in selected ||
32059                 entity.hasInterestingTags() ||
32060                 entity.isIntersection(graph)) {
32061                 vertices.push(entity);
32062             }
32063         }
32064
32065         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-persistent')
32066             .filter(filter)
32067             .call(draw, vertices, 'vertex-persistent', graph, zoom);
32068
32069         drawHover(surface, graph, extent, zoom);
32070     }
32071
32072     function drawHover(surface, graph, extent, zoom) {
32073         var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent) : {};
32074
32075         surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-hover')
32076             .call(draw, d3.values(hovered), 'vertex-hover', graph, zoom);
32077     }
32078
32079     drawVertices.drawHover = function(surface, graph, target, extent, zoom) {
32080         if (target === hover) return;
32081         hover = target;
32082         drawHover(surface, graph, extent, zoom);
32083     };
32084
32085     return drawVertices;
32086 };
32087 iD.ui = function(context) {
32088     function render(container) {
32089         var map = context.map();
32090
32091         if (iD.detect().opera) container.classed('opera', true);
32092
32093         var hash = iD.behavior.Hash(context);
32094
32095         hash();
32096
32097         if (!hash.hadHash) {
32098             map.centerZoom([0, 0], 2);
32099         }
32100
32101         container.append('svg')
32102             .attr('id', 'defs')
32103             .call(iD.svg.Defs(context));
32104
32105         container.append('div')
32106             .attr('id', 'sidebar')
32107             .attr('class', 'col4')
32108             .call(ui.sidebar);
32109
32110         var content = container.append('div')
32111             .attr('id', 'content');
32112
32113         var bar = content.append('div')
32114             .attr('id', 'bar')
32115             .attr('class', 'fillD');
32116
32117         content.append('div')
32118             .attr('id', 'map')
32119             .call(map);
32120
32121         content
32122             .call(iD.ui.MapInMap(context));
32123
32124         content.append('div')
32125             .call(iD.ui.Info(context));
32126
32127         bar.append('div')
32128             .attr('class', 'spacer col4');
32129
32130         var limiter = bar.append('div')
32131             .attr('class', 'limiter');
32132
32133         limiter.append('div')
32134             .attr('class', 'button-wrap joined col3')
32135             .call(iD.ui.Modes(context), limiter);
32136
32137         limiter.append('div')
32138             .attr('class', 'button-wrap joined col1')
32139             .call(iD.ui.UndoRedo(context));
32140
32141         limiter.append('div')
32142             .attr('class', 'button-wrap col1')
32143             .call(iD.ui.Save(context));
32144
32145         bar.append('div')
32146             .attr('class', 'full-screen')
32147             .call(iD.ui.FullScreen(context));
32148
32149         bar.append('div')
32150             .attr('class', 'spinner')
32151             .call(iD.ui.Spinner(context));
32152
32153         var controls = bar.append('div')
32154             .attr('class', 'map-controls');
32155
32156         controls.append('div')
32157             .attr('class', 'map-control zoombuttons')
32158             .call(iD.ui.Zoom(context));
32159
32160         controls.append('div')
32161             .attr('class', 'map-control geolocate-control')
32162             .call(iD.ui.Geolocate(context));
32163
32164         controls.append('div')
32165             .attr('class', 'map-control background-control')
32166             .call(iD.ui.Background(context));
32167
32168         controls.append('div')
32169             .attr('class', 'map-control map-data-control')
32170             .call(iD.ui.MapData(context));
32171
32172         controls.append('div')
32173             .attr('class', 'map-control help-control')
32174             .call(iD.ui.Help(context));
32175
32176         var about = content.append('div')
32177             .attr('id', 'about');
32178
32179         about.append('div')
32180             .attr('id', 'attrib')
32181             .call(iD.ui.Attribution(context));
32182
32183         var footer = about.append('div')
32184             .attr('id', 'footer')
32185             .attr('class', 'fillD');
32186
32187         footer.append('div')
32188             .attr('class', 'api-status')
32189             .call(iD.ui.Status(context));
32190
32191         footer.append('div')
32192             .attr('id', 'scale-block')
32193             .call(iD.ui.Scale(context));
32194
32195         var aboutList = footer.append('div')
32196             .attr('id', 'info-block')
32197             .append('ul')
32198             .attr('id', 'about-list');
32199
32200         if (!context.embed()) {
32201             aboutList.call(iD.ui.Account(context));
32202         }
32203
32204         aboutList.append('li')
32205             .append('a')
32206             .attr('target', '_blank')
32207             .attr('tabindex', -1)
32208             .attr('href', 'https://github.com/openstreetmap/iD')
32209             .text(iD.version);
32210
32211         var issueLinks = aboutList.append('li');
32212
32213         issueLinks.append('a')
32214             .attr('target', '_blank')
32215             .attr('tabindex', -1)
32216             .attr('href', 'https://github.com/openstreetmap/iD/issues')
32217             .call(iD.svg.Icon('#icon-bug', 'light'))
32218             .call(bootstrap.tooltip()
32219                 .title(t('report_a_bug'))
32220                 .placement('top')
32221             );
32222
32223         issueLinks.append('a')
32224             .attr('target', '_blank')
32225             .attr('tabindex', -1)
32226             .attr('href', 'https://github.com/openstreetmap/iD/blob/master/CONTRIBUTING.md#translating')
32227             .call(iD.svg.Icon('#icon-translate', 'light'))
32228             .call(bootstrap.tooltip()
32229                 .title(t('help_translate'))
32230                 .placement('top')
32231             );
32232
32233         aboutList.append('li')
32234             .attr('class', 'feature-warning')
32235             .attr('tabindex', -1)
32236             .call(iD.ui.FeatureInfo(context));
32237
32238         aboutList.append('li')
32239             .attr('class', 'user-list')
32240             .attr('tabindex', -1)
32241             .call(iD.ui.Contributors(context));
32242
32243         window.onbeforeunload = function() {
32244             return context.save();
32245         };
32246
32247         window.onunload = function() {
32248             context.history().unlock();
32249         };
32250
32251         var mapDimensions = map.dimensions();
32252
32253         d3.select(window).on('resize.editor', function() {
32254             mapDimensions = content.dimensions(null);
32255             map.dimensions(mapDimensions);
32256         });
32257
32258         function pan(d) {
32259             return function() {
32260                 d3.event.preventDefault();
32261                 if (!context.inIntro()) context.pan(d);
32262             };
32263         }
32264
32265         // pan amount
32266         var pa = 10;
32267
32268         var keybinding = d3.keybinding('main')
32269             .on('⌫', function() { d3.event.preventDefault(); })
32270             .on('←', pan([pa, 0]))
32271             .on('↑', pan([0, pa]))
32272             .on('→', pan([-pa, 0]))
32273             .on('↓', pan([0, -pa]))
32274             .on('⇧←', pan([mapDimensions[0], 0]))
32275             .on('⇧↑', pan([0, mapDimensions[1]]))
32276             .on('⇧→', pan([-mapDimensions[0], 0]))
32277             .on('⇧↓', pan([0, -mapDimensions[1]]))
32278             .on(iD.ui.cmd('⌘←'), pan([mapDimensions[0], 0]))
32279             .on(iD.ui.cmd('⌘↑'), pan([0, mapDimensions[1]]))
32280             .on(iD.ui.cmd('⌘→'), pan([-mapDimensions[0], 0]))
32281             .on(iD.ui.cmd('⌘↓'), pan([0, -mapDimensions[1]]));
32282
32283         d3.select(document)
32284             .call(keybinding);
32285
32286         context.enter(iD.modes.Browse(context));
32287
32288         context.container()
32289             .call(iD.ui.Splash(context))
32290             .call(iD.ui.Restore(context));
32291
32292         var authenticating = iD.ui.Loading(context)
32293             .message(t('loading_auth'));
32294
32295         context.connection()
32296             .on('authenticating.ui', function() {
32297                 context.container()
32298                     .call(authenticating);
32299             })
32300             .on('authenticated.ui', function() {
32301                 authenticating.close();
32302             });
32303     }
32304
32305     function ui(container) {
32306         context.container(container);
32307         context.loadLocale(function() {
32308             render(container);
32309         });
32310     }
32311
32312     ui.sidebar = iD.ui.Sidebar(context);
32313
32314     return ui;
32315 };
32316
32317 iD.ui.tooltipHtml = function(text, key) {
32318     var s = '<span>' + text + '</span>';
32319     if (key) {
32320         s += '<div class="keyhint-wrap">' +
32321             '<span> ' + (t('tooltip_keyhint')) + ' </span>' +
32322             '<span class="keyhint"> ' + key + '</span></div>';
32323     }
32324     return s;
32325 };
32326 iD.ui.Account = function(context) {
32327     var connection = context.connection();
32328
32329     function update(selection) {
32330         if (!connection.authenticated()) {
32331             selection.selectAll('#userLink, #logoutLink')
32332                 .classed('hide', true);
32333             return;
32334         }
32335
32336         connection.userDetails(function(err, details) {
32337             var userLink = selection.select('#userLink'),
32338                 logoutLink = selection.select('#logoutLink');
32339
32340             userLink.html('');
32341             logoutLink.html('');
32342
32343             if (err) return;
32344
32345             selection.selectAll('#userLink, #logoutLink')
32346                 .classed('hide', false);
32347
32348             // Link
32349             userLink.append('a')
32350                 .attr('href', connection.userURL(details.display_name))
32351                 .attr('target', '_blank');
32352
32353             // Add thumbnail or dont
32354             if (details.image_url) {
32355                 userLink.append('img')
32356                     .attr('class', 'icon pre-text user-icon')
32357                     .attr('src', details.image_url);
32358             } else {
32359                 userLink
32360                     .call(iD.svg.Icon('#icon-avatar', 'pre-text light'));
32361             }
32362
32363             // Add user name
32364             userLink.append('span')
32365                 .attr('class', 'label')
32366                 .text(details.display_name);
32367
32368             logoutLink.append('a')
32369                 .attr('class', 'logout')
32370                 .attr('href', '#')
32371                 .text(t('logout'))
32372                 .on('click.logout', function() {
32373                     d3.event.preventDefault();
32374                     connection.logout();
32375                 });
32376         });
32377     }
32378
32379     return function(selection) {
32380         selection.append('li')
32381             .attr('id', 'logoutLink')
32382             .classed('hide', true);
32383
32384         selection.append('li')
32385             .attr('id', 'userLink')
32386             .classed('hide', true);
32387
32388         connection.on('auth.account', function() { update(selection); });
32389         update(selection);
32390     };
32391 };
32392 iD.ui.Attribution = function(context) {
32393     var selection;
32394
32395     function attribution(data, klass) {
32396         var div = selection.selectAll('.' + klass)
32397             .data([0]);
32398
32399         div.enter()
32400             .append('div')
32401             .attr('class', klass);
32402
32403         var background = div.selectAll('.attribution')
32404             .data(data, function(d) { return d.name(); });
32405
32406         background.enter()
32407             .append('span')
32408             .attr('class', 'attribution')
32409             .each(function(d) {
32410                 if (d.terms_html) {
32411                     d3.select(this)
32412                         .html(d.terms_html);
32413                     return;
32414                 }
32415
32416                 var source = d.terms_text || d.id || d.name();
32417
32418                 if (d.logo) {
32419                     source = '<img class="source-image" src="' + context.imagePath(d.logo) + '">';
32420                 }
32421
32422                 if (d.terms_url) {
32423                     d3.select(this)
32424                         .append('a')
32425                         .attr('href', d.terms_url)
32426                         .attr('target', '_blank')
32427                         .html(source);
32428                 } else {
32429                     d3.select(this)
32430                         .text(source);
32431                 }
32432             });
32433
32434         background.exit()
32435             .remove();
32436
32437         var copyright = background.selectAll('.copyright-notice')
32438             .data(function(d) {
32439                 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
32440                 return notice ? [notice] : [];
32441             });
32442
32443         copyright.enter()
32444             .append('span')
32445             .attr('class', 'copyright-notice');
32446
32447         copyright.text(String);
32448
32449         copyright.exit()
32450             .remove();
32451     }
32452
32453     function update() {
32454         attribution([context.background().baseLayerSource()], 'base-layer-attribution');
32455         attribution(context.background().overlayLayerSources().filter(function (s) {
32456             return s.validZoom(context.map().zoom());
32457         }), 'overlay-layer-attribution');
32458     }
32459
32460     return function(select) {
32461         selection = select;
32462
32463         context.background()
32464             .on('change.attribution', update);
32465
32466         context.map()
32467             .on('move.attribution', _.throttle(update, 400, {leading: false}));
32468
32469         update();
32470     };
32471 };
32472 iD.ui.Background = function(context) {
32473     var key = 'B',
32474         opacities = [1, 0.75, 0.5, 0.25],
32475         directions = [
32476             ['right', [0.5, 0]],
32477             ['top', [0, -0.5]],
32478             ['left', [-0.5, 0]],
32479             ['bottom', [0, 0.5]]],
32480         opacityDefault = (context.storage('background-opacity') !== null) ?
32481             (+context.storage('background-opacity')) : 1.0,
32482         customTemplate = context.storage('background-custom-template') || '';
32483
32484     // Can be 0 from <1.3.0 use or due to issue #1923.
32485     if (opacityDefault === 0) opacityDefault = 1.0;
32486
32487     function background(selection) {
32488
32489         function sortSources(a, b) {
32490             return a.best() ? -1
32491                 : b.best() ? 1
32492                 : a.id === 'none' ? 1
32493                 : b.id === 'none' ? -1
32494                 : d3.ascending(a, b);
32495         }
32496
32497         function setOpacity(d) {
32498             var bg = context.container().selectAll('.layer-background')
32499                 .transition()
32500                 .style('opacity', d)
32501                 .attr('data-opacity', d);
32502
32503             if (!iD.detect().opera) {
32504                 iD.util.setTransform(bg, 0, 0);
32505             }
32506
32507             opacityList.selectAll('li')
32508                 .classed('active', function(_) { return _ === d; });
32509
32510             context.storage('background-opacity', d);
32511         }
32512
32513         function selectLayer() {
32514             function active(d) {
32515                 return context.background().showsLayer(d);
32516             }
32517
32518             content.selectAll('.layer, .custom_layer')
32519                 .classed('active', active)
32520                 .selectAll('input')
32521                 .property('checked', active);
32522         }
32523
32524         function clickSetSource(d) {
32525             d3.event.preventDefault();
32526             context.background().baseLayerSource(d);
32527             selectLayer();
32528         }
32529
32530         function editCustom() {
32531             d3.event.preventDefault();
32532             var template = window.prompt(t('background.custom_prompt'), customTemplate);
32533             if (!template ||
32534                 template.indexOf('google.com') !== -1 ||
32535                 template.indexOf('googleapis.com') !== -1 ||
32536                 template.indexOf('google.ru') !== -1) {
32537                 selectLayer();
32538                 return;
32539             }
32540             setCustom(template);
32541         }
32542
32543         function setCustom(template) {
32544             context.background().baseLayerSource(iD.BackgroundSource.Custom(template));
32545             selectLayer();
32546             context.storage('background-custom-template', template);
32547         }
32548
32549         function clickSetOverlay(d) {
32550             d3.event.preventDefault();
32551             context.background().toggleOverlayLayer(d);
32552             selectLayer();
32553         }
32554
32555         function drawList(layerList, type, change, filter) {
32556             var sources = context.background()
32557                 .sources(context.map().extent())
32558                 .filter(filter);
32559
32560             var layerLinks = layerList.selectAll('li.layer')
32561                 .data(sources, function(d) { return d.name(); })
32562                 .sort(sortSources);
32563
32564             var enter = layerLinks.enter()
32565                 .insert('li', '.custom_layer')
32566                 .attr('class', 'layer')
32567                 .classed('best', function(d) { return d.best(); });
32568
32569             // only set tooltips for layers with tooltips
32570             enter.filter(function(d) { return d.description; })
32571                 .call(bootstrap.tooltip()
32572                     .title(function(d) { return d.description; })
32573                     .placement('top'));
32574
32575             enter.filter(function(d) { return d.best(); })
32576                 .append('div')
32577                 .attr('class', 'best')
32578                 .call(bootstrap.tooltip()
32579                     .title(t('background.best_imagery'))
32580                     .placement('left'))
32581                 .append('span')
32582                 .html('&#9733;');
32583
32584             var label = enter.append('label');
32585
32586             label.append('input')
32587                 .attr('type', type)
32588                 .attr('name', 'layers')
32589                 .on('change', change);
32590
32591             label.append('span')
32592                 .text(function(d) { return d.name(); });
32593
32594             layerLinks.exit()
32595                 .remove();
32596
32597             layerList.style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
32598         }
32599
32600         function update() {
32601             backgroundList.call(drawList, 'radio', clickSetSource, function(d) { return !d.overlay; });
32602             overlayList.call(drawList, 'checkbox', clickSetOverlay, function(d) { return d.overlay; });
32603
32604             selectLayer();
32605
32606             var source = context.background().baseLayerSource();
32607             if (source.id === 'custom') {
32608                 customTemplate = source.template;
32609             }
32610
32611             updateOffsetVal();
32612         }
32613
32614         function updateOffsetVal() {
32615             var meters = iD.geo.offsetToMeters(context.background().offset()),
32616                 x = +meters[0].toFixed(2),
32617                 y = +meters[1].toFixed(2);
32618
32619             d3.selectAll('.nudge-inner-rect')
32620                 .select('input')
32621                 .classed('error', false)
32622                 .property('value', x + ', ' + y);
32623
32624             d3.selectAll('.nudge-reset')
32625                 .classed('disabled', function() {
32626                     return (x === 0 && y === 0);
32627                 });
32628         }
32629
32630         function resetOffset() {
32631             context.background().offset([0, 0]);
32632             updateOffsetVal();
32633         }
32634
32635         function nudge(d) {
32636             context.background().nudge(d, context.map().zoom());
32637             updateOffsetVal();
32638         }
32639
32640         function buttonOffset(d) {
32641             var timeout = window.setTimeout(function() {
32642                     interval = window.setInterval(nudge.bind(null, d), 100);
32643                 }, 500),
32644                 interval;
32645
32646             d3.select(window).on('mouseup', function() {
32647                 window.clearInterval(interval);
32648                 window.clearTimeout(timeout);
32649                 d3.select(window).on('mouseup', null);
32650             });
32651
32652             nudge(d);
32653         }
32654
32655         function inputOffset() {
32656             var input = d3.select(this);
32657             var d = input.node().value;
32658
32659             if (d === '') return resetOffset();
32660
32661             d = d.replace(/;/g, ',').split(',').map(function(n) {
32662                 // if n is NaN, it will always get mapped to false.
32663                 return !isNaN(n) && n;
32664             });
32665
32666             if (d.length !== 2 || !d[0] || !d[1]) {
32667                 input.classed('error', true);
32668                 return;
32669             }
32670
32671             context.background().offset(iD.geo.metersToOffset(d));
32672             updateOffsetVal();
32673         }
32674
32675         function dragOffset() {
32676             var origin = [d3.event.clientX, d3.event.clientY];
32677
32678             d3.select(window)
32679                 .on('mousemove.offset', function() {
32680                     var latest = [d3.event.clientX, d3.event.clientY];
32681                     var d = [
32682                         -(origin[0] - latest[0]) / 4,
32683                         -(origin[1] - latest[1]) / 4
32684                     ];
32685
32686                     origin = latest;
32687                     nudge(d);
32688                 })
32689                 .on('mouseup.offset', function() {
32690                     d3.select(window)
32691                         .on('mousemove.offset', null)
32692                         .on('mouseup.offset', null);
32693                 });
32694
32695             d3.event.preventDefault();
32696         }
32697
32698         function hide() {
32699             setVisible(false);
32700         }
32701
32702         function toggle() {
32703             if (d3.event) d3.event.preventDefault();
32704             tooltip.hide(button);
32705             setVisible(!button.classed('active'));
32706         }
32707
32708         function setVisible(show) {
32709             if (show !== shown) {
32710                 button.classed('active', show);
32711                 shown = show;
32712
32713                 if (show) {
32714                     selection.on('mousedown.background-inside', function() {
32715                         return d3.event.stopPropagation();
32716                     });
32717                     content.style('display', 'block')
32718                         .style('right', '-300px')
32719                         .transition()
32720                         .duration(200)
32721                         .style('right', '0px');
32722                 } else {
32723                     content.style('display', 'block')
32724                         .style('right', '0px')
32725                         .transition()
32726                         .duration(200)
32727                         .style('right', '-300px')
32728                         .each('end', function() {
32729                             d3.select(this).style('display', 'none');
32730                         });
32731                     selection.on('mousedown.background-inside', null);
32732                 }
32733             }
32734         }
32735
32736
32737         var content = selection.append('div')
32738                 .attr('class', 'fillL map-overlay col3 content hide'),
32739             tooltip = bootstrap.tooltip()
32740                 .placement('left')
32741                 .html(true)
32742                 .title(iD.ui.tooltipHtml(t('background.description'), key)),
32743             button = selection.append('button')
32744                 .attr('tabindex', -1)
32745                 .on('click', toggle)
32746                 .call(iD.svg.Icon('#icon-layers', 'light'))
32747                 .call(tooltip),
32748             shown = false;
32749
32750
32751         /* opacity switcher */
32752
32753         var opa = content.append('div')
32754                 .attr('class', 'opacity-options-wrapper');
32755
32756         opa.append('h4')
32757             .text(t('background.title'));
32758
32759         var opacityList = opa.append('ul')
32760             .attr('class', 'opacity-options');
32761
32762         opacityList.selectAll('div.opacity')
32763             .data(opacities)
32764             .enter()
32765             .append('li')
32766             .attr('data-original-title', function(d) {
32767                 return t('background.percent_brightness', { opacity: (d * 100) });
32768             })
32769             .on('click.set-opacity', setOpacity)
32770             .html('<div class="select-box"></div>')
32771             .call(bootstrap.tooltip()
32772                 .placement('left'))
32773             .append('div')
32774             .attr('class', 'opacity')
32775             .style('opacity', function(d) { return 1.25 - d; });
32776
32777
32778         /* background switcher */
32779
32780         var backgroundList = content.append('ul')
32781             .attr('class', 'layer-list');
32782
32783         var custom = backgroundList.append('li')
32784             .attr('class', 'custom_layer')
32785             .datum(iD.BackgroundSource.Custom());
32786
32787         custom.append('button')
32788             .attr('class', 'layer-browse')
32789             .call(bootstrap.tooltip()
32790                 .title(t('background.custom_button'))
32791                 .placement('left'))
32792             .on('click', editCustom)
32793             .call(iD.svg.Icon('#icon-search'));
32794
32795         var label = custom.append('label');
32796
32797         label.append('input')
32798             .attr('type', 'radio')
32799             .attr('name', 'layers')
32800             .on('change', function () {
32801                 if (customTemplate) {
32802                     setCustom(customTemplate);
32803                 } else {
32804                     editCustom();
32805                 }
32806             });
32807
32808         label.append('span')
32809             .text(t('background.custom'));
32810
32811         content.append('div')
32812             .attr('class', 'imagery-faq')
32813             .append('a')
32814             .attr('target', '_blank')
32815             .attr('tabindex', -1)
32816             .call(iD.svg.Icon('#icon-out-link', 'inline'))
32817             .attr('href', 'https://github.com/openstreetmap/iD/blob/master/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
32818             .append('span')
32819             .text(t('background.imagery_source_faq'));
32820
32821         var overlayList = content.append('ul')
32822             .attr('class', 'layer-list');
32823
32824         var controls = content.append('div')
32825             .attr('class', 'controls-list');
32826
32827
32828         /* minimap toggle */
32829
32830         var minimapLabel = controls
32831             .append('label')
32832             .call(bootstrap.tooltip()
32833                 .html(true)
32834                 .title(iD.ui.tooltipHtml(t('background.minimap.tooltip'), '/'))
32835                 .placement('top')
32836             );
32837
32838         minimapLabel.classed('minimap-toggle', true)
32839             .append('input')
32840             .attr('type', 'checkbox')
32841             .on('change', function() {
32842                 iD.ui.MapInMap.toggle();
32843                 d3.event.preventDefault();
32844             });
32845
32846         minimapLabel.append('span')
32847             .text(t('background.minimap.description'));
32848
32849
32850         /* imagery offset controls */
32851
32852         var adjustments = content.append('div')
32853             .attr('class', 'adjustments');
32854
32855         adjustments.append('a')
32856             .text(t('background.fix_misalignment'))
32857             .attr('href', '#')
32858             .classed('hide-toggle', true)
32859             .classed('expanded', false)
32860             .on('click', function() {
32861                 var exp = d3.select(this).classed('expanded');
32862                 nudgeContainer.style('display', exp ? 'none' : 'block');
32863                 d3.select(this).classed('expanded', !exp);
32864                 d3.event.preventDefault();
32865             });
32866
32867         var nudgeContainer = adjustments.append('div')
32868             .attr('class', 'nudge-container cf')
32869             .style('display', 'none');
32870
32871         nudgeContainer.append('div')
32872             .attr('class', 'nudge-instructions')
32873             .text(t('background.offset'));
32874
32875         var nudgeRect = nudgeContainer.append('div')
32876             .attr('class', 'nudge-outer-rect')
32877             .on('mousedown', dragOffset);
32878
32879         nudgeRect.append('div')
32880             .attr('class', 'nudge-inner-rect')
32881             .append('input')
32882             .on('change', inputOffset)
32883             .on('mousedown', function() {
32884                 d3.event.stopPropagation();
32885             });
32886
32887         nudgeContainer.append('div')
32888             .selectAll('button')
32889             .data(directions).enter()
32890             .append('button')
32891             .attr('class', function(d) { return d[0] + ' nudge'; })
32892             .on('mousedown', function(d) {
32893                 buttonOffset(d[1]);
32894             });
32895
32896         nudgeContainer.append('button')
32897             .attr('title', t('background.reset'))
32898             .attr('class', 'nudge-reset disabled')
32899             .on('click', resetOffset)
32900             .call(iD.svg.Icon('#icon-undo'));
32901
32902         context.map()
32903             .on('move.background-update', _.debounce(update, 1000));
32904
32905         context.background()
32906             .on('change.background-update', update);
32907
32908
32909         update();
32910         setOpacity(opacityDefault);
32911
32912         var keybinding = d3.keybinding('background')
32913             .on(key, toggle)
32914             .on('F', hide)
32915             .on('H', hide);
32916
32917         d3.select(document)
32918             .call(keybinding);
32919
32920         context.surface().on('mousedown.background-outside', hide);
32921         context.container().on('mousedown.background-outside', hide);
32922     }
32923
32924     return background;
32925 };
32926 // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
32927 // For example, ⌘Z -> Ctrl+Z
32928 iD.ui.cmd = function(code) {
32929     if (iD.detect().os === 'mac') {
32930         return code;
32931     }
32932
32933     if (iD.detect().os === 'win') {
32934         if (code === '⌘⇧Z') return 'Ctrl+Y';
32935     }
32936
32937     var result = '',
32938         replacements = {
32939             '⌘': 'Ctrl',
32940             '⇧': 'Shift',
32941             '⌥': 'Alt',
32942             '⌫': 'Backspace',
32943             '⌦': 'Delete'
32944         };
32945
32946     for (var i = 0; i < code.length; i++) {
32947         if (code[i] in replacements) {
32948             result += replacements[code[i]] + '+';
32949         } else {
32950             result += code[i];
32951         }
32952     }
32953
32954     return result;
32955 };
32956 iD.ui.Commit = function(context) {
32957     var dispatch = d3.dispatch('cancel', 'save');
32958
32959     function commit(selection) {
32960         var changes = context.history().changes(),
32961             summary = context.history().difference().summary();
32962
32963         function zoomToEntity(change) {
32964             var entity = change.entity;
32965             if (change.changeType !== 'deleted' &&
32966                 context.graph().entity(entity.id).geometry(context.graph()) !== 'vertex') {
32967                 context.map().zoomTo(entity);
32968                 context.surface().selectAll(
32969                     iD.util.entityOrMemberSelector([entity.id], context.graph()))
32970                     .classed('hover', true);
32971             }
32972         }
32973
32974         var header = selection.append('div')
32975             .attr('class', 'header fillL');
32976
32977         header.append('h3')
32978             .text(t('commit.title'));
32979
32980         var body = selection.append('div')
32981             .attr('class', 'body');
32982
32983
32984         // Comment Section
32985         var commentSection = body.append('div')
32986             .attr('class', 'modal-section form-field commit-form');
32987
32988         commentSection.append('label')
32989             .attr('class', 'form-label')
32990             .text(t('commit.message_label'));
32991
32992         var commentField = commentSection.append('textarea')
32993             .attr('placeholder', t('commit.description_placeholder'))
32994             .attr('maxlength', 255)
32995             .property('value', context.storage('comment') || '')
32996             .on('input.save', checkComment)
32997             .on('change.save', checkComment)
32998             .on('blur.save', function() {
32999                 context.storage('comment', this.value);
33000             });
33001
33002         function checkComment() {
33003             d3.selectAll('.save-section .save-button')
33004                 .attr('disabled', (this.value.length ? null : true));
33005
33006             var googleWarning = clippyArea
33007                .html('')
33008                .selectAll('a')
33009                .data(this.value.match(/google/i) ? [true] : []);
33010
33011             googleWarning.exit().remove();
33012
33013             googleWarning.enter()
33014                .append('a')
33015                .attr('target', '_blank')
33016                .attr('tabindex', -1)
33017                .call(iD.svg.Icon('#icon-alert', 'inline'))
33018                .attr('href', t('commit.google_warning_link'))
33019                .append('span')
33020                .text(t('commit.google_warning'));
33021         }
33022
33023         commentField.node().select();
33024
33025         context.connection().userChangesets(function (err, changesets) {
33026             if (err) return;
33027
33028             var comments = [];
33029
33030             for (var i = 0; i < changesets.length; i++) {
33031                 if (changesets[i].tags.comment) {
33032                     comments.push({
33033                         title: changesets[i].tags.comment,
33034                         value: changesets[i].tags.comment
33035                     });
33036                 }
33037             }
33038
33039             commentField.call(d3.combobox().caseSensitive(true).data(comments));
33040         });
33041
33042         var clippyArea = commentSection.append('div')
33043             .attr('class', 'clippy-area');
33044
33045
33046         var changeSetInfo = commentSection.append('div')
33047             .attr('class', 'changeset-info');
33048
33049         changeSetInfo.append('a')
33050             .attr('target', '_blank')
33051             .attr('tabindex', -1)
33052             .call(iD.svg.Icon('#icon-out-link', 'inline'))
33053             .attr('href', t('commit.about_changeset_comments_link'))
33054             .append('span')
33055             .text(t('commit.about_changeset_comments'));
33056
33057         // Warnings
33058         var warnings = body.selectAll('div.warning-section')
33059             .data([context.history().validate(changes)])
33060             .enter()
33061             .append('div')
33062             .attr('class', 'modal-section warning-section fillL2')
33063             .style('display', function(d) { return _.isEmpty(d) ? 'none' : null; })
33064             .style('background', '#ffb');
33065
33066         warnings.append('h3')
33067             .text(t('commit.warnings'));
33068
33069         var warningLi = warnings.append('ul')
33070             .attr('class', 'changeset-list')
33071             .selectAll('li')
33072             .data(function(d) { return d; })
33073             .enter()
33074             .append('li')
33075             .style()
33076             .on('mouseover', mouseover)
33077             .on('mouseout', mouseout)
33078             .on('click', warningClick);
33079
33080         warningLi
33081             .call(iD.svg.Icon('#icon-alert', 'pre-text'));
33082
33083         warningLi
33084             .append('strong').text(function(d) {
33085                 return d.message;
33086             });
33087
33088         warningLi.filter(function(d) { return d.tooltip; })
33089             .call(bootstrap.tooltip()
33090                 .title(function(d) { return d.tooltip; })
33091                 .placement('top')
33092             );
33093
33094
33095         // Upload Explanation
33096         var saveSection = body.append('div')
33097             .attr('class','modal-section save-section fillL cf');
33098
33099         var prose = saveSection.append('p')
33100             .attr('class', 'commit-info')
33101             .html(t('commit.upload_explanation'));
33102
33103         context.connection().userDetails(function(err, user) {
33104             if (err) return;
33105
33106             var userLink = d3.select(document.createElement('div'));
33107
33108             if (user.image_url) {
33109                 userLink.append('img')
33110                     .attr('src', user.image_url)
33111                     .attr('class', 'icon pre-text user-icon');
33112             }
33113
33114             userLink.append('a')
33115                 .attr('class','user-info')
33116                 .text(user.display_name)
33117                 .attr('href', context.connection().userURL(user.display_name))
33118                 .attr('tabindex', -1)
33119                 .attr('target', '_blank');
33120
33121             prose.html(t('commit.upload_explanation_with_user', {user: userLink.html()}));
33122         });
33123
33124
33125         // Buttons
33126         var buttonSection = saveSection.append('div')
33127             .attr('class','buttons fillL cf');
33128
33129         var cancelButton = buttonSection.append('button')
33130             .attr('class', 'secondary-action col5 button cancel-button')
33131             .on('click.cancel', function() { dispatch.cancel(); });
33132
33133         cancelButton.append('span')
33134             .attr('class', 'label')
33135             .text(t('commit.cancel'));
33136
33137         var saveButton = buttonSection.append('button')
33138             .attr('class', 'action col5 button save-button')
33139             .attr('disabled', function() {
33140                 var n = d3.select('.commit-form textarea').node();
33141                 return (n && n.value.length) ? null : true;
33142             })
33143             .on('click.save', function() {
33144                 dispatch.save({
33145                     comment: commentField.node().value
33146                 });
33147             });
33148
33149         saveButton.append('span')
33150             .attr('class', 'label')
33151             .text(t('commit.save'));
33152
33153
33154         // Changes
33155         var changeSection = body.selectAll('div.commit-section')
33156             .data([0])
33157             .enter()
33158             .append('div')
33159             .attr('class', 'commit-section modal-section fillL2');
33160
33161         changeSection.append('h3')
33162             .text(t('commit.changes', {count: summary.length}));
33163
33164         var li = changeSection.append('ul')
33165             .attr('class', 'changeset-list')
33166             .selectAll('li')
33167             .data(summary)
33168             .enter()
33169             .append('li')
33170             .on('mouseover', mouseover)
33171             .on('mouseout', mouseout)
33172             .on('click', zoomToEntity);
33173
33174         li.each(function(d) {
33175             d3.select(this)
33176                 .call(iD.svg.Icon('#icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
33177         });
33178
33179         li.append('span')
33180             .attr('class', 'change-type')
33181             .text(function(d) {
33182                 return t('commit.' + d.changeType) + ' ';
33183             });
33184
33185         li.append('strong')
33186             .attr('class', 'entity-type')
33187             .text(function(d) {
33188                 return context.presets().match(d.entity, d.graph).name();
33189             });
33190
33191         li.append('span')
33192             .attr('class', 'entity-name')
33193             .text(function(d) {
33194                 var name = iD.util.displayName(d.entity) || '',
33195                     string = '';
33196                 if (name !== '') string += ':';
33197                 return string += ' ' + name;
33198             });
33199
33200         li.style('opacity', 0)
33201             .transition()
33202             .style('opacity', 1);
33203
33204
33205         function mouseover(d) {
33206             if (d.entity) {
33207                 context.surface().selectAll(
33208                     iD.util.entityOrMemberSelector([d.entity.id], context.graph())
33209                 ).classed('hover', true);
33210             }
33211         }
33212
33213         function mouseout() {
33214             context.surface().selectAll('.hover')
33215                 .classed('hover', false);
33216         }
33217
33218         function warningClick(d) {
33219             if (d.entity) {
33220                 context.map().zoomTo(d.entity);
33221                 context.enter(
33222                     iD.modes.Select(context, [d.entity.id])
33223                         .suppressMenu(true));
33224             }
33225         }
33226
33227         // Call checkComment off the bat, in case a changeset
33228         // comment is recovered from localStorage
33229         commentField.trigger('input');
33230     }
33231
33232     return d3.rebind(commit, dispatch, 'on');
33233 };
33234 iD.ui.confirm = function(selection) {
33235     var modal = iD.ui.modal(selection);
33236
33237     modal.select('.modal')
33238         .classed('modal-alert', true);
33239
33240     var section = modal.select('.content');
33241
33242     section.append('div')
33243         .attr('class', 'modal-section header');
33244
33245     section.append('div')
33246         .attr('class', 'modal-section message-text');
33247
33248     var buttons = section.append('div')
33249         .attr('class', 'modal-section buttons cf');
33250
33251     modal.okButton = function() {
33252         buttons
33253             .append('button')
33254             .attr('class', 'action col4')
33255             .on('click.confirm', function() {
33256                 modal.remove();
33257             })
33258             .text(t('confirm.okay'));
33259
33260         return modal;
33261     };
33262
33263     return modal;
33264 };
33265 iD.ui.Conflicts = function(context) {
33266     var dispatch = d3.dispatch('download', 'cancel', 'save'),
33267         list;
33268
33269     function conflicts(selection) {
33270         var header = selection
33271             .append('div')
33272             .attr('class', 'header fillL');
33273
33274         header
33275             .append('button')
33276             .attr('class', 'fr')
33277             .on('click', function() { dispatch.cancel(); })
33278             .call(iD.svg.Icon('#icon-close'));
33279
33280         header
33281             .append('h3')
33282             .text(t('save.conflict.header'));
33283
33284         var body = selection
33285             .append('div')
33286             .attr('class', 'body fillL');
33287
33288         body
33289             .append('div')
33290             .attr('class', 'conflicts-help')
33291             .text(t('save.conflict.help'))
33292             .append('a')
33293             .attr('class', 'conflicts-download')
33294             .text(t('save.conflict.download_changes'))
33295             .on('click.download', function() { dispatch.download(); });
33296
33297         body
33298             .append('div')
33299             .attr('class', 'conflict-container fillL3')
33300             .call(showConflict, 0);
33301
33302         body
33303             .append('div')
33304             .attr('class', 'conflicts-done')
33305             .attr('opacity', 0)
33306             .style('display', 'none')
33307             .text(t('save.conflict.done'));
33308
33309         var buttons = body
33310             .append('div')
33311             .attr('class','buttons col12 joined conflicts-buttons');
33312
33313         buttons
33314             .append('button')
33315             .attr('disabled', list.length > 1)
33316             .attr('class', 'action conflicts-button col6')
33317             .text(t('save.title'))
33318             .on('click.try_again', function() { dispatch.save(); });
33319
33320         buttons
33321             .append('button')
33322             .attr('class', 'secondary-action conflicts-button col6')
33323             .text(t('confirm.cancel'))
33324             .on('click.cancel', function() { dispatch.cancel(); });
33325     }
33326
33327
33328     function showConflict(selection, index) {
33329         if (index < 0 || index >= list.length) return;
33330
33331         var parent = d3.select(selection.node().parentNode);
33332
33333         // enable save button if this is the last conflict being reviewed..
33334         if (index === list.length - 1) {
33335             window.setTimeout(function() {
33336                 parent.select('.conflicts-button')
33337                     .attr('disabled', null);
33338
33339                 parent.select('.conflicts-done')
33340                     .transition()
33341                     .attr('opacity', 1)
33342                     .style('display', 'block');
33343             }, 250);
33344         }
33345
33346         var item = selection
33347             .selectAll('.conflict')
33348             .data([list[index]]);
33349
33350         var enter = item.enter()
33351             .append('div')
33352             .attr('class', 'conflict');
33353
33354         enter
33355             .append('h4')
33356             .attr('class', 'conflict-count')
33357             .text(t('save.conflict.count', { num: index + 1, total: list.length }));
33358
33359         enter
33360             .append('a')
33361             .attr('class', 'conflict-description')
33362             .attr('href', '#')
33363             .text(function(d) { return d.name; })
33364             .on('click', function(d) {
33365                 zoomToEntity(d.id);
33366                 d3.event.preventDefault();
33367             });
33368
33369         var details = enter
33370             .append('div')
33371             .attr('class', 'conflict-detail-container');
33372
33373         details
33374             .append('ul')
33375             .attr('class', 'conflict-detail-list')
33376             .selectAll('li')
33377             .data(function(d) { return d.details || []; })
33378             .enter()
33379             .append('li')
33380             .attr('class', 'conflict-detail-item')
33381             .html(function(d) { return d; });
33382
33383         details
33384             .append('div')
33385             .attr('class', 'conflict-choices')
33386             .call(addChoices);
33387
33388         details
33389             .append('div')
33390             .attr('class', 'conflict-nav-buttons joined cf')
33391             .selectAll('button')
33392             .data(['previous', 'next'])
33393             .enter()
33394             .append('button')
33395             .text(function(d) { return t('save.conflict.' + d); })
33396             .attr('class', 'conflict-nav-button action col6')
33397             .attr('disabled', function(d, i) {
33398                 return (i === 0 && index === 0) ||
33399                     (i === 1 && index === list.length - 1) || null;
33400             })
33401             .on('click', function(d, i) {
33402                 var container = parent.select('.conflict-container'),
33403                 sign = (i === 0 ? -1 : 1);
33404
33405                 container
33406                     .selectAll('.conflict')
33407                     .remove();
33408
33409                 container
33410                     .call(showConflict, index + sign);
33411
33412                 d3.event.preventDefault();
33413             });
33414
33415         item.exit()
33416             .remove();
33417
33418     }
33419
33420     function addChoices(selection) {
33421         var choices = selection
33422             .append('ul')
33423             .attr('class', 'layer-list')
33424             .selectAll('li')
33425             .data(function(d) { return d.choices || []; });
33426
33427         var enter = choices.enter()
33428             .append('li')
33429             .attr('class', 'layer');
33430
33431         var label = enter
33432             .append('label');
33433
33434         label
33435             .append('input')
33436             .attr('type', 'radio')
33437             .attr('name', function(d) { return d.id; })
33438             .on('change', function(d, i) {
33439                 var ul = this.parentNode.parentNode.parentNode;
33440                 ul.__data__.chosen = i;
33441                 choose(ul, d);
33442             });
33443
33444         label
33445             .append('span')
33446             .text(function(d) { return d.text; });
33447
33448         choices
33449             .each(function(d, i) {
33450                 var ul = this.parentNode;
33451                 if (ul.__data__.chosen === i) choose(ul, d);
33452             });
33453     }
33454
33455     function choose(ul, datum) {
33456         if (d3.event) d3.event.preventDefault();
33457
33458         d3.select(ul)
33459             .selectAll('li')
33460             .classed('active', function(d) { return d === datum; })
33461             .selectAll('input')
33462             .property('checked', function(d) { return d === datum; });
33463
33464         var extent = iD.geo.Extent(),
33465             entity;
33466
33467         entity = context.graph().hasEntity(datum.id);
33468         if (entity) extent._extend(entity.extent(context.graph()));
33469
33470         datum.action();
33471
33472         entity = context.graph().hasEntity(datum.id);
33473         if (entity) extent._extend(entity.extent(context.graph()));
33474
33475         zoomToEntity(datum.id, extent);
33476     }
33477
33478     function zoomToEntity(id, extent) {
33479         context.surface().selectAll('.hover')
33480             .classed('hover', false);
33481
33482         var entity = context.graph().hasEntity(id);
33483         if (entity) {
33484             if (extent) {
33485                 context.map().trimmedExtent(extent);
33486             } else {
33487                 context.map().zoomTo(entity);
33488             }
33489             context.surface().selectAll(
33490                 iD.util.entityOrMemberSelector([entity.id], context.graph()))
33491                 .classed('hover', true);
33492         }
33493     }
33494
33495
33496     // The conflict list should be an array of objects like:
33497     // {
33498     //     id: id,
33499     //     name: entityName(local),
33500     //     details: merge.conflicts(),
33501     //     chosen: 1,
33502     //     choices: [
33503     //         choice(id, keepMine, forceLocal),
33504     //         choice(id, keepTheirs, forceRemote)
33505     //     ]
33506     // }
33507     conflicts.list = function(_) {
33508         if (!arguments.length) return list;
33509         list = _;
33510         return conflicts;
33511     };
33512
33513     return d3.rebind(conflicts, dispatch, 'on');
33514 };
33515 iD.ui.Contributors = function(context) {
33516     var debouncedUpdate = _.debounce(function() { update(); }, 1000),
33517         limit = 4,
33518         hidden = false,
33519         wrap = d3.select(null);
33520
33521     function update() {
33522         var users = {},
33523             entities = context.intersects(context.map().extent());
33524
33525         entities.forEach(function(entity) {
33526             if (entity && entity.user) users[entity.user] = true;
33527         });
33528
33529         var u = Object.keys(users),
33530             subset = u.slice(0, u.length > limit ? limit - 1 : limit);
33531
33532         wrap.html('')
33533             .call(iD.svg.Icon('#icon-nearby', 'pre-text light'));
33534
33535         var userList = d3.select(document.createElement('span'));
33536
33537         userList.selectAll()
33538             .data(subset)
33539             .enter()
33540             .append('a')
33541             .attr('class', 'user-link')
33542             .attr('href', function(d) { return context.connection().userURL(d); })
33543             .attr('target', '_blank')
33544             .attr('tabindex', -1)
33545             .text(String);
33546
33547         if (u.length > limit) {
33548             var count = d3.select(document.createElement('span'));
33549
33550             count.append('a')
33551                 .attr('target', '_blank')
33552                 .attr('tabindex', -1)
33553                 .attr('href', function() {
33554                     return context.connection().changesetsURL(context.map().center(), context.map().zoom());
33555                 })
33556                 .text(u.length - limit + 1);
33557
33558             wrap.append('span')
33559                 .html(t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
33560
33561         } else {
33562             wrap.append('span')
33563                 .html(t('contributors.list', { users: userList.html() }));
33564         }
33565
33566         if (!u.length) {
33567             hidden = true;
33568             wrap
33569                 .transition()
33570                 .style('opacity', 0);
33571
33572         } else if (hidden) {
33573             wrap
33574                 .transition()
33575                 .style('opacity', 1);
33576         }
33577     }
33578
33579     return function(selection) {
33580         wrap = selection;
33581         update();
33582
33583         context.connection().on('loaded.contributors', debouncedUpdate);
33584         context.map().on('move.contributors', debouncedUpdate);
33585     };
33586 };
33587 iD.ui.Disclosure = function() {
33588     var dispatch = d3.dispatch('toggled'),
33589         title,
33590         expanded = false,
33591         content = function () {};
33592
33593     var disclosure = function(selection) {
33594         var $link = selection.selectAll('.hide-toggle')
33595             .data([0]);
33596
33597         $link.enter().append('a')
33598             .attr('href', '#')
33599             .attr('class', 'hide-toggle');
33600
33601         $link.text(title)
33602             .on('click', toggle)
33603             .classed('expanded', expanded);
33604
33605         var $body = selection.selectAll('div')
33606             .data([0]);
33607
33608         $body.enter().append('div');
33609
33610         $body.classed('hide', !expanded)
33611             .call(content);
33612
33613         function toggle() {
33614             expanded = !expanded;
33615             $link.classed('expanded', expanded);
33616             $body.call(iD.ui.Toggle(expanded));
33617             dispatch.toggled(expanded);
33618         }
33619     };
33620
33621     disclosure.title = function(_) {
33622         if (!arguments.length) return title;
33623         title = _;
33624         return disclosure;
33625     };
33626
33627     disclosure.expanded = function(_) {
33628         if (!arguments.length) return expanded;
33629         expanded = _;
33630         return disclosure;
33631     };
33632
33633     disclosure.content = function(_) {
33634         if (!arguments.length) return content;
33635         content = _;
33636         return disclosure;
33637     };
33638
33639     return d3.rebind(disclosure, dispatch, 'on');
33640 };
33641 iD.ui.EntityEditor = function(context) {
33642     var dispatch = d3.dispatch('choose'),
33643         state = 'select',
33644         coalesceChanges = false,
33645         modified = false,
33646         base,
33647         id,
33648         preset,
33649         reference;
33650
33651     var presetEditor = iD.ui.preset(context)
33652         .on('change', changeTags);
33653     var rawTagEditor = iD.ui.RawTagEditor(context)
33654         .on('change', changeTags);
33655
33656     function entityEditor(selection) {
33657         var entity = context.entity(id),
33658             tags = _.clone(entity.tags);
33659
33660         var $header = selection.selectAll('.header')
33661             .data([0]);
33662
33663         // Enter
33664         var $enter = $header.enter().append('div')
33665             .attr('class', 'header fillL cf');
33666
33667         $enter.append('button')
33668             .attr('class', 'fl preset-reset preset-choose')
33669             .append('span')
33670             .html('&#9668;');
33671
33672         $enter.append('button')
33673             .attr('class', 'fr preset-close')
33674             .call(iD.svg.Icon(modified ? '#icon-apply' : '#icon-close'));
33675
33676         $enter.append('h3');
33677
33678         // Update
33679         $header.select('h3')
33680             .text(t('inspector.edit'));
33681
33682         $header.select('.preset-close')
33683             .on('click', function() {
33684                 context.enter(iD.modes.Browse(context));
33685             });
33686
33687         var $body = selection.selectAll('.inspector-body')
33688             .data([0]);
33689
33690         // Enter
33691         $enter = $body.enter().append('div')
33692             .attr('class', 'inspector-body');
33693
33694         $enter.append('div')
33695             .attr('class', 'preset-list-item inspector-inner')
33696             .append('div')
33697             .attr('class', 'preset-list-button-wrap')
33698             .append('button')
33699             .attr('class', 'preset-list-button preset-reset')
33700             .call(bootstrap.tooltip()
33701                 .title(t('inspector.back_tooltip'))
33702                 .placement('bottom'))
33703             .append('div')
33704             .attr('class', 'label');
33705
33706         $body.select('.preset-list-button-wrap')
33707             .call(reference.button);
33708
33709         $body.select('.preset-list-item')
33710             .call(reference.body);
33711
33712         $enter.append('div')
33713             .attr('class', 'inspector-border inspector-preset');
33714
33715         $enter.append('div')
33716             .attr('class', 'inspector-border raw-tag-editor inspector-inner');
33717
33718         $enter.append('div')
33719             .attr('class', 'inspector-border raw-member-editor inspector-inner');
33720
33721         $enter.append('div')
33722             .attr('class', 'raw-membership-editor inspector-inner');
33723
33724         selection.selectAll('.preset-reset')
33725             .on('click', function() {
33726                 dispatch.choose(preset);
33727             });
33728
33729         // Update
33730         $body.select('.preset-list-item button')
33731             .call(iD.ui.PresetIcon()
33732                 .geometry(context.geometry(id))
33733                 .preset(preset));
33734
33735         $body.select('.preset-list-item .label')
33736             .text(preset.name());
33737
33738         $body.select('.inspector-preset')
33739             .call(presetEditor
33740                 .preset(preset)
33741                 .entityID(id)
33742                 .tags(tags)
33743                 .state(state));
33744
33745         $body.select('.raw-tag-editor')
33746             .call(rawTagEditor
33747                 .preset(preset)
33748                 .entityID(id)
33749                 .tags(tags)
33750                 .state(state));
33751
33752         if (entity.type === 'relation') {
33753             $body.select('.raw-member-editor')
33754                 .style('display', 'block')
33755                 .call(iD.ui.RawMemberEditor(context)
33756                     .entityID(id));
33757         } else {
33758             $body.select('.raw-member-editor')
33759                 .style('display', 'none');
33760         }
33761
33762         $body.select('.raw-membership-editor')
33763             .call(iD.ui.RawMembershipEditor(context)
33764                 .entityID(id));
33765
33766         function historyChanged() {
33767             if (state === 'hide') return;
33768
33769             var entity = context.hasEntity(id),
33770                 graph = context.graph();
33771             if (!entity) return;
33772
33773             entityEditor.preset(context.presets().match(entity, graph));
33774             entityEditor.modified(base !== graph);
33775             entityEditor(selection);
33776         }
33777
33778         context.history()
33779             .on('change.entity-editor', historyChanged);
33780     }
33781
33782     function clean(o) {
33783
33784         function cleanVal(k, v) {
33785             function keepSpaces(k) {
33786                 var whitelist = ['opening_hours', 'service_times', 'collection_times',
33787                     'operating_times', 'smoking_hours', 'happy_hours'];
33788                 return _.any(whitelist, function(s) { return k.indexOf(s) !== -1; });
33789             }
33790
33791             var blacklist = ['description', 'note', 'fixme'];
33792             if (_.any(blacklist, function(s) { return k.indexOf(s) !== -1; })) return v;
33793
33794             var cleaned = v.split(';')
33795                 .map(function(s) { return s.trim(); })
33796                 .join(keepSpaces(k) ? '; ' : ';');
33797
33798             // The code below is not intended to validate websites and emails.
33799             // It is only intended to prevent obvious copy-paste errors. (#2323)
33800
33801             // clean website- and email-like tags
33802             if (k.indexOf('website') !== -1 ||
33803                 k.indexOf('email') !== -1 ||
33804                 cleaned.indexOf('http') === 0) {
33805                 cleaned = cleaned
33806                     .replace(/[\u200B-\u200F\uFEFF]/g, '');  // strip LRM and other zero width chars
33807
33808             }
33809
33810             return cleaned;
33811         }
33812
33813         var out = {}, k, v;
33814         for (k in o) {
33815             if (k && (v = o[k]) !== undefined) {
33816                 out[k] = cleanVal(k, v);
33817             }
33818         }
33819         return out;
33820     }
33821
33822     // Tag changes that fire on input can all get coalesced into a single
33823     // history operation when the user leaves the field.  #2342
33824     function changeTags(changed, onInput) {
33825         var entity = context.entity(id),
33826             annotation = t('operations.change_tags.annotation'),
33827             tags = _.extend({}, entity.tags, changed);
33828
33829         if (!onInput) {
33830             tags = clean(tags);
33831         }
33832         if (!_.isEqual(entity.tags, tags)) {
33833             if (coalesceChanges) {
33834                 context.overwrite(iD.actions.ChangeTags(id, tags), annotation);
33835             } else {
33836                 context.perform(iD.actions.ChangeTags(id, tags), annotation);
33837                 coalesceChanges = !!onInput;
33838             }
33839         }
33840     }
33841
33842     entityEditor.modified = function(_) {
33843         if (!arguments.length) return modified;
33844         modified = _;
33845         d3.selectAll('button.preset-close use')
33846             .attr('xlink:href', (modified ? '#icon-apply' : '#icon-close'));
33847     };
33848
33849     entityEditor.state = function(_) {
33850         if (!arguments.length) return state;
33851         state = _;
33852         return entityEditor;
33853     };
33854
33855     entityEditor.entityID = function(_) {
33856         if (!arguments.length) return id;
33857         id = _;
33858         base = context.graph();
33859         entityEditor.preset(context.presets().match(context.entity(id), base));
33860         entityEditor.modified(false);
33861         coalesceChanges = false;
33862         return entityEditor;
33863     };
33864
33865     entityEditor.preset = function(_) {
33866         if (!arguments.length) return preset;
33867         if (_ !== preset) {
33868             preset = _;
33869             reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context)
33870                 .showing(false);
33871         }
33872         return entityEditor;
33873     };
33874
33875     return d3.rebind(entityEditor, dispatch, 'on');
33876 };
33877 iD.ui.FeatureInfo = function(context) {
33878     function update(selection) {
33879         var features = context.features(),
33880             stats = features.stats(),
33881             count = 0,
33882             hiddenList = _.compact(_.map(features.hidden(), function(k) {
33883                 if (stats[k]) {
33884                     count += stats[k];
33885                     return String(stats[k]) + ' ' + t('feature.' + k + '.description');
33886                 }
33887             }));
33888
33889         selection.html('');
33890
33891         if (hiddenList.length) {
33892             var tooltip = bootstrap.tooltip()
33893                     .placement('top')
33894                     .html(true)
33895                     .title(function() {
33896                         return iD.ui.tooltipHtml(hiddenList.join('<br/>'));
33897                     });
33898
33899             var warning = selection.append('a')
33900                 .attr('href', '#')
33901                 .attr('tabindex', -1)
33902                 .html(t('feature_info.hidden_warning', { count: count }))
33903                 .call(tooltip)
33904                 .on('click', function() {
33905                     tooltip.hide(warning);
33906                     // open map data panel?
33907                     d3.event.preventDefault();
33908                 });
33909         }
33910
33911         selection
33912             .classed('hide', !hiddenList.length);
33913     }
33914
33915     return function(selection) {
33916         update(selection);
33917
33918         context.features().on('change.feature_info', function() {
33919             update(selection);
33920         });
33921     };
33922 };
33923 iD.ui.FeatureList = function(context) {
33924     var geocodeResults;
33925
33926     function featureList(selection) {
33927         var header = selection.append('div')
33928             .attr('class', 'header fillL cf');
33929
33930         header.append('h3')
33931             .text(t('inspector.feature_list'));
33932
33933         function keypress() {
33934             var q = search.property('value'),
33935                 items = list.selectAll('.feature-list-item');
33936             if (d3.event.keyCode === 13 && q.length && items.size()) {
33937                 click(items.datum());
33938             }
33939         }
33940
33941         function inputevent() {
33942             geocodeResults = undefined;
33943             drawList();
33944         }
33945
33946         var searchWrap = selection.append('div')
33947             .attr('class', 'search-header');
33948
33949         var search = searchWrap.append('input')
33950             .attr('placeholder', t('inspector.search'))
33951             .attr('type', 'search')
33952             .on('keypress', keypress)
33953             .on('input', inputevent);
33954
33955         searchWrap
33956             .call(iD.svg.Icon('#icon-search', 'pre-text'));
33957
33958         var listWrap = selection.append('div')
33959             .attr('class', 'inspector-body');
33960
33961         var list = listWrap.append('div')
33962             .attr('class', 'feature-list cf');
33963
33964         context
33965             .on('exit.feature-list', clearSearch);
33966         context.map()
33967             .on('drawn.feature-list', mapDrawn);
33968
33969         function clearSearch() {
33970             search.property('value', '');
33971             drawList();
33972         }
33973
33974         function mapDrawn(e) {
33975             if (e.full) {
33976                 drawList();
33977             }
33978         }
33979
33980         function features() {
33981             var entities = {},
33982                 result = [],
33983                 graph = context.graph(),
33984                 q = search.property('value').toLowerCase();
33985
33986             if (!q) return result;
33987
33988             var idMatch = q.match(/^([nwr])([0-9]+)$/);
33989
33990             if (idMatch) {
33991                 result.push({
33992                     id: idMatch[0],
33993                     geometry: idMatch[1] === 'n' ? 'point' : idMatch[1] === 'w' ? 'line' : 'relation',
33994                     type: idMatch[1] === 'n' ? t('inspector.node') : idMatch[1] === 'w' ? t('inspector.way') : t('inspector.relation'),
33995                     name: idMatch[2]
33996                 });
33997             }
33998
33999             var locationMatch = sexagesimal.pair(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
34000
34001             if (locationMatch) {
34002                 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
34003                 result.push({
34004                     id: -1,
34005                     geometry: 'point',
34006                     type: t('inspector.location'),
34007                     name: loc[0].toFixed(6) + ', ' + loc[1].toFixed(6),
34008                     location: loc
34009                 });
34010             }
34011
34012             function addEntity(entity) {
34013                 if (entity.id in entities || result.length > 200)
34014                     return;
34015
34016                 entities[entity.id] = true;
34017
34018                 var name = iD.util.displayName(entity) || '';
34019                 if (name.toLowerCase().indexOf(q) >= 0) {
34020                     result.push({
34021                         id: entity.id,
34022                         entity: entity,
34023                         geometry: context.geometry(entity.id),
34024                         type: context.presets().match(entity, graph).name(),
34025                         name: name
34026                     });
34027                 }
34028
34029                 graph.parentRelations(entity).forEach(function(parent) {
34030                     addEntity(parent);
34031                 });
34032             }
34033
34034             var visible = context.surface().selectAll('.point, .line, .area')[0];
34035             for (var i = 0; i < visible.length && result.length <= 200; i++) {
34036                 addEntity(visible[i].__data__);
34037             }
34038
34039             (geocodeResults || []).forEach(function(d) {
34040                 // https://github.com/openstreetmap/iD/issues/1890
34041                 if (d.osm_type && d.osm_id) {
34042                     result.push({
34043                         id: iD.Entity.id.fromOSM(d.osm_type, d.osm_id),
34044                         geometry: d.osm_type === 'relation' ? 'relation' : d.osm_type === 'way' ? 'line' : 'point',
34045                         type: d.type !== 'yes' ? (d.type.charAt(0).toUpperCase() + d.type.slice(1)).replace('_', ' ')
34046                                                : (d.class.charAt(0).toUpperCase() + d.class.slice(1)).replace('_', ' '),
34047                         name: d.display_name,
34048                         extent: new iD.geo.Extent(
34049                             [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
34050                             [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
34051                     });
34052                 }
34053             });
34054
34055             return result;
34056         }
34057
34058         function drawList() {
34059             var value = search.property('value'),
34060                 results = features();
34061
34062             list.classed('filtered', value.length);
34063
34064             var noResultsWorldwide = geocodeResults && geocodeResults.length === 0;
34065
34066             var resultsIndicator = list.selectAll('.no-results-item')
34067                 .data([0])
34068                 .enter().append('button')
34069                 .property('disabled', true)
34070                 .attr('class', 'no-results-item')
34071                 .call(iD.svg.Icon('#icon-alert', 'pre-text'));
34072
34073             resultsIndicator.append('span')
34074                 .attr('class', 'entity-name');
34075
34076             list.selectAll('.no-results-item .entity-name')
34077                 .text(noResultsWorldwide ? t('geocoder.no_results_worldwide') : t('geocoder.no_results_visible'));
34078
34079             list.selectAll('.geocode-item')
34080                 .data([0])
34081                 .enter().append('button')
34082                 .attr('class', 'geocode-item')
34083                 .on('click', geocode)
34084                 .append('div')
34085                 .attr('class', 'label')
34086                 .append('span')
34087                 .attr('class', 'entity-name')
34088                 .text(t('geocoder.search'));
34089
34090             list.selectAll('.no-results-item')
34091                 .style('display', (value.length && !results.length) ? 'block' : 'none');
34092
34093             list.selectAll('.geocode-item')
34094                 .style('display', (value && geocodeResults === undefined) ? 'block' : 'none');
34095
34096             list.selectAll('.feature-list-item')
34097                 .data([-1])
34098                 .remove();
34099
34100             var items = list.selectAll('.feature-list-item')
34101                 .data(results, function(d) { return d.id; });
34102
34103             var enter = items.enter()
34104                 .insert('button', '.geocode-item')
34105                 .attr('class', 'feature-list-item')
34106                 .on('mouseover', mouseover)
34107                 .on('mouseout', mouseout)
34108                 .on('click', click);
34109
34110             var label = enter
34111                 .append('div')
34112                 .attr('class', 'label');
34113
34114             label.each(function(d) {
34115                 d3.select(this)
34116                     .call(iD.svg.Icon('#icon-' + d.geometry, 'pre-text'));
34117             });
34118
34119             label.append('span')
34120                 .attr('class', 'entity-type')
34121                 .text(function(d) { return d.type; });
34122
34123             label.append('span')
34124                 .attr('class', 'entity-name')
34125                 .text(function(d) { return d.name; });
34126
34127             enter.style('opacity', 0)
34128                 .transition()
34129                 .style('opacity', 1);
34130
34131             items.order();
34132
34133             items.exit()
34134                 .remove();
34135         }
34136
34137         function mouseover(d) {
34138             if (d.id === -1) return;
34139
34140             context.surface().selectAll(iD.util.entityOrMemberSelector([d.id], context.graph()))
34141                 .classed('hover', true);
34142         }
34143
34144         function mouseout() {
34145             context.surface().selectAll('.hover')
34146                 .classed('hover', false);
34147         }
34148
34149         function click(d) {
34150             d3.event.preventDefault();
34151             if (d.location) {
34152                 context.map().centerZoom([d.location[1], d.location[0]], 20);
34153             }
34154             else if (d.entity) {
34155                 if (d.entity.type === 'node') {
34156                     context.map().center(d.entity.loc);
34157                 } else if (d.entity.type === 'way') {
34158                     var center = context.projection(context.map().center()),
34159                         edge = iD.geo.chooseEdge(context.childNodes(d.entity), center, context.projection);
34160                     context.map().center(edge.loc);
34161                 }
34162                 context.enter(iD.modes.Select(context, [d.entity.id]).suppressMenu(true));
34163             } else {
34164                 context.zoomToEntity(d.id);
34165             }
34166         }
34167
34168         function geocode() {
34169             var searchVal = encodeURIComponent(search.property('value'));
34170             d3.json('https://nominatim.openstreetmap.org/search/' + searchVal + '?limit=10&format=json', function(err, resp) {
34171                 geocodeResults = resp || [];
34172                 drawList();
34173             });
34174         }
34175     }
34176
34177     return featureList;
34178 };
34179 iD.ui.flash = function(selection) {
34180     var modal = iD.ui.modal(selection);
34181
34182     modal.select('.modal').classed('modal-flash', true);
34183
34184     modal.select('.content')
34185         .classed('modal-section', true)
34186         .append('div')
34187         .attr('class', 'description');
34188
34189     modal.on('click.flash', function() { modal.remove(); });
34190
34191     setTimeout(function() {
34192         modal.remove();
34193         return true;
34194     }, 1500);
34195
34196     return modal;
34197 };
34198 iD.ui.FullScreen = function(context) {
34199     var element = context.container().node(),
34200         keybinding = d3.keybinding('full-screen');
34201         // button;
34202
34203     function getFullScreenFn() {
34204         if (element.requestFullscreen) {
34205             return element.requestFullscreen;
34206         } else if (element.msRequestFullscreen) {
34207             return  element.msRequestFullscreen;
34208         } else if (element.mozRequestFullScreen) {
34209             return  element.mozRequestFullScreen;
34210         } else if (element.webkitRequestFullscreen) {
34211             return element.webkitRequestFullscreen;
34212         }
34213     }
34214
34215     function getExitFullScreenFn() {
34216         if (document.exitFullscreen) {
34217             return document.exitFullscreen;
34218         } else if (document.msExitFullscreen) {
34219             return  document.msExitFullscreen;
34220         } else if (document.mozCancelFullScreen) {
34221             return  document.mozCancelFullScreen;
34222         } else if (document.webkitExitFullscreen) {
34223             return document.webkitExitFullscreen;
34224         }
34225     }
34226
34227     function isFullScreen() {
34228         return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement ||
34229             document.msFullscreenElement;
34230     }
34231
34232     function isSupported() {
34233         return !!getFullScreenFn();
34234     }
34235
34236     function fullScreen() {
34237         d3.event.preventDefault();
34238         if (!isFullScreen()) {
34239             // button.classed('active', true);
34240             getFullScreenFn().apply(element);
34241         } else {
34242             // button.classed('active', false);
34243             getExitFullScreenFn().apply(document);
34244         }
34245     }
34246
34247     return function() { // selection) {
34248         if (!isSupported())
34249             return;
34250
34251         // var tooltip = bootstrap.tooltip()
34252         //     .placement('left');
34253
34254         // button = selection.append('button')
34255         //     .attr('title', t('full_screen'))
34256         //     .attr('tabindex', -1)
34257         //     .on('click', fullScreen)
34258         //     .call(tooltip);
34259
34260         // button.append('span')
34261         //     .attr('class', 'icon full-screen');
34262
34263         keybinding
34264             .on('f11', fullScreen)
34265             .on(iD.ui.cmd('⌘⇧F'), fullScreen);
34266
34267         d3.select(document)
34268             .call(keybinding);
34269     };
34270 };
34271 iD.ui.Geolocate = function(context) {
34272     var geoOptions = { enableHighAccuracy: false, timeout: 6000 /* 6sec */ },
34273         locating = iD.ui.Loading(context).message(t('geolocate.locating')).blocking(true),
34274         timeoutId;
34275
34276     function click() {
34277         context.enter(iD.modes.Browse(context));
34278         context.container().call(locating);
34279         navigator.geolocation.getCurrentPosition(success, error, geoOptions);
34280
34281         // This timeout ensures that we still call finish() even if
34282         // the user declines to share their location in Firefox
34283         timeoutId = setTimeout(finish, 10000 /* 10sec */ );
34284     }
34285
34286     function success(position) {
34287         var map = context.map(),
34288             extent = iD.geo.Extent([position.coords.longitude, position.coords.latitude])
34289                 .padByMeters(position.coords.accuracy);
34290
34291         map.centerZoom(extent.center(), Math.min(20, map.extentZoom(extent)));
34292         finish();
34293     }
34294
34295     function error() {
34296         finish();
34297     }
34298
34299     function finish() {
34300         locating.close();  // unblock ui
34301         if (timeoutId) { clearTimeout(timeoutId); }
34302         timeoutId = undefined;
34303     }
34304
34305     return function(selection) {
34306         if (!navigator.geolocation) return;
34307
34308         selection.append('button')
34309             .attr('tabindex', -1)
34310             .attr('title', t('geolocate.title'))
34311             .on('click', click)
34312             .call(iD.svg.Icon('#icon-geolocate', 'light'))
34313             .call(bootstrap.tooltip()
34314                 .placement('left'));
34315     };
34316 };
34317 iD.ui.Help = function(context) {
34318     var key = 'H';
34319
34320     var docKeys = [
34321         'help.help',
34322         'help.editing_saving',
34323         'help.roads',
34324         'help.gps',
34325         'help.imagery',
34326         'help.addresses',
34327         'help.inspector',
34328         'help.buildings',
34329         'help.relations'];
34330
34331     var docs = docKeys.map(function(key) {
34332         var text = t(key);
34333         return {
34334             title: text.split('\n')[0].replace('#', '').trim(),
34335             html: marked(text.split('\n').slice(1).join('\n'))
34336         };
34337     });
34338
34339     function help(selection) {
34340
34341         function hide() {
34342             setVisible(false);
34343         }
34344
34345         function toggle() {
34346             if (d3.event) d3.event.preventDefault();
34347             tooltip.hide(button);
34348             setVisible(!button.classed('active'));
34349         }
34350
34351         function setVisible(show) {
34352             if (show !== shown) {
34353                 button.classed('active', show);
34354                 shown = show;
34355
34356                 if (show) {
34357                     selection.on('mousedown.help-inside', function() {
34358                         return d3.event.stopPropagation();
34359                     });
34360                     pane.style('display', 'block')
34361                         .style('right', '-500px')
34362                         .transition()
34363                         .duration(200)
34364                         .style('right', '0px');
34365                 } else {
34366                     pane.style('right', '0px')
34367                         .transition()
34368                         .duration(200)
34369                         .style('right', '-500px')
34370                         .each('end', function() {
34371                             d3.select(this).style('display', 'none');
34372                         });
34373                     selection.on('mousedown.help-inside', null);
34374                 }
34375             }
34376         }
34377
34378         function clickHelp(d, i) {
34379             pane.property('scrollTop', 0);
34380             doctitle.html(d.title);
34381             body.html(d.html);
34382             body.selectAll('a')
34383                 .attr('target', '_blank');
34384             menuItems.classed('selected', function(m) {
34385                 return m.title === d.title;
34386             });
34387
34388             nav.html('');
34389
34390             if (i > 0) {
34391                 var prevLink = nav.append('a')
34392                     .attr('class', 'previous')
34393                     .on('click', function() {
34394                         clickHelp(docs[i - 1], i - 1);
34395                     });
34396                 prevLink.append('span').html('&#9668; ' + docs[i - 1].title);
34397             }
34398             if (i < docs.length - 1) {
34399                 var nextLink = nav.append('a')
34400                     .attr('class', 'next')
34401                     .on('click', function() {
34402                         clickHelp(docs[i + 1], i + 1);
34403                     });
34404                 nextLink.append('span').html(docs[i + 1].title + ' &#9658;');
34405             }
34406         }
34407
34408         function clickWalkthrough() {
34409             d3.select(document.body).call(iD.ui.intro(context));
34410             setVisible(false);
34411         }
34412
34413
34414         var pane = selection.append('div')
34415                 .attr('class', 'help-wrap map-overlay fillL col5 content hide'),
34416             tooltip = bootstrap.tooltip()
34417                 .placement('left')
34418                 .html(true)
34419                 .title(iD.ui.tooltipHtml(t('help.title'), key)),
34420             button = selection.append('button')
34421                 .attr('tabindex', -1)
34422                 .on('click', toggle)
34423                 .call(iD.svg.Icon('#icon-help', 'light'))
34424                 .call(tooltip),
34425             shown = false;
34426
34427
34428         var toc = pane.append('ul')
34429             .attr('class', 'toc');
34430
34431         var menuItems = toc.selectAll('li')
34432             .data(docs)
34433             .enter()
34434             .append('li')
34435             .append('a')
34436             .html(function(d) { return d.title; })
34437             .on('click', clickHelp);
34438
34439         toc.append('li')
34440             .attr('class','walkthrough')
34441             .append('a')
34442             .text(t('splash.walkthrough'))
34443             .on('click', clickWalkthrough);
34444
34445         var content = pane.append('div')
34446             .attr('class', 'left-content');
34447
34448         var doctitle = content.append('h2')
34449             .text(t('help.title'));
34450
34451         var body = content.append('div')
34452             .attr('class', 'body');
34453
34454         var nav = content.append('div')
34455             .attr('class', 'nav');
34456
34457         clickHelp(docs[0], 0);
34458
34459         var keybinding = d3.keybinding('help')
34460             .on(key, toggle)
34461             .on('B', hide)
34462             .on('F', hide);
34463
34464         d3.select(document)
34465             .call(keybinding);
34466
34467         context.surface().on('mousedown.help-outside', hide);
34468         context.container().on('mousedown.help-outside', hide);
34469     }
34470
34471     return help;
34472 };
34473 iD.ui.Info = function(context) {
34474     var key = iD.ui.cmd('⌘I'),
34475         imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
34476         hidden = true;
34477
34478     function info(selection) {
34479         function radiansToMeters(r) {
34480             // using WGS84 authalic radius (6371007.1809 m)
34481             return r * 6371007.1809;
34482         }
34483
34484         function steradiansToSqmeters(r) {
34485             // http://gis.stackexchange.com/a/124857/40446
34486             return r / 12.56637 * 510065621724000;
34487         }
34488
34489         function toLineString(feature) {
34490             if (feature.type === 'LineString') return feature;
34491
34492             var result = { type: 'LineString', coordinates: [] };
34493             if (feature.type === 'Polygon') {
34494                 result.coordinates = feature.coordinates[0];
34495             } else if (feature.type === 'MultiPolygon') {
34496                 result.coordinates = feature.coordinates[0][0];
34497             }
34498
34499             return result;
34500         }
34501
34502         function displayLength(m) {
34503             var d = m * (imperial ? 3.28084 : 1),
34504                 p, unit;
34505
34506             if (imperial) {
34507                 if (d >= 5280) {
34508                     d /= 5280;
34509                     unit = 'mi';
34510                 } else {
34511                     unit = 'ft';
34512                 }
34513             } else {
34514                 if (d >= 1000) {
34515                     d /= 1000;
34516                     unit = 'km';
34517                 } else {
34518                     unit = 'm';
34519                 }
34520             }
34521
34522             // drop unnecessary precision
34523             p = d > 1000 ? 0 : d > 100 ? 1 : 2;
34524
34525             return String(d.toFixed(p)) + ' ' + unit;
34526         }
34527
34528         function displayArea(m2) {
34529             var d = m2 * (imperial ? 10.7639111056 : 1),
34530                 d1, d2, p1, p2, unit1, unit2;
34531
34532             if (imperial) {
34533                 if (d >= 6969600) {     // > 0.25mi² show mi²
34534                     d1 = d / 27878400;
34535                     unit1 = 'mi²';
34536                 } else {
34537                     d1 = d;
34538                     unit1 = 'ft²';
34539                 }
34540
34541                 if (d > 4356 && d < 43560000) {   // 0.1 - 1000 acres
34542                     d2 = d / 43560;
34543                     unit2 = 'ac';
34544                 }
34545
34546             } else {
34547                 if (d >= 250000) {    // > 0.25km² show km²
34548                     d1 = d / 1000000;
34549                     unit1 = 'km²';
34550                 } else {
34551                     d1 = d;
34552                     unit1 = 'm²';
34553                 }
34554
34555                 if (d > 1000 && d < 10000000) {   // 0.1 - 1000 hectares
34556                     d2 = d / 10000;
34557                     unit2 = 'ha';
34558                 }
34559             }
34560
34561             // drop unnecessary precision
34562             p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2;
34563             p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2;
34564
34565             return String(d1.toFixed(p1)) + ' ' + unit1 +
34566                 (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : '');
34567         }
34568
34569
34570         function redraw() {
34571             if (hidden) return;
34572
34573             var resolver = context.graph(),
34574                 selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }),
34575                 singular = selected.length === 1 ? selected[0] : null,
34576                 extent = iD.geo.Extent(),
34577                 entity;
34578
34579             wrap.html('');
34580             wrap.append('h4')
34581                 .attr('class', 'infobox-heading fillD')
34582                 .text(singular || t('infobox.selected', { n: selected.length }));
34583
34584             if (!selected.length) return;
34585
34586             var center;
34587             for (var i = 0; i < selected.length; i++) {
34588                 entity = context.entity(selected[i]);
34589                 extent._extend(entity.extent(resolver));
34590             }
34591             center = extent.center();
34592
34593
34594             var list = wrap.append('ul');
34595
34596             // multiple wrap, just display extent center..
34597             if (!singular) {
34598                 list.append('li')
34599                     .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
34600                 return;
34601             }
34602
34603             // single wrap, display details..
34604             if (!entity) return;
34605             var geometry = entity.geometry(resolver);
34606
34607             if (geometry === 'line' || geometry === 'area') {
34608                 var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()),
34609                     feature = entity.asGeoJSON(resolver),
34610                     length = radiansToMeters(d3.geo.length(toLineString(feature))),
34611                     lengthLabel = t('infobox.' + (closed ? 'perimeter' : 'length')),
34612                     centroid = d3.geo.centroid(feature);
34613
34614                 list.append('li')
34615                     .text(t('infobox.geometry') + ': ' +
34616                         (closed ? t('infobox.closed') + ' ' : '') + t('geometry.' + geometry) );
34617
34618                 if (closed) {
34619                     var area = steradiansToSqmeters(entity.area(resolver));
34620                     list.append('li')
34621                         .text(t('infobox.area') + ': ' + displayArea(area));
34622                 }
34623
34624                 list.append('li')
34625                     .text(lengthLabel + ': ' + displayLength(length));
34626
34627                 list.append('li')
34628                     .text(t('infobox.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5));
34629
34630
34631                 var toggle  = imperial ? 'imperial' : 'metric';
34632                 wrap.append('a')
34633                     .text(t('infobox.' + toggle))
34634                     .attr('href', '#')
34635                     .attr('class', 'button')
34636                     .on('click', function() {
34637                         d3.event.preventDefault();
34638                         imperial = !imperial;
34639                         redraw();
34640                     });
34641
34642             } else {
34643                 var centerLabel = t('infobox.' + (entity.type === 'node' ? 'location' : 'center'));
34644
34645                 list.append('li')
34646                     .text(t('infobox.geometry') + ': ' + t('geometry.' + geometry));
34647
34648                 list.append('li')
34649                     .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5));
34650             }
34651         }
34652
34653
34654         function toggle() {
34655             if (d3.event) d3.event.preventDefault();
34656
34657             hidden = !hidden;
34658
34659             if (hidden) {
34660                 wrap
34661                     .style('display', 'block')
34662                     .style('opacity', 1)
34663                     .transition()
34664                     .duration(200)
34665                     .style('opacity', 0)
34666                     .each('end', function() {
34667                         d3.select(this).style('display', 'none');
34668                     });
34669             } else {
34670                 wrap
34671                     .style('display', 'block')
34672                     .style('opacity', 0)
34673                     .transition()
34674                     .duration(200)
34675                     .style('opacity', 1);
34676
34677                 redraw();
34678             }
34679         }
34680
34681
34682         var wrap = selection.selectAll('.infobox')
34683             .data([0]);
34684
34685         wrap.enter()
34686             .append('div')
34687             .attr('class', 'infobox fillD2')
34688             .style('display', (hidden ? 'none' : 'block'));
34689
34690         context.map()
34691             .on('drawn.info', redraw);
34692
34693         redraw();
34694
34695         var keybinding = d3.keybinding('info')
34696             .on(key, toggle);
34697
34698         d3.select(document)
34699             .call(keybinding);
34700     }
34701
34702     return info;
34703 };
34704 iD.ui.Inspector = function(context) {
34705     var presetList = iD.ui.PresetList(context),
34706         entityEditor = iD.ui.EntityEditor(context),
34707         state = 'select',
34708         entityID,
34709         newFeature = false;
34710
34711     function inspector(selection) {
34712         presetList
34713             .entityID(entityID)
34714             .autofocus(newFeature)
34715             .on('choose', setPreset);
34716
34717         entityEditor
34718             .state(state)
34719             .entityID(entityID)
34720             .on('choose', showList);
34721
34722         var $wrap = selection.selectAll('.panewrap')
34723             .data([0]);
34724
34725         var $enter = $wrap.enter().append('div')
34726             .attr('class', 'panewrap');
34727
34728         $enter.append('div')
34729             .attr('class', 'preset-list-pane pane');
34730
34731         $enter.append('div')
34732             .attr('class', 'entity-editor-pane pane');
34733
34734         var $presetPane = $wrap.select('.preset-list-pane');
34735         var $editorPane = $wrap.select('.entity-editor-pane');
34736
34737         var graph = context.graph(),
34738             entity = context.entity(entityID),
34739             showEditor = state === 'hover' ||
34740                 entity.isUsed(graph) ||
34741                 entity.isHighwayIntersection(graph);
34742
34743         if (showEditor) {
34744             $wrap.style('right', '0%');
34745             $editorPane.call(entityEditor);
34746         } else {
34747             $wrap.style('right', '-100%');
34748             $presetPane.call(presetList);
34749         }
34750
34751         var $footer = selection.selectAll('.footer')
34752             .data([0]);
34753
34754         $footer.enter().append('div')
34755             .attr('class', 'footer');
34756
34757         selection.select('.footer')
34758             .call(iD.ui.ViewOnOSM(context)
34759                 .entityID(entityID));
34760
34761         function showList(preset) {
34762             $wrap.transition()
34763                 .styleTween('right', function() { return d3.interpolate('0%', '-100%'); });
34764
34765             $presetPane.call(presetList
34766                 .preset(preset)
34767                 .autofocus(true));
34768         }
34769
34770         function setPreset(preset) {
34771             $wrap.transition()
34772                 .styleTween('right', function() { return d3.interpolate('-100%', '0%'); });
34773
34774             $editorPane.call(entityEditor
34775                 .preset(preset));
34776         }
34777     }
34778
34779     inspector.state = function(_) {
34780         if (!arguments.length) return state;
34781         state = _;
34782         entityEditor.state(state);
34783         return inspector;
34784     };
34785
34786     inspector.entityID = function(_) {
34787         if (!arguments.length) return entityID;
34788         entityID = _;
34789         return inspector;
34790     };
34791
34792     inspector.newFeature = function(_) {
34793         if (!arguments.length) return newFeature;
34794         newFeature = _;
34795         return inspector;
34796     };
34797
34798     return inspector;
34799 };
34800 iD.ui.intro = function(context) {
34801     var step;
34802
34803     function intro(selection) {
34804
34805         function localizedName(id) {
34806             var features = {
34807                 n2140018997: 'city_hall',
34808                 n367813436: 'fire_department',
34809                 w203988286: 'memory_isle_park',
34810                 w203972937: 'riverwalk_trail',
34811                 w203972938: 'riverwalk_trail',
34812                 w203972940: 'riverwalk_trail',
34813                 w41785752: 'w_michigan_ave',
34814                 w134150789: 'w_michigan_ave',
34815                 w134150795: 'w_michigan_ave',
34816                 w134150800: 'w_michigan_ave',
34817                 w134150811: 'w_michigan_ave',
34818                 w134150802: 'e_michigan_ave',
34819                 w134150836: 'e_michigan_ave',
34820                 w41074896: 'e_michigan_ave',
34821                 w17965834: 'spring_st',
34822                 w203986457: 'scidmore_park',
34823                 w203049587: 'petting_zoo',
34824                 w17967397: 'n_andrews_st',
34825                 w17967315: 's_andrews_st',
34826                 w17967326: 'n_constantine_st',
34827                 w17966400: 's_constantine_st',
34828                 w170848823: 'rocky_river',
34829                 w170848824: 'rocky_river',
34830                 w170848331: 'rocky_river',
34831                 w17967752: 'railroad_dr',
34832                 w17965998: 'conrail_rr',
34833                 w134150845: 'conrail_rr',
34834                 w170989131: 'st_joseph_river',
34835                 w143497377: 'n_main_st',
34836                 w134150801: 's_main_st',
34837                 w134150830: 's_main_st',
34838                 w17966462: 's_main_st',
34839                 w17967734: 'water_st',
34840                 w17964996: 'foster_st',
34841                 w170848330: 'portage_river',
34842                 w17965351: 'flower_st',
34843                 w17965502: 'elm_st',
34844                 w17965402: 'walnut_st',
34845                 w17964793: 'morris_ave',
34846                 w17967444: 'east_st',
34847                 w17966984: 'portage_ave'
34848             };
34849             return features[id] && t('intro.graph.' + features[id]);
34850         }
34851
34852         context.enter(iD.modes.Browse(context));
34853
34854         // Save current map state
34855         var history = context.history().toJSON(),
34856             hash = window.location.hash,
34857             center = context.map().center(),
34858             zoom = context.map().zoom(),
34859             background = context.background().baseLayerSource(),
34860             opacity = d3.selectAll('#map .layer-background').style('opacity'),
34861             loadedTiles = context.connection().loadedTiles(),
34862             baseEntities = context.history().graph().base().entities,
34863             introGraph, name;
34864
34865         // Block saving
34866         context.inIntro(true);
34867
34868         // Load semi-real data used in intro
34869         context.connection().toggle(false).flush();
34870         context.history().reset();
34871
34872         introGraph = JSON.parse(iD.introGraph);
34873         for (var key in introGraph) {
34874             introGraph[key] = iD.Entity(introGraph[key]);
34875             name = localizedName(key);
34876             if (name) {
34877                 introGraph[key].tags.name = name;
34878             }
34879         }
34880         context.history().merge(d3.values(iD.Graph().load(introGraph).entities));
34881         context.background().bing();
34882
34883         d3.selectAll('#map .layer-background').style('opacity', 1);
34884
34885         var curtain = d3.curtain();
34886         selection.call(curtain);
34887
34888         function reveal(box, text, options) {
34889             options = options || {};
34890             if (text) curtain.reveal(box, text, options.tooltipClass, options.duration);
34891             else curtain.reveal(box, '', '', options.duration);
34892         }
34893
34894         var steps = ['navigation', 'point', 'area', 'line', 'startEditing'].map(function(step, i) {
34895             var s = iD.ui.intro[step](context, reveal)
34896                 .on('done', function() {
34897                     entered.filter(function(d) {
34898                         return d.title === s.title;
34899                     }).classed('finished', true);
34900                     enter(steps[i + 1]);
34901                 });
34902             return s;
34903         });
34904
34905         steps[steps.length - 1].on('startEditing', function() {
34906             curtain.remove();
34907             navwrap.remove();
34908             d3.selectAll('#map .layer-background').style('opacity', opacity);
34909             context.connection().toggle(true).flush().loadedTiles(loadedTiles);
34910             context.history().reset().merge(d3.values(baseEntities));
34911             context.background().baseLayerSource(background);
34912             if (history) context.history().fromJSON(history, false);
34913             context.map().centerZoom(center, zoom);
34914             window.location.replace(hash);
34915             context.inIntro(false);
34916         });
34917
34918         var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
34919
34920         var buttonwrap = navwrap.append('div')
34921             .attr('class', 'joined')
34922             .selectAll('button.step');
34923
34924         var entered = buttonwrap
34925             .data(steps)
34926             .enter()
34927             .append('button')
34928             .attr('class', 'step')
34929             .on('click', enter);
34930
34931         entered
34932             .call(iD.svg.Icon('#icon-apply', 'pre-text'));
34933
34934         entered
34935             .append('label')
34936             .text(function(d) { return t(d.title); });
34937
34938         enter(steps[0]);
34939
34940         function enter (newStep) {
34941             if (step) { step.exit(); }
34942
34943             context.enter(iD.modes.Browse(context));
34944
34945             step = newStep;
34946             step.enter();
34947
34948             entered.classed('active', function(d) {
34949                 return d.title === step.title;
34950             });
34951         }
34952
34953     }
34954     return intro;
34955 };
34956
34957 iD.ui.intro.pointBox = function(point, context) {
34958     var rect = context.surfaceRect();
34959     point = context.projection(point);
34960     return {
34961         left: point[0] + rect.left - 30,
34962         top: point[1] + rect.top - 50,
34963         width: 60,
34964         height: 70
34965     };
34966 };
34967
34968 iD.ui.intro.pad = function(box, padding, context) {
34969     if (box instanceof Array) {
34970         var rect = context.surfaceRect();
34971         box = context.projection(box);
34972         box = {
34973             left: box[0] + rect.left,
34974             top: box[1] + rect.top
34975         };
34976     }
34977     return {
34978         left: box.left - padding,
34979         top: box.top - padding,
34980         width: (box.width || 0) + 2 * padding,
34981         height: (box.width || 0) + 2 * padding
34982     };
34983 };
34984
34985 iD.ui.intro.icon = function(name, svgklass) {
34986     return '<svg class="icon ' + (svgklass || '') + '">' +
34987         '<use xlink:href="' + name + '"></use></svg>';
34988 };
34989 iD.ui.Lasso = function(context) {
34990     var group, polygon;
34991
34992     lasso.coordinates = [];
34993
34994     function lasso(selection) {
34995
34996         context.container().classed('lasso', true);
34997
34998         group = selection.append('g')
34999             .attr('class', 'lasso hide');
35000
35001         polygon = group.append('path')
35002             .attr('class', 'lasso-path');
35003
35004         group.call(iD.ui.Toggle(true));
35005
35006     }
35007
35008     function draw() {
35009         if (polygon) {
35010             polygon.data([lasso.coordinates])
35011                 .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; });
35012         }
35013     }
35014
35015     lasso.extent = function () {
35016         return lasso.coordinates.reduce(function(extent, point) {
35017             return extent.extend(iD.geo.Extent(point));
35018         }, iD.geo.Extent());
35019     };
35020
35021     lasso.p = function(_) {
35022         if (!arguments.length) return lasso;
35023         lasso.coordinates.push(_);
35024         draw();
35025         return lasso;
35026     };
35027
35028     lasso.close = function() {
35029         if (group) {
35030             group.call(iD.ui.Toggle(false, function() {
35031                 d3.select(this).remove();
35032             }));
35033         }
35034         context.container().classed('lasso', false);
35035     };
35036
35037     return lasso;
35038 };
35039 iD.ui.Loading = function(context) {
35040     var message = '',
35041         blocking = false,
35042         modal;
35043
35044     var loading = function(selection) {
35045         modal = iD.ui.modal(selection, blocking);
35046
35047         var loadertext = modal.select('.content')
35048             .classed('loading-modal', true)
35049             .append('div')
35050             .attr('class', 'modal-section fillL');
35051
35052         loadertext.append('img')
35053             .attr('class', 'loader')
35054             .attr('src', context.imagePath('loader-white.gif'));
35055
35056         loadertext.append('h3')
35057             .text(message);
35058
35059         modal.select('button.close')
35060             .attr('class', 'hide');
35061
35062         return loading;
35063     };
35064
35065     loading.message = function(_) {
35066         if (!arguments.length) return message;
35067         message = _;
35068         return loading;
35069     };
35070
35071     loading.blocking = function(_) {
35072         if (!arguments.length) return blocking;
35073         blocking = _;
35074         return loading;
35075     };
35076
35077     loading.close = function() {
35078         modal.remove();
35079     };
35080
35081     return loading;
35082 };
35083 iD.ui.MapData = function(context) {
35084     var key = 'F',
35085         features = context.features().keys(),
35086         layers = context.layers(),
35087         fills = ['wireframe', 'partial', 'full'],
35088         fillDefault = context.storage('area-fill') || 'partial',
35089         fillSelected = fillDefault;
35090
35091
35092     function map_data(selection) {
35093
35094         function showsFeature(d) {
35095             return context.features().enabled(d);
35096         }
35097
35098         function autoHiddenFeature(d) {
35099             return context.features().autoHidden(d);
35100         }
35101
35102         function clickFeature(d) {
35103             context.features().toggle(d);
35104             update();
35105         }
35106
35107         function showsFill(d) {
35108             return fillSelected === d;
35109         }
35110
35111         function setFill(d) {
35112             _.each(fills, function(opt) {
35113                 context.surface().classed('fill-' + opt, Boolean(opt === d));
35114             });
35115
35116             fillSelected = d;
35117             if (d !== 'wireframe') {
35118                 fillDefault = d;
35119                 context.storage('area-fill', d);
35120             }
35121             update();
35122         }
35123
35124         function toggleLayer(which) {
35125             var layer = layers.layer(which);
35126             if (layer) {
35127                 layer.enabled(!layer.enabled());
35128                 update();
35129             }
35130         }
35131
35132         function clickGpx() {
35133             toggleLayer('gpx');
35134         }
35135
35136         function clickMapillaryImages() {
35137             toggleLayer('mapillary-images');
35138         }
35139
35140         function clickMapillarySigns() {
35141             toggleLayer('mapillary-signs');
35142         }
35143
35144
35145         function drawMapillaryItems(selection) {
35146             var mapillaryImages = layers.layer('mapillary-images'),
35147                 mapillarySigns = layers.layer('mapillary-signs'),
35148                 supportsMapillaryImages = mapillaryImages && mapillaryImages.supported(),
35149                 supportsMapillarySigns = mapillarySigns && mapillarySigns.supported(),
35150                 showsMapillaryImages = supportsMapillaryImages && mapillaryImages.enabled(),
35151                 showsMapillarySigns = supportsMapillarySigns && mapillarySigns.enabled();
35152
35153             var mapillaryList = selection
35154                 .selectAll('.layer-list-mapillary')
35155                 .data([0]);
35156
35157             // Enter
35158             mapillaryList
35159                 .enter()
35160                 .append('ul')
35161                 .attr('class', 'layer-list layer-list-mapillary');
35162
35163             var mapillaryImageLayerItem = mapillaryList
35164                 .selectAll('.list-item-mapillary-images')
35165                 .data(supportsMapillaryImages ? [0] : []);
35166
35167             var enterImages = mapillaryImageLayerItem.enter()
35168                 .append('li')
35169                 .attr('class', 'list-item-mapillary-images');
35170
35171             var labelImages = enterImages.append('label')
35172                 .call(bootstrap.tooltip()
35173                     .title(t('mapillary_images.tooltip'))
35174                     .placement('top'));
35175
35176             labelImages.append('input')
35177                 .attr('type', 'checkbox')
35178                 .on('change', clickMapillaryImages);
35179
35180             labelImages.append('span')
35181                 .text(t('mapillary_images.title'));
35182
35183
35184             var mapillarySignLayerItem = mapillaryList
35185                 .selectAll('.list-item-mapillary-signs')
35186                 .data(supportsMapillarySigns ? [0] : []);
35187
35188             var enterSigns = mapillarySignLayerItem.enter()
35189                 .append('li')
35190                 .attr('class', 'list-item-mapillary-signs');
35191
35192             var labelSigns = enterSigns.append('label')
35193                 .call(bootstrap.tooltip()
35194                     .title(t('mapillary_signs.tooltip'))
35195                     .placement('top'));
35196
35197             labelSigns.append('input')
35198                 .attr('type', 'checkbox')
35199                 .on('change', clickMapillarySigns);
35200
35201             labelSigns.append('span')
35202                 .text(t('mapillary_signs.title'));
35203
35204             // Update
35205             mapillaryImageLayerItem
35206                 .classed('active', showsMapillaryImages)
35207                 .selectAll('input')
35208                 .property('checked', showsMapillaryImages);
35209
35210             mapillarySignLayerItem
35211                 .classed('active', showsMapillarySigns)
35212                 .selectAll('input')
35213                 .property('checked', showsMapillarySigns);
35214
35215             // Exit
35216             mapillaryImageLayerItem.exit()
35217                 .remove();
35218             mapillarySignLayerItem.exit()
35219                 .remove();
35220         }
35221
35222
35223         function drawGpxItem(selection) {
35224             var gpx = layers.layer('gpx'),
35225                 hasGpx = gpx && gpx.hasGpx(),
35226                 showsGpx = hasGpx && gpx.enabled();
35227
35228             var gpxLayerItem = selection
35229                 .selectAll('.layer-list-gpx')
35230                 .data(gpx ? [0] : []);
35231
35232             // Enter
35233             var enter = gpxLayerItem.enter()
35234                 .append('ul')
35235                 .attr('class', 'layer-list layer-list-gpx')
35236                 .append('li')
35237                 .classed('list-item-gpx', true);
35238
35239             enter.append('button')
35240                 .attr('class', 'list-item-gpx-extent')
35241                 .call(bootstrap.tooltip()
35242                     .title(t('gpx.zoom'))
35243                     .placement('left'))
35244                 .on('click', function() {
35245                     d3.event.preventDefault();
35246                     d3.event.stopPropagation();
35247                     gpx.fitZoom();
35248                 })
35249                 .call(iD.svg.Icon('#icon-search'));
35250
35251             enter.append('button')
35252                 .attr('class', 'list-item-gpx-browse')
35253                 .call(bootstrap.tooltip()
35254                     .title(t('gpx.browse'))
35255                     .placement('left'))
35256                 .on('click', function() {
35257                     d3.select(document.createElement('input'))
35258                         .attr('type', 'file')
35259                         .on('change', function() {
35260                             gpx.files(d3.event.target.files);
35261                         })
35262                         .node().click();
35263                 })
35264                 .call(iD.svg.Icon('#icon-geolocate'));
35265
35266             var labelGpx = enter.append('label')
35267                 .call(bootstrap.tooltip()
35268                     .title(t('gpx.drag_drop'))
35269                     .placement('top'));
35270
35271             labelGpx.append('input')
35272                 .attr('type', 'checkbox')
35273                 .on('change', clickGpx);
35274
35275             labelGpx.append('span')
35276                 .text(t('gpx.local_layer'));
35277
35278             // Update
35279             gpxLayerItem
35280                 .classed('active', showsGpx)
35281                 .selectAll('input')
35282                 .property('disabled', !hasGpx)
35283                 .property('checked', showsGpx);
35284
35285             // Exit
35286             gpxLayerItem.exit()
35287                 .remove();
35288         }
35289
35290
35291         function drawList(selection, data, type, name, change, active) {
35292             var items = selection.selectAll('li')
35293                 .data(data);
35294
35295             // Enter
35296             var enter = items.enter()
35297                 .append('li')
35298                 .attr('class', 'layer')
35299                 .call(bootstrap.tooltip()
35300                     .html(true)
35301                     .title(function(d) {
35302                         var tip = t(name + '.' + d + '.tooltip'),
35303                             key = (d === 'wireframe' ? 'W' : null);
35304
35305                         if (name === 'feature' && autoHiddenFeature(d)) {
35306                             tip += '<div>' + t('map_data.autohidden') + '</div>';
35307                         }
35308                         return iD.ui.tooltipHtml(tip, key);
35309                     })
35310                     .placement('top')
35311                 );
35312
35313             var label = enter.append('label');
35314
35315             label.append('input')
35316                 .attr('type', type)
35317                 .attr('name', name)
35318                 .on('change', change);
35319
35320             label.append('span')
35321                 .text(function(d) { return t(name + '.' + d + '.description'); });
35322
35323             // Update
35324             items
35325                 .classed('active', active)
35326                 .selectAll('input')
35327                 .property('checked', active)
35328                 .property('indeterminate', function(d) {
35329                     return (name === 'feature' && autoHiddenFeature(d));
35330                 });
35331
35332             // Exit
35333             items.exit()
35334                 .remove();
35335         }
35336
35337
35338         function update() {
35339             dataLayerContainer.call(drawMapillaryItems);
35340             dataLayerContainer.call(drawGpxItem);
35341
35342             fillList.call(drawList, fills, 'radio', 'area_fill', setFill, showsFill);
35343
35344             featureList.call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature);
35345         }
35346
35347         function hidePanel() {
35348             setVisible(false);
35349         }
35350
35351         function togglePanel() {
35352             if (d3.event) d3.event.preventDefault();
35353             tooltip.hide(button);
35354             setVisible(!button.classed('active'));
35355         }
35356
35357         function toggleWireframe() {
35358             if (d3.event) {
35359                 d3.event.preventDefault();
35360                 d3.event.stopPropagation();
35361             }
35362             setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe'));
35363             context.map().pan([0,0]);  // trigger a redraw
35364         }
35365
35366         function setVisible(show) {
35367             if (show !== shown) {
35368                 button.classed('active', show);
35369                 shown = show;
35370
35371                 if (show) {
35372                     update();
35373                     selection.on('mousedown.map_data-inside', function() {
35374                         return d3.event.stopPropagation();
35375                     });
35376                     content.style('display', 'block')
35377                         .style('right', '-300px')
35378                         .transition()
35379                         .duration(200)
35380                         .style('right', '0px');
35381                 } else {
35382                     content.style('display', 'block')
35383                         .style('right', '0px')
35384                         .transition()
35385                         .duration(200)
35386                         .style('right', '-300px')
35387                         .each('end', function() {
35388                             d3.select(this).style('display', 'none');
35389                         });
35390                     selection.on('mousedown.map_data-inside', null);
35391                 }
35392             }
35393         }
35394
35395
35396         var content = selection.append('div')
35397                 .attr('class', 'fillL map-overlay col3 content hide'),
35398             tooltip = bootstrap.tooltip()
35399                 .placement('left')
35400                 .html(true)
35401                 .title(iD.ui.tooltipHtml(t('map_data.description'), key)),
35402             button = selection.append('button')
35403                 .attr('tabindex', -1)
35404                 .on('click', togglePanel)
35405                 .call(iD.svg.Icon('#icon-data', 'light'))
35406                 .call(tooltip),
35407             shown = false;
35408
35409         content.append('h4')
35410             .text(t('map_data.title'));
35411
35412
35413         // data layers
35414         content.append('a')
35415             .text(t('map_data.data_layers'))
35416             .attr('href', '#')
35417             .classed('hide-toggle', true)
35418             .classed('expanded', true)
35419             .on('click', function() {
35420                 var exp = d3.select(this).classed('expanded');
35421                 dataLayerContainer.style('display', exp ? 'none' : 'block');
35422                 d3.select(this).classed('expanded', !exp);
35423                 d3.event.preventDefault();
35424             });
35425
35426         var dataLayerContainer = content.append('div')
35427             .attr('class', 'data-data-layers')
35428             .style('display', 'block');
35429
35430
35431         // area fills
35432         content.append('a')
35433             .text(t('map_data.fill_area'))
35434             .attr('href', '#')
35435             .classed('hide-toggle', true)
35436             .classed('expanded', false)
35437             .on('click', function() {
35438                 var exp = d3.select(this).classed('expanded');
35439                 fillContainer.style('display', exp ? 'none' : 'block');
35440                 d3.select(this).classed('expanded', !exp);
35441                 d3.event.preventDefault();
35442             });
35443
35444         var fillContainer = content.append('div')
35445             .attr('class', 'data-area-fills')
35446             .style('display', 'none');
35447
35448         var fillList = fillContainer.append('ul')
35449             .attr('class', 'layer-list layer-fill-list');
35450
35451
35452         // feature filters
35453         content.append('a')
35454             .text(t('map_data.map_features'))
35455             .attr('href', '#')
35456             .classed('hide-toggle', true)
35457             .classed('expanded', false)
35458             .on('click', function() {
35459                 var exp = d3.select(this).classed('expanded');
35460                 featureContainer.style('display', exp ? 'none' : 'block');
35461                 d3.select(this).classed('expanded', !exp);
35462                 d3.event.preventDefault();
35463             });
35464
35465         var featureContainer = content.append('div')
35466             .attr('class', 'data-feature-filters')
35467             .style('display', 'none');
35468
35469         var featureList = featureContainer.append('ul')
35470             .attr('class', 'layer-list layer-feature-list');
35471
35472
35473         context.features()
35474             .on('change.map_data-update', update);
35475
35476         setFill(fillDefault);
35477
35478         var keybinding = d3.keybinding('features')
35479             .on(key, togglePanel)
35480             .on('W', toggleWireframe)
35481             .on('B', hidePanel)
35482             .on('H', hidePanel);
35483
35484         d3.select(document)
35485             .call(keybinding);
35486
35487         context.surface().on('mousedown.map_data-outside', hidePanel);
35488         context.container().on('mousedown.map_data-outside', hidePanel);
35489     }
35490
35491     return map_data;
35492 };
35493 iD.ui.MapInMap = function(context) {
35494     var key = '/';
35495
35496     function map_in_map(selection) {
35497         var backgroundLayer = iD.TileLayer(),
35498             overlayLayers = {},
35499             projection = iD.geo.RawMercator(),
35500             gpxLayer = iD.svg.Gpx(projection, context).showLabels(false),
35501             zoom = d3.behavior.zoom()
35502                 .scaleExtent([ztok(0.5), ztok(24)])
35503                 .on('zoom', zoomPan),
35504             transformed = false,
35505             panning = false,
35506             hidden = true,
35507             zDiff = 6,    // by default, minimap renders at (main zoom - 6)
35508             tStart, tLast, tCurr, kLast, kCurr, tiles, svg, timeoutId;
35509
35510         function ztok(z) { return 256 * Math.pow(2, z); }
35511         function ktoz(k) { return Math.log(k) / Math.LN2 - 8; }
35512
35513
35514         function startMouse() {
35515             context.surface().on('mouseup.map-in-map-outside', endMouse);
35516             context.container().on('mouseup.map-in-map-outside', endMouse);
35517
35518             tStart = tLast = tCurr = projection.translate();
35519             panning = true;
35520         }
35521
35522
35523         function zoomPan() {
35524             var e = d3.event.sourceEvent,
35525                 t = d3.event.translate,
35526                 k = d3.event.scale,
35527                 zMain = ktoz(context.projection.scale() * 2 * Math.PI),
35528                 zMini = ktoz(k);
35529
35530             // restrict minimap zoom to < (main zoom - 3)
35531             if (zMini > zMain - 3) {
35532                 zMini = zMain - 3;
35533                 zoom.scale(kCurr).translate(tCurr);  // restore last good values
35534                 return;
35535             }
35536
35537             tCurr = t;
35538             kCurr = k;
35539             zDiff = zMain - zMini;
35540
35541             var scale = kCurr / kLast,
35542                 tX = (tCurr[0] / scale - tLast[0]) * scale,
35543                 tY = (tCurr[1] / scale - tLast[1]) * scale;
35544
35545             iD.util.setTransform(tiles, tX, tY, scale);
35546             iD.util.setTransform(svg, 0, 0, scale);
35547             transformed = true;
35548
35549             queueRedraw();
35550
35551             e.preventDefault();
35552             e.stopPropagation();
35553         }
35554
35555
35556         function endMouse() {
35557             context.surface().on('mouseup.map-in-map-outside', null);
35558             context.container().on('mouseup.map-in-map-outside', null);
35559
35560             updateProjection();
35561             panning = false;
35562
35563             if (tCurr[0] !== tStart[0] && tCurr[1] !== tStart[1]) {
35564                 var dMini = wrap.dimensions(),
35565                     cMini = [ dMini[0] / 2, dMini[1] / 2 ];
35566
35567                 context.map().center(projection.invert(cMini));
35568             }
35569         }
35570
35571
35572         function updateProjection() {
35573             var loc = context.map().center(),
35574                 dMini = wrap.dimensions(),
35575                 cMini = [ dMini[0] / 2, dMini[1] / 2 ],
35576                 tMain = context.projection.translate(),
35577                 kMain = context.projection.scale(),
35578                 zMain = ktoz(kMain * 2 * Math.PI),
35579                 zMini = Math.max(zMain - zDiff, 0.5),
35580                 kMini = ztok(zMini);
35581
35582             projection
35583                 .translate(tMain)
35584                 .scale(kMini / (2 * Math.PI));
35585
35586             var s = projection(loc),
35587                 mouse = panning ? [ tCurr[0] - tStart[0], tCurr[1] - tStart[1] ] : [0, 0],
35588                 tMini = [
35589                     cMini[0] - s[0] + tMain[0] + mouse[0],
35590                     cMini[1] - s[1] + tMain[1] + mouse[1]
35591                 ];
35592
35593             projection
35594                 .translate(tMini)
35595                 .clipExtent([[0, 0], dMini]);
35596
35597             zoom
35598                 .center(cMini)
35599                 .translate(tMini)
35600                 .scale(kMini);
35601
35602             tLast = tCurr = tMini;
35603             kLast = kCurr = kMini;
35604
35605             if (transformed) {
35606                 iD.util.setTransform(tiles, 0, 0);
35607                 iD.util.setTransform(svg, 0, 0);
35608                 transformed = false;
35609             }
35610         }
35611
35612
35613         function redraw() {
35614             if (hidden) return;
35615
35616             updateProjection();
35617
35618             var dMini = wrap.dimensions(),
35619                 zMini = ktoz(projection.scale() * 2 * Math.PI);
35620
35621             // setup tile container
35622             tiles = wrap
35623                 .selectAll('.map-in-map-tiles')
35624                 .data([0]);
35625
35626             tiles
35627                 .enter()
35628                 .append('div')
35629                 .attr('class', 'map-in-map-tiles');
35630
35631             // redraw background
35632             backgroundLayer
35633                 .source(context.background().baseLayerSource())
35634                 .projection(projection)
35635                 .dimensions(dMini);
35636
35637             var background = tiles
35638                 .selectAll('.map-in-map-background')
35639                 .data([0]);
35640
35641             background.enter()
35642                 .append('div')
35643                 .attr('class', 'map-in-map-background');
35644
35645             background
35646                 .call(backgroundLayer);
35647
35648
35649             // redraw overlay
35650             var overlaySources = context.background().overlayLayerSources();
35651             var activeOverlayLayers = [];
35652             for (var i = 0; i < overlaySources.length; i++) {
35653                 if (overlaySources[i].validZoom(zMini)) {
35654                     if (!overlayLayers[i]) overlayLayers[i] = iD.TileLayer();
35655                     activeOverlayLayers.push(overlayLayers[i]
35656                         .source(overlaySources[i])
35657                         .projection(projection)
35658                         .dimensions(dMini));
35659                 }
35660             }
35661
35662             var overlay = tiles
35663                 .selectAll('.map-in-map-overlay')
35664                 .data([0]);
35665
35666             overlay.enter()
35667                 .append('div')
35668                 .attr('class', 'map-in-map-overlay');
35669
35670             var overlays = overlay
35671                 .selectAll('div')
35672                 .data(activeOverlayLayers, function(d) { return d.source().name(); });
35673
35674             overlays.enter().append('div');
35675             overlays.each(function(layer) {
35676                 d3.select(this).call(layer);
35677             });
35678
35679             overlays.exit()
35680                 .remove();
35681
35682
35683             var gpx = tiles
35684                 .selectAll('.map-in-map-gpx')
35685                 .data(gpxLayer.enabled() ? [0] : []);
35686
35687             gpx.enter()
35688                 .append('svg')
35689                 .attr('class', 'map-in-map-gpx');
35690
35691             gpx.exit()
35692                 .remove();
35693
35694             gpx.call(gpxLayer);
35695
35696
35697             // redraw bounding box
35698             if (!panning) {
35699                 var getPath = d3.geo.path().projection(projection),
35700                     bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
35701
35702                 svg = wrap.selectAll('.map-in-map-svg')
35703                     .data([0]);
35704
35705                 svg.enter()
35706                     .append('svg')
35707                     .attr('class', 'map-in-map-svg');
35708
35709                 var path = svg.selectAll('.map-in-map-bbox')
35710                     .data([bbox]);
35711
35712                 path.enter()
35713                     .append('path')
35714                     .attr('class', 'map-in-map-bbox');
35715
35716                 path
35717                     .attr('d', getPath)
35718                     .classed('thick', function(d) { return getPath.area(d) < 30; });
35719             }
35720         }
35721
35722
35723         function queueRedraw() {
35724             clearTimeout(timeoutId);
35725             timeoutId = setTimeout(function() { redraw(); }, 300);
35726         }
35727
35728
35729         function toggle() {
35730             if (d3.event) d3.event.preventDefault();
35731
35732             hidden = !hidden;
35733
35734             var label = d3.select('.minimap-toggle');
35735             label.classed('active', !hidden)
35736                 .select('input').property('checked', !hidden);
35737
35738             if (hidden) {
35739                 wrap
35740                     .style('display', 'block')
35741                     .style('opacity', 1)
35742                     .transition()
35743                     .duration(200)
35744                     .style('opacity', 0)
35745                     .each('end', function() {
35746                         d3.select(this).style('display', 'none');
35747                     });
35748             } else {
35749                 wrap
35750                     .style('display', 'block')
35751                     .style('opacity', 0)
35752                     .transition()
35753                     .duration(200)
35754                     .style('opacity', 1);
35755
35756                 redraw();
35757             }
35758         }
35759
35760         iD.ui.MapInMap.toggle = toggle;
35761
35762         var wrap = selection.selectAll('.map-in-map')
35763             .data([0]);
35764
35765         wrap.enter()
35766             .append('div')
35767             .attr('class', 'map-in-map')
35768             .style('display', (hidden ? 'none' : 'block'))
35769             .on('mousedown.map-in-map', startMouse)
35770             .on('mouseup.map-in-map', endMouse)
35771             .call(zoom)
35772             .on('dblclick.zoom', null);
35773
35774         context.map()
35775             .on('drawn.map-in-map', function(drawn) {
35776                 if (drawn.full === true) redraw();
35777             });
35778
35779         redraw();
35780
35781         var keybinding = d3.keybinding('map-in-map')
35782             .on(key, toggle);
35783
35784         d3.select(document)
35785             .call(keybinding);
35786     }
35787
35788     return map_in_map;
35789 };
35790 iD.ui.modal = function(selection, blocking) {
35791     var keybinding = d3.keybinding('modal');
35792     var previous = selection.select('div.modal');
35793     var animate = previous.empty();
35794
35795     previous.transition()
35796         .duration(200)
35797         .style('opacity', 0)
35798         .remove();
35799
35800     var shaded = selection
35801         .append('div')
35802         .attr('class', 'shaded')
35803         .style('opacity', 0);
35804
35805     shaded.close = function() {
35806         shaded
35807             .transition()
35808             .duration(200)
35809             .style('opacity',0)
35810             .remove();
35811         modal
35812             .transition()
35813             .duration(200)
35814             .style('top','0px');
35815
35816         keybinding.off();
35817     };
35818
35819
35820     var modal = shaded.append('div')
35821         .attr('class', 'modal fillL col6');
35822
35823     if (!blocking) {
35824         shaded.on('click.remove-modal', function() {
35825             if (d3.event.target === this) {
35826                 shaded.close();
35827             }
35828         });
35829
35830         modal.append('button')
35831             .attr('class', 'close')
35832             .on('click', shaded.close)
35833             .call(iD.svg.Icon('#icon-close'));
35834
35835         keybinding
35836             .on('⌫', shaded.close)
35837             .on('⎋', shaded.close);
35838
35839         d3.select(document).call(keybinding);
35840     }
35841
35842     modal.append('div')
35843         .attr('class', 'content');
35844
35845     if (animate) {
35846         shaded.transition().style('opacity', 1);
35847     } else {
35848         shaded.style('opacity', 1);
35849     }
35850
35851     return shaded;
35852 };
35853 iD.ui.Modes = function(context) {
35854     var modes = [
35855         iD.modes.AddPoint(context),
35856         iD.modes.AddLine(context),
35857         iD.modes.AddArea(context)];
35858
35859     function editable() {
35860         return context.editable() && context.mode().id !== 'save';
35861     }
35862
35863     return function(selection) {
35864         var buttons = selection.selectAll('button.add-button')
35865             .data(modes);
35866
35867        buttons.enter().append('button')
35868            .attr('tabindex', -1)
35869            .attr('class', function(mode) { return mode.id + ' add-button col4'; })
35870            .on('click.mode-buttons', function(mode) {
35871                if (mode.id === context.mode().id) {
35872                    context.enter(iD.modes.Browse(context));
35873                } else {
35874                    context.enter(mode);
35875                }
35876            })
35877            .call(bootstrap.tooltip()
35878                .placement('bottom')
35879                .html(true)
35880                .title(function(mode) {
35881                    return iD.ui.tooltipHtml(mode.description, mode.key);
35882                }));
35883
35884         context.map()
35885             .on('move.modes', _.debounce(update, 500));
35886
35887         context
35888             .on('enter.modes', update);
35889
35890         buttons.each(function(d) {
35891             d3.select(this)
35892                 .call(iD.svg.Icon('#icon-' + d.button, 'pre-text'));
35893         });
35894
35895         buttons.append('span')
35896             .attr('class', 'label')
35897             .text(function(mode) { return mode.title; });
35898
35899         context.on('enter.editor', function(entered) {
35900             buttons.classed('active', function(mode) { return entered.button === mode.button; });
35901             context.container()
35902                 .classed('mode-' + entered.id, true);
35903         });
35904
35905         context.on('exit.editor', function(exited) {
35906             context.container()
35907                 .classed('mode-' + exited.id, false);
35908         });
35909
35910         var keybinding = d3.keybinding('mode-buttons');
35911
35912         modes.forEach(function(m) {
35913             keybinding.on(m.key, function() { if (editable()) context.enter(m); });
35914         });
35915
35916         d3.select(document)
35917             .call(keybinding);
35918
35919         function update() {
35920             buttons.property('disabled', !editable());
35921         }
35922     };
35923 };
35924 iD.ui.Notice = function(context) {
35925     return function(selection) {
35926         var div = selection.append('div')
35927             .attr('class', 'notice');
35928
35929         var button = div.append('button')
35930             .attr('class', 'zoom-to notice')
35931             .on('click', function() { context.map().zoom(context.minEditableZoom()); });
35932
35933         button
35934             .call(iD.svg.Icon('#icon-plus', 'pre-text'))
35935             .append('span')
35936             .attr('class', 'label')
35937             .text(t('zoom_in_edit'));
35938
35939         function disableTooHigh() {
35940             div.style('display', context.editable() ? 'none' : 'block');
35941         }
35942
35943         context.map()
35944             .on('move.notice', _.debounce(disableTooHigh, 500));
35945
35946         disableTooHigh();
35947     };
35948 };
35949 iD.ui.PresetIcon = function() {
35950     var preset, geometry;
35951
35952     function presetIcon(selection) {
35953         selection.each(render);
35954     }
35955
35956     function render() {
35957         var selection = d3.select(this),
35958             p = preset.apply(this, arguments),
35959             geom = geometry.apply(this, arguments),
35960             icon = p.icon || (geom === 'line' ? 'other-line' : 'marker-stroked'),
35961             maki = iD.data.featureIcons.hasOwnProperty(icon + '-24');
35962
35963         if (icon === 'dentist') maki = true;  // workaround for dentist icon missing in `maki-sprite.json`
35964
35965         function tag_classes(p) {
35966             var s = '';
35967             for (var i in p.tags) {
35968                 s += ' tag-' + i;
35969                 if (p.tags[i] !== '*') {
35970                     s += ' tag-' + i + '-' + p.tags[i];
35971                 }
35972             }
35973             return s;
35974         }
35975
35976         var $fill = selection.selectAll('.preset-icon-fill')
35977             .data([0]);
35978
35979         $fill.enter().append('div');
35980
35981         $fill.attr('class', function() {
35982             return 'preset-icon-fill preset-icon-fill-' + geom + tag_classes(p);
35983         });
35984
35985         var $frame = selection.selectAll('.preset-icon-frame')
35986             .data([0]);
35987
35988         $frame.enter()
35989             .append('div')
35990             .call(iD.svg.Icon('#preset-icon-frame'));
35991
35992         $frame.attr('class', function() {
35993             return 'preset-icon-frame ' + (geom === 'area' ? '' : 'hide');
35994         });
35995
35996
35997         var $icon = selection.selectAll('.preset-icon')
35998             .data([0]);
35999
36000         $icon.enter()
36001             .append('div')
36002             .attr('class', 'preset-icon')
36003             .call(iD.svg.Icon(''));
36004
36005         $icon
36006             .attr('class', 'preset-icon preset-icon-' + (maki ? '32' : (geom === 'area' ? '44' : '60')));
36007
36008         $icon.selectAll('svg')
36009             .attr('class', function() {
36010                 return 'icon ' + icon + tag_classes(p);
36011             });
36012
36013         $icon.selectAll('use')       // workaround: maki parking-24 broken?
36014             .attr('href', '#' + icon + (maki ? ( icon === 'parking' ? '-18' : '-24') : ''));
36015     }
36016
36017     presetIcon.preset = function(_) {
36018         if (!arguments.length) return preset;
36019         preset = d3.functor(_);
36020         return presetIcon;
36021     };
36022
36023     presetIcon.geometry = function(_) {
36024         if (!arguments.length) return geometry;
36025         geometry = d3.functor(_);
36026         return presetIcon;
36027     };
36028
36029     return presetIcon;
36030 };
36031 iD.ui.preset = function(context) {
36032     var event = d3.dispatch('change'),
36033         state,
36034         fields,
36035         preset,
36036         tags,
36037         id;
36038
36039     function UIField(field, entity, show) {
36040         field = _.clone(field);
36041
36042         field.input = iD.ui.preset[field.type](field, context)
36043             .on('change', event.change);
36044
36045         if (field.input.entity) field.input.entity(entity);
36046
36047         field.keys = field.keys || [field.key];
36048
36049         field.show = show;
36050
36051         field.shown = function() {
36052             return field.id === 'name' || field.show || _.any(field.keys, function(key) { return !!tags[key]; });
36053         };
36054
36055         field.modified = function() {
36056             var original = context.graph().base().entities[entity.id];
36057             return _.any(field.keys, function(key) {
36058                 return original ? tags[key] !== original.tags[key] : tags[key];
36059             });
36060         };
36061
36062         field.revert = function() {
36063             var original = context.graph().base().entities[entity.id],
36064                 t = {};
36065             field.keys.forEach(function(key) {
36066                 t[key] = original ? original.tags[key] : undefined;
36067             });
36068             return t;
36069         };
36070
36071         field.present = function() {
36072             return _.any(field.keys, function(key) {
36073                 return tags[key];
36074             });
36075         };
36076
36077         field.remove = function() {
36078             var t = {};
36079             field.keys.forEach(function(key) {
36080                 t[key] = undefined;
36081             });
36082             return t;
36083         };
36084
36085         return field;
36086     }
36087
36088     function fieldKey(field) {
36089         return field.id;
36090     }
36091
36092     function presets(selection) {
36093         selection.call(iD.ui.Disclosure()
36094             .title(t('inspector.all_fields'))
36095             .expanded(context.storage('preset_fields.expanded') !== 'false')
36096             .on('toggled', toggled)
36097             .content(content));
36098
36099         function toggled(expanded) {
36100             context.storage('preset_fields.expanded', expanded);
36101         }
36102     }
36103
36104     function content(selection) {
36105         if (!fields) {
36106             var entity = context.entity(id),
36107                 geometry = context.geometry(id);
36108
36109             fields = [UIField(context.presets().field('name'), entity)];
36110
36111             preset.fields.forEach(function(field) {
36112                 if (field.matchGeometry(geometry)) {
36113                     fields.push(UIField(field, entity, true));
36114                 }
36115             });
36116
36117             if (entity.isHighwayIntersection(context.graph())) {
36118                 fields.push(UIField(context.presets().field('restrictions'), entity, true));
36119             }
36120
36121             context.presets().universal().forEach(function(field) {
36122                 if (preset.fields.indexOf(field) < 0) {
36123                     fields.push(UIField(field, entity));
36124                 }
36125             });
36126         }
36127
36128         var shown = fields.filter(function(field) { return field.shown(); }),
36129             notShown = fields.filter(function(field) { return !field.shown(); });
36130
36131         var $form = selection.selectAll('.preset-form')
36132             .data([0]);
36133
36134         $form.enter().append('div')
36135             .attr('class', 'preset-form inspector-inner fillL3');
36136
36137         var $fields = $form.selectAll('.form-field')
36138             .data(shown, fieldKey);
36139
36140         // Enter
36141
36142         var $enter = $fields.enter()
36143             .append('div')
36144             .attr('class', function(field) {
36145                 return 'form-field form-field-' + field.id;
36146             });
36147
36148         var $label = $enter.append('label')
36149             .attr('class', 'form-label')
36150             .attr('for', function(field) { return 'preset-input-' + field.id; })
36151             .text(function(field) { return field.label(); });
36152
36153         var wrap = $label.append('div')
36154             .attr('class', 'form-label-button-wrap');
36155
36156         wrap.append('button')
36157             .attr('class', 'remove-icon')
36158             .attr('tabindex', -1)
36159             .call(iD.svg.Icon('#operation-delete'));
36160
36161         wrap.append('button')
36162             .attr('class', 'modified-icon')
36163             .attr('tabindex', -1)
36164             .call(iD.svg.Icon('#icon-undo'));
36165
36166         // Update
36167
36168         $fields.select('.form-label-button-wrap .remove-icon')
36169             .on('click', remove);
36170
36171         $fields.select('.modified-icon')
36172             .on('click', revert);
36173
36174         $fields
36175             .order()
36176             .classed('modified', function(field) {
36177                 return field.modified();
36178             })
36179             .classed('present', function(field) {
36180                 return field.present();
36181             })
36182             .each(function(field) {
36183                 var reference = iD.ui.TagReference(field.reference || {key: field.key}, context);
36184
36185                 if (state === 'hover') {
36186                     reference.showing(false);
36187                 }
36188
36189                 d3.select(this)
36190                     .call(field.input)
36191                     .selectAll('input')
36192                     .on('keydown', function() {
36193                         // if user presses enter, and combobox is not active, accept edits..
36194                         if (d3.event.keyCode === 13 && d3.select('.combobox').empty()) {
36195                             context.enter(iD.modes.Browse(context));
36196                         }
36197                     })
36198                     .call(reference.body)
36199                     .select('.form-label-button-wrap')
36200                     .call(reference.button);
36201
36202                 field.input.tags(tags);
36203             });
36204
36205         $fields.exit()
36206             .remove();
36207
36208         notShown = notShown.map(function(field) {
36209             return {
36210                 title: field.label(),
36211                 value: field.label(),
36212                 field: field
36213             };
36214         });
36215
36216         var $more = selection.selectAll('.more-fields')
36217             .data((notShown.length > 0) ? [0] : []);
36218
36219         $more.enter().append('div')
36220             .attr('class', 'more-fields')
36221             .append('label')
36222                 .text(t('inspector.add_fields'));
36223
36224         var $input = $more.selectAll('.value')
36225             .data([0]);
36226
36227         $input.enter().append('input')
36228             .attr('class', 'value')
36229             .attr('type', 'text');
36230
36231         $input.value('')
36232             .attr('placeholder', function() {
36233                 var placeholder = [];
36234                 for (var field in notShown) {
36235                     placeholder.push(notShown[field].title);
36236                 }
36237                 return placeholder.slice(0,3).join(', ') + ((placeholder.length > 3) ? '…' : '');
36238             })
36239             .call(d3.combobox().data(notShown)
36240                 .minItems(1)
36241                 .on('accept', show));
36242
36243         $more.exit()
36244             .remove();
36245
36246         $input.exit()
36247             .remove();
36248
36249         function show(field) {
36250             field = field.field;
36251             field.show = true;
36252             content(selection);
36253             field.input.focus();
36254         }
36255
36256         function revert(field) {
36257             d3.event.stopPropagation();
36258             d3.event.preventDefault();
36259             event.change(field.revert());
36260         }
36261
36262         function remove(field) {
36263             d3.event.stopPropagation();
36264             d3.event.preventDefault();
36265             event.change(field.remove());
36266         }
36267     }
36268
36269     presets.preset = function(_) {
36270         if (!arguments.length) return preset;
36271         if (preset && preset.id === _.id) return presets;
36272         preset = _;
36273         fields = null;
36274         return presets;
36275     };
36276
36277     presets.state = function(_) {
36278         if (!arguments.length) return state;
36279         state = _;
36280         return presets;
36281     };
36282
36283     presets.tags = function(_) {
36284         if (!arguments.length) return tags;
36285         tags = _;
36286         // Don't reset fields here.
36287         return presets;
36288     };
36289
36290     presets.entityID = function(_) {
36291         if (!arguments.length) return id;
36292         if (id === _) return presets;
36293         id = _;
36294         fields = null;
36295         return presets;
36296     };
36297
36298     return d3.rebind(presets, event, 'on');
36299 };
36300 iD.ui.PresetList = function(context) {
36301     var event = d3.dispatch('choose'),
36302         id,
36303         currentPreset,
36304         autofocus = false;
36305
36306     function presetList(selection) {
36307         var geometry = context.geometry(id),
36308             presets = context.presets().matchGeometry(geometry);
36309
36310         selection.html('');
36311
36312         var messagewrap = selection.append('div')
36313             .attr('class', 'header fillL cf');
36314
36315         var message = messagewrap.append('h3')
36316             .text(t('inspector.choose'));
36317
36318         if (context.entity(id).isUsed(context.graph())) {
36319             messagewrap.append('button')
36320                 .attr('class', 'preset-choose')
36321                 .on('click', function() { event.choose(currentPreset); })
36322                 .append('span')
36323                 .html('&#9658;');
36324         } else {
36325             messagewrap.append('button')
36326                 .attr('class', 'close')
36327                 .on('click', function() {
36328                     context.enter(iD.modes.Browse(context));
36329                 })
36330                 .call(iD.svg.Icon('#icon-close'));
36331         }
36332
36333         function keydown() {
36334             // hack to let delete shortcut work when search is autofocused
36335             if (search.property('value').length === 0 &&
36336                 (d3.event.keyCode === d3.keybinding.keyCodes['⌫'] ||
36337                  d3.event.keyCode === d3.keybinding.keyCodes['⌦'])) {
36338                 d3.event.preventDefault();
36339                 d3.event.stopPropagation();
36340                 iD.operations.Delete([id], context)();
36341             } else if (search.property('value').length === 0 &&
36342                 (d3.event.ctrlKey || d3.event.metaKey) &&
36343                 d3.event.keyCode === d3.keybinding.keyCodes.z) {
36344                 d3.event.preventDefault();
36345                 d3.event.stopPropagation();
36346                 context.undo();
36347             } else if (!d3.event.ctrlKey && !d3.event.metaKey) {
36348                 d3.select(this).on('keydown', null);
36349             }
36350         }
36351
36352         function keypress() {
36353             // enter
36354             var value = search.property('value');
36355             if (d3.event.keyCode === 13 && value.length) {
36356                 list.selectAll('.preset-list-item:first-child').datum().choose();
36357             }
36358         }
36359
36360         function inputevent() {
36361             var value = search.property('value');
36362             list.classed('filtered', value.length);
36363             if (value.length) {
36364                 var results = presets.search(value, geometry);
36365                 message.text(t('inspector.results', {
36366                     n: results.collection.length,
36367                     search: value
36368                 }));
36369                 list.call(drawList, results);
36370             } else {
36371                 list.call(drawList, context.presets().defaults(geometry, 36));
36372                 message.text(t('inspector.choose'));
36373             }
36374         }
36375
36376         var searchWrap = selection.append('div')
36377             .attr('class', 'search-header');
36378
36379         var search = searchWrap.append('input')
36380             .attr('class', 'preset-search-input')
36381             .attr('placeholder', t('inspector.search'))
36382             .attr('type', 'search')
36383             .on('keydown', keydown)
36384             .on('keypress', keypress)
36385             .on('input', inputevent);
36386
36387         searchWrap
36388             .call(iD.svg.Icon('#icon-search', 'pre-text'));
36389
36390         if (autofocus) {
36391             search.node().focus();
36392         }
36393
36394         var listWrap = selection.append('div')
36395             .attr('class', 'inspector-body');
36396
36397         var list = listWrap.append('div')
36398             .attr('class', 'preset-list fillL cf')
36399             .call(drawList, context.presets().defaults(geometry, 36));
36400     }
36401
36402     function drawList(list, presets) {
36403         var collection = presets.collection.map(function(preset) {
36404             return preset.members ? CategoryItem(preset) : PresetItem(preset);
36405         });
36406
36407         var items = list.selectAll('.preset-list-item')
36408             .data(collection, function(d) { return d.preset.id; });
36409
36410         items.enter().append('div')
36411             .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
36412             .classed('current', function(item) { return item.preset === currentPreset; })
36413             .each(function(item) {
36414                 d3.select(this).call(item);
36415             })
36416             .style('opacity', 0)
36417             .transition()
36418             .style('opacity', 1);
36419
36420         items.order();
36421
36422         items.exit()
36423             .remove();
36424     }
36425
36426     function CategoryItem(preset) {
36427         var box, sublist, shown = false;
36428
36429         function item(selection) {
36430             var wrap = selection.append('div')
36431                 .attr('class', 'preset-list-button-wrap category col12');
36432
36433             wrap.append('button')
36434                 .attr('class', 'preset-list-button')
36435                 .classed('expanded', false)
36436                 .call(iD.ui.PresetIcon()
36437                     .geometry(context.geometry(id))
36438                     .preset(preset))
36439                 .on('click', function() {
36440                     var isExpanded = d3.select(this).classed('expanded');
36441                     var triangle = isExpanded ? '▶ ' :  '▼ ';
36442                     d3.select(this).classed('expanded', !isExpanded);
36443                     d3.select(this).selectAll('.label').text(triangle + preset.name());
36444                     item.choose();
36445                 })
36446                 .append('div')
36447                 .attr('class', 'label')
36448                 .text(function() {
36449                   return '▶ ' + preset.name();
36450                 });
36451
36452             box = selection.append('div')
36453                 .attr('class', 'subgrid col12')
36454                 .style('max-height', '0px')
36455                 .style('opacity', 0);
36456
36457             box.append('div')
36458                 .attr('class', 'arrow');
36459
36460             sublist = box.append('div')
36461                 .attr('class', 'preset-list fillL3 cf fl');
36462         }
36463
36464         item.choose = function() {
36465             if (!box || !sublist) return;
36466
36467             if (shown) {
36468                 shown = false;
36469                 box.transition()
36470                     .duration(200)
36471                     .style('opacity', '0')
36472                     .style('max-height', '0px')
36473                     .style('padding-bottom', '0px');
36474             } else {
36475                 shown = true;
36476                 sublist.call(drawList, preset.members);
36477                 box.transition()
36478                     .duration(200)
36479                     .style('opacity', '1')
36480                     .style('max-height', 200 + preset.members.collection.length * 80 + 'px')
36481                     .style('padding-bottom', '20px');
36482             }
36483         };
36484
36485         item.preset = preset;
36486
36487         return item;
36488     }
36489
36490     function PresetItem(preset) {
36491         function item(selection) {
36492             var wrap = selection.append('div')
36493                 .attr('class', 'preset-list-button-wrap col12');
36494
36495             wrap.append('button')
36496                 .attr('class', 'preset-list-button')
36497                 .call(iD.ui.PresetIcon()
36498                     .geometry(context.geometry(id))
36499                     .preset(preset))
36500                 .on('click', item.choose)
36501                 .append('div')
36502                 .attr('class', 'label')
36503                 .text(preset.name());
36504
36505             wrap.call(item.reference.button);
36506             selection.call(item.reference.body);
36507         }
36508
36509         item.choose = function() {
36510             context.presets().choose(preset);
36511
36512             context.perform(
36513                 iD.actions.ChangePreset(id, currentPreset, preset),
36514                 t('operations.change_tags.annotation'));
36515
36516             event.choose(preset);
36517         };
36518
36519         item.help = function() {
36520             d3.event.stopPropagation();
36521             item.reference.toggle();
36522         };
36523
36524         item.preset = preset;
36525         item.reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context);
36526
36527         return item;
36528     }
36529
36530     presetList.autofocus = function(_) {
36531         if (!arguments.length) return autofocus;
36532         autofocus = _;
36533         return presetList;
36534     };
36535
36536     presetList.entityID = function(_) {
36537         if (!arguments.length) return id;
36538         id = _;
36539         presetList.preset(context.presets().match(context.entity(id), context.graph()));
36540         return presetList;
36541     };
36542
36543     presetList.preset = function(_) {
36544         if (!arguments.length) return currentPreset;
36545         currentPreset = _;
36546         return presetList;
36547     };
36548
36549     return d3.rebind(presetList, event, 'on');
36550 };
36551 iD.ui.RadialMenu = function(context, operations) {
36552     var menu,
36553         center = [0, 0],
36554         tooltip;
36555
36556     var radialMenu = function(selection) {
36557         if (!operations.length)
36558             return;
36559
36560         selection.node().parentNode.focus();
36561
36562         function click(operation) {
36563             d3.event.stopPropagation();
36564             if (operation.disabled())
36565                 return;
36566             operation();
36567             radialMenu.close();
36568         }
36569
36570         menu = selection.append('g')
36571             .attr('class', 'radial-menu')
36572             .attr('transform', 'translate(' + center + ')')
36573             .attr('opacity', 0);
36574
36575         menu.transition()
36576             .attr('opacity', 1);
36577
36578         var r = 50,
36579             a = Math.PI / 4,
36580             a0 = -Math.PI / 4,
36581             a1 = a0 + (operations.length - 1) * a;
36582
36583         menu.append('path')
36584             .attr('class', 'radial-menu-background')
36585             .attr('d', 'M' + r * Math.sin(a0) + ',' +
36586                              r * Math.cos(a0) +
36587                       ' A' + r + ',' + r + ' 0 ' + (operations.length > 5 ? '1' : '0') + ',0 ' +
36588                              (r * Math.sin(a1) + 1e-3) + ',' +
36589                              (r * Math.cos(a1) + 1e-3)) // Force positive-length path (#1305)
36590             .attr('stroke-width', 50)
36591             .attr('stroke-linecap', 'round');
36592
36593         var button = menu.selectAll()
36594             .data(operations)
36595             .enter()
36596             .append('g')
36597             .attr('class', function(d) { return 'radial-menu-item radial-menu-item-' + d.id; })
36598             .classed('disabled', function(d) { return d.disabled(); })
36599             .attr('transform', function(d, i) {
36600                 return 'translate(' + iD.geo.roundCoords([
36601                         r * Math.sin(a0 + i * a),
36602                         r * Math.cos(a0 + i * a)]).join(',') + ')';
36603             });
36604
36605         button.append('circle')
36606             .attr('r', 15)
36607             .on('click', click)
36608             .on('mousedown', mousedown)
36609             .on('mouseover', mouseover)
36610             .on('mouseout', mouseout);
36611
36612         button.append('use')
36613             .attr('transform', 'translate(-10,-10)')
36614             .attr('width', '20')
36615             .attr('height', '20')
36616             .attr('xlink:href', function(d) { return '#operation-' + d.id; });
36617
36618         tooltip = d3.select(document.body)
36619             .append('div')
36620             .attr('class', 'tooltip-inner radial-menu-tooltip');
36621
36622         function mousedown() {
36623             d3.event.stopPropagation(); // https://github.com/openstreetmap/iD/issues/1869
36624         }
36625
36626         function mouseover(d, i) {
36627             var rect = context.surfaceRect(),
36628                 angle = a0 + i * a,
36629                 top = rect.top + (r + 25) * Math.cos(angle) + center[1] + 'px',
36630                 left = rect.left + (r + 25) * Math.sin(angle) + center[0] + 'px',
36631                 bottom = rect.height - (r + 25) * Math.cos(angle) - center[1] + 'px',
36632                 right = rect.width - (r + 25) * Math.sin(angle) - center[0] + 'px';
36633
36634             tooltip
36635                 .style('top', null)
36636                 .style('left', null)
36637                 .style('bottom', null)
36638                 .style('right', null)
36639                 .style('display', 'block')
36640                 .html(iD.ui.tooltipHtml(d.tooltip(), d.keys[0]));
36641
36642             if (i === 0) {
36643                 tooltip
36644                     .style('right', right)
36645                     .style('top', top);
36646             } else if (i >= 4) {
36647                 tooltip
36648                     .style('left', left)
36649                     .style('bottom', bottom);
36650             } else {
36651                 tooltip
36652                     .style('left', left)
36653                     .style('top', top);
36654             }
36655         }
36656
36657         function mouseout() {
36658             tooltip.style('display', 'none');
36659         }
36660     };
36661
36662     radialMenu.close = function() {
36663         if (menu) {
36664             menu
36665                 .style('pointer-events', 'none')
36666                 .transition()
36667                 .attr('opacity', 0)
36668                 .remove();
36669         }
36670
36671         if (tooltip) {
36672             tooltip.remove();
36673         }
36674     };
36675
36676     radialMenu.center = function(_) {
36677         if (!arguments.length) return center;
36678         center = _;
36679         return radialMenu;
36680     };
36681
36682     return radialMenu;
36683 };
36684 iD.ui.RawMemberEditor = function(context) {
36685     var id;
36686
36687     function selectMember(d) {
36688         d3.event.preventDefault();
36689         context.enter(iD.modes.Select(context, [d.id]));
36690     }
36691
36692     function changeRole(d) {
36693         var role = d3.select(this).property('value');
36694         var member = {id: d.id, type: d.type, role: role};
36695         context.perform(
36696             iD.actions.ChangeMember(d.relation.id, member, d.index),
36697             t('operations.change_role.annotation'));
36698     }
36699
36700     function deleteMember(d) {
36701         context.perform(
36702             iD.actions.DeleteMember(d.relation.id, d.index),
36703             t('operations.delete_member.annotation'));
36704
36705         if (!context.hasEntity(d.relation.id)) {
36706             context.enter(iD.modes.Browse(context));
36707         }
36708     }
36709
36710     function rawMemberEditor(selection) {
36711         var entity = context.entity(id),
36712             memberships = [];
36713
36714         entity.members.forEach(function(member, index) {
36715             memberships.push({
36716                 index: index,
36717                 id: member.id,
36718                 type: member.type,
36719                 role: member.role,
36720                 relation: entity,
36721                 member: context.hasEntity(member.id)
36722             });
36723         });
36724
36725         selection.call(iD.ui.Disclosure()
36726             .title(t('inspector.all_members') + ' (' + memberships.length + ')')
36727             .expanded(true)
36728             .on('toggled', toggled)
36729             .content(content));
36730
36731         function toggled(expanded) {
36732             if (expanded) {
36733                 selection.node().parentNode.scrollTop += 200;
36734             }
36735         }
36736
36737         function content($wrap) {
36738             var $list = $wrap.selectAll('.member-list')
36739                 .data([0]);
36740
36741             $list.enter().append('ul')
36742                 .attr('class', 'member-list');
36743
36744             var $items = $list.selectAll('li')
36745                 .data(memberships, function(d) {
36746                     return iD.Entity.key(d.relation) + ',' + d.index + ',' +
36747                         (d.member ? iD.Entity.key(d.member) : 'incomplete');
36748                 });
36749
36750             var $enter = $items.enter().append('li')
36751                 .attr('class', 'member-row form-field')
36752                 .classed('member-incomplete', function(d) { return !d.member; });
36753
36754             $enter.each(function(d) {
36755                 if (d.member) {
36756                     var $label = d3.select(this).append('label')
36757                         .attr('class', 'form-label')
36758                         .append('a')
36759                         .attr('href', '#')
36760                         .on('click', selectMember);
36761
36762                     $label.append('span')
36763                         .attr('class', 'member-entity-type')
36764                         .text(function(d) { return context.presets().match(d.member, context.graph()).name(); });
36765
36766                     $label.append('span')
36767                         .attr('class', 'member-entity-name')
36768                         .text(function(d) { return iD.util.displayName(d.member); });
36769
36770                 } else {
36771                     d3.select(this).append('label')
36772                         .attr('class', 'form-label')
36773                         .text(t('inspector.incomplete'));
36774                 }
36775             });
36776
36777             $enter.append('input')
36778                 .attr('class', 'member-role')
36779                 .property('type', 'text')
36780                 .attr('maxlength', 255)
36781                 .attr('placeholder', t('inspector.role'))
36782                 .property('value', function(d) { return d.role; })
36783                 .on('change', changeRole);
36784
36785             $enter.append('button')
36786                 .attr('tabindex', -1)
36787                 .attr('class', 'remove button-input-action member-delete minor')
36788                 .on('click', deleteMember)
36789                 .call(iD.svg.Icon('#operation-delete'));
36790
36791             $items.exit()
36792                 .remove();
36793         }
36794     }
36795
36796     rawMemberEditor.entityID = function(_) {
36797         if (!arguments.length) return id;
36798         id = _;
36799         return rawMemberEditor;
36800     };
36801
36802     return rawMemberEditor;
36803 };
36804 iD.ui.RawMembershipEditor = function(context) {
36805     var id, showBlank;
36806
36807     function selectRelation(d) {
36808         d3.event.preventDefault();
36809         context.enter(iD.modes.Select(context, [d.relation.id]));
36810     }
36811
36812     function changeRole(d) {
36813         var role = d3.select(this).property('value');
36814         context.perform(
36815             iD.actions.ChangeMember(d.relation.id, _.extend({}, d.member, {role: role}), d.index),
36816             t('operations.change_role.annotation'));
36817     }
36818
36819     function addMembership(d, role) {
36820         showBlank = false;
36821
36822         if (d.relation) {
36823             context.perform(
36824                 iD.actions.AddMember(d.relation.id, {id: id, type: context.entity(id).type, role: role}),
36825                 t('operations.add_member.annotation'));
36826
36827         } else {
36828             var relation = iD.Relation();
36829
36830             context.perform(
36831                 iD.actions.AddEntity(relation),
36832                 iD.actions.AddMember(relation.id, {id: id, type: context.entity(id).type, role: role}),
36833                 t('operations.add.annotation.relation'));
36834
36835             context.enter(iD.modes.Select(context, [relation.id]));
36836         }
36837     }
36838
36839     function deleteMembership(d) {
36840         context.perform(
36841             iD.actions.DeleteMember(d.relation.id, d.index),
36842             t('operations.delete_member.annotation'));
36843     }
36844
36845     function relations(q) {
36846         var newRelation = {
36847                 relation: null,
36848                 value: t('inspector.new_relation')
36849             },
36850             result = [],
36851             graph = context.graph();
36852
36853         context.intersects(context.extent()).forEach(function(entity) {
36854             if (entity.type !== 'relation' || entity.id === id)
36855                 return;
36856
36857             var presetName = context.presets().match(entity, graph).name(),
36858                 entityName = iD.util.displayName(entity) || '';
36859
36860             var value = presetName + ' ' + entityName;
36861             if (q && value.toLowerCase().indexOf(q.toLowerCase()) === -1)
36862                 return;
36863
36864             result.push({
36865                 relation: entity,
36866                 value: value
36867             });
36868         });
36869
36870         result.sort(function(a, b) {
36871             return iD.Relation.creationOrder(a.relation, b.relation);
36872         });
36873
36874         // Dedupe identical names by appending relation id - see #2891
36875         var dupeGroups = _(result)
36876             .groupBy('value')
36877             .filter(function(v) { return v.length > 1; })
36878             .value();
36879
36880         dupeGroups.forEach(function(group) {
36881             group.forEach(function(obj) {
36882                 obj.value += ' ' + obj.relation.id;
36883             });
36884         });
36885
36886         result.unshift(newRelation);
36887
36888         return result;
36889     }
36890
36891     function rawMembershipEditor(selection) {
36892         var entity = context.entity(id),
36893             memberships = [];
36894
36895         context.graph().parentRelations(entity).forEach(function(relation) {
36896             relation.members.forEach(function(member, index) {
36897                 if (member.id === entity.id) {
36898                     memberships.push({relation: relation, member: member, index: index});
36899                 }
36900             });
36901         });
36902
36903         selection.call(iD.ui.Disclosure()
36904             .title(t('inspector.all_relations') + ' (' + memberships.length + ')')
36905             .expanded(true)
36906             .on('toggled', toggled)
36907             .content(content));
36908
36909         function toggled(expanded) {
36910             if (expanded) {
36911                 selection.node().parentNode.scrollTop += 200;
36912             }
36913         }
36914
36915         function content($wrap) {
36916             var $list = $wrap.selectAll('.member-list')
36917                 .data([0]);
36918
36919             $list.enter().append('ul')
36920                 .attr('class', 'member-list');
36921
36922             var $items = $list.selectAll('li.member-row-normal')
36923                 .data(memberships, function(d) { return iD.Entity.key(d.relation) + ',' + d.index; });
36924
36925             var $enter = $items.enter().append('li')
36926                 .attr('class', 'member-row member-row-normal form-field');
36927
36928             var $label = $enter.append('label')
36929                 .attr('class', 'form-label')
36930                 .append('a')
36931                 .attr('href', '#')
36932                 .on('click', selectRelation);
36933
36934             $label.append('span')
36935                 .attr('class', 'member-entity-type')
36936                 .text(function(d) { return context.presets().match(d.relation, context.graph()).name(); });
36937
36938             $label.append('span')
36939                 .attr('class', 'member-entity-name')
36940                 .text(function(d) { return iD.util.displayName(d.relation); });
36941
36942             $enter.append('input')
36943                 .attr('class', 'member-role')
36944                 .property('type', 'text')
36945                 .attr('maxlength', 255)
36946                 .attr('placeholder', t('inspector.role'))
36947                 .property('value', function(d) { return d.member.role; })
36948                 .on('change', changeRole);
36949
36950             $enter.append('button')
36951                 .attr('tabindex', -1)
36952                 .attr('class', 'remove button-input-action member-delete minor')
36953                 .on('click', deleteMembership)
36954                 .call(iD.svg.Icon('#operation-delete'));
36955
36956             $items.exit()
36957                 .remove();
36958
36959             if (showBlank) {
36960                 var $new = $list.selectAll('.member-row-new')
36961                     .data([0]);
36962
36963                 $enter = $new.enter().append('li')
36964                     .attr('class', 'member-row member-row-new form-field');
36965
36966                 $enter.append('input')
36967                     .attr('type', 'text')
36968                     .attr('class', 'member-entity-input')
36969                     .call(d3.combobox()
36970                         .minItems(1)
36971                         .fetcher(function(value, callback) {
36972                             callback(relations(value));
36973                         })
36974                         .on('accept', function(d) {
36975                             addMembership(d, $new.select('.member-role').property('value'));
36976                         }));
36977
36978                 $enter.append('input')
36979                     .attr('class', 'member-role')
36980                     .property('type', 'text')
36981                     .attr('maxlength', 255)
36982                     .attr('placeholder', t('inspector.role'))
36983                     .on('change', changeRole);
36984
36985                 $enter.append('button')
36986                     .attr('tabindex', -1)
36987                     .attr('class', 'remove button-input-action member-delete minor')
36988                     .on('click', deleteMembership)
36989                     .call(iD.svg.Icon('#operation-delete'));
36990
36991             } else {
36992                 $list.selectAll('.member-row-new')
36993                     .remove();
36994             }
36995
36996             var $add = $wrap.selectAll('.add-relation')
36997                 .data([0]);
36998
36999             $add.enter()
37000                 .append('button')
37001                 .attr('class', 'add-relation')
37002                 .call(iD.svg.Icon('#icon-plus', 'light'));
37003
37004             $wrap.selectAll('.add-relation')
37005                 .on('click', function() {
37006                     showBlank = true;
37007                     content($wrap);
37008                     $list.selectAll('.member-entity-input').node().focus();
37009                 });
37010         }
37011     }
37012
37013     rawMembershipEditor.entityID = function(_) {
37014         if (!arguments.length) return id;
37015         id = _;
37016         return rawMembershipEditor;
37017     };
37018
37019     return rawMembershipEditor;
37020 };
37021 iD.ui.RawTagEditor = function(context) {
37022     var event = d3.dispatch('change'),
37023         showBlank = false,
37024         state,
37025         preset,
37026         tags,
37027         id;
37028
37029     function rawTagEditor(selection) {
37030         var count = Object.keys(tags).filter(function(d) { return d; }).length;
37031
37032         selection.call(iD.ui.Disclosure()
37033             .title(t('inspector.all_tags') + ' (' + count + ')')
37034             .expanded(context.storage('raw_tag_editor.expanded') === 'true' || preset.isFallback())
37035             .on('toggled', toggled)
37036             .content(content));
37037
37038         function toggled(expanded) {
37039             context.storage('raw_tag_editor.expanded', expanded);
37040             if (expanded) {
37041                 selection.node().parentNode.scrollTop += 200;
37042             }
37043         }
37044     }
37045
37046     function content($wrap) {
37047         var entries = d3.entries(tags);
37048
37049         if (!entries.length || showBlank) {
37050             showBlank = false;
37051             entries.push({key: '', value: ''});
37052         }
37053
37054         var $list = $wrap.selectAll('.tag-list')
37055             .data([0]);
37056
37057         $list.enter().append('ul')
37058             .attr('class', 'tag-list');
37059
37060         var $newTag = $wrap.selectAll('.add-tag')
37061             .data([0]);
37062
37063         $newTag.enter()
37064             .append('button')
37065             .attr('class', 'add-tag')
37066             .call(iD.svg.Icon('#icon-plus', 'light'));
37067
37068         $newTag.on('click', addTag);
37069
37070         var $items = $list.selectAll('li')
37071             .data(entries, function(d) { return d.key; });
37072
37073         // Enter
37074
37075         var $enter = $items.enter().append('li')
37076             .attr('class', 'tag-row cf');
37077
37078         $enter.append('div')
37079             .attr('class', 'key-wrap')
37080             .append('input')
37081             .property('type', 'text')
37082             .attr('class', 'key')
37083             .attr('maxlength', 255);
37084
37085         $enter.append('div')
37086             .attr('class', 'input-wrap-position')
37087             .append('input')
37088             .property('type', 'text')
37089             .attr('class', 'value')
37090             .attr('maxlength', 255);
37091
37092         $enter.append('button')
37093             .attr('tabindex', -1)
37094             .attr('class', 'remove minor')
37095             .call(iD.svg.Icon('#operation-delete'));
37096
37097         if (context.taginfo()) {
37098             $enter.each(bindTypeahead);
37099         }
37100
37101         // Update
37102
37103         $items.order();
37104
37105         $items.each(function(tag) {
37106             var isRelation = (context.entity(id).type === 'relation'),
37107                 reference;
37108             if (isRelation && tag.key === 'type')
37109                 reference = iD.ui.TagReference({rtype: tag.value}, context);
37110             else
37111                 reference = iD.ui.TagReference({key: tag.key, value: tag.value}, context);
37112
37113             if (state === 'hover') {
37114                 reference.showing(false);
37115             }
37116
37117             d3.select(this)
37118                 .call(reference.button)
37119                 .call(reference.body);
37120         });
37121
37122         $items.select('input.key')
37123             .attr('title', function(d) { return d.key; })
37124             .value(function(d) { return d.key; })
37125             .on('blur', keyChange)
37126             .on('change', keyChange);
37127
37128         $items.select('input.value')
37129             .attr('title', function(d) { return d.value; })
37130             .value(function(d) { return d.value; })
37131             .on('blur', valueChange)
37132             .on('change', valueChange)
37133             .on('keydown.push-more', pushMore);
37134
37135         $items.select('button.remove')
37136             .on('click', removeTag);
37137
37138         $items.exit()
37139             .each(unbind)
37140             .remove();
37141
37142         function pushMore() {
37143             if (d3.event.keyCode === 9 && !d3.event.shiftKey &&
37144                 $list.selectAll('li:last-child input.value').node() === this) {
37145                 addTag();
37146             }
37147         }
37148
37149         function bindTypeahead() {
37150             var row = d3.select(this),
37151                 key = row.selectAll('input.key'),
37152                 value = row.selectAll('input.value');
37153
37154             function sort(value, data) {
37155                 var sameletter = [],
37156                     other = [];
37157                 for (var i = 0; i < data.length; i++) {
37158                     if (data[i].value.substring(0, value.length) === value) {
37159                         sameletter.push(data[i]);
37160                     } else {
37161                         other.push(data[i]);
37162                     }
37163                 }
37164                 return sameletter.concat(other);
37165             }
37166
37167             key.call(d3.combobox()
37168                 .fetcher(function(value, callback) {
37169                     context.taginfo().keys({
37170                         debounce: true,
37171                         geometry: context.geometry(id),
37172                         query: value
37173                     }, function(err, data) {
37174                         if (!err) callback(sort(value, data));
37175                     });
37176                 }));
37177
37178             value.call(d3.combobox()
37179                 .fetcher(function(value, callback) {
37180                     context.taginfo().values({
37181                         debounce: true,
37182                         key: key.value(),
37183                         geometry: context.geometry(id),
37184                         query: value
37185                     }, function(err, data) {
37186                         if (!err) callback(sort(value, data));
37187                     });
37188                 }));
37189         }
37190
37191         function unbind() {
37192             var row = d3.select(this);
37193
37194             row.selectAll('input.key')
37195                 .call(d3.combobox.off);
37196
37197             row.selectAll('input.value')
37198                 .call(d3.combobox.off);
37199         }
37200
37201         function keyChange(d) {
37202             var kOld = d.key,
37203                 kNew = this.value.trim(),
37204                 tag = {};
37205
37206             if (kNew && kNew !== kOld) {
37207                 var match = kNew.match(/^(.*?)(?:_(\d+))?$/),
37208                     base = match[1],
37209                     suffix = +(match[2] || 1);
37210                 while (tags[kNew]) {  // rename key if already in use
37211                     kNew = base + '_' + suffix++;
37212                 }
37213             }
37214             tag[kOld] = undefined;
37215             tag[kNew] = d.value;
37216             d.key = kNew; // Maintain DOM identity through the subsequent update.
37217             this.value = kNew;
37218             event.change(tag);
37219         }
37220
37221         function valueChange(d) {
37222             var tag = {};
37223             tag[d.key] = this.value;
37224             event.change(tag);
37225         }
37226
37227         function removeTag(d) {
37228             var tag = {};
37229             tag[d.key] = undefined;
37230             event.change(tag);
37231             d3.select(this.parentNode).remove();
37232         }
37233
37234         function addTag() {
37235             // Wrapped in a setTimeout in case it's being called from a blur
37236             // handler. Without the setTimeout, the call to `content` would
37237             // wipe out the pending value change.
37238             setTimeout(function() {
37239                 showBlank = true;
37240                 content($wrap);
37241                 $list.selectAll('li:last-child input.key').node().focus();
37242             }, 0);
37243         }
37244     }
37245
37246     rawTagEditor.state = function(_) {
37247         if (!arguments.length) return state;
37248         state = _;
37249         return rawTagEditor;
37250     };
37251
37252     rawTagEditor.preset = function(_) {
37253         if (!arguments.length) return preset;
37254         preset = _;
37255         return rawTagEditor;
37256     };
37257
37258     rawTagEditor.tags = function(_) {
37259         if (!arguments.length) return tags;
37260         tags = _;
37261         return rawTagEditor;
37262     };
37263
37264     rawTagEditor.entityID = function(_) {
37265         if (!arguments.length) return id;
37266         id = _;
37267         return rawTagEditor;
37268     };
37269
37270     return d3.rebind(rawTagEditor, event, 'on');
37271 };
37272 iD.ui.Restore = function(context) {
37273     return function(selection) {
37274         if (!context.history().lock() || !context.history().restorableChanges())
37275             return;
37276
37277         var modal = iD.ui.modal(selection, true);
37278
37279         modal.select('.modal')
37280             .attr('class', 'modal fillL col6');
37281
37282         var introModal = modal.select('.content');
37283
37284         introModal.attr('class','cf');
37285
37286         introModal.append('div')
37287             .attr('class', 'modal-section')
37288             .append('h3')
37289             .text(t('restore.heading'));
37290
37291         introModal.append('div')
37292             .attr('class','modal-section')
37293             .append('p')
37294             .text(t('restore.description'));
37295
37296         var buttonWrap = introModal.append('div')
37297             .attr('class', 'modal-actions cf');
37298
37299         var restore = buttonWrap.append('button')
37300             .attr('class', 'restore col6')
37301             .text(t('restore.restore'))
37302             .on('click', function() {
37303                 context.history().restore();
37304                 modal.remove();
37305             });
37306
37307         buttonWrap.append('button')
37308             .attr('class', 'reset col6')
37309             .text(t('restore.reset'))
37310             .on('click', function() {
37311                 context.history().clearSaved();
37312                 modal.remove();
37313             });
37314
37315         restore.node().focus();
37316     };
37317 };
37318 iD.ui.Save = function(context) {
37319     var history = context.history(),
37320         key = iD.ui.cmd('⌘S');
37321
37322     function saving() {
37323         return context.mode().id === 'save';
37324     }
37325
37326     function save() {
37327         d3.event.preventDefault();
37328         if (!context.inIntro() && !saving() && history.hasChanges()) {
37329             context.enter(iD.modes.Save(context));
37330         }
37331     }
37332
37333     return function(selection) {
37334         var tooltip = bootstrap.tooltip()
37335             .placement('bottom')
37336             .html(true)
37337             .title(iD.ui.tooltipHtml(t('save.no_changes'), key));
37338
37339         var button = selection.append('button')
37340             .attr('class', 'save col12 disabled')
37341             .attr('tabindex', -1)
37342             .on('click', save)
37343             .call(tooltip);
37344
37345         button.append('span')
37346             .attr('class', 'label')
37347             .text(t('save.title'));
37348
37349         button.append('span')
37350             .attr('class', 'count')
37351             .text('0');
37352
37353         var keybinding = d3.keybinding('undo-redo')
37354             .on(key, save, true);
37355
37356         d3.select(document)
37357             .call(keybinding);
37358
37359         var numChanges = 0;
37360
37361         context.history().on('change.save', function() {
37362             var _ = history.difference().summary().length;
37363             if (_ === numChanges)
37364                 return;
37365             numChanges = _;
37366
37367             tooltip.title(iD.ui.tooltipHtml(t(numChanges > 0 ?
37368                     'save.help' : 'save.no_changes'), key));
37369
37370             button
37371                 .classed('disabled', numChanges === 0)
37372                 .classed('has-count', numChanges > 0);
37373
37374             button.select('span.count')
37375                 .text(numChanges);
37376         });
37377
37378         context.on('enter.save', function() {
37379             button.property('disabled', saving());
37380             if (saving()) button.call(tooltip.hide);
37381         });
37382     };
37383 };
37384 iD.ui.Scale = function(context) {
37385     var projection = context.projection,
37386         maxLength = 180,
37387         tickHeight = 8;
37388
37389     function scaleDefs(loc1, loc2) {
37390         var lat = (loc2[1] + loc1[1]) / 2,
37391             imperial = (iD.detect().locale.toLowerCase() === 'en-us'),
37392             conversion = (imperial ? 3.28084 : 1),
37393             dist = iD.geo.lonToMeters(loc2[0] - loc1[0], lat) * conversion,
37394             scale = { dist: 0, px: 0, text: '' },
37395             buckets, i, val, dLon;
37396
37397         if (imperial) {
37398             buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
37399         } else {
37400             buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
37401         }
37402
37403         // determine a user-friendly endpoint for the scale
37404         for (i = 0; i < buckets.length; i++) {
37405             val = buckets[i];
37406             if (dist >= val) {
37407                 scale.dist = Math.floor(dist / val) * val;
37408                 break;
37409             }
37410         }
37411
37412         dLon = iD.geo.metersToLon(scale.dist / conversion, lat);
37413         scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
37414
37415         if (imperial) {
37416             if (scale.dist >= 5280) {
37417                 scale.dist /= 5280;
37418                 scale.text = String(scale.dist) + ' mi';
37419             } else {
37420                 scale.text = String(scale.dist) + ' ft';
37421             }
37422         } else {
37423             if (scale.dist >= 1000) {
37424                 scale.dist /= 1000;
37425                 scale.text = String(scale.dist) + ' km';
37426             } else {
37427                 scale.text = String(scale.dist) + ' m';
37428             }
37429         }
37430
37431         return scale;
37432     }
37433
37434     function update(selection) {
37435         // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
37436         var dims = context.map().dimensions(),
37437             loc1 = projection.invert([0, dims[1]]),
37438             loc2 = projection.invert([maxLength, dims[1]]),
37439             scale = scaleDefs(loc1, loc2);
37440
37441         selection.select('#scalepath')
37442             .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
37443
37444         selection.select('#scaletext')
37445             .attr('x', scale.px + 8)
37446             .attr('y', tickHeight)
37447             .text(scale.text);
37448     }
37449
37450     return function(selection) {
37451         var g = selection.append('svg')
37452             .attr('id', 'scale')
37453             .append('g')
37454             .attr('transform', 'translate(10,11)');
37455
37456         g.append('path').attr('id', 'scalepath');
37457         g.append('text').attr('id', 'scaletext');
37458
37459         update(selection);
37460
37461         context.map().on('move.scale', function() {
37462             update(selection);
37463         });
37464     };
37465 };
37466 iD.ui.SelectionList = function(context, selectedIDs) {
37467
37468     function selectEntity(entity) {
37469         context.enter(iD.modes.Select(context, [entity.id]).suppressMenu(true));
37470     }
37471
37472
37473     function selectionList(selection) {
37474         selection.classed('selection-list-pane', true);
37475
37476         var header = selection.append('div')
37477             .attr('class', 'header fillL cf');
37478
37479         header.append('h3')
37480             .text(t('inspector.multiselect'));
37481
37482         var listWrap = selection.append('div')
37483             .attr('class', 'inspector-body');
37484
37485         var list = listWrap.append('div')
37486             .attr('class', 'feature-list cf');
37487
37488         context.history().on('change.selection-list', drawList);
37489         drawList();
37490
37491         function drawList() {
37492             var entities = selectedIDs
37493                 .map(function(id) { return context.hasEntity(id); })
37494                 .filter(function(entity) { return entity; });
37495
37496             var items = list.selectAll('.feature-list-item')
37497                 .data(entities, iD.Entity.key);
37498
37499             var enter = items.enter().append('button')
37500                 .attr('class', 'feature-list-item')
37501                 .on('click', selectEntity);
37502
37503             // Enter
37504             var label = enter.append('div')
37505                 .attr('class', 'label')
37506                 .call(iD.svg.Icon('', 'pre-text'));
37507
37508             label.append('span')
37509                 .attr('class', 'entity-type');
37510
37511             label.append('span')
37512                 .attr('class', 'entity-name');
37513
37514             // Update
37515             items.selectAll('use')
37516                 .attr('href', function() {
37517                     var entity = this.parentNode.parentNode.__data__;
37518                     return '#icon-' + context.geometry(entity.id);
37519                 });
37520
37521             items.selectAll('.entity-type')
37522                 .text(function(entity) { return context.presets().match(entity, context.graph()).name(); });
37523
37524             items.selectAll('.entity-name')
37525                 .text(function(entity) { return iD.util.displayName(entity); });
37526
37527             // Exit
37528             items.exit()
37529                 .remove();
37530         }
37531     }
37532
37533     return selectionList;
37534
37535 };
37536 iD.ui.Sidebar = function(context) {
37537     var inspector = iD.ui.Inspector(context),
37538         current;
37539
37540     function sidebar(selection) {
37541         var featureListWrap = selection.append('div')
37542             .attr('class', 'feature-list-pane')
37543             .call(iD.ui.FeatureList(context));
37544
37545         selection.call(iD.ui.Notice(context));
37546
37547         var inspectorWrap = selection.append('div')
37548             .attr('class', 'inspector-hidden inspector-wrap fr');
37549
37550         function hover(id) {
37551             if (!current && context.hasEntity(id)) {
37552                 featureListWrap.classed('inspector-hidden', true);
37553                 inspectorWrap.classed('inspector-hidden', false)
37554                     .classed('inspector-hover', true);
37555
37556                 if (inspector.entityID() !== id || inspector.state() !== 'hover') {
37557                     inspector
37558                         .state('hover')
37559                         .entityID(id);
37560
37561                     inspectorWrap.call(inspector);
37562                 }
37563             } else if (!current) {
37564                 featureListWrap.classed('inspector-hidden', false);
37565                 inspectorWrap.classed('inspector-hidden', true);
37566                 inspector.state('hide');
37567             }
37568         }
37569
37570         sidebar.hover = _.throttle(hover, 200);
37571
37572         sidebar.select = function(id, newFeature) {
37573             if (!current && id) {
37574                 featureListWrap.classed('inspector-hidden', true);
37575                 inspectorWrap.classed('inspector-hidden', false)
37576                     .classed('inspector-hover', false);
37577
37578                 if (inspector.entityID() !== id || inspector.state() !== 'select') {
37579                     inspector
37580                         .state('select')
37581                         .entityID(id)
37582                         .newFeature(newFeature);
37583
37584                     inspectorWrap.call(inspector);
37585                 }
37586             } else if (!current) {
37587                 featureListWrap.classed('inspector-hidden', false);
37588                 inspectorWrap.classed('inspector-hidden', true);
37589                 inspector.state('hide');
37590             }
37591         };
37592
37593         sidebar.show = function(component) {
37594             featureListWrap.classed('inspector-hidden', true);
37595             inspectorWrap.classed('inspector-hidden', true);
37596             if (current) current.remove();
37597             current = selection.append('div')
37598                 .attr('class', 'sidebar-component')
37599                 .call(component);
37600         };
37601
37602         sidebar.hide = function() {
37603             featureListWrap.classed('inspector-hidden', false);
37604             inspectorWrap.classed('inspector-hidden', true);
37605             if (current) current.remove();
37606             current = null;
37607         };
37608     }
37609
37610     sidebar.hover = function() {};
37611     sidebar.hover.cancel = function() {};
37612     sidebar.select = function() {};
37613     sidebar.show = function() {};
37614     sidebar.hide = function() {};
37615
37616     return sidebar;
37617 };
37618 iD.ui.SourceSwitch = function(context) {
37619     var keys;
37620
37621     function click() {
37622         d3.event.preventDefault();
37623
37624         if (context.history().hasChanges() &&
37625             !window.confirm(t('source_switch.lose_changes'))) return;
37626
37627         var live = d3.select(this)
37628             .classed('live');
37629
37630         context.connection()
37631             .switch(live ? keys[1] : keys[0]);
37632
37633         context.enter(iD.modes.Browse(context));
37634         context.flush();
37635
37636         d3.select(this)
37637             .text(live ? t('source_switch.dev') : t('source_switch.live'))
37638             .classed('live', !live);
37639     }
37640
37641     var sourceSwitch = function(selection) {
37642         selection.append('a')
37643             .attr('href', '#')
37644             .text(t('source_switch.live'))
37645             .classed('live', true)
37646             .attr('tabindex', -1)
37647             .on('click', click);
37648     };
37649
37650     sourceSwitch.keys = function(_) {
37651         if (!arguments.length) return keys;
37652         keys = _;
37653         return sourceSwitch;
37654     };
37655
37656     return sourceSwitch;
37657 };
37658 iD.ui.Spinner = function(context) {
37659     var connection = context.connection();
37660
37661     return function(selection) {
37662         var img = selection.append('img')
37663             .attr('src', context.imagePath('loader-black.gif'))
37664             .style('opacity', 0);
37665
37666         connection.on('loading.spinner', function() {
37667             img.transition()
37668                 .style('opacity', 1);
37669         });
37670
37671         connection.on('loaded.spinner', function() {
37672             img.transition()
37673                 .style('opacity', 0);
37674         });
37675     };
37676 };
37677 iD.ui.Splash = function(context) {
37678     return function(selection) {
37679         if (context.storage('sawSplash'))
37680              return;
37681
37682         context.storage('sawSplash', true);
37683
37684         var modal = iD.ui.modal(selection);
37685
37686         modal.select('.modal')
37687             .attr('class', 'modal-splash modal col6');
37688
37689         var introModal = modal.select('.content')
37690             .append('div')
37691             .attr('class', 'fillL');
37692
37693         introModal.append('div')
37694             .attr('class','modal-section cf')
37695             .append('h3').text(t('splash.welcome'));
37696
37697         introModal.append('div')
37698             .attr('class','modal-section')
37699             .append('p')
37700             .html(t('splash.text', {
37701                 version: iD.version,
37702                 website: '<a href="http://ideditor.com/">ideditor.com</a>',
37703                 github: '<a href="https://github.com/openstreetmap/iD">github.com</a>'
37704             }));
37705
37706         var buttons = introModal.append('div').attr('class', 'modal-actions cf');
37707
37708         buttons.append('button')
37709             .attr('class', 'col6 walkthrough')
37710             .text(t('splash.walkthrough'))
37711             .on('click', function() {
37712                 d3.select(document.body).call(iD.ui.intro(context));
37713                 modal.close();
37714             });
37715
37716         buttons.append('button')
37717             .attr('class', 'col6 start')
37718             .text(t('splash.start'))
37719             .on('click', modal.close);
37720
37721         modal.select('button.close').attr('class','hide');
37722
37723     };
37724 };
37725 iD.ui.Status = function(context) {
37726     var connection = context.connection(),
37727         errCount = 0;
37728
37729     return function(selection) {
37730
37731         function update() {
37732
37733             connection.status(function(err, apiStatus) {
37734
37735                 selection.html('');
37736
37737                 if (err && errCount++ < 2) return;
37738
37739                 if (err) {
37740                     selection.text(t('status.error'));
37741
37742                 } else if (apiStatus === 'readonly') {
37743                     selection.text(t('status.readonly'));
37744
37745                 } else if (apiStatus === 'offline') {
37746                     selection.text(t('status.offline'));
37747                 }
37748
37749                 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
37750                 if (!err) errCount = 0;
37751
37752             });
37753         }
37754
37755         connection.on('auth', function() { update(selection); });
37756         window.setInterval(update, 90000);
37757         update(selection);
37758     };
37759 };
37760 iD.ui.Success = function(context) {
37761     var dispatch = d3.dispatch('cancel'),
37762         changeset;
37763
37764     function success(selection) {
37765         var message = (changeset.comment || t('success.edited_osm')).substring(0, 130) +
37766             ' ' + context.connection().changesetURL(changeset.id);
37767
37768         var header = selection.append('div')
37769             .attr('class', 'header fillL');
37770
37771         header.append('button')
37772             .attr('class', 'fr')
37773             .on('click', function() { dispatch.cancel(); })
37774             .call(iD.svg.Icon('#icon-close'));
37775
37776         header.append('h3')
37777             .text(t('success.just_edited'));
37778
37779         var body = selection.append('div')
37780             .attr('class', 'body save-success fillL');
37781
37782         body.append('p')
37783             .html(t('success.help_html'));
37784
37785         body.append('a')
37786             .attr('class', 'details')
37787             .attr('target', '_blank')
37788             .attr('tabindex', -1)
37789             .call(iD.svg.Icon('#icon-out-link', 'inline'))
37790             .attr('href', t('success.help_link_url'))
37791             .append('span')
37792             .text(t('success.help_link_text'));
37793
37794         var changesetURL = context.connection().changesetURL(changeset.id);
37795
37796         body.append('a')
37797             .attr('class', 'button col12 osm')
37798             .attr('target', '_blank')
37799             .attr('href', changesetURL)
37800             .text(t('success.view_on_osm'));
37801
37802         var sharing = {
37803             facebook: 'https://facebook.com/sharer/sharer.php?u=' + encodeURIComponent(changesetURL),
37804             twitter: 'https://twitter.com/intent/tweet?source=webclient&text=' + encodeURIComponent(message),
37805             google: 'https://plus.google.com/share?url=' + encodeURIComponent(changesetURL)
37806         };
37807
37808         body.selectAll('.button.social')
37809             .data(d3.entries(sharing))
37810             .enter()
37811             .append('a')
37812             .attr('class', 'button social col4')
37813             .attr('target', '_blank')
37814             .attr('href', function(d) { return d.value; })
37815             .call(bootstrap.tooltip()
37816                 .title(function(d) { return t('success.' + d.key); })
37817                 .placement('bottom'))
37818             .each(function(d) { d3.select(this).call(iD.svg.Icon('#logo-' + d.key, 'social')); });
37819     }
37820
37821     success.changeset = function(_) {
37822         if (!arguments.length) return changeset;
37823         changeset = _;
37824         return success;
37825     };
37826
37827     return d3.rebind(success, dispatch, 'on');
37828 };
37829 iD.ui.TagReference = function(tag, context) {
37830     var tagReference = {},
37831         button,
37832         body,
37833         loaded,
37834         showing;
37835
37836     function findLocal(data) {
37837         var locale = iD.detect().locale.toLowerCase(),
37838             localized;
37839
37840         localized = _.find(data, function(d) {
37841             return d.lang.toLowerCase() === locale;
37842         });
37843         if (localized) return localized;
37844
37845         // try the non-regional version of a language, like
37846         // 'en' if the language is 'en-US'
37847         if (locale.indexOf('-') !== -1) {
37848             var first = locale.split('-')[0];
37849             localized = _.find(data, function(d) {
37850                 return d.lang.toLowerCase() === first;
37851             });
37852             if (localized) return localized;
37853         }
37854
37855         // finally fall back to english
37856         return _.find(data, function(d) {
37857             return d.lang.toLowerCase() === 'en';
37858         });
37859     }
37860
37861     function load(param) {
37862         button.classed('tag-reference-loading', true);
37863
37864         context.taginfo().docs(param, function show(err, data) {
37865             var docs;
37866             if (!err && data) {
37867                 docs = findLocal(data);
37868             }
37869
37870             body.html('');
37871
37872             if (!docs || !docs.description) {
37873                 if (param.hasOwnProperty('value')) {
37874                     load(_.omit(param, 'value'));   // retry with key only
37875                 } else {
37876                     body.append('p').text(t('inspector.no_documentation_key'));
37877                     done();
37878                 }
37879                 return;
37880             }
37881
37882             if (docs.image && docs.image.thumb_url_prefix) {
37883                 body
37884                     .append('img')
37885                     .attr('class', 'wiki-image')
37886                     .attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix)
37887                     .on('load', function() { done(); })
37888                     .on('error', function() { d3.select(this).remove(); done(); });
37889             } else {
37890                 done();
37891             }
37892
37893             body
37894                 .append('p')
37895                 .text(docs.description);
37896
37897             body
37898                 .append('a')
37899                 .attr('target', '_blank')
37900                 .attr('tabindex', -1)
37901                 .attr('href', 'https://wiki.openstreetmap.org/wiki/' + docs.title)
37902                 .call(iD.svg.Icon('#icon-out-link', 'inline'))
37903                 .append('span')
37904                 .text(t('inspector.reference'));
37905         });
37906     }
37907
37908     function done() {
37909         loaded = true;
37910
37911         button.classed('tag-reference-loading', false);
37912
37913         body.transition()
37914             .duration(200)
37915             .style('max-height', '200px')
37916             .style('opacity', '1');
37917
37918         showing = true;
37919     }
37920
37921     function hide(selection) {
37922         selection = selection || body.transition().duration(200);
37923
37924         selection
37925             .style('max-height', '0px')
37926             .style('opacity', '0');
37927
37928         showing = false;
37929     }
37930
37931     tagReference.button = function(selection) {
37932         button = selection.selectAll('.tag-reference-button')
37933             .data([0]);
37934
37935         button.enter()
37936             .append('button')
37937             .attr('class', 'tag-reference-button')
37938             .attr('tabindex', -1)
37939             .call(iD.svg.Icon('#icon-inspect'));
37940
37941         button.on('click', function () {
37942             d3.event.stopPropagation();
37943             d3.event.preventDefault();
37944             if (showing) {
37945                 hide();
37946             } else if (loaded) {
37947                 done();
37948             } else {
37949                 if (context.taginfo()) {
37950                     load(tag);
37951                 }
37952             }
37953         });
37954     };
37955
37956     tagReference.body = function(selection) {
37957         body = selection.selectAll('.tag-reference-body')
37958             .data([0]);
37959
37960         body.enter().append('div')
37961             .attr('class', 'tag-reference-body cf')
37962             .style('max-height', '0')
37963             .style('opacity', '0');
37964
37965         if (showing === false) {
37966             hide(body);
37967         }
37968     };
37969
37970     tagReference.showing = function(_) {
37971         if (!arguments.length) return showing;
37972         showing = _;
37973         return tagReference;
37974     };
37975
37976     return tagReference;
37977 };
37978 // toggles the visibility of ui elements, using a combination of the
37979 // hide class, which sets display=none, and a d3 transition for opacity.
37980 // this will cause blinking when called repeatedly, so check that the
37981 // value actually changes between calls.
37982 iD.ui.Toggle = function(show, callback) {
37983     return function(selection) {
37984         selection
37985             .style('opacity', show ? 0 : 1)
37986             .classed('hide', false)
37987             .transition()
37988             .style('opacity', show ? 1 : 0)
37989             .each('end', function() {
37990                 d3.select(this)
37991                     .classed('hide', !show)
37992                     .style('opacity', null);
37993                 if (callback) callback.apply(this);
37994             });
37995     };
37996 };
37997 iD.ui.UndoRedo = function(context) {
37998     var commands = [{
37999         id: 'undo',
38000         cmd: iD.ui.cmd('⌘Z'),
38001         action: function() { if (!(context.inIntro() || saving())) context.undo(); },
38002         annotation: function() { return context.history().undoAnnotation(); }
38003     }, {
38004         id: 'redo',
38005         cmd: iD.ui.cmd('⌘⇧Z'),
38006         action: function() {if (!(context.inIntro() || saving())) context.redo(); },
38007         annotation: function() { return context.history().redoAnnotation(); }
38008     }];
38009
38010     function saving() {
38011         return context.mode().id === 'save';
38012     }
38013
38014     return function(selection) {
38015         var tooltip = bootstrap.tooltip()
38016             .placement('bottom')
38017             .html(true)
38018             .title(function (d) {
38019                 return iD.ui.tooltipHtml(d.annotation() ?
38020                     t(d.id + '.tooltip', {action: d.annotation()}) :
38021                     t(d.id + '.nothing'), d.cmd);
38022             });
38023
38024         var buttons = selection.selectAll('button')
38025             .data(commands)
38026             .enter().append('button')
38027             .attr('class', 'col6 disabled')
38028             .on('click', function(d) { return d.action(); })
38029             .call(tooltip);
38030
38031         buttons.each(function(d) {
38032             d3.select(this)
38033                 .call(iD.svg.Icon('#icon-' + d.id));
38034         });
38035
38036         var keybinding = d3.keybinding('undo')
38037             .on(commands[0].cmd, function() { d3.event.preventDefault(); commands[0].action(); })
38038             .on(commands[1].cmd, function() { d3.event.preventDefault(); commands[1].action(); });
38039
38040         d3.select(document)
38041             .call(keybinding);
38042
38043         context.history()
38044             .on('change.undo_redo', update);
38045
38046         context
38047             .on('enter.undo_redo', update);
38048
38049         function update() {
38050             buttons
38051                 .property('disabled', saving())
38052                 .classed('disabled', function(d) { return !d.annotation(); })
38053                 .each(function() {
38054                     var selection = d3.select(this);
38055                     if (selection.property('tooltipVisible')) {
38056                         selection.call(tooltip.show);
38057                     }
38058                 });
38059         }
38060     };
38061 };
38062 iD.ui.ViewOnOSM = function(context) {
38063     var id;
38064
38065     function viewOnOSM(selection) {
38066         var entity = context.entity(id);
38067
38068         selection.style('display', entity.isNew() ? 'none' : null);
38069
38070         var $link = selection.selectAll('.view-on-osm')
38071             .data([0]);
38072
38073         $link.enter()
38074             .append('a')
38075             .attr('class', 'view-on-osm')
38076             .attr('target', '_blank')
38077             .call(iD.svg.Icon('#icon-out-link', 'inline'))
38078             .append('span')
38079             .text(t('inspector.view_on_osm'));
38080
38081         $link
38082             .attr('href', context.connection().entityURL(entity));
38083     }
38084
38085     viewOnOSM.entityID = function(_) {
38086         if (!arguments.length) return id;
38087         id = _;
38088         return viewOnOSM;
38089     };
38090
38091     return viewOnOSM;
38092 };
38093 iD.ui.Zoom = function(context) {
38094     var zooms = [{
38095         id: 'zoom-in',
38096         icon: 'plus',
38097         title: t('zoom.in'),
38098         action: context.zoomIn,
38099         key: '+'
38100     }, {
38101         id: 'zoom-out',
38102         icon: 'minus',
38103         title: t('zoom.out'),
38104         action: context.zoomOut,
38105         key: '-'
38106     }];
38107
38108     function zoomIn() {
38109         d3.event.preventDefault();
38110         if (!context.inIntro()) context.zoomIn();
38111     }
38112
38113     function zoomOut() {
38114         d3.event.preventDefault();
38115         if (!context.inIntro()) context.zoomOut();
38116     }
38117
38118     function zoomInFurther() {
38119         d3.event.preventDefault();
38120         if (!context.inIntro()) context.zoomInFurther();
38121     }
38122
38123     function zoomOutFurther() {
38124         d3.event.preventDefault();
38125         if (!context.inIntro()) context.zoomOutFurther();
38126     }
38127
38128
38129     return function(selection) {
38130         var button = selection.selectAll('button')
38131             .data(zooms)
38132             .enter().append('button')
38133             .attr('tabindex', -1)
38134             .attr('class', function(d) { return d.id; })
38135             .on('click.editor', function(d) { d.action(); })
38136             .call(bootstrap.tooltip()
38137                 .placement('left')
38138                 .html(true)
38139                 .title(function(d) {
38140                     return iD.ui.tooltipHtml(d.title, d.key);
38141                 }));
38142
38143         button.each(function(d) {
38144             d3.select(this)
38145                 .call(iD.svg.Icon('#icon-' + d.icon, 'light'));
38146         });
38147
38148         var keybinding = d3.keybinding('zoom');
38149
38150         _.each(['=','ffequals','plus','ffplus'], function(key) {
38151             keybinding.on(key, zoomIn);
38152             keybinding.on('⇧' + key, zoomIn);
38153             keybinding.on(iD.ui.cmd('⌘' + key), zoomInFurther);
38154             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomInFurther);
38155         });
38156         _.each(['-','ffminus','_','dash'], function(key) {
38157             keybinding.on(key, zoomOut);
38158             keybinding.on('⇧' + key, zoomOut);
38159             keybinding.on(iD.ui.cmd('⌘' + key), zoomOutFurther);
38160             keybinding.on(iD.ui.cmd('⌘⇧' + key), zoomOutFurther);
38161         });
38162
38163         d3.select(document)
38164             .call(keybinding);
38165     };
38166 };
38167 iD.ui.preset.access = function(field) {
38168     var dispatch = d3.dispatch('change'),
38169         items;
38170
38171     function access(selection) {
38172         var wrap = selection.selectAll('.preset-input-wrap')
38173             .data([0]);
38174
38175         wrap.enter().append('div')
38176             .attr('class', 'cf preset-input-wrap')
38177             .append('ul');
38178
38179         items = wrap.select('ul').selectAll('li')
38180             .data(field.keys);
38181
38182         // Enter
38183
38184         var enter = items.enter().append('li')
38185             .attr('class', function(d) { return 'cf preset-access-' + d; });
38186
38187         enter.append('span')
38188             .attr('class', 'col6 label preset-label-access')
38189             .attr('for', function(d) { return 'preset-input-access-' + d; })
38190             .text(function(d) { return field.t('types.' + d); });
38191
38192         enter.append('div')
38193             .attr('class', 'col6 preset-input-access-wrap')
38194             .append('input')
38195             .attr('type', 'text')
38196             .attr('class', 'preset-input-access')
38197             .attr('id', function(d) { return 'preset-input-access-' + d; })
38198             .each(function(d) {
38199                 d3.select(this)
38200                     .call(d3.combobox()
38201                         .data(access.options(d)));
38202             });
38203
38204         // Update
38205
38206         wrap.selectAll('.preset-input-access')
38207             .on('change', change)
38208             .on('blur', change);
38209     }
38210
38211     function change(d) {
38212         var tag = {};
38213         tag[d] = d3.select(this).value() || undefined;
38214         dispatch.change(tag);
38215     }
38216
38217     access.options = function(type) {
38218         var options = ['no', 'permissive', 'private', 'destination'];
38219
38220         if (type !== 'access') {
38221             options.unshift('yes');
38222             options.push('designated');
38223
38224             if (type === 'bicycle') {
38225                 options.push('dismount');
38226             }
38227         }
38228
38229         return options.map(function(option) {
38230             return {
38231                 title: field.t('options.' + option + '.description'),
38232                 value: option
38233             };
38234         });
38235     };
38236
38237     var placeholders = {
38238         footway: {
38239             foot: 'designated',
38240             motor_vehicle: 'no'
38241         },
38242         steps: {
38243             foot: 'yes',
38244             motor_vehicle: 'no',
38245             bicycle: 'no',
38246             horse: 'no'
38247         },
38248         pedestrian: {
38249             foot: 'yes',
38250             motor_vehicle: 'no'
38251         },
38252         cycleway: {
38253             motor_vehicle: 'no',
38254             bicycle: 'designated'
38255         },
38256         bridleway: {
38257             motor_vehicle: 'no',
38258             horse: 'designated'
38259         },
38260         path: {
38261             foot: 'yes',
38262             motor_vehicle: 'no',
38263             bicycle: 'yes',
38264             horse: 'yes'
38265         },
38266         motorway: {
38267             foot: 'no',
38268             motor_vehicle: 'yes',
38269             bicycle: 'no',
38270             horse: 'no'
38271         },
38272         trunk: {
38273             motor_vehicle: 'yes'
38274         },
38275         primary: {
38276             foot: 'yes',
38277             motor_vehicle: 'yes',
38278             bicycle: 'yes',
38279             horse: 'yes'
38280         },
38281         secondary: {
38282             foot: 'yes',
38283             motor_vehicle: 'yes',
38284             bicycle: 'yes',
38285             horse: 'yes'
38286         },
38287         tertiary: {
38288             foot: 'yes',
38289             motor_vehicle: 'yes',
38290             bicycle: 'yes',
38291             horse: 'yes'
38292         },
38293         residential: {
38294             foot: 'yes',
38295             motor_vehicle: 'yes',
38296             bicycle: 'yes',
38297             horse: 'yes'
38298         },
38299         unclassified: {
38300             foot: 'yes',
38301             motor_vehicle: 'yes',
38302             bicycle: 'yes',
38303             horse: 'yes'
38304         },
38305         service: {
38306             foot: 'yes',
38307             motor_vehicle: 'yes',
38308             bicycle: 'yes',
38309             horse: 'yes'
38310         },
38311         motorway_link: {
38312             foot: 'no',
38313             motor_vehicle: 'yes',
38314             bicycle: 'no',
38315             horse: 'no'
38316         },
38317         trunk_link: {
38318             motor_vehicle: 'yes'
38319         },
38320         primary_link: {
38321             foot: 'yes',
38322             motor_vehicle: 'yes',
38323             bicycle: 'yes',
38324             horse: 'yes'
38325         },
38326         secondary_link: {
38327             foot: 'yes',
38328             motor_vehicle: 'yes',
38329             bicycle: 'yes',
38330             horse: 'yes'
38331         },
38332         tertiary_link: {
38333             foot: 'yes',
38334             motor_vehicle: 'yes',
38335             bicycle: 'yes',
38336             horse: 'yes'
38337         }
38338     };
38339
38340     access.tags = function(tags) {
38341         items.selectAll('.preset-input-access')
38342             .value(function(d) { return tags[d] || ''; })
38343             .attr('placeholder', function() {
38344                 return tags.access ? tags.access : field.placeholder();
38345             });
38346
38347         // items.selectAll('#preset-input-access-access')
38348         //     .attr('placeholder', 'yes');
38349
38350         _.forEach(placeholders[tags.highway], function(v, k) {
38351             items.selectAll('#preset-input-access-' + k)
38352                 .attr('placeholder', function() { return (tags.access || v); });
38353         });
38354     };
38355
38356     access.focus = function() {
38357         items.selectAll('.preset-input-access')
38358             .node().focus();
38359     };
38360
38361     return d3.rebind(access, dispatch, 'on');
38362 };
38363 iD.ui.preset.address = function(field, context) {
38364     var dispatch = d3.dispatch('init', 'change'),
38365         wrap,
38366         entity,
38367         isInitialized;
38368
38369     var widths = {
38370         housenumber: 1/3,
38371         street: 2/3,
38372         city: 2/3,
38373         state: 1/4,
38374         postcode: 1/3
38375     };
38376
38377     function getStreets() {
38378         var extent = entity.extent(context.graph()),
38379             l = extent.center(),
38380             box = iD.geo.Extent(l).padByMeters(200);
38381
38382         return context.intersects(box)
38383             .filter(isAddressable)
38384             .map(function(d) {
38385                 var loc = context.projection([
38386                     (extent[0][0] + extent[1][0]) / 2,
38387                     (extent[0][1] + extent[1][1]) / 2]),
38388                     choice = iD.geo.chooseEdge(context.childNodes(d), loc, context.projection);
38389                 return {
38390                     title: d.tags.name,
38391                     value: d.tags.name,
38392                     dist: choice.distance
38393                 };
38394             }).sort(function(a, b) {
38395                 return a.dist - b.dist;
38396             });
38397
38398         function isAddressable(d) {
38399             return d.tags.highway && d.tags.name && d.type === 'way';
38400         }
38401     }
38402
38403     function getCities() {
38404         var extent = entity.extent(context.graph()),
38405             l = extent.center(),
38406             box = iD.geo.Extent(l).padByMeters(200);
38407
38408         return context.intersects(box)
38409             .filter(isAddressable)
38410             .map(function(d) {
38411                 return {
38412                     title: d.tags['addr:city'] || d.tags.name,
38413                     value: d.tags['addr:city'] || d.tags.name,
38414                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
38415                 };
38416             }).sort(function(a, b) {
38417                 return a.dist - b.dist;
38418             });
38419
38420         function isAddressable(d) {
38421             if (d.tags.name &&
38422                 (d.tags.admin_level === '8' || d.tags.border_type === 'city'))
38423                 return true;
38424
38425             if (d.tags.place && d.tags.name && (
38426                     d.tags.place === 'city' ||
38427                     d.tags.place === 'town' ||
38428                     d.tags.place === 'village'))
38429                 return true;
38430
38431             if (d.tags['addr:city']) return true;
38432
38433             return false;
38434         }
38435     }
38436
38437     function getPostCodes() {
38438         var extent = entity.extent(context.graph()),
38439             l = extent.center(),
38440             box = iD.geo.Extent(l).padByMeters(200);
38441
38442         return context.intersects(box)
38443             .filter(isAddressable)
38444             .map(function(d) {
38445                 return {
38446                     title: d.tags['addr:postcode'],
38447                     value: d.tags['addr:postcode'],
38448                     dist: iD.geo.sphericalDistance(d.extent(context.graph()).center(), l)
38449                 };
38450             }).sort(function(a, b) {
38451                 return a.dist - b.dist;
38452             });
38453
38454         function isAddressable(d) {
38455             return d.tags['addr:postcode'];
38456         }
38457     }
38458
38459     function address(selection) {
38460         isInitialized = false;
38461
38462         wrap = selection.selectAll('.preset-input-wrap')
38463             .data([0]);
38464
38465         // Enter
38466
38467         wrap.enter()
38468             .append('div')
38469             .attr('class', 'preset-input-wrap');
38470
38471         var center = entity.extent(context.graph()).center(),
38472             addressFormat;
38473
38474         iD.services.nominatim().countryCode(center, function (err, countryCode) {
38475             addressFormat = _.find(iD.data.addressFormats, function (a) {
38476                 return a && a.countryCodes && _.contains(a.countryCodes, countryCode);
38477             }) || _.first(iD.data.addressFormats);
38478
38479             function row(r) {
38480                 // Normalize widths.
38481                 var total = _.reduce(r, function(sum, field) {
38482                     return sum + (widths[field] || 0.5);
38483                 }, 0);
38484
38485                 return r.map(function (field) {
38486                     return {
38487                         id: field,
38488                         width: (widths[field] || 0.5) / total
38489                     };
38490                 });
38491             }
38492
38493             wrap.selectAll('div')
38494                 .data(addressFormat.format)
38495                 .enter()
38496                 .append('div')
38497                 .attr('class', 'addr-row')
38498                 .selectAll('input')
38499                 .data(row)
38500                 .enter()
38501                 .append('input')
38502                 .property('type', 'text')
38503                 .attr('placeholder', function (d) { return field.t('placeholders.' + d.id); })
38504                 .attr('class', function (d) { return 'addr-' + d.id; })
38505                 .style('width', function (d) { return d.width * 100 + '%'; });
38506
38507             // Update
38508
38509             wrap.selectAll('.addr-street')
38510                 .call(d3.combobox()
38511                     .fetcher(function(value, callback) {
38512                         callback(getStreets());
38513                     }));
38514
38515             wrap.selectAll('.addr-city')
38516                 .call(d3.combobox()
38517                     .fetcher(function(value, callback) {
38518                         callback(getCities());
38519                     }));
38520
38521             wrap.selectAll('.addr-postcode')
38522                 .call(d3.combobox()
38523                     .fetcher(function(value, callback) {
38524                         callback(getPostCodes());
38525                     }));
38526
38527             wrap.selectAll('input')
38528                 .on('blur', change())
38529                 .on('change', change());
38530
38531             wrap.selectAll('input:not(.combobox-input)')
38532                 .on('input', change(true));
38533
38534             dispatch.init();
38535             isInitialized = true;
38536         });
38537     }
38538
38539     function change(onInput) {
38540         return function() {
38541             var tags = {};
38542
38543             wrap.selectAll('input')
38544                 .each(function (field) {
38545                     tags['addr:' + field.id] = this.value || undefined;
38546                 });
38547
38548             dispatch.change(tags, onInput);
38549         };
38550     }
38551
38552     function updateTags(tags) {
38553         wrap.selectAll('input')
38554             .value(function (field) {
38555                 return tags['addr:' + field.id] || '';
38556             });
38557     }
38558
38559     address.entity = function(_) {
38560         if (!arguments.length) return entity;
38561         entity = _;
38562         return address;
38563     };
38564
38565     address.tags = function(tags) {
38566         if (isInitialized) {
38567             updateTags(tags);
38568         } else {
38569             dispatch.on('init', function () {
38570                 updateTags(tags);
38571             });
38572         }
38573     };
38574
38575     address.focus = function() {
38576         var node = wrap.selectAll('input').node();
38577         if (node) node.focus();
38578     };
38579
38580     return d3.rebind(address, dispatch, 'on');
38581 };
38582 iD.ui.preset.check =
38583 iD.ui.preset.defaultcheck = function(field) {
38584     var dispatch = d3.dispatch('change'),
38585         options = field.strings && field.strings.options,
38586         values = [],
38587         texts = [],
38588         entity, value, box, text, label;
38589
38590     if (options) {
38591         for (var k in options) {
38592             values.push(k === 'undefined' ? undefined : k);
38593             texts.push(field.t('options.' + k, { 'default': options[k] }));
38594         }
38595     } else {
38596         values = [undefined, 'yes'];
38597         texts = [t('inspector.unknown'), t('inspector.check.yes')];
38598         if (field.type === 'check') {
38599             values.push('no');
38600             texts.push(t('inspector.check.no'));
38601         }
38602     }
38603
38604     var check = function(selection) {
38605         // hack: pretend oneway field is a oneway_yes field
38606         // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
38607         if (field.id === 'oneway') {
38608             for (var key in entity.tags) {
38609                 if (key in iD.oneWayTags && (entity.tags[key] in iD.oneWayTags[key])) {
38610                     texts[0] = t('presets.fields.oneway_yes.options.undefined');
38611                     break;
38612                 }
38613             }
38614         }
38615
38616         selection.classed('checkselect', 'true');
38617
38618         label = selection.selectAll('.preset-input-wrap')
38619             .data([0]);
38620
38621         var enter = label.enter().append('label')
38622             .attr('class', 'preset-input-wrap');
38623
38624         enter.append('input')
38625             .property('indeterminate', field.type === 'check')
38626             .attr('type', 'checkbox')
38627             .attr('id', 'preset-input-' + field.id);
38628
38629         enter.append('span')
38630             .text(texts[0])
38631             .attr('class', 'value');
38632
38633         box = label.select('input')
38634             .on('click', function() {
38635                 var t = {};
38636                 t[field.key] = values[(values.indexOf(value) + 1) % values.length];
38637                 dispatch.change(t);
38638                 d3.event.stopPropagation();
38639             });
38640
38641         text = label.select('span.value');
38642     };
38643
38644     check.entity = function(_) {
38645         if (!arguments.length) return entity;
38646         entity = _;
38647         return check;
38648     };
38649
38650     check.tags = function(tags) {
38651         value = tags[field.key];
38652         box.property('indeterminate', field.type === 'check' && !value);
38653         box.property('checked', value === 'yes');
38654         text.text(texts[values.indexOf(value)]);
38655         label.classed('set', !!value);
38656     };
38657
38658     check.focus = function() {
38659         box.node().focus();
38660     };
38661
38662     return d3.rebind(check, dispatch, 'on');
38663 };
38664 iD.ui.preset.combo =
38665 iD.ui.preset.typeCombo =
38666 iD.ui.preset.multiCombo = function(field, context) {
38667     var dispatch = d3.dispatch('change'),
38668         isMulti = (field.type === 'multiCombo'),
38669         optstrings = field.strings && field.strings.options,
38670         optarray = field.options,
38671         snake_case = (field.snake_case || (field.snake_case === undefined)),
38672         combobox = d3.combobox().minItems(isMulti ? 1 : 2),
38673         comboData = [],
38674         multiData = [],
38675         container,
38676         input,
38677         entity;
38678
38679     // ensure multiCombo field.key ends with a ':'
38680     if (isMulti && field.key.match(/:$/) === null) {
38681         field.key += ':';
38682     }
38683
38684
38685     function snake(s) {
38686         return s.replace(/\s+/g, '_');
38687     }
38688
38689     function unsnake(s) {
38690         return s.replace(/_+/g, ' ');
38691     }
38692
38693     function clean(s) {
38694         return s.split(';')
38695             .map(function(s) { return s.trim(); })
38696             .join(';');
38697     }
38698
38699
38700     // returns the tag value for a display value
38701     // (for multiCombo, dval should be the key suffix, not the entire key)
38702     function tagValue(dval) {
38703         dval = clean(dval || '');
38704
38705         if (optstrings) {
38706             var match = _.find(comboData, function(o) { return o.value === dval && o.key; });
38707             if (match) {
38708                 return match.key;
38709             }
38710         }
38711
38712         if (field.type === 'typeCombo' && !dval) {
38713             return 'yes';
38714         }
38715
38716         return (snake_case ? snake(dval) : dval) || undefined;
38717     }
38718
38719
38720     // returns the display value for a tag value
38721     // (for multiCombo, tval should be the key suffix, not the entire key)
38722     function displayValue(tval) {
38723         tval = tval || '';
38724
38725         if (optstrings) {
38726             var match = _.find(comboData, function(o) { return o.key === tval && o.value; });
38727             if (match) {
38728                 return match.value;
38729             }
38730         }
38731
38732         if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
38733             return '';
38734         }
38735
38736         return snake_case ? unsnake(tval) : tval;
38737     }
38738
38739
38740     function objectDifference(a, b) {
38741         return _.reject(a, function(d1) {
38742             return _.any(b, function(d2) { return d1.value === d2.value; });
38743         });
38744     }
38745
38746
38747     function initCombo(selection, attachTo) {
38748         if (optstrings) {
38749             selection.attr('readonly', 'readonly');
38750             selection.call(combobox, attachTo);
38751             setStaticValues(setPlaceholder);
38752
38753         } else if (optarray) {
38754             selection.call(combobox, attachTo);
38755             setStaticValues(setPlaceholder);
38756
38757         } else if (context.taginfo()) {
38758             selection.call(combobox.fetcher(setTaginfoValues), attachTo);
38759             setTaginfoValues('', setPlaceholder);
38760         }
38761     }
38762
38763
38764     function setStaticValues(callback) {
38765         if (!(optstrings || optarray)) return;
38766
38767         if (optstrings) {
38768             comboData = Object.keys(optstrings).map(function(k) {
38769                 var v = field.t('options.' + k, { 'default': optstrings[k] });
38770                 return {
38771                     key: k,
38772                     value: v,
38773                     title: v
38774                 };
38775             });
38776
38777         } else if (optarray) {
38778             comboData = optarray.map(function(k) {
38779                 var v = snake_case ? unsnake(k) : k;
38780                 return {
38781                     key: k,
38782                     value: v,
38783                     title: v
38784                 };
38785             });
38786         }
38787
38788         combobox.data(objectDifference(comboData, multiData));
38789         if (callback) callback(comboData);
38790     }
38791
38792
38793     function setTaginfoValues(q, callback) {
38794         var fn = isMulti ? 'multikeys' : 'values';
38795         context.taginfo()[fn]({
38796             debounce: true,
38797             key: field.key,
38798             geometry: context.geometry(entity.id),
38799             query: (isMulti ? field.key : '') + q
38800         }, function(err, data) {
38801             if (err) return;
38802             comboData = _.pluck(data, 'value').map(function(k) {
38803                 if (isMulti) k = k.replace(field.key, '');
38804                 var v = snake_case ? unsnake(k) : k;
38805                 return {
38806                     key: k,
38807                     value: v,
38808                     title: v
38809                 };
38810             });
38811             comboData = objectDifference(comboData, multiData);
38812             if (callback) callback(comboData);
38813         });
38814     }
38815
38816
38817     function setPlaceholder(d) {
38818         var ph;
38819         if (isMulti) {
38820             ph = field.placeholder() || t('inspector.add');
38821         } else {
38822             var vals = _.pluck(d, 'value').filter(function(s) { return s.length < 20; }),
38823                 placeholders = vals.length > 1 ? vals : _.pluck(d, 'key');
38824             ph = field.placeholder() || placeholders.slice(0, 3).join(', ');
38825         }
38826
38827         input.attr('placeholder', ph + '…');
38828     }
38829
38830
38831     function change() {
38832         var val = tagValue(input.value()),
38833             t = {};
38834
38835         if (isMulti) {
38836             if (!val) return;
38837             container.classed('active', false);
38838             input.value('');
38839             field.keys.push(field.key + val);
38840             t[field.key + val] = 'yes';
38841             window.setTimeout(function() { input.node().focus(); }, 10);
38842
38843         } else {
38844             t[field.key] = val;
38845         }
38846
38847         dispatch.change(t);
38848     }
38849
38850
38851     function removeMultikey(d) {
38852         d3.event.stopPropagation();
38853         var t = {};
38854         t[d.key] = undefined;
38855         dispatch.change(t);
38856     }
38857
38858
38859     function combo(selection) {
38860         if (isMulti) {
38861             container = selection.selectAll('ul').data([0]);
38862
38863             container.enter()
38864                 .append('ul')
38865                 .attr('class', 'form-field-multicombo')
38866                 .on('click', function() {
38867                     window.setTimeout(function() { input.node().focus(); }, 10);
38868                 });
38869
38870         } else {
38871             container = selection;
38872         }
38873
38874         input = container.selectAll('input')
38875             .data([0]);
38876
38877         input.enter()
38878             .append('input')
38879             .attr('type', 'text')
38880             .attr('id', 'preset-input-' + field.id)
38881             .call(initCombo, selection);
38882
38883         input
38884             .on('change', change)
38885             .on('blur', change);
38886
38887         if (isMulti) {
38888             combobox
38889                 .on('accept', function() {
38890                     input.node().blur();
38891                     input.node().focus();
38892                 });
38893
38894             input
38895                 .on('focus', function() { container.classed('active', true); });
38896         }
38897     }
38898
38899
38900     combo.tags = function(tags) {
38901         if (isMulti) {
38902             multiData = [];
38903
38904             // Build multiData array containing keys already set..
38905             Object.keys(tags).forEach(function(key) {
38906                 if (key.indexOf(field.key) !== 0 || tags[key].toLowerCase() !== 'yes') return;
38907
38908                 var suffix = key.substring(field.key.length);
38909                 multiData.push({
38910                     key: key,
38911                     value: displayValue(suffix)
38912                 });
38913             });
38914
38915             // Set keys for form-field modified (needed for undo and reset buttons)..
38916             field.keys = _.pluck(multiData, 'key');
38917
38918             // Exclude existing multikeys from combo options..
38919             var available = objectDifference(comboData, multiData);
38920             combobox.data(available);
38921
38922             // Hide "Add" button if this field uses fixed set of
38923             // translateable optstrings and they're all currently used..
38924             container.selectAll('.combobox-input, .combobox-caret')
38925                 .classed('hide', optstrings && !available.length);
38926
38927
38928             // Render chips
38929             var chips = container.selectAll('.chips').data(multiData);
38930
38931             var enter = chips.enter()
38932                 .insert('li', 'input')
38933                 .attr('class', 'chips');
38934
38935             enter.append('span');
38936             enter.append('a');
38937
38938             chips.select('span')
38939                 .text(function(d) { return d.value; });
38940
38941             chips.select('a')
38942                 .on('click', removeMultikey)
38943                 .attr('class', 'remove')
38944                 .text('×');
38945
38946             chips.exit()
38947                 .remove();
38948
38949         } else {
38950             input.value(displayValue(tags[field.key]));
38951         }
38952     };
38953
38954
38955     combo.focus = function() {
38956         input.node().focus();
38957     };
38958
38959
38960     combo.entity = function(_) {
38961         if (!arguments.length) return entity;
38962         entity = _;
38963         return combo;
38964     };
38965
38966
38967     return d3.rebind(combo, dispatch, 'on');
38968 };
38969 iD.ui.preset.cycleway = function(field) {
38970     var dispatch = d3.dispatch('change'),
38971         items;
38972
38973     function cycleway(selection) {
38974         var wrap = selection.selectAll('.preset-input-wrap')
38975             .data([0]);
38976
38977         wrap.enter().append('div')
38978             .attr('class', 'cf preset-input-wrap')
38979             .append('ul');
38980
38981         items = wrap.select('ul').selectAll('li')
38982             .data(field.keys);
38983
38984         // Enter
38985
38986         var enter = items.enter().append('li')
38987             .attr('class', function(d) { return 'cf preset-cycleway-' + d; });
38988
38989         enter.append('span')
38990             .attr('class', 'col6 label preset-label-cycleway')
38991             .attr('for', function(d) { return 'preset-input-cycleway-' + d; })
38992             .text(function(d) { return field.t('types.' + d); });
38993
38994         enter.append('div')
38995             .attr('class', 'col6 preset-input-cycleway-wrap')
38996             .append('input')
38997             .attr('type', 'text')
38998             .attr('class', 'preset-input-cycleway')
38999             .attr('id', function(d) { return 'preset-input-cycleway-' + d; })
39000             .each(function(d) {
39001                 d3.select(this)
39002                     .call(d3.combobox()
39003                         .data(cycleway.options(d)));
39004             });
39005
39006         // Update
39007
39008         wrap.selectAll('.preset-input-cycleway')
39009             .on('change', change)
39010             .on('blur', change);
39011     }
39012
39013     function change() {
39014         var inputs = d3.selectAll('.preset-input-cycleway')[0],
39015             left = d3.select(inputs[0]).value(),
39016             right = d3.select(inputs[1]).value(),
39017             tag = {};
39018         if (left === 'none' || left === '') { left = undefined; }
39019         if (right === 'none' || right === '') { right = undefined; }
39020
39021         // Always set both left and right as changing one can affect the other
39022         tag = {
39023             cycleway: undefined,
39024             'cycleway:left': left,
39025             'cycleway:right': right
39026         };
39027
39028         // If the left and right tags match, use the cycleway tag to tag both
39029         // sides the same way
39030         if (left === right) {
39031             tag = {
39032                 cycleway: left,
39033                 'cycleway:left': undefined,
39034                 'cycleway:right': undefined
39035             };
39036         }
39037
39038         dispatch.change(tag);
39039     }
39040
39041     cycleway.options = function() {
39042         return d3.keys(field.strings.options).map(function(option) {
39043             return {
39044                 title: field.t('options.' + option + '.description'),
39045                 value: option
39046             };
39047         });
39048     };
39049
39050     cycleway.tags = function(tags) {
39051         items.selectAll('.preset-input-cycleway')
39052             .value(function(d) {
39053                 // If cycleway is set, always return that
39054                 if (tags.cycleway) {
39055                     return tags.cycleway;
39056                 }
39057                 return tags[d] || '';
39058             })
39059             .attr('placeholder', field.placeholder());
39060     };
39061
39062     cycleway.focus = function() {
39063         items.selectAll('.preset-input-cycleway')
39064             .node().focus();
39065     };
39066
39067     return d3.rebind(cycleway, dispatch, 'on');
39068 };
39069 iD.ui.preset.text =
39070 iD.ui.preset.number =
39071 iD.ui.preset.tel =
39072 iD.ui.preset.email =
39073 iD.ui.preset.url = function(field) {
39074
39075     var dispatch = d3.dispatch('change'),
39076         input;
39077
39078     function i(selection) {
39079         input = selection.selectAll('input')
39080             .data([0]);
39081
39082         input.enter().append('input')
39083             .attr('type', field.type)
39084             .attr('id', 'preset-input-' + field.id)
39085             .attr('placeholder', field.placeholder() || t('inspector.unknown'));
39086
39087         input
39088             .on('input', change(true))
39089             .on('blur', change())
39090             .on('change', change());
39091
39092         if (field.type === 'number') {
39093             input.attr('type', 'text');
39094
39095             var spinControl = selection.selectAll('.spin-control')
39096                 .data([0]);
39097
39098             var enter = spinControl.enter().append('div')
39099                 .attr('class', 'spin-control');
39100
39101             enter.append('button')
39102                 .datum(1)
39103                 .attr('class', 'increment')
39104                 .attr('tabindex', -1);
39105
39106             enter.append('button')
39107                 .datum(-1)
39108                 .attr('class', 'decrement')
39109                 .attr('tabindex', -1);
39110
39111             spinControl.selectAll('button')
39112                 .on('click', function(d) {
39113                     d3.event.preventDefault();
39114                     var num = parseInt(input.node().value || 0, 10);
39115                     if (!isNaN(num)) input.node().value = num + d;
39116                     change()();
39117                 });
39118         }
39119     }
39120
39121     function change(onInput) {
39122         return function() {
39123             var t = {};
39124             t[field.key] = input.value() || undefined;
39125             dispatch.change(t, onInput);
39126         };
39127     }
39128
39129     i.tags = function(tags) {
39130         input.value(tags[field.key] || '');
39131     };
39132
39133     i.focus = function() {
39134         input.node().focus();
39135     };
39136
39137     return d3.rebind(i, dispatch, 'on');
39138 };
39139 iD.ui.preset.localized = function(field, context) {
39140     var dispatch = d3.dispatch('change', 'input'),
39141         wikipedia = iD.services.wikipedia(),
39142         input, localizedInputs, wikiTitles,
39143         entity;
39144
39145     function i(selection) {
39146         input = selection.selectAll('.localized-main')
39147             .data([0]);
39148
39149         input.enter().append('input')
39150             .attr('type', 'text')
39151             .attr('id', 'preset-input-' + field.id)
39152             .attr('class', 'localized-main')
39153             .attr('placeholder', field.placeholder());
39154
39155         if (field.id === 'name') {
39156             var preset = context.presets().match(entity, context.graph());
39157             input.call(d3.combobox().fetcher(
39158                 iD.util.SuggestNames(preset, iD.data.suggestions)
39159             ));
39160         }
39161
39162         input
39163             .on('input', change(true))
39164             .on('blur', change())
39165             .on('change', change());
39166
39167         var translateButton = selection.selectAll('.localized-add')
39168             .data([0]);
39169
39170         translateButton.enter()
39171             .append('button')
39172             .attr('class', 'button-input-action localized-add minor')
39173             .attr('tabindex', -1)
39174             .call(iD.svg.Icon('#icon-plus'))
39175             .call(bootstrap.tooltip()
39176                 .title(t('translate.translate'))
39177                 .placement('left'));
39178
39179         translateButton
39180             .on('click', addNew);
39181
39182         localizedInputs = selection.selectAll('.localized-wrap')
39183             .data([0]);
39184
39185         localizedInputs.enter().append('div')
39186             .attr('class', 'localized-wrap');
39187     }
39188
39189     function addNew() {
39190         d3.event.preventDefault();
39191         var data = localizedInputs.selectAll('div.entry').data();
39192         var defaultLang = iD.detect().locale.toLowerCase().split('-')[0];
39193         var langExists = _.find(data, function(datum) { return datum.lang === defaultLang;});
39194         var isLangEn = defaultLang.indexOf('en') > -1;
39195         if (isLangEn || langExists) {
39196           defaultLang = '';
39197         }
39198         data.push({ lang: defaultLang, value: '' });
39199         localizedInputs.call(render, data);
39200     }
39201
39202     function change(onInput) {
39203         return function() {
39204             var t = {};
39205             t[field.key] = d3.select(this).value() || undefined;
39206             dispatch.change(t, onInput);
39207         };
39208     }
39209
39210     function key(lang) { return field.key + ':' + lang; }
39211
39212     function changeLang(d) {
39213         var lang = d3.select(this).value(),
39214             t = {},
39215             language = _.find(iD.data.wikipedia, function(d) {
39216                 return d[0].toLowerCase() === lang.toLowerCase() ||
39217                     d[1].toLowerCase() === lang.toLowerCase();
39218             });
39219
39220         if (language) lang = language[2];
39221
39222         if (d.lang && d.lang !== lang) {
39223             t[key(d.lang)] = undefined;
39224         }
39225
39226         var value = d3.select(this.parentNode)
39227             .selectAll('.localized-value')
39228             .value();
39229
39230         if (lang && value) {
39231             t[key(lang)] = value;
39232         } else if (lang && wikiTitles && wikiTitles[d.lang]) {
39233             t[key(lang)] = wikiTitles[d.lang];
39234         }
39235
39236         d.lang = lang;
39237         dispatch.change(t);
39238     }
39239
39240     function changeValue(d) {
39241         if (!d.lang) return;
39242         var t = {};
39243         t[key(d.lang)] = d3.select(this).value() || undefined;
39244         dispatch.change(t);
39245     }
39246
39247     function fetcher(value, cb) {
39248         var v = value.toLowerCase();
39249
39250         cb(iD.data.wikipedia.filter(function(d) {
39251             return d[0].toLowerCase().indexOf(v) >= 0 ||
39252             d[1].toLowerCase().indexOf(v) >= 0 ||
39253             d[2].toLowerCase().indexOf(v) >= 0;
39254         }).map(function(d) {
39255             return { value: d[1] };
39256         }));
39257     }
39258
39259     function render(selection, data) {
39260         var wraps = selection.selectAll('div.entry').
39261             data(data, function(d) { return d.lang; });
39262
39263         var innerWrap = wraps.enter()
39264             .insert('div', ':first-child');
39265
39266         innerWrap.attr('class', 'entry')
39267             .each(function() {
39268                 var wrap = d3.select(this);
39269                 var langcombo = d3.combobox().fetcher(fetcher).minItems(0);
39270
39271                 var label = wrap.append('label')
39272                     .attr('class','form-label')
39273                     .text(t('translate.localized_translation_label'))
39274                     .attr('for','localized-lang');
39275
39276                 label.append('button')
39277                     .attr('class', 'minor remove')
39278                     .on('click', function(d){
39279                         d3.event.preventDefault();
39280                         var t = {};
39281                         t[key(d.lang)] = undefined;
39282                         dispatch.change(t);
39283                         d3.select(this.parentNode.parentNode)
39284                             .style('top','0')
39285                             .style('max-height','240px')
39286                             .transition()
39287                             .style('opacity', '0')
39288                             .style('max-height','0px')
39289                             .remove();
39290                     })
39291                     .call(iD.svg.Icon('#operation-delete'));
39292
39293                 wrap.append('input')
39294                     .attr('class', 'localized-lang')
39295                     .attr('type', 'text')
39296                     .attr('placeholder',t('translate.localized_translation_language'))
39297                     .on('blur', changeLang)
39298                     .on('change', changeLang)
39299                     .call(langcombo);
39300
39301                 wrap.append('input')
39302                     .on('blur', changeValue)
39303                     .on('change', changeValue)
39304                     .attr('type', 'text')
39305                     .attr('placeholder', t('translate.localized_translation_name'))
39306                     .attr('class', 'localized-value');
39307             });
39308
39309         innerWrap
39310             .style('margin-top', '0px')
39311             .style('max-height', '0px')
39312             .style('opacity', '0')
39313             .transition()
39314             .duration(200)
39315             .style('margin-top', '10px')
39316             .style('max-height', '240px')
39317             .style('opacity', '1')
39318             .each('end', function() {
39319                 d3.select(this)
39320                     .style('max-height', '')
39321                     .style('overflow', 'visible');
39322             });
39323
39324         wraps.exit()
39325             .transition()
39326             .duration(200)
39327             .style('max-height','0px')
39328             .style('opacity', '0')
39329             .style('top','-10px')
39330             .remove();
39331
39332         var entry = selection.selectAll('.entry');
39333
39334         entry.select('.localized-lang')
39335             .value(function(d) {
39336                 var lang = _.find(iD.data.wikipedia, function(lang) { return lang[2] === d.lang; });
39337                 return lang ? lang[1] : d.lang;
39338             });
39339
39340         entry.select('.localized-value')
39341             .value(function(d) { return d.value; });
39342     }
39343
39344     i.tags = function(tags) {
39345         // Fetch translations from wikipedia
39346         if (tags.wikipedia && !wikiTitles) {
39347             wikiTitles = {};
39348             var wm = tags.wikipedia.match(/([^:]+):(.+)/);
39349             if (wm && wm[0] && wm[1]) {
39350                 wikipedia.translations(wm[1], wm[2], function(d) {
39351                     wikiTitles = d;
39352                 });
39353             }
39354         }
39355
39356         input.value(tags[field.key] || '');
39357
39358         var postfixed = [], k, m;
39359         for (k in tags) {
39360             m = k.match(/^(.*):([a-zA-Z_-]+)$/);
39361             if (m && m[1] === field.key && m[2]) {
39362                 postfixed.push({ lang: m[2], value: tags[k] });
39363             }
39364         }
39365
39366         localizedInputs.call(render, postfixed.reverse());
39367     };
39368
39369     i.focus = function() {
39370         input.node().focus();
39371     };
39372
39373     i.entity = function(_) {
39374         entity = _;
39375     };
39376
39377     return d3.rebind(i, dispatch, 'on');
39378 };
39379 iD.ui.preset.maxspeed = function(field, context) {
39380     var dispatch = d3.dispatch('change'),
39381         entity,
39382         imperial,
39383         unitInput,
39384         combobox,
39385         input;
39386
39387     var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
39388         imperialValues = [20, 25, 30, 35, 40, 45, 50, 55, 65, 70];
39389
39390     function maxspeed(selection) {
39391         combobox = d3.combobox();
39392         var unitCombobox = d3.combobox().data(['km/h', 'mph'].map(comboValues));
39393
39394         input = selection.selectAll('#preset-input-' + field.id)
39395             .data([0]);
39396
39397         input.enter().append('input')
39398             .attr('type', 'text')
39399             .attr('id', 'preset-input-' + field.id)
39400             .attr('placeholder', field.placeholder());
39401
39402         input
39403             .call(combobox)
39404             .on('change', change)
39405             .on('blur', change);
39406
39407         var childNodes = context.graph().childNodes(context.entity(entity.id)),
39408             loc = childNodes[~~(childNodes.length/2)].loc;
39409
39410         imperial = _.any(iD.data.imperial.features, function(f) {
39411             return _.any(f.geometry.coordinates, function(d) {
39412                 return iD.geo.pointInPolygon(loc, d[0]);
39413             });
39414         });
39415
39416         unitInput = selection.selectAll('input.maxspeed-unit')
39417             .data([0]);
39418
39419         unitInput.enter().append('input')
39420             .attr('type', 'text')
39421             .attr('class', 'maxspeed-unit');
39422
39423         unitInput
39424             .on('blur', changeUnits)
39425             .on('change', changeUnits)
39426             .call(unitCombobox);
39427
39428         function changeUnits() {
39429             imperial = unitInput.value() === 'mph';
39430             unitInput.value(imperial ? 'mph' : 'km/h');
39431             setSuggestions();
39432             change();
39433         }
39434
39435     }
39436
39437     function setSuggestions() {
39438         combobox.data((imperial ? imperialValues : metricValues).map(comboValues));
39439         unitInput.value(imperial ? 'mph' : 'km/h');
39440     }
39441
39442     function comboValues(d) {
39443         return {
39444             value: d.toString(),
39445             title: d.toString()
39446         };
39447     }
39448
39449     function change() {
39450         var tag = {},
39451             value = input.value();
39452
39453         if (!value) {
39454             tag[field.key] = undefined;
39455         } else if (isNaN(value) || !imperial) {
39456             tag[field.key] = value;
39457         } else {
39458             tag[field.key] = value + ' mph';
39459         }
39460
39461         dispatch.change(tag);
39462     }
39463
39464     maxspeed.tags = function(tags) {
39465         var value = tags[field.key];
39466
39467         if (value && value.indexOf('mph') >= 0) {
39468             value = parseInt(value, 10);
39469             imperial = true;
39470         } else if (value) {
39471             imperial = false;
39472         }
39473
39474         setSuggestions();
39475
39476         input.value(value || '');
39477     };
39478
39479     maxspeed.focus = function() {
39480         input.node().focus();
39481     };
39482
39483     maxspeed.entity = function(_) {
39484         entity = _;
39485     };
39486
39487     return d3.rebind(maxspeed, dispatch, 'on');
39488 };
39489 iD.ui.preset.radio = function(field) {
39490     var dispatch = d3.dispatch('change'),
39491         labels, radios, placeholder;
39492
39493     function radio(selection) {
39494         selection.classed('preset-radio', true);
39495
39496         var wrap = selection.selectAll('.preset-input-wrap')
39497             .data([0]);
39498
39499         var buttonWrap = wrap.enter().append('div')
39500             .attr('class', 'preset-input-wrap toggle-list');
39501
39502         buttonWrap.append('span')
39503             .attr('class', 'placeholder');
39504
39505         placeholder = selection.selectAll('.placeholder');
39506
39507         labels = wrap.selectAll('label')
39508             .data(field.options || field.keys);
39509
39510         var enter = labels.enter().append('label');
39511
39512         enter.append('input')
39513             .attr('type', 'radio')
39514             .attr('name', field.id)
39515             .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
39516             .attr('checked', false);
39517
39518         enter.append('span')
39519             .text(function(d) { return field.t('options.' + d, { 'default': d }); });
39520
39521         radios = labels.selectAll('input')
39522             .on('change', change);
39523     }
39524
39525     function change() {
39526         var t = {};
39527         if (field.key) t[field.key] = undefined;
39528         radios.each(function(d) {
39529             var active = d3.select(this).property('checked');
39530             if (field.key) {
39531                 if (active) t[field.key] = d;
39532             } else {
39533                 t[d] = active ? 'yes' : undefined;
39534             }
39535         });
39536         dispatch.change(t);
39537     }
39538
39539     radio.tags = function(tags) {
39540         function checked(d) {
39541             if (field.key) {
39542                 return tags[field.key] === d;
39543             } else {
39544                 return !!(tags[d] && tags[d] !== 'no');
39545             }
39546         }
39547
39548         labels.classed('active', checked);
39549         radios.property('checked', checked);
39550         var selection = radios.filter(function() { return this.checked; });
39551         if (selection.empty()) {
39552             placeholder.text(t('inspector.none'));
39553         } else {
39554             placeholder.text(selection.attr('value'));
39555         }
39556     };
39557
39558     radio.focus = function() {
39559         radios.node().focus();
39560     };
39561
39562     return d3.rebind(radio, dispatch, 'on');
39563 };
39564 iD.ui.preset.restrictions = function(field, context) {
39565     var dispatch = d3.dispatch('change'),
39566         hover = iD.behavior.Hover(context),
39567         vertexID,
39568         fromNodeID;
39569
39570
39571     function restrictions(selection) {
39572         // if form field is hidden or has detached from dom, clean up.
39573         if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
39574             selection.call(restrictions.off);
39575             return;
39576         }
39577
39578         var wrap = selection.selectAll('.preset-input-wrap')
39579             .data([0]);
39580
39581         var enter = wrap.enter()
39582             .append('div')
39583             .attr('class', 'preset-input-wrap');
39584
39585         enter
39586             .append('div')
39587             .attr('class', 'restriction-help');
39588
39589
39590         var intersection = iD.geo.Intersection(context.graph(), vertexID),
39591             graph = intersection.graph,
39592             vertex = graph.entity(vertexID),
39593             filter = d3.functor(true),
39594             extent = iD.geo.Extent(),
39595             projection = iD.geo.RawMercator();
39596
39597         var d = wrap.dimensions(),
39598             c = [d[0] / 2, d[1] / 2],
39599             z = 24;
39600
39601         projection
39602             .scale(256 * Math.pow(2, z) / (2 * Math.PI));
39603
39604         var s = projection(vertex.loc);
39605
39606         projection
39607             .translate([c[0] - s[0], c[1] - s[1]])
39608             .clipExtent([[0, 0], d]);
39609
39610         var drawLayers = iD.svg.Layers(projection, context).only('osm').dimensions(d),
39611             drawVertices = iD.svg.Vertices(projection, context),
39612             drawLines = iD.svg.Lines(projection, context),
39613             drawTurns = iD.svg.Turns(projection, context);
39614
39615         enter
39616             .call(drawLayers)
39617             .selectAll('.surface')
39618             .call(hover);
39619
39620
39621         var surface = wrap.selectAll('.surface');
39622
39623         surface
39624             .dimensions(d)
39625             .call(drawVertices, graph, [vertex], filter, extent, z)
39626             .call(drawLines, graph, intersection.ways, filter)
39627             .call(drawTurns, graph, intersection.turns(fromNodeID));
39628
39629         surface
39630             .on('click.restrictions', click)
39631             .on('mouseover.restrictions', mouseover)
39632             .on('mouseout.restrictions', mouseout);
39633
39634         surface
39635             .selectAll('.selected')
39636             .classed('selected', false);
39637
39638         if (fromNodeID) {
39639             surface
39640                 .selectAll('.' + intersection.highways[fromNodeID].id)
39641                 .classed('selected', true);
39642         }
39643
39644         mouseout();
39645
39646         context.history()
39647             .on('change.restrictions', render);
39648
39649         d3.select(window)
39650             .on('resize.restrictions', function() {
39651                 wrap.dimensions(null);
39652                 render();
39653             });
39654
39655         function click() {
39656             var datum = d3.event.target.__data__;
39657             if (datum instanceof iD.Entity) {
39658                 fromNodeID = intersection.adjacentNodeId(datum.id);
39659                 render();
39660             } else if (datum instanceof iD.geo.Turn) {
39661                 if (datum.restriction) {
39662                     context.perform(
39663                         iD.actions.UnrestrictTurn(datum, projection),
39664                         t('operations.restriction.annotation.delete'));
39665                 } else {
39666                     context.perform(
39667                         iD.actions.RestrictTurn(datum, projection),
39668                         t('operations.restriction.annotation.create'));
39669                 }
39670             }
39671         }
39672
39673         function mouseover() {
39674             var datum = d3.event.target.__data__;
39675             if (datum instanceof iD.geo.Turn) {
39676                 var graph = context.graph(),
39677                     presets = context.presets(),
39678                     preset;
39679
39680                 if (datum.restriction) {
39681                     preset = presets.match(graph.entity(datum.restriction), graph);
39682                 } else {
39683                     preset = presets.item('type/restriction/' +
39684                         iD.geo.inferRestriction(
39685                             graph,
39686                             datum.from,
39687                             datum.via,
39688                             datum.to,
39689                             projection));
39690                 }
39691
39692                 wrap.selectAll('.restriction-help')
39693                     .text(t('operations.restriction.help.' +
39694                         (datum.restriction ? 'toggle_off' : 'toggle_on'),
39695                         {restriction: preset.name()}));
39696             }
39697         }
39698
39699         function mouseout() {
39700             wrap.selectAll('.restriction-help')
39701                 .text(t('operations.restriction.help.' +
39702                     (fromNodeID ? 'toggle' : 'select')));
39703         }
39704
39705         function render() {
39706             if (context.hasEntity(vertexID)) {
39707                 restrictions(selection);
39708             }
39709         }
39710     }
39711
39712     restrictions.entity = function(_) {
39713         if (!vertexID || vertexID !== _.id) {
39714             fromNodeID = null;
39715             vertexID = _.id;
39716         }
39717     };
39718
39719     restrictions.tags = function() {};
39720     restrictions.focus = function() {};
39721
39722     restrictions.off = function(selection) {
39723         selection.selectAll('.surface')
39724             .call(hover.off)
39725             .on('click.restrictions', null)
39726             .on('mouseover.restrictions', null)
39727             .on('mouseout.restrictions', null);
39728
39729         context.history()
39730             .on('change.restrictions', null);
39731
39732         d3.select(window)
39733             .on('resize.restrictions', null);
39734     };
39735
39736     return d3.rebind(restrictions, dispatch, 'on');
39737 };
39738 iD.ui.preset.textarea = function(field) {
39739     var dispatch = d3.dispatch('change'),
39740         input;
39741
39742     function i(selection) {
39743         input = selection.selectAll('textarea')
39744             .data([0]);
39745
39746         input.enter().append('textarea')
39747             .attr('id', 'preset-input-' + field.id)
39748             .attr('placeholder', field.placeholder() || t('inspector.unknown'))
39749             .attr('maxlength', 255);
39750
39751         input
39752             .on('input', change(true))
39753             .on('blur', change())
39754             .on('change', change());
39755     }
39756
39757     function change(onInput) {
39758         return function() {
39759             var t = {};
39760             t[field.key] = input.value() || undefined;
39761             dispatch.change(t, onInput);
39762         };
39763     }
39764
39765     i.tags = function(tags) {
39766         input.value(tags[field.key] || '');
39767     };
39768
39769     i.focus = function() {
39770         input.node().focus();
39771     };
39772
39773     return d3.rebind(i, dispatch, 'on');
39774 };
39775 iD.ui.preset.wikipedia = function(field, context) {
39776     var dispatch = d3.dispatch('change'),
39777         wikipedia = iD.services.wikipedia(),
39778         link, entity, lang, title;
39779
39780     function i(selection) {
39781         var langcombo = d3.combobox()
39782             .fetcher(function(value, cb) {
39783                 var v = value.toLowerCase();
39784
39785                 cb(iD.data.wikipedia.filter(function(d) {
39786                     return d[0].toLowerCase().indexOf(v) >= 0 ||
39787                         d[1].toLowerCase().indexOf(v) >= 0 ||
39788                         d[2].toLowerCase().indexOf(v) >= 0;
39789                 }).map(function(d) {
39790                     return { value: d[1] };
39791                 }));
39792             });
39793
39794         var titlecombo = d3.combobox()
39795             .fetcher(function(value, cb) {
39796
39797                 if (!value) value = context.entity(entity.id).tags.name || '';
39798                 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
39799
39800                 searchfn(language()[2], value, function(query, data) {
39801                     cb(data.map(function(d) {
39802                         return { value: d };
39803                     }));
39804                 });
39805             });
39806
39807         lang = selection.selectAll('input.wiki-lang')
39808             .data([0]);
39809
39810         lang.enter().append('input')
39811             .attr('type', 'text')
39812             .attr('class', 'wiki-lang')
39813             .attr('placeholder', t('translate.localized_translation_language'))
39814             .value('English');
39815
39816         lang
39817             .call(langcombo)
39818             .on('blur', changeLang)
39819             .on('change', changeLang);
39820
39821         title = selection.selectAll('input.wiki-title')
39822             .data([0]);
39823
39824         title.enter().append('input')
39825             .attr('type', 'text')
39826             .attr('class', 'wiki-title')
39827             .attr('id', 'preset-input-' + field.id);
39828
39829         title
39830             .call(titlecombo)
39831             .on('blur', change)
39832             .on('change', change);
39833
39834         link = selection.selectAll('a.wiki-link')
39835             .data([0]);
39836
39837         link.enter().append('a')
39838             .attr('class', 'wiki-link button-input-action minor')
39839             .attr('tabindex', -1)
39840             .attr('target', '_blank')
39841             .call(iD.svg.Icon('#icon-out-link', 'inline'));
39842     }
39843
39844     function language() {
39845         var value = lang.value().toLowerCase();
39846         var locale = iD.detect().locale.toLowerCase();
39847         var localeLanguage;
39848         return _.find(iD.data.wikipedia, function(d) {
39849             if (d[2] === locale) localeLanguage = d;
39850             return d[0].toLowerCase() === value ||
39851                 d[1].toLowerCase() === value ||
39852                 d[2] === value;
39853         }) || localeLanguage || ['English', 'English', 'en'];
39854     }
39855
39856     function changeLang() {
39857         lang.value(language()[1]);
39858         change();
39859     }
39860
39861     function change() {
39862         var value = title.value(),
39863             m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/),
39864             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
39865             anchor;
39866
39867         if (l) {
39868             // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
39869             value = decodeURIComponent(m[2]).replace(/_/g, ' ');
39870             if (m[3]) {
39871                 try {
39872                     // Best-effort `anchordecode:` implementation
39873                     anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
39874                 } catch (e) {
39875                     anchor = decodeURIComponent(m[3]);
39876                 }
39877                 value += '#' + anchor.replace(/_/g, ' ');
39878             }
39879             value = value.slice(0, 1).toUpperCase() + value.slice(1);
39880             lang.value(l[1]);
39881             title.value(value);
39882         }
39883
39884         var t = {};
39885         t[field.key] = value ? language()[2] + ':' + value : undefined;
39886         dispatch.change(t);
39887     }
39888
39889     i.tags = function(tags) {
39890         var value = tags[field.key] || '',
39891             m = value.match(/([^:]+):([^#]+)(?:#(.+))?/),
39892             l = m && _.find(iD.data.wikipedia, function(d) { return m[1] === d[2]; }),
39893             anchor = m && m[3];
39894
39895         // value in correct format
39896         if (l) {
39897             lang.value(l[1]);
39898             title.value(m[2] + (anchor ? ('#' + anchor) : ''));
39899             if (anchor) {
39900                 try {
39901                     // Best-effort `anchorencode:` implementation
39902                     anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
39903                 } catch (e) {
39904                     anchor = anchor.replace(/ /g, '_');
39905                 }
39906             }
39907             link.attr('href', 'https://' + m[1] + '.wikipedia.org/wiki/' +
39908                       m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : ''));
39909
39910         // unrecognized value format
39911         } else {
39912             title.value(value);
39913             if (value && value !== '') {
39914                 lang.value('');
39915             }
39916             link.attr('href', 'https://en.wikipedia.org/wiki/Special:Search?search=' + value);
39917         }
39918     };
39919
39920     i.entity = function(_) {
39921         entity = _;
39922     };
39923
39924     i.focus = function() {
39925         title.node().focus();
39926     };
39927
39928     return d3.rebind(i, dispatch, 'on');
39929 };
39930 iD.ui.intro.area = function(context, reveal) {
39931     var event = d3.dispatch('done'),
39932         timeout;
39933
39934     var step = {
39935         title: 'intro.areas.title'
39936     };
39937
39938     step.enter = function() {
39939         var playground = [-85.63552, 41.94159],
39940             corner = [-85.63565411045074, 41.9417715536927];
39941         context.map().centerZoom(playground, 19);
39942         reveal('button.add-area',
39943             t('intro.areas.add', { button: iD.ui.intro.icon('#icon-area', 'pre-text') }),
39944             { tooltipClass: 'intro-areas-add' });
39945
39946         context.on('enter.intro', addArea);
39947
39948         function addArea(mode) {
39949             if (mode.id !== 'add-area') return;
39950             context.on('enter.intro', drawArea);
39951
39952             var padding = 120 * Math.pow(2, context.map().zoom() - 19);
39953             var pointBox = iD.ui.intro.pad(corner, padding, context);
39954             reveal(pointBox, t('intro.areas.corner'));
39955
39956             context.map().on('move.intro', function() {
39957                 padding = 120 * Math.pow(2, context.map().zoom() - 19);
39958                 pointBox = iD.ui.intro.pad(corner, padding, context);
39959                 reveal(pointBox, t('intro.areas.corner'), {duration: 0});
39960             });
39961         }
39962
39963         function drawArea(mode) {
39964             if (mode.id !== 'draw-area') return;
39965             context.on('enter.intro', enterSelect);
39966
39967             var padding = 150 * Math.pow(2, context.map().zoom() - 19);
39968             var pointBox = iD.ui.intro.pad(playground, padding, context);
39969             reveal(pointBox, t('intro.areas.place'));
39970
39971             context.map().on('move.intro', function() {
39972                 padding = 150 * Math.pow(2, context.map().zoom() - 19);
39973                 pointBox = iD.ui.intro.pad(playground, padding, context);
39974                 reveal(pointBox, t('intro.areas.place'), {duration: 0});
39975             });
39976         }
39977
39978         function enterSelect(mode) {
39979             if (mode.id !== 'select') return;
39980             context.map().on('move.intro', null);
39981             context.on('enter.intro', null);
39982
39983             timeout = setTimeout(function() {
39984                 reveal('.preset-search-input',
39985                     t('intro.areas.search',
39986                     { name: context.presets().item('leisure/playground').name() }));
39987                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
39988             }, 500);
39989         }
39990
39991         function keySearch() {
39992             var first = d3.select('.preset-list-item:first-child');
39993             if (first.classed('preset-leisure-playground')) {
39994                 reveal(first.select('.preset-list-button').node(), t('intro.areas.choose'));
39995                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
39996                 d3.select('.preset-search-input').on('keyup.intro', null);
39997             }
39998         }
39999
40000         function selectedPreset() {
40001             reveal('.pane',
40002                 t('intro.areas.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
40003             context.on('exit.intro', event.done);
40004         }
40005     };
40006
40007     step.exit = function() {
40008         window.clearTimeout(timeout);
40009         context.on('enter.intro', null);
40010         context.on('exit.intro', null);
40011         context.history().on('change.intro', null);
40012         context.map().on('move.intro', null);
40013         d3.select('.preset-search-input').on('keyup.intro', null);
40014     };
40015
40016     return d3.rebind(step, event, 'on');
40017 };
40018 iD.ui.intro.line = function(context, reveal) {
40019     var event = d3.dispatch('done'),
40020         timeouts = [];
40021
40022     var step = {
40023         title: 'intro.lines.title'
40024     };
40025
40026     function timeout(f, t) {
40027         timeouts.push(window.setTimeout(f, t));
40028     }
40029
40030     function eventCancel() {
40031         d3.event.stopPropagation();
40032         d3.event.preventDefault();
40033     }
40034
40035     step.enter = function() {
40036         var centroid = [-85.62830, 41.95699];
40037         var midpoint = [-85.62975395449628, 41.95787501510204];
40038         var start = [-85.6297754121684, 41.95805253325314];
40039         var intersection = [-85.62974496187628, 41.95742515554585];
40040
40041         context.map().centerZoom(start, 18);
40042         reveal('button.add-line',
40043             t('intro.lines.add', { button: iD.ui.intro.icon('#icon-line', 'pre-text') }),
40044             { tooltipClass: 'intro-lines-add' });
40045
40046         context.on('enter.intro', addLine);
40047
40048         function addLine(mode) {
40049             if (mode.id !== 'add-line') return;
40050             context.on('enter.intro', drawLine);
40051
40052             var padding = 150 * Math.pow(2, context.map().zoom() - 18);
40053             var pointBox = iD.ui.intro.pad(start, padding, context);
40054             reveal(pointBox, t('intro.lines.start'));
40055
40056             context.map().on('move.intro', function() {
40057                 padding = 150 * Math.pow(2, context.map().zoom() - 18);
40058                 pointBox = iD.ui.intro.pad(start, padding, context);
40059                 reveal(pointBox, t('intro.lines.start'), {duration: 0});
40060             });
40061         }
40062
40063         function drawLine(mode) {
40064             if (mode.id !== 'draw-line') return;
40065             context.history().on('change.intro', addIntersection);
40066             context.on('enter.intro', retry);
40067
40068             var padding = 300 * Math.pow(2, context.map().zoom() - 19);
40069             var pointBox = iD.ui.intro.pad(midpoint, padding, context);
40070             reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}));
40071
40072             context.map().on('move.intro', function() {
40073                 padding = 300 * Math.pow(2, context.map().zoom() - 19);
40074                 pointBox = iD.ui.intro.pad(midpoint, padding, context);
40075                 reveal(pointBox, t('intro.lines.intersect', {name: t('intro.graph.flower_st')}), {duration: 0});
40076             });
40077         }
40078
40079         // ended line before creating intersection
40080         function retry(mode) {
40081             if (mode.id !== 'select') return;
40082             var pointBox = iD.ui.intro.pad(intersection, 30, context),
40083                 ids = mode.selectedIDs();
40084             reveal(pointBox, t('intro.lines.restart', {name: t('intro.graph.flower_st')}));
40085             d3.select(window).on('mousedown.intro', eventCancel, true);
40086
40087             timeout(function() {
40088                 context.replace(iD.actions.DeleteMultiple(ids));
40089                 step.exit();
40090                 step.enter();
40091             }, 3000);
40092         }
40093
40094         function addIntersection(changes) {
40095             if ( _.any(changes.created(), function(d) {
40096                 return d.type === 'node' && context.graph().parentWays(d).length > 1;
40097             })) {
40098                 context.history().on('change.intro', null);
40099                 context.on('enter.intro', enterSelect);
40100
40101                 var padding = 900 * Math.pow(2, context.map().zoom() - 19);
40102                 var pointBox = iD.ui.intro.pad(centroid, padding, context);
40103                 reveal(pointBox, t('intro.lines.finish'));
40104
40105                 context.map().on('move.intro', function() {
40106                     padding = 900 * Math.pow(2, context.map().zoom() - 19);
40107                     pointBox = iD.ui.intro.pad(centroid, padding, context);
40108                     reveal(pointBox, t('intro.lines.finish'), {duration: 0});
40109                 });
40110             }
40111         }
40112
40113         function enterSelect(mode) {
40114             if (mode.id !== 'select') return;
40115             context.map().on('move.intro', null);
40116             context.on('enter.intro', null);
40117             d3.select('#curtain').style('pointer-events', 'all');
40118
40119             presetCategory();
40120         }
40121
40122         function presetCategory() {
40123             timeout(function() {
40124                 d3.select('#curtain').style('pointer-events', 'none');
40125                 var road = d3.select('.preset-category-road .preset-list-button');
40126                 reveal(road.node(), t('intro.lines.road'));
40127                 road.one('click.intro', roadCategory);
40128             }, 500);
40129         }
40130
40131         function roadCategory() {
40132             timeout(function() {
40133                 var grid = d3.select('.subgrid');
40134                 reveal(grid.node(), t('intro.lines.residential'));
40135                 grid.selectAll(':not(.preset-highway-residential) .preset-list-button')
40136                     .one('click.intro', retryPreset);
40137                 grid.selectAll('.preset-highway-residential .preset-list-button')
40138                     .one('click.intro', roadDetails);
40139             }, 500);
40140         }
40141
40142         // selected wrong road type
40143         function retryPreset() {
40144             timeout(function() {
40145                 var preset = d3.select('.entity-editor-pane .preset-list-button');
40146                 reveal(preset.node(), t('intro.lines.wrong_preset'));
40147                 preset.one('click.intro', presetCategory);
40148             }, 500);
40149         }
40150
40151         function roadDetails() {
40152             reveal('.pane',
40153                 t('intro.lines.describe', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
40154             context.on('exit.intro', event.done);
40155         }
40156
40157     };
40158
40159     step.exit = function() {
40160         d3.select(window).on('mousedown.intro', null, true);
40161         d3.select('#curtain').style('pointer-events', 'none');
40162         timeouts.forEach(window.clearTimeout);
40163         context.on('enter.intro', null);
40164         context.on('exit.intro', null);
40165         context.map().on('move.intro', null);
40166         context.history().on('change.intro', null);
40167     };
40168
40169     return d3.rebind(step, event, 'on');
40170 };
40171 iD.ui.intro.navigation = function(context, reveal) {
40172     var event = d3.dispatch('done'),
40173         timeouts = [];
40174
40175     var step = {
40176         title: 'intro.navigation.title'
40177     };
40178
40179     function set(f, t) {
40180         timeouts.push(window.setTimeout(f, t));
40181     }
40182
40183     function eventCancel() {
40184         d3.event.stopPropagation();
40185         d3.event.preventDefault();
40186     }
40187
40188     step.enter = function() {
40189         var rect = context.surfaceRect(),
40190             map = {
40191                 left: rect.left + 10,
40192                 top: rect.top + 70,
40193                 width: rect.width - 70,
40194                 height: rect.height - 170
40195             };
40196
40197         context.map().centerZoom([-85.63591, 41.94285], 19);
40198
40199         reveal(map, t('intro.navigation.drag'));
40200
40201         context.map().on('move.intro', _.debounce(function() {
40202             context.map().on('move.intro', null);
40203             townhall();
40204             context.on('enter.intro', inspectTownHall);
40205         }, 400));
40206
40207         function townhall() {
40208             var hall = [-85.63645945147184, 41.942986488012565];
40209
40210             var point = context.projection(hall);
40211             if (point[0] < 0 || point[0] > rect.width ||
40212                 point[1] < 0 || point[1] > rect.height) {
40213                 context.map().center(hall);
40214             }
40215
40216             var box = iD.ui.intro.pointBox(hall, context);
40217             reveal(box, t('intro.navigation.select'));
40218
40219             context.map().on('move.intro', function() {
40220                 var box = iD.ui.intro.pointBox(hall, context);
40221                 reveal(box, t('intro.navigation.select'), {duration: 0});
40222             });
40223         }
40224
40225         function inspectTownHall(mode) {
40226             if (mode.id !== 'select') return;
40227             context.on('enter.intro', null);
40228             context.map().on('move.intro', null);
40229             set(function() {
40230                 reveal('.entity-editor-pane',
40231                     t('intro.navigation.pane', { button: iD.ui.intro.icon('#icon-close', 'pre-text') }));
40232                 context.on('exit.intro', streetSearch);
40233             }, 700);
40234         }
40235
40236         function streetSearch() {
40237             context.on('exit.intro', null);
40238             reveal('.search-header input',
40239                 t('intro.navigation.search', { name: t('intro.graph.spring_st') }));
40240             d3.select('.search-header input').on('keyup.intro', searchResult);
40241         }
40242
40243         function searchResult() {
40244             var first = d3.select('.feature-list-item:nth-child(0n+2)'),  // skip No Results item
40245                 firstName = first.select('.entity-name'),
40246                 name = t('intro.graph.spring_st');
40247
40248             if (!firstName.empty() && firstName.text() === name) {
40249                 reveal(first.node(), t('intro.navigation.choose', { name: name }));
40250                 context.on('exit.intro', selectedStreet);
40251                 d3.select('.search-header input')
40252                     .on('keydown.intro', eventCancel, true)
40253                     .on('keyup.intro', null);
40254             }
40255         }
40256
40257         function selectedStreet() {
40258             var springSt = [-85.63585099140167, 41.942506848938926];
40259             context.map().center(springSt);
40260             context.on('exit.intro', event.done);
40261             set(function() {
40262                 reveal('.entity-editor-pane',
40263                     t('intro.navigation.chosen', {
40264                         name: t('intro.graph.spring_st'),
40265                         button: iD.ui.intro.icon('#icon-close', 'pre-text')
40266                     }));
40267             }, 400);
40268         }
40269     };
40270
40271     step.exit = function() {
40272         timeouts.forEach(window.clearTimeout);
40273         context.map().on('move.intro', null);
40274         context.on('enter.intro', null);
40275         context.on('exit.intro', null);
40276         d3.select('.search-header input')
40277             .on('keydown.intro', null)
40278             .on('keyup.intro', null);
40279     };
40280
40281     return d3.rebind(step, event, 'on');
40282 };
40283 iD.ui.intro.point = function(context, reveal) {
40284     var event = d3.dispatch('done'),
40285         timeouts = [];
40286
40287     var step = {
40288         title: 'intro.points.title'
40289     };
40290
40291     function setTimeout(f, t) {
40292         timeouts.push(window.setTimeout(f, t));
40293     }
40294
40295     function eventCancel() {
40296         d3.event.stopPropagation();
40297         d3.event.preventDefault();
40298     }
40299
40300     step.enter = function() {
40301         context.map().centerZoom([-85.63279, 41.94394], 19);
40302         reveal('button.add-point',
40303             t('intro.points.add', { button: iD.ui.intro.icon('#icon-point', 'pre-text') }),
40304             { tooltipClass: 'intro-points-add' });
40305
40306         var corner = [-85.632481,41.944094];
40307
40308         context.on('enter.intro', addPoint);
40309
40310         function addPoint(mode) {
40311             if (mode.id !== 'add-point') return;
40312             context.on('enter.intro', enterSelect);
40313
40314             var pointBox = iD.ui.intro.pad(corner, 150, context);
40315             reveal(pointBox, t('intro.points.place'));
40316
40317             context.map().on('move.intro', function() {
40318                 pointBox = iD.ui.intro.pad(corner, 150, context);
40319                 reveal(pointBox, t('intro.points.place'), {duration: 0});
40320             });
40321         }
40322
40323         function enterSelect(mode) {
40324             if (mode.id !== 'select') return;
40325             context.map().on('move.intro', null);
40326             context.on('enter.intro', null);
40327
40328             setTimeout(function() {
40329                 reveal('.preset-search-input',
40330                     t('intro.points.search', {name: context.presets().item('amenity/cafe').name()}));
40331                 d3.select('.preset-search-input').on('keyup.intro', keySearch);
40332             }, 500);
40333         }
40334
40335         function keySearch() {
40336             var first = d3.select('.preset-list-item:first-child');
40337             if (first.classed('preset-amenity-cafe')) {
40338                 reveal(first.select('.preset-list-button').node(), t('intro.points.choose'));
40339                 d3.selection.prototype.one.call(context.history(), 'change.intro', selectedPreset);
40340                 d3.select('.preset-search-input')
40341                     .on('keydown.intro', eventCancel, true)
40342                     .on('keyup.intro', null);
40343             }
40344         }
40345
40346         function selectedPreset() {
40347             setTimeout(function() {
40348                 reveal('.entity-editor-pane', t('intro.points.describe'), {tooltipClass: 'intro-points-describe'});
40349                 context.history().on('change.intro', closeEditor);
40350                 context.on('exit.intro', selectPoint);
40351             }, 400);
40352         }
40353
40354         function closeEditor() {
40355             d3.select('.preset-search-input').on('keydown.intro', null);
40356             context.history().on('change.intro', null);
40357             reveal('.entity-editor-pane',
40358                 t('intro.points.close', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
40359         }
40360
40361         function selectPoint() {
40362             context.on('exit.intro', null);
40363             context.history().on('change.intro', null);
40364             context.on('enter.intro', enterReselect);
40365
40366             var pointBox = iD.ui.intro.pad(corner, 150, context);
40367             reveal(pointBox, t('intro.points.reselect'));
40368
40369             context.map().on('move.intro', function() {
40370                 pointBox = iD.ui.intro.pad(corner, 150, context);
40371                 reveal(pointBox, t('intro.points.reselect'), {duration: 0});
40372             });
40373         }
40374
40375         function enterReselect(mode) {
40376             if (mode.id !== 'select') return;
40377             context.map().on('move.intro', null);
40378             context.on('enter.intro', null);
40379
40380             setTimeout(function() {
40381                 reveal('.entity-editor-pane',
40382                     t('intro.points.fixname', { button: iD.ui.intro.icon('#icon-apply', 'pre-text') }));
40383                 context.on('exit.intro', deletePoint);
40384             }, 500);
40385         }
40386
40387         function deletePoint() {
40388             context.on('exit.intro', null);
40389             context.on('enter.intro', enterDelete);
40390
40391             var pointBox = iD.ui.intro.pad(corner, 150, context);
40392             reveal(pointBox, t('intro.points.reselect_delete'));
40393
40394             context.map().on('move.intro', function() {
40395                 pointBox = iD.ui.intro.pad(corner, 150, context);
40396                 reveal(pointBox, t('intro.points.reselect_delete'), {duration: 0});
40397             });
40398         }
40399
40400         function enterDelete(mode) {
40401             if (mode.id !== 'select') return;
40402             context.map().on('move.intro', null);
40403             context.on('enter.intro', null);
40404             context.on('exit.intro', deletePoint);
40405             context.map().on('move.intro', deletePoint);
40406             context.history().on('change.intro', deleted);
40407
40408             setTimeout(function() {
40409                 var node = d3.select('.radial-menu-item-delete').node();
40410                 var pointBox = iD.ui.intro.pad(node.getBoundingClientRect(), 50, context);
40411                 reveal(pointBox,
40412                     t('intro.points.delete', { button: iD.ui.intro.icon('#operation-delete', 'pre-text') }));
40413             }, 300);
40414         }
40415
40416         function deleted(changed) {
40417             if (changed.deleted().length) event.done();
40418         }
40419
40420     };
40421
40422     step.exit = function() {
40423         timeouts.forEach(window.clearTimeout);
40424         context.on('exit.intro', null);
40425         context.on('enter.intro', null);
40426         context.map().on('move.intro', null);
40427         context.history().on('change.intro', null);
40428         d3.select('.preset-search-input')
40429             .on('keyup.intro', null)
40430             .on('keydown.intro', null);
40431     };
40432
40433     return d3.rebind(step, event, 'on');
40434 };
40435 iD.ui.intro.startEditing = function(context, reveal) {
40436     var event = d3.dispatch('done', 'startEditing'),
40437         modal,
40438         timeouts = [];
40439
40440     var step = {
40441         title: 'intro.startediting.title'
40442     };
40443
40444     function timeout(f, t) {
40445         timeouts.push(window.setTimeout(f, t));
40446     }
40447
40448     step.enter = function() {
40449         reveal('.map-control.help-control',
40450             t('intro.startediting.help', { button: iD.ui.intro.icon('#icon-help', 'pre-text') }));
40451
40452         timeout(function() {
40453             reveal('#bar button.save', t('intro.startediting.save'));
40454         }, 5000);
40455
40456         timeout(function() {
40457             reveal('#surface');
40458         }, 10000);
40459
40460         timeout(function() {
40461             modal = iD.ui.modal(context.container());
40462
40463             modal.select('.modal')
40464                 .attr('class', 'modal-splash modal col6');
40465
40466             modal.selectAll('.close').remove();
40467
40468             var startbutton = modal.select('.content')
40469                 .attr('class', 'fillL')
40470                     .append('button')
40471                         .attr('class', 'modal-section huge-modal-button')
40472                         .on('click', function() {
40473                             modal.remove();
40474                         });
40475
40476                 startbutton.append('div')
40477                     .attr('class','illustration');
40478                 startbutton.append('h2')
40479                     .text(t('intro.startediting.start'));
40480
40481             event.startEditing();
40482         }, 10500);
40483     };
40484
40485     step.exit = function() {
40486         if (modal) modal.remove();
40487         timeouts.forEach(window.clearTimeout);
40488     };
40489
40490     return d3.rebind(step, event, 'on');
40491 };
40492 iD.presets = function() {
40493
40494     // an iD.presets.Collection with methods for
40495     // loading new data and returning defaults
40496
40497     var all = iD.presets.Collection([]),
40498         defaults = { area: all, line: all, point: all, vertex: all, relation: all },
40499         fields = {},
40500         universal = [],
40501         recent = iD.presets.Collection([]);
40502
40503     // Index of presets by (geometry, tag key).
40504     var index = {
40505         point: {},
40506         vertex: {},
40507         line: {},
40508         area: {},
40509         relation: {}
40510     };
40511
40512     all.match = function(entity, resolver) {
40513         var geometry = entity.geometry(resolver),
40514             geometryMatches = index[geometry],
40515             best = -1,
40516             match;
40517
40518         for (var k in entity.tags) {
40519             var keyMatches = geometryMatches[k];
40520             if (!keyMatches) continue;
40521
40522             for (var i = 0; i < keyMatches.length; i++) {
40523                 var score = keyMatches[i].matchScore(entity);
40524                 if (score > best) {
40525                     best = score;
40526                     match = keyMatches[i];
40527                 }
40528             }
40529         }
40530
40531         return match || all.item(geometry);
40532     };
40533
40534     // Because of the open nature of tagging, iD will never have a complete
40535     // list of tags used in OSM, so we want it to have logic like "assume
40536     // that a closed way with an amenity tag is an area, unless the amenity
40537     // is one of these specific types". This function computes a structure
40538     // that allows testing of such conditions, based on the presets designated
40539     // as as supporting (or not supporting) the area geometry.
40540     //
40541     // The returned object L is a whitelist/blacklist of tags. A closed way
40542     // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
40543     // (see `iD.Way#isArea()`). In other words, the keys of L form the whitelist,
40544     // and the subkeys form the blacklist.
40545     all.areaKeys = function() {
40546         var areaKeys = {},
40547             ignore = ['barrier', 'highway', 'footway', 'railway', 'type'],
40548             presets = _.reject(all.collection, 'suggestion');
40549
40550         // whitelist
40551         presets.forEach(function(d) {
40552             for (var key in d.tags) break;
40553             if (!key) return;
40554             if (ignore.indexOf(key) !== -1) return;
40555
40556             if (d.geometry.indexOf('area') !== -1) {
40557                 areaKeys[key] = areaKeys[key] || {};
40558             }
40559         });
40560
40561         // blacklist
40562         presets.forEach(function(d) {
40563             for (var key in d.tags) break;
40564             if (!key) return;
40565             if (ignore.indexOf(key) !== -1) return;
40566
40567             var value = d.tags[key];
40568             if (d.geometry.indexOf('area') === -1 &&
40569                 d.geometry.indexOf('line') !== -1 &&
40570                 key in areaKeys && value !== '*') {
40571                 areaKeys[key][value] = true;
40572             }
40573         });
40574
40575         return areaKeys;
40576     };
40577
40578     all.load = function(d) {
40579
40580         if (d.fields) {
40581             _.forEach(d.fields, function(d, id) {
40582                 fields[id] = iD.presets.Field(id, d);
40583                 if (d.universal) universal.push(fields[id]);
40584             });
40585         }
40586
40587         if (d.presets) {
40588             _.forEach(d.presets, function(d, id) {
40589                 all.collection.push(iD.presets.Preset(id, d, fields));
40590             });
40591         }
40592
40593         if (d.categories) {
40594             _.forEach(d.categories, function(d, id) {
40595                 all.collection.push(iD.presets.Category(id, d, all));
40596             });
40597         }
40598
40599         if (d.defaults) {
40600             var getItem = _.bind(all.item, all);
40601             defaults = {
40602                 area: iD.presets.Collection(d.defaults.area.map(getItem)),
40603                 line: iD.presets.Collection(d.defaults.line.map(getItem)),
40604                 point: iD.presets.Collection(d.defaults.point.map(getItem)),
40605                 vertex: iD.presets.Collection(d.defaults.vertex.map(getItem)),
40606                 relation: iD.presets.Collection(d.defaults.relation.map(getItem))
40607             };
40608         }
40609
40610         for (var i = 0; i < all.collection.length; i++) {
40611             var preset = all.collection[i],
40612                 geometry = preset.geometry;
40613
40614             for (var j = 0; j < geometry.length; j++) {
40615                 var g = index[geometry[j]];
40616                 for (var k in preset.tags) {
40617                     (g[k] = g[k] || []).push(preset);
40618                 }
40619             }
40620         }
40621
40622         return all;
40623     };
40624
40625     all.field = function(id) {
40626         return fields[id];
40627     };
40628
40629     all.universal = function() {
40630         return universal;
40631     };
40632
40633     all.defaults = function(geometry, n) {
40634         var rec = recent.matchGeometry(geometry).collection.slice(0, 4),
40635             def = _.uniq(rec.concat(defaults[geometry].collection)).slice(0, n - 1);
40636         return iD.presets.Collection(_.unique(rec.concat(def).concat(all.item(geometry))));
40637     };
40638
40639     all.choose = function(preset) {
40640         if (!preset.isFallback()) {
40641             recent = iD.presets.Collection(_.unique([preset].concat(recent.collection)));
40642         }
40643         return all;
40644     };
40645
40646     return all;
40647 };
40648 iD.presets.Category = function(id, category, all) {
40649     category = _.clone(category);
40650
40651     category.id = id;
40652
40653     category.members = iD.presets.Collection(category.members.map(function(id) {
40654         return all.item(id);
40655     }));
40656
40657     category.matchGeometry = function(geometry) {
40658         return category.geometry.indexOf(geometry) >= 0;
40659     };
40660
40661     category.matchScore = function() { return -1; };
40662
40663     category.name = function() {
40664         return t('presets.categories.' + id + '.name', {'default': id});
40665     };
40666
40667     category.terms = function() {
40668         return [];
40669     };
40670
40671     return category;
40672 };
40673 iD.presets.Collection = function(collection) {
40674
40675     var maxSearchResults = 50,
40676         maxSuggestionResults = 10;
40677
40678     var presets = {
40679
40680         collection: collection,
40681
40682         item: function(id) {
40683             return _.find(collection, function(d) {
40684                 return d.id === id;
40685             });
40686         },
40687
40688         matchGeometry: function(geometry) {
40689             return iD.presets.Collection(collection.filter(function(d) {
40690                 return d.matchGeometry(geometry);
40691             }));
40692         },
40693
40694         search: function(value, geometry) {
40695             if (!value) return this;
40696
40697             value = value.toLowerCase();
40698
40699             var searchable = _.filter(collection, function(a) {
40700                     return a.searchable !== false && a.suggestion !== true;
40701                 }),
40702                 suggestions = _.filter(collection, function(a) {
40703                     return a.suggestion === true;
40704                 });
40705
40706             function leading(a) {
40707                 var index = a.indexOf(value);
40708                 return index === 0 || a[index - 1] === ' ';
40709             }
40710
40711             // matches value to preset.name
40712             var leading_name = _.filter(searchable, function(a) {
40713                     return leading(a.name().toLowerCase());
40714                 }).sort(function(a, b) {
40715                     var i = a.name().toLowerCase().indexOf(value) - b.name().toLowerCase().indexOf(value);
40716                     if (i === 0) return a.name().length - b.name().length;
40717                     else return i;
40718                 });
40719
40720             // matches value to preset.terms values
40721             var leading_terms = _.filter(searchable, function(a) {
40722                     return _.any(a.terms() || [], leading);
40723                 });
40724
40725             // matches value to preset.tags values
40726             var leading_tag_values = _.filter(searchable, function(a) {
40727                     return _.any(_.without(_.values(a.tags || {}), '*'), leading);
40728                 });
40729
40730
40731             // finds close matches to value in preset.name
40732             var levenstein_name = searchable.map(function(a) {
40733                     return {
40734                         preset: a,
40735                         dist: iD.util.editDistance(value, a.name().toLowerCase())
40736                     };
40737                 }).filter(function(a) {
40738                     return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
40739                 }).sort(function(a, b) {
40740                     return a.dist - b.dist;
40741                 }).map(function(a) {
40742                     return a.preset;
40743                 });
40744
40745             // finds close matches to value in preset.terms
40746             var leventstein_terms = _.filter(searchable, function(a) {
40747                     return _.any(a.terms() || [], function(b) {
40748                         return iD.util.editDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
40749                     });
40750                 });
40751
40752             function suggestionName(name) {
40753                 var nameArray = name.split(' - ');
40754                 if (nameArray.length > 1) {
40755                     name = nameArray.slice(0, nameArray.length-1).join(' - ');
40756                 }
40757                 return name.toLowerCase();
40758             }
40759
40760             var leading_suggestions = _.filter(suggestions, function(a) {
40761                     return leading(suggestionName(a.name()));
40762                 }).sort(function(a, b) {
40763                     a = suggestionName(a.name());
40764                     b = suggestionName(b.name());
40765                     var i = a.indexOf(value) - b.indexOf(value);
40766                     if (i === 0) return a.length - b.length;
40767                     else return i;
40768                 });
40769
40770             var leven_suggestions = suggestions.map(function(a) {
40771                     return {
40772                         preset: a,
40773                         dist: iD.util.editDistance(value, suggestionName(a.name()))
40774                     };
40775                 }).filter(function(a) {
40776                     return a.dist + Math.min(value.length - suggestionName(a.preset.name()).length, 0) < 1;
40777                 }).sort(function(a, b) {
40778                     return a.dist - b.dist;
40779                 }).map(function(a) {
40780                     return a.preset;
40781                 });
40782
40783             var other = presets.item(geometry);
40784
40785             var results = leading_name.concat(
40786                             leading_terms,
40787                             leading_tag_values,
40788                             leading_suggestions.slice(0, maxSuggestionResults+5),
40789                             levenstein_name,
40790                             leventstein_terms,
40791                             leven_suggestions.slice(0, maxSuggestionResults)
40792                         ).slice(0, maxSearchResults-1);
40793
40794             return iD.presets.Collection(_.unique(
40795                     results.concat(other)
40796                 ));
40797         }
40798     };
40799
40800     return presets;
40801 };
40802 iD.presets.Field = function(id, field) {
40803     field = _.clone(field);
40804
40805     field.id = id;
40806
40807     field.matchGeometry = function(geometry) {
40808         return !field.geometry || field.geometry === geometry;
40809     };
40810
40811     field.t = function(scope, options) {
40812         return t('presets.fields.' + id + '.' + scope, options);
40813     };
40814
40815     field.label = function() {
40816         return field.t('label', {'default': id});
40817     };
40818
40819     var placeholder = field.placeholder;
40820     field.placeholder = function() {
40821         return field.t('placeholder', {'default': placeholder});
40822     };
40823
40824     return field;
40825 };
40826 iD.presets.Preset = function(id, preset, fields) {
40827     preset = _.clone(preset);
40828
40829     preset.id = id;
40830     preset.fields = (preset.fields || []).map(getFields);
40831     preset.geometry = (preset.geometry || []);
40832
40833     function getFields(f) {
40834         return fields[f];
40835     }
40836
40837     preset.matchGeometry = function(geometry) {
40838         return preset.geometry.indexOf(geometry) >= 0;
40839     };
40840
40841     var matchScore = preset.matchScore || 1;
40842     preset.matchScore = function(entity) {
40843         var tags = preset.tags,
40844             score = 0;
40845
40846         for (var t in tags) {
40847             if (entity.tags[t] === tags[t]) {
40848                 score += matchScore;
40849             } else if (tags[t] === '*' && t in entity.tags) {
40850                 score += matchScore / 2;
40851             } else {
40852                 return -1;
40853             }
40854         }
40855
40856         return score;
40857     };
40858
40859     preset.t = function(scope, options) {
40860         return t('presets.presets.' + id + '.' + scope, options);
40861     };
40862
40863     var name = preset.name;
40864     preset.name = function() {
40865         if (preset.suggestion) {
40866             id = id.split('/');
40867             id = id[0] + '/' + id[1];
40868             return name + ' - ' + t('presets.presets.' + id + '.name');
40869         }
40870         return preset.t('name', {'default': name});
40871     };
40872
40873     preset.terms = function() {
40874         return preset.t('terms', {'default': ''}).toLowerCase().trim().split(/\s*,+\s*/);
40875     };
40876
40877     preset.isFallback = function() {
40878         var tagCount = Object.keys(preset.tags).length;
40879         return tagCount === 0 || (tagCount === 1 && preset.tags.hasOwnProperty('area'));
40880     };
40881
40882     preset.reference = function(geometry) {
40883         var key = Object.keys(preset.tags)[0],
40884             value = preset.tags[key];
40885
40886         if (geometry === 'relation' && key === 'type') {
40887             return { rtype: value };
40888         } else if (value === '*') {
40889             return { key: key };
40890         } else {
40891             return { key: key, value: value };
40892         }
40893     };
40894
40895     var removeTags = preset.removeTags || preset.tags;
40896     preset.removeTags = function(tags, geometry) {
40897         tags = _.omit(tags, _.keys(removeTags));
40898
40899         for (var f in preset.fields) {
40900             var field = preset.fields[f];
40901             if (field.matchGeometry(geometry) && field.default === tags[field.key]) {
40902                 delete tags[field.key];
40903             }
40904         }
40905
40906         delete tags.area;
40907         return tags;
40908     };
40909
40910     var applyTags = preset.addTags || preset.tags;
40911     preset.applyTags = function(tags, geometry) {
40912         var k;
40913
40914         tags = _.clone(tags);
40915
40916         for (k in applyTags) {
40917             if (applyTags[k] === '*') {
40918                 tags[k] = 'yes';
40919             } else {
40920                 tags[k] = applyTags[k];
40921             }
40922         }
40923
40924         // Add area=yes if necessary.
40925         // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
40926         // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
40927         // 2. chosen preset doesn't have a key in areaKeys (`railway=station`)
40928         if (geometry === 'area') {
40929             var needsAreaTag = true;
40930             if (preset.geometry.indexOf('line') === -1) {
40931                 for (k in applyTags) {
40932                     if (k in iD.areaKeys) {
40933                         needsAreaTag = false;
40934                         break;
40935                     }
40936                 }
40937             }
40938             if (needsAreaTag) {
40939                 tags.area = 'yes';
40940             }
40941         }
40942
40943         for (var f in preset.fields) {
40944             var field = preset.fields[f];
40945             if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
40946                 tags[field.key] = field.default;
40947             }
40948         }
40949
40950         return tags;
40951     };
40952
40953     return preset;
40954 };
40955 iD.validations = {};
40956 iD.validations.DeprecatedTag = function() {
40957
40958     var validation = function(changes) {
40959         var warnings = [];
40960         for (var i = 0; i < changes.created.length; i++) {
40961             var change = changes.created[i],
40962                 deprecatedTags = change.deprecatedTags();
40963
40964             if (!_.isEmpty(deprecatedTags)) {
40965                 var tags = iD.util.tagText({ tags: deprecatedTags });
40966                 warnings.push({
40967                     id: 'deprecated_tags',
40968                     message: t('validations.deprecated_tags', { tags: tags }),
40969                     entity: change
40970                 });
40971             }
40972         }
40973         return warnings;
40974     };
40975
40976     return validation;
40977 };
40978 iD.validations.ManyDeletions = function() {
40979     var threshold = 100;
40980
40981     var validation = function(changes) {
40982         var warnings = [];
40983         if (changes.deleted.length > threshold) {
40984             warnings.push({
40985                 id: 'many_deletions',
40986                 message: t('validations.many_deletions', { n: changes.deleted.length })
40987             });
40988         }
40989         return warnings;
40990     };
40991
40992     return validation;
40993 };
40994 iD.validations.MissingTag = function() {
40995
40996     // Slightly stricter check than Entity#isUsed (#3091)
40997     function hasTags(entity, graph) {
40998         return _.without(Object.keys(entity.tags), 'area', 'name').length > 0 ||
40999             graph.parentRelations(entity).length > 0;
41000     }
41001
41002     var validation = function(changes, graph) {
41003         var warnings = [];
41004         for (var i = 0; i < changes.created.length; i++) {
41005             var change = changes.created[i],
41006                 geometry = change.geometry(graph);
41007
41008             if ((geometry === 'point' || geometry === 'line' || geometry === 'area') && !hasTags(change, graph)) {
41009                 warnings.push({
41010                     id: 'missing_tag',
41011                     message: t('validations.untagged_' + geometry),
41012                     tooltip: t('validations.untagged_' + geometry + '_tooltip'),
41013                     entity: change
41014                 });
41015             }
41016         }
41017         return warnings;
41018     };
41019
41020     return validation;
41021 };
41022 iD.validations.TagSuggestsArea = function() {
41023
41024     // https://github.com/openstreetmap/josm/blob/mirror/src/org/
41025     // openstreetmap/josm/data/validation/tests/UnclosedWays.java#L80
41026     function tagSuggestsArea(tags) {
41027         if (_.isEmpty(tags)) return false;
41028
41029         var presence = ['landuse', 'amenities', 'tourism', 'shop'];
41030         for (var i = 0; i < presence.length; i++) {
41031             if (tags[presence[i]] !== undefined) {
41032                 return presence[i] + '=' + tags[presence[i]];
41033             }
41034         }
41035
41036         if (tags.building && tags.building === 'yes') return 'building=yes';
41037     }
41038
41039     var validation = function(changes, graph) {
41040         var warnings = [];
41041         for (var i = 0; i < changes.created.length; i++) {
41042             var change = changes.created[i],
41043                 geometry = change.geometry(graph),
41044                 suggestion = (geometry === 'line' ? tagSuggestsArea(change.tags) : undefined);
41045
41046             if (suggestion) {
41047                 warnings.push({
41048                     id: 'tag_suggests_area',
41049                     message: t('validations.tag_suggests_area', { tag: suggestion }),
41050                     entity: change
41051                 });
41052             }
41053         }
41054         return warnings;
41055     };
41056
41057     return validation;
41058 };
41059 })();
41060 window.locale = { _current: 'en' };
41061
41062 locale.current = function(_) {
41063     if (!arguments.length) return locale._current;
41064     if (locale[_] !== undefined) locale._current = _;
41065     else if (locale[_.split('-')[0]]) locale._current = _.split('-')[0];
41066     return locale;
41067 };
41068
41069 function t(s, o, loc) {
41070     loc = loc || locale._current;
41071
41072     var path = s.split(".").reverse(),
41073         rep = locale[loc];
41074
41075     while (rep !== undefined && path.length) rep = rep[path.pop()];
41076
41077     if (rep !== undefined) {
41078         if (o) for (var k in o) rep = rep.replace('{' + k + '}', o[k]);
41079         return rep;
41080     }
41081
41082     if (loc !== 'en') {
41083         return t(s, o, 'en');
41084     }
41085
41086     if (o && 'default' in o) {
41087         return o['default'];
41088     }
41089
41090     var missing = 'Missing ' + loc + ' translation: ' + s;
41091     if (typeof console !== "undefined") console.error(missing);
41092
41093     return missing;
41094 }
41095 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 (required)","message_label":"Changeset comment","upload_explanation":"The changes you upload will be visible on all maps that use OpenStreetMap data.","upload_explanation_with_user":"The changes you upload as {user} will be visible on all maps that use OpenStreetMap data.","save":"Save","cancel":"Cancel","changes":"{count} Changes","warnings":"Warnings","modified":"Modified","deleted":"Deleted","created":"Created","about_changeset_comments":"About changeset comments","about_changeset_comments_link":"//wiki.openstreetmap.org/wiki/Good_changeset_comments","google_warning":"You mentioned Google in this comment: remember that copying from Google Maps is strictly forbidden.","google_warning_link":"http://www.openstreetmap.org/copyright"},"contributors":{"list":"Edits by {users}","truncated_list":"Edits by {users} and {count} others"},"infobox":{"selected":"{n} selected","geometry":"Geometry","closed":"closed","center":"Center","perimeter":"Perimeter","length":"Length","area":"Area","centroid":"Centroid","location":"Location","metric":"Metric","imperial":"Imperial"},"geometry":{"point":"point","vertex":"vertex","line":"line","area":"area","relation":"relation"},"geocoder":{"search":"Search worldwide...","no_results_visible":"No results in visible map area","no_results_worldwide":"No results found"},"geolocate":{"title":"Show My Location","locating":"Locating, please wait..."},"inspector":{"no_documentation_combination":"There is no documentation available for this tag combination","no_documentation_key":"There is no documentation available for this key","show_more":"Show More","view_on_osm":"View on openstreetmap.org","all_fields":"All fields","all_tags":"All tags","all_members":"All members","all_relations":"All relations","new_relation":"New relation...","role":"Role","choose":"Select feature type","results":"{n} results for {search}","reference":"View on OpenStreetMap Wiki","back_tooltip":"Change feature","remove":"Remove","search":"Search","multiselect":"Selected items","unknown":"Unknown","incomplete":"<not downloaded>","feature_list":"Search features","edit":"Edit feature","check":{"yes":"Yes","no":"No"},"add":"Add","none":"None","node":"Node","way":"Way","relation":"Relation","location":"Location","add_fields":"Add field:"},"background":{"title":"Background","description":"Background settings","percent_brightness":"{opacity}% brightness","none":"None","best_imagery":"Best known imagery source for this location","custom":"Custom","custom_button":"Edit custom background","custom_prompt":"Enter a tile URL template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme.","fix_misalignment":"Adjust imagery offset","imagery_source_faq":"Where does this imagery come from?","reset":"reset","offset":"Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters.","minimap":{"description":"Minimap","tooltip":"Show a zoomed out map to help locate the area currently displayed."}},"map_data":{"title":"Map Data","description":"Map Data","data_layers":"Data Layers","fill_area":"Fill Areas","map_features":"Map Features","autohidden":"These features have been automatically hidden because too many would be shown on the screen.  You can zoom in to edit them."},"feature":{"points":{"description":"Points","tooltip":"Points of Interest"},"traffic_roads":{"description":"Traffic Roads","tooltip":"Highways, Streets, etc."},"service_roads":{"description":"Service Roads","tooltip":"Service Roads, Parking Aisles, Tracks, etc."},"paths":{"description":"Paths","tooltip":"Sidewalks, Foot Paths, Cycle Paths, etc."},"buildings":{"description":"Buildings","tooltip":"Buildings, Shelters, Garages, etc."},"landuse":{"description":"Landuse Features","tooltip":"Forests, Farmland, Parks, Residential, Commercial, etc."},"boundaries":{"description":"Boundaries","tooltip":"Administrative Boundaries"},"water":{"description":"Water Features","tooltip":"Rivers, Lakes, Ponds, Basins, etc."},"rail":{"description":"Rail Features","tooltip":"Railways"},"power":{"description":"Power Features","tooltip":"Power Lines, Power Plants, Substations, etc."},"past_future":{"description":"Past/Future","tooltip":"Proposed, Construction, Abandoned, Demolished, etc."},"others":{"description":"Others","tooltip":"Everything Else"}},"area_fill":{"wireframe":{"description":"No Fill (Wireframe)","tooltip":"Enabling wireframe mode makes it easy to see the background imagery."},"partial":{"description":"Partial Fill","tooltip":"Areas are drawn with fill only around their inner edges. (Recommended for beginner mappers)"},"full":{"description":"Full Fill","tooltip":"Areas are drawn fully filled."}},"restore":{"heading":"You have unsaved changes","description":"Do you wish to restore unsaved changes from a previous editing session?","restore":"Restore","reset":"Reset"},"save":{"title":"Save","help":"Save changes to OpenStreetMap, making them visible to other users.","no_changes":"No changes to save.","error":"Errors occurred while trying to save","status_code":"Server returned status code {code}","unknown_error_details":"Please ensure you are connected to the internet.","uploading":"Uploading changes to OpenStreetMap.","unsaved_changes":"You have unsaved changes","conflict":{"header":"Resolve conflicting edits","count":"Conflict {num} of {total}","previous":"< Previous","next":"Next >","keep_local":"Keep mine","keep_remote":"Use theirs","restore":"Restore","delete":"Leave Deleted","download_changes":"Or download your changes.","done":"All conflicts resolved!","help":"Another user changed some of the same map features you changed.\nClick on each item below for more details about the conflict, and choose whether to keep\nyour changes or the other user's changes.\n"}},"merge_remote_changes":{"conflict":{"deleted":"This object has been deleted by {user}.","location":"This object was moved by both you and {user}.","nodelist":"Nodes were changed by both you and {user}.","memberlist":"Relation members were changed by both you and {user}.","tags":"You changed the <b>{tag}</b> tag to \"{local}\" and {user} changed it to \"{remote}\"."}},"success":{"edited_osm":"Edited OSM!","just_edited":"You just edited OpenStreetMap!","view_on_osm":"View on OSM","facebook":"Share on Facebook","twitter":"Share on Twitter","google":"Share on Google+","help_html":"Your changes should appear in the \"Standard\" layer in a few minutes. Other layers, and certain features, may take longer.","help_link_text":"Details","help_link_url":"https://wiki.openstreetmap.org/wiki/FAQ#I_have_just_made_some_changes_to_the_map._How_do_I_get_to_see_my_changes.3F"},"confirm":{"okay":"Okay","cancel":"Cancel"},"splash":{"welcome":"Welcome to the iD OpenStreetMap editor","text":"iD is a friendly but powerful tool for contributing to the world's best free world map. This is version {version}. For more information see {website} and report bugs at {github}.","walkthrough":"Start the Walkthrough","start":"Edit Now"},"source_switch":{"live":"live","lose_changes":"You have unsaved changes. Switching the map server will discard them. Are you sure you want to switch servers?","dev":"dev"},"tag_reference":{"description":"Description","on_wiki":"{tag} on wiki.osm.org","used_with":"used with {type}"},"validations":{"untagged_point":"Untagged point","untagged_line":"Untagged line","untagged_area":"Untagged area","many_deletions":"You're deleting {n} objects. Are you sure you want to do this? This will delete them from the map that everyone else sees on openstreetmap.org.","tag_suggests_area":"The tag {tag} suggests line should be area, but it is not an area","untagged_point_tooltip":"Select a feature type that describes what this point is.","untagged_line_tooltip":"Select a feature type that describes what this line is.","untagged_area_tooltip":"Select a feature type that describes what this area is.","deprecated_tags":"Deprecated tags: {tags}"},"zoom":{"in":"Zoom In","out":"Zoom Out"},"cannot_zoom":"Cannot zoom out further in current mode.","full_screen":"Toggle Full Screen","gpx":{"local_layer":"Local GPX file","drag_drop":"Drag and drop a .gpx file on the page, or click the button to the right to browse","zoom":"Zoom to GPX track","browse":"Browse for a .gpx file"},"mapillary_images":{"tooltip":"Street-level photos from Mapillary","title":"Photo Overlay (Mapillary)"},"mapillary_signs":{"tooltip":"Traffic signs from Mapillary","title":"Traffic Sign Overlay (Mapillary)"},"mapillary":{"view_on_mapillary":"View this image on Mapillary"},"help":{"title":"Help","help":"# Help\n\nThis is an editor for [OpenStreetMap](http://www.openstreetmap.org/), the\nfree and editable map of the world. You can use it to add and update\ndata in your area, making an open-source and open-data map of the world\nbetter for everyone.\n\nEdits that you make on this map will be visible to everyone who uses\nOpenStreetMap. In order to make an edit, you'll need to\n[log in](https://www.openstreetmap.org/login).\n\nThe [iD editor](http://ideditor.com/) is a collaborative project with [source\ncode available on GitHub](https://github.com/openstreetmap/iD).\n","editing_saving":"# Editing & Saving\n\nThis editor is designed to work primarily online, and you're accessing\nit through a website right now.\n\n### Selecting Features\n\nTo select a map feature, like a road or point of interest, click\non it on the map. This will highlight the selected feature, open a panel with\ndetails about it, and show a menu of things you can do with the feature.\n\nTo select multiple features, hold down the 'Shift' key. Then either click\non the features you want to select, or drag on the map to draw a rectangle.\nThis will draw a box and select all the points within it.\n\n### Saving Edits\n\nWhen you make changes like editing roads, buildings, and places, these are\nstored locally until you save them to the server. Don't worry if you make\na mistake - you can undo changes by clicking the undo button, and redo\nchanges by clicking the redo button.\n\nClick 'Save' to finish a group of edits - for instance, if you've completed\nan area of town and would like to start on a new area. You'll have a chance\nto review what you've done, and the editor supplies helpful suggestions\nand warnings if something doesn't seem right about the changes.\n\nIf everything looks good, you can enter a short comment explaining the change\nyou made, and click 'Save' again to post the changes\nto [OpenStreetMap.org](http://www.openstreetmap.org/), where they are visible\nto all other users and available for others to build and improve upon.\n\nIf you can't finish your edits in one sitting, you can leave the editor\nwindow and come back (on the same browser and computer), and the\neditor application will offer to restore your work.\n\n### Using the editor\n\nA list of available keyboard shortcuts can be found [here](http://wiki.openstreetmap.org/wiki/ID/Shortcuts).\n","roads":"# Roads\n\nYou can create, fix, and delete roads with this editor. Roads can be all\nkinds: paths, highways, trails, cycleways, and more - any often-crossed\nsegment should be mappable.\n\n### Selecting\n\nClick on a road to select it. An outline should become visible, along\nwith a small tools menu on the map and a sidebar showing more information\nabout the road.\n\n### Modifying\n\nOften you'll see roads that aren't aligned to the imagery behind them\nor to a GPS track. You can adjust these roads so they are in the correct\nplace.\n\nFirst click on the road you want to change. This will highlight it and show\ncontrol points along it that you can drag to better locations. If\nyou want to add new control points for more detail, double-click a part\nof the road without a node, and one will be added.\n\nIf the road connects to another road, but doesn't properly connect on\nthe map, you can drag one of its control points onto the other road in\norder to join them. Having roads connect is important for the map\nand essential for providing driving directions.\n\nYou can also click the 'Move' tool or press the `M` shortcut key to move the entire road at\none time, and then click again to save that movement.\n\n### Deleting\n\nIf a road is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the road could simply be newly built.\n\nYou can delete a road by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n\n### Creating\n\nFound somewhere there should be a road but there isn't? Click the 'Line'\nicon in the top-left of the editor or press the shortcut key `2` to start drawing\na line.\n\nClick on the start of the road on the map to start drawing. If the road\nbranches off from an existing road, start by clicking on the place where they connect.\n\nThen click on points along the road so that it follows the right path, according\nto satellite imagery or GPS. If the road you are drawing crosses another road, connect\nit by clicking on the intersection point. When you're done drawing, double-click\nor press 'Return' or 'Enter' on your keyboard.\n","gps":"# GPS\n\nCollected GPS traces are one valuable source of data for OpenStreetMap. This editor\nsupports local traces - `.gpx` files on your local computer. You can collect\nthis kind of GPS trace with a number of smartphone applications as well as\npersonal GPS hardware.\n\nFor information on how to perform a GPS survey, read\n[Mapping with a smartphone, GPS, or paper](http://learnosm.org/en/mobile-mapping/).\n\nTo use a GPX track for mapping, drag and drop the GPX file onto the map\neditor. If it's recognized, it will be added to the map as a bright purple\nline. Click on the 'Map Data' menu on the right side to enable,\ndisable, or zoom to this new GPX-powered layer.\n\nThe GPX track isn't directly uploaded to OpenStreetMap - the best way to\nuse it is to draw on the map, using it as a guide for the new features that\nyou add, and also to [upload it to OpenStreetMap](http://www.openstreetmap.org/trace/create)\nfor other users to use.\n","imagery":"# Imagery\n\nAerial imagery is an important resource for mapping. A combination of\nairplane flyovers, satellite views, and freely-compiled sources are available\nin the editor under the 'Background Settings' menu on the right.\n\nBy default a [Bing Maps](http://www.bing.com/maps/) satellite layer is\npresented in the editor, but as you pan and zoom the map to new geographical\nareas, new sources will become available. Some countries, like the United\nStates, France, and Denmark have very high-quality imagery available for some areas.\n\nImagery is sometimes offset from the map data because of a mistake on the\nimagery provider's side. If you see a lot of roads shifted from the background,\ndon't immediately move them all to match the background. Instead you can adjust\nthe imagery so that it matches the existing data by clicking 'Fix alignment' at\nthe bottom of the Background Settings UI.\n","addresses":"# Addresses\n\nAddresses are some of the most useful information for the map.\n\nAlthough addresses are often represented as parts of streets, in OpenStreetMap\nthey're recorded as attributes of buildings and places along streets.\n\nYou can add address information to places mapped as building outlines\nas well as those mapped as single points. The optimal source of address\ndata is from an on-the-ground survey or personal knowledge - as with any\nother feature, copying from commercial sources like Google Maps is strictly\nforbidden.\n","inspector":"# Using the Inspector\n\nThe inspector is the section on the left side of the page that allows you to\nedit the details of the selected feature.\n\n### Selecting a Feature Type\n\nAfter you add a point, line, or area, you can choose what type of feature it\nis, like whether it's a highway or residential road, supermarket or cafe.\nThe inspector will display buttons for common feature types, and you can\nfind others by typing what you're looking for in the search box.\n\nClick the 'i' in the bottom-right-hand corner of a feature type button to\nlearn more about it. Click a button to choose that type.\n\n### Using Forms and Editing Tags\n\nAfter you choose a feature type, or when you select a feature that already\nhas a type assigned, the inspector will display fields with details about\nthe feature like its name and address.\n\nBelow the fields you see, you can click the 'Add field' dropdown to add\nother details, like a Wikipedia link, wheelchair access, and more.\n\nAt the bottom of the inspector, click 'Additional tags' to add arbitrary\nother tags to the element. [Taginfo](http://taginfo.openstreetmap.org/) is a\ngreat resource for learn more about popular tag combinations.\n\nChanges you make in the inspector are automatically applied to the map.\nYou can undo them at any time by clicking the 'Undo' button.\n","buildings":"# Buildings\n\nOpenStreetMap is the world's largest database of buildings. You can create\nand improve this database.\n\n### Selecting\n\nYou can select a building by clicking on its border. This will highlight the\nbuilding and open a small tools menu and a sidebar showing more information\nabout the building.\n\n### Modifying\n\nSometimes buildings are incorrectly placed or have incorrect tags.\n\nTo move an entire building, select it, then click the 'Move' tool. Move your\nmouse to shift the building, and click when it's correctly placed.\n\nTo fix the specific shape of a building, click and drag the nodes that form\nits border into better places.\n\n### Creating\n\nOne of the main questions around adding buildings to the map is that\nOpenStreetMap records buildings both as shapes and points. The rule of thumb\nis to _map a building as a shape whenever possible_, and map companies, homes,\namenities, and other things that operate out of buildings as points placed\nwithin the building shape.\n\nStart drawing a building as a shape by clicking the 'Area' button in the top\nleft of the interface, and end it either by pressing 'Return' on your keyboard\nor clicking on the first node drawn to close the shape.\n\n### Deleting\n\nIf a building is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the building could simply be newly built.\n\nYou can delete a building by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n","relations":"# Relations\n\nA relation is a special type of feature in OpenStreetMap that groups together\nother features. For example, two common types of relations are *route relations*,\nwhich group together sections of road that belong to a specific freeway or\nhighway, and *multipolygons*, which group together several lines that define\na complex area (one with several pieces or holes in it like a donut).\n\nThe group of features in a relation are called *members*. In the sidebar, you can\nsee which relations a feature is a member of, and click on a relation there\nto select the it. When the relation is selected, you can see all of its\nmembers listed in the sidebar and highlighted on the map.\n\nFor the most part, iD will take care of maintaining relations automatically\nwhile you edit. The main thing you should be aware of is that if you delete a\nsection of road to redraw it more accurately, you should make sure that the\nnew section is a member of the same relations as the original.\n\n## Editing Relations\n\nIf you want to edit relations, here are the basics.\n\nTo add a feature to a relation, select the feature, click the \"+\" button in the\n\"All relations\" section of the sidebar, and select or type the name of the relation.\n\nTo create a new relation, select the first feature that should be a member,\nclick the \"+\" button in the \"All relations\" section, and select \"New relation...\".\n\nTo remove a feature from a relation, select the feature and click the trash\nbutton next to the relation you want to remove it from.\n\nYou can create multipolygons with holes using the \"Merge\" tool. Draw two areas (inner\nand outer), hold the Shift key and click on each of them to select them both, and then\nclick the \"Merge\" (+) button.\n"},"intro":{"graph":{"city_hall":"Three Rivers City Hall","fire_department":"Three Rivers Fire Department","memory_isle_park":"Memory Isle Park","riverwalk_trail":"Riverwalk Trail","w_michigan_ave":"West Michigan Avenue","e_michigan_ave":"East Michigan Avenue","spring_st":"Spring Street","scidmore_park":"Scidmore Park","petting_zoo":"Scidmore Park Petting Zoo","n_andrews_st":"North Andrews Street","s_andrews_st":"South Andrews Street","n_constantine_st":"North Constantine Street","s_constantine_st":"South Constantine Street","rocky_river":"Rocky River","railroad_dr":"Railroad Drive","conrail_rr":"Conrail Railroad","st_joseph_river":"Saint Joseph River","n_main_st":"North Main Street","s_main_st":"South Main Street","water_st":"Water Street","foster_st":"Foster Street","portage_river":"Portage River","flower_st":"Flower Street","elm_st":"Elm Street","walnut_st":"Walnut Street","morris_ave":"Morris Avenue","east_st":"East Street","portage_ave":"Portage Avenue"},"navigation":{"title":"Navigation","drag":"The main map area shows OpenStreetMap data on top of a background. You can navigate by dragging and scrolling, just like any web map. **Drag the map!**","select":"Map features are represented three ways: using points, lines or areas. All features can be selected by clicking on them. **Click on the point to select it.**","pane":"When a feature is selected, the feature editor is displayed. The header shows us the feature type and the main pane shows the feature's attributes, such as its name and address. **Close the feature editor by pressing the {button} button in the top right.**","search":"You can also search for features in the current view, or worldwide. **Search for '{name}'**","choose":"**Choose {name} from the list to select it.**","chosen":"Great! {name} is now selected. **Close the feature editor by pressing the {button} button.**"},"points":{"title":"Points","add":"Points can be used to represent features such as shops, restaurants, and monuments. They mark a specific location, and describe what's there. **Click the {button} Point button to add a new point.**","place":"The point can be placed by clicking on the map. **Click the map to place the new point on top of the building.**","search":"There are many different features that can be represented by points. The point you just added is a Cafe. **Search for '{name}'**","choose":"**Choose Cafe from the list.**","describe":"The point is now marked as a cafe. Using the feature editor, we can add more information about the feature. **Add a name**","close":"The feature editor will remember all of your changes automatically.  When you change a feature, the close button will change to a checkmark. **Click the {button} button to close the feature editor**","reselect":"Often points will already exist, but have mistakes or be incomplete. We can edit existing points. **Click to select the point you just created.**","fixname":"**Change the name, then click the {button} button to close the feature editor.**","reselect_delete":"All features on the map can be deleted. **Click to select the point you created.**","delete":"The menu around the point contains operations that can be performed on it, including delete. **Click on the {button} button to delete the point.**"},"areas":{"title":"Areas","add":"Areas are used to show the boundaries of features like lakes, buildings, and residential areas. They can be also be used for more detailed mapping of many features you might normally map as points. **Click the {button} Area button to add a new area.**","corner":"Areas are drawn by placing nodes that mark the boundary of the area. **Click to place a starting node on one of the corners of the playground.**","place":"Draw the area by placing more nodes. Finish the area by clicking on the starting node. **Draw an area for the playground.**","search":"**Search for '{name}'.**","choose":"**Choose Playground from the list.**","describe":"**Add a name, then click the {button} button to close the feature editor**"},"lines":{"title":"Lines","add":"Lines are used to represent features such as roads, railroads, and rivers. **Click the {button} Line button to add a new line.**","start":"**Start the line by clicking on the end of the road.**","intersect":"Click to add more nodes to the line. You can drag the map while drawing if necessary. Roads, and many other types of lines, are part of a larger network. It is important for these lines to be connected properly in order for routing applications to work. **Click on {name} to create an intersection connecting the two lines.**","finish":"Lines can be finished by clicking on the last node again. **Finish drawing the road.**","road":"**Select Road from the list**","residential":"There are different types of roads, the most common of which is Residential. **Choose the Residential road type**","describe":"**Name the road, then click the {button} button to close the feature editor.**","restart":"The road needs to intersect {name}.","wrong_preset":"You didn't select the Residential road type. **Click here to choose again**"},"startediting":{"title":"Start Editing","help":"You can replay this walkthrough or view more documentation by clicking the {button} Help button.","save":"Don't forget to regularly save your changes!","start":"Start mapping!"}},"presets":{"categories":{"category-building":{"name":"Building Features"},"category-golf":{"name":"Golf Features"},"category-landuse":{"name":"Land Use Features"},"category-path":{"name":"Path Features"},"category-rail":{"name":"Rail Features"},"category-restriction":{"name":"Restriction Features"},"category-road":{"name":"Road Features"},"category-route":{"name":"Route Features"},"category-water-area":{"name":"Water Features"},"category-water-line":{"name":"Water Features"}},"fields":{"access":{"label":"Allowed Access","placeholder":"Not Specified","types":{"access":"All","foot":"Foot","motor_vehicle":"Motor Vehicles","bicycle":"Bicycles","horse":"Horses"},"options":{"yes":{"title":"Allowed","description":"Access permitted by law; a right of way"},"no":{"title":"Prohibited","description":"Access not permitted to the general public"},"permissive":{"title":"Permissive","description":"Access permitted until such time as the owner revokes the permission"},"private":{"title":"Private","description":"Access permitted only with permission of the owner on an individual basis"},"designated":{"title":"Designated","description":"Access permitted according to signs or specific local laws"},"destination":{"title":"Destination","description":"Access permitted only to reach a destination"},"dismount":{"title":"Dismount","description":"Access permitted but rider must dismount"}}},"access_simple":{"label":"Allowed Access","placeholder":"yes"},"access_toilets":{"label":"Access"},"address":{"label":"Address","placeholders":{"housename":"Housename","housenumber":"123","conscriptionnumber":"123","street":"Street","city":"City","postcode":"Postcode","place":"Place","hamlet":"Hamlet","suburb":"Suburb","subdistrict":"Subdistrict","district":"District","province":"Province","state":"State","country":"Country"}},"admin_level":{"label":"Admin Level"},"aerialway":{"label":"Type"},"aerialway/access":{"label":"Access","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aerialway/bubble":{"label":"Bubble"},"aerialway/capacity":{"label":"Capacity (per hour)","placeholder":"500, 2500, 5000..."},"aerialway/duration":{"label":"Duration (minutes)","placeholder":"1, 2, 3..."},"aerialway/heating":{"label":"Heated"},"aerialway/occupancy":{"label":"Occupancy","placeholder":"2, 4, 8..."},"aerialway/summer/access":{"label":"Access (summer)","options":{"entry":"Entry","exit":"Exit","both":"Both"}},"aeroway":{"label":"Type"},"amenity":{"label":"Type"},"area/highway":{"label":"Type"},"artist":{"label":"Artist"},"artwork_type":{"label":"Type"},"atm":{"label":"ATM"},"backrest":{"label":"Backrest"},"barrier":{"label":"Type"},"bench":{"label":"Bench"},"bicycle_parking":{"label":"Type"},"bin":{"label":"Waste Bin"},"boundary":{"label":"Type"},"brand":{"label":"Brand"},"building":{"label":"Building"},"building_area":{"label":"Building"},"capacity":{"label":"Capacity","placeholder":"50, 100, 200..."},"cardinal_direction":{"label":"Direction","options":{"N":"North","E":"East","S":"South","W":"West","NE":"Northeast","SE":"Southeast","SW":"Southwest","NW":"Northwest","NNE":"North-northeast","ENE":"East-northeast","ESE":"East-southeast","SSE":"South-southeast","SSW":"South-southwest","WSW":"West-southwest","WNW":"West-northwest","NNW":"North-northwest"}},"clock_direction":{"label":"Direction","options":{"clockwise":"Clockwise","anticlockwise":"Counterclockwise"}},"collection_times":{"label":"Collection Times"},"construction":{"label":"Type"},"content":{"label":"Contents"},"country":{"label":"Country"},"covered":{"label":"Covered"},"craft":{"label":"Type"},"crop":{"label":"Crop"},"crossing":{"label":"Type"},"cuisine":{"label":"Cuisine"},"currency_multi":{"label":"Currency Types"},"cycleway":{"label":"Bike Lanes","placeholder":"none","types":{"cycleway:left":"Left side","cycleway:right":"Right side"},"options":{"none":{"title":"None","description":"No bike lane"},"lane":{"title":"Standard bike lane","description":"A bike lane separated from auto traffic by a painted line"},"shared_lane":{"title":"Shared bike lane","description":"A bike lane with no separation from auto traffic"},"track":{"title":"Bike track","description":"A bike lane separated from traffic by a physical barrier"},"share_busway":{"title":"Bike lane shared with bus","description":"A bike lane shared with a bus lane"},"opposite_lane":{"title":"Opposite bike lane","description":"A bike lane that travels in the opposite direction of traffic"},"opposite":{"title":"Contraflow bike lane","description":"A bike lane that travels in both directions on a one-way street"}}},"delivery":{"label":"Delivery"},"denomination":{"label":"Denomination"},"denotation":{"label":"Denotation"},"description":{"label":"Description"},"diaper":{"label":"Diaper Changing Available"},"dock":{"label":"Type"},"drive_through":{"label":"Drive-Through"},"electrified":{"label":"Electrification","placeholder":"Contact Line, Electrified Rail...","options":{"contact_line":"Contact Line","rail":"Electrified Rail","yes":"Yes (unspecified)","no":"No"}},"elevation":{"label":"Elevation"},"emergency":{"label":"Emergency"},"entrance":{"label":"Type"},"except":{"label":"Exceptions"},"fax":{"label":"Fax","placeholder":"+31 42 123 4567"},"fee":{"label":"Fee"},"fire_hydrant/type":{"label":"Type","options":{"pillar":"Pillar/Aboveground","underground":"Underground","wall":"Wall","pond":"Pond"}},"fixme":{"label":"Fix Me"},"fuel":{"label":"Fuel"},"fuel_multi":{"label":"Fuel Types"},"gauge":{"label":"Gauge"},"gender":{"label":"Gender","placeholder":"Unknown","options":{"male":"Male","female":"Female","unisex":"Unisex"}},"generator/method":{"label":"Method"},"generator/source":{"label":"Source"},"generator/type":{"label":"Type"},"golf_hole":{"label":"Reference","placeholder":"Hole number (1-18)"},"handicap":{"label":"Handicap","placeholder":"1-18"},"handrail":{"label":"Handrail"},"highway":{"label":"Type"},"historic":{"label":"Type"},"hoops":{"label":"Hoops","placeholder":"1, 2, 4..."},"iata":{"label":"IATA"},"icao":{"label":"ICAO"},"incline":{"label":"Incline"},"incline_steps":{"label":"Incline","options":{"up":"Up","down":"Down"}},"information":{"label":"Type"},"internet_access":{"label":"Internet Access","options":{"yes":"Yes","no":"No","wlan":"Wifi","wired":"Wired","terminal":"Terminal"}},"lamp_type":{"label":"Type"},"landuse":{"label":"Type"},"lanes":{"label":"Lanes","placeholder":"1, 2, 3..."},"layer":{"label":"Layer"},"leaf_cycle":{"label":"Leaf Cycle","options":{"evergreen":"Evergreen","deciduous":"Deciduous","semi_evergreen":"Semi-Evergreen","semi_deciduous":"Semi-Deciduous","mixed":"Mixed"}},"leaf_cycle_singular":{"label":"Leaf Cycle","options":{"evergreen":"Evergreen","deciduous":"Deciduous","semi_evergreen":"Semi-Evergreen","semi_deciduous":"Semi-Deciduous"}},"leaf_type":{"label":"Leaf Type","options":{"broadleaved":"Broadleaved","needleleaved":"Needleleaved","mixed":"Mixed","leafless":"Leafless"}},"leaf_type_singular":{"label":"Leaf Type","options":{"broadleaved":"Broadleaved","needleleaved":"Needleleaved","leafless":"Leafless"}},"leisure":{"label":"Type"},"length":{"label":"Length (Meters)"},"level":{"label":"Level"},"levels":{"label":"Levels","placeholder":"2, 4, 6..."},"lit":{"label":"Lit"},"location":{"label":"Location"},"man_made":{"label":"Type"},"maxspeed":{"label":"Speed Limit","placeholder":"40, 50, 60..."},"maxstay":{"label":"Max Stay"},"mtb/scale":{"label":"Mountain Biking Difficulty","placeholder":"0, 1, 2, 3...","options":{"0":"0: Solid gravel/packed earth, no obstacles, wide curves","1":"1: Some loose surface, small obstacles, wide curves","2":"2: Much loose surface, large obstacles, easy hairpins","3":"3: Slippery surface, large obstacles, tight hairpins","4":"4: Loose surface or boulders, dangerous hairpins","5":"5: Maximum difficulty, boulder fields, landslides","6":"6: Not rideable except by the very best mountain bikers"}},"mtb/scale/imba":{"label":"IMBA Trail Difficulty","placeholder":"Easy, Medium, Difficult...","options":{"0":"Easiest (white circle)","1":"Easy (green circle)","2":"Medium (blue square)","3":"Difficult (black diamond)","4":"Extremely Difficult (double black diamond)"}},"mtb/scale/uphill":{"label":"Mountain Biking Uphill Difficulty","placeholder":"0, 1, 2, 3...","options":{"0":"0: Avg. incline <10%, gravel/packed earth, no obstacles","1":"1: Avg. incline <15%, gravel/packed earth, few small objects","2":"2: Avg. incline <20%, stable surface, fistsize rocks/roots","3":"3: Avg. incline <25%, variable surface, fistsize rocks/branches","4":"4: Avg. incline <30%, poor condition, big rocks/branches","5":"5: Very steep, bike generally needs to be pushed or carried"}},"name":{"label":"Name","placeholder":"Common name (if any)"},"natural":{"label":"Natural"},"network":{"label":"Network"},"note":{"label":"Note"},"office":{"label":"Type"},"oneway":{"label":"One Way","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"oneway_yes":{"label":"One Way","options":{"undefined":"Assumed to be Yes","yes":"Yes","no":"No"}},"opening_hours":{"label":"Hours"},"operator":{"label":"Operator"},"par":{"label":"Par","placeholder":"3, 4, 5..."},"park_ride":{"label":"Park and Ride"},"parking":{"label":"Type","options":{"surface":"Surface","multi-storey":"Multilevel","underground":"Underground","sheds":"Sheds","carports":"Carports","garage_boxes":"Garage Boxes","lane":"Roadside Lane"}},"payment_multi":{"label":"Payment Types"},"phone":{"label":"Phone","placeholder":"+31 42 123 4567"},"piste/difficulty":{"label":"Difficulty","placeholder":"Easy, Intermediate, Advanced...","options":{"novice":"Novice (instructional)","easy":"Easy (green circle)","intermediate":"Intermediate (blue square)","advanced":"Advanced (black diamond)","expert":"Expert (double black diamond)","freeride":"Freeride (off-piste)","extreme":"Extreme (climbing equipment required)"}},"piste/grooming":{"label":"Grooming","options":{"classic":"Classic","mogul":"Mogul","backcountry":"Backcountry","classic+skating":"Classic and Skating","scooter":"Scooter/Snowmobile","skating":"Skating"}},"piste/type":{"label":"Type","options":{"downhill":"Downhill","nordic":"Nordic","skitour":"Skitour","sled":"Sled","hike":"Hike","sleigh":"Sleigh","ice_skate":"Ice Skate","snow_park":"Snow Park","playground":"Playground"}},"place":{"label":"Type"},"population":{"label":"Population"},"power":{"label":"Type"},"power_supply":{"label":"Power Supply"},"railway":{"label":"Type"},"recycling_accepts":{"label":"Accepts"},"ref":{"label":"Reference"},"relation":{"label":"Type"},"religion":{"label":"Religion"},"restriction":{"label":"Type"},"restrictions":{"label":"Turn Restrictions"},"route":{"label":"Type"},"route_master":{"label":"Type"},"sac_scale":{"label":"Hiking Difficulty","placeholder":"Mountain Hiking, Alpine Hiking...","options":{"hiking":"T1: Hiking","mountain_hiking":"T2: Mountain Hiking","demanding_mountain_hiking":"T3: Demanding Mountain Hiking","alpine_hiking":"T4: Alpine Hiking","demanding_alpine_hiking":"T5: Demanding Alpine Hiking","difficult_alpine_hiking":"T6: Difficult Alpine Hiking"}},"sanitary_dump_station":{"label":"Toilet Disposal"},"seasonal":{"label":"Seasonal"},"service":{"label":"Type"},"service/bicycle/chain_tool":{"label":"Chain Tool","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service/bicycle/pump":{"label":"Air Pump","options":{"undefined":"Assumed to be No","yes":"Yes","no":"No"}},"service_rail":{"label":"Service Type","options":{"spur":"Spur","yard":"Yard","siding":"Siding","crossover":"Crossover"}},"shelter":{"label":"Shelter"},"shelter_type":{"label":"Type"},"shop":{"label":"Type"},"site":{"label":"Type"},"sloped_curb":{"label":"Sloped Curb"},"smoking":{"label":"Smoking","placeholder":"No, Separated, Yes...","options":{"no":"No smoking anywhere","separated":"In smoking areas, not physically isolated","isolated":"In smoking areas, physically isolated","outside":"Allowed outside","yes":"Allowed everywhere","dedicated":"Dedicated to smokers (e.g. smokers' club)"}},"smoothness":{"label":"Smoothness","placeholder":"Thin Rollers, Wheels, Off-Road...","options":{"excellent":"Thin Rollers: rollerblade, skateboard","good":"Thin Wheels: racing bike","intermediate":"Wheels: city bike, wheelchair, scooter","bad":"Robust Wheels: trekking bike, car, rickshaw","very_bad":"High Clearance: light duty off-road vehicle","horrible":"Off-Road: heavy duty off-road vehicle","very_horrible":"Specialized off-road: tractor, ATV","impassable":"Impassable / No wheeled vehicle"}},"social_facility_for":{"label":"People served","placeholder":"Homeless, Disabled, Child, etc"},"source":{"label":"Source"},"sport":{"label":"Sport"},"sport_ice":{"label":"Sport"},"sport_racing":{"label":"Sport"},"structure":{"label":"Structure","placeholder":"Unknown","options":{"bridge":"Bridge","tunnel":"Tunnel","embankment":"Embankment","cutting":"Cutting","ford":"Ford"}},"studio":{"label":"Type"},"substation":{"label":"Type"},"supervised":{"label":"Supervised"},"surface":{"label":"Surface"},"tactile_paving":{"label":"Tactile Paving"},"takeaway":{"label":"Takeaway","placeholder":"Yes, No, Takeaway Only...","options":{"yes":"Yes","no":"No","only":"Takeaway Only"}},"toilets/disposal":{"label":"Disposal","options":{"flush":"Flush","pitlatrine":"Pit/Latrine","chemical":"Chemical","bucket":"Bucket"}},"tourism":{"label":"Type"},"towertype":{"label":"Tower type"},"tracktype":{"label":"Track Type","placeholder":"Solid, Mostly Solid, Soft...","options":{"grade1":"Solid: paved or heavily compacted hardcore surface","grade2":"Mostly Solid: gravel/rock with some soft material mixed in","grade3":"Even mixture of hard and soft materials","grade4":"Mostly Soft: soil/sand/grass with some hard material mixed in","grade5":"Soft: soil/sand/grass"}},"traffic_signals":{"label":"Type"},"trail_visibility":{"label":"Trail Visibility","placeholder":"Excellent, Good, Bad...","options":{"excellent":"Excellent: unambiguous path or markers everywhere","good":"Good: markers visible, sometimes require searching","intermediate":"Intermediate: few markers, path mostly visible","bad":"Bad: no markers, path sometimes invisible/pathless","horrible":"Horrible: often pathless, some orientation skills required","no":"No: pathless, excellent orientation skills required"}},"trees":{"label":"Trees"},"tunnel":{"label":"Tunnel"},"vending":{"label":"Type of Goods"},"water":{"label":"Type"},"water_point":{"label":"Water Point"},"waterway":{"label":"Type"},"website":{"label":"Website","placeholder":"http://example.com/"},"wetland":{"label":"Type"},"wheelchair":{"label":"Wheelchair Access"},"width":{"label":"Width (Meters)"},"wikipedia":{"label":"Wikipedia"}},"presets":{"address":{"name":"Address","terms":""},"aerialway":{"name":"Aerialway","terms":"ski lift,funifor,funitel"},"aerialway/cable_car":{"name":"Cable Car","terms":"tramway,ropeway"},"aerialway/chair_lift":{"name":"Chair Lift","terms":""},"aerialway/gondola":{"name":"Gondola","terms":""},"aerialway/magic_carpet":{"name":"Magic Carpet Lift","terms":""},"aerialway/platter":{"name":"Platter Lift","terms":"button lift,poma lift"},"aerialway/pylon":{"name":"Aerialway Pylon","terms":""},"aerialway/rope_tow":{"name":"Rope Tow Lift","terms":"handle tow,bugel lift"},"aerialway/station":{"name":"Aerialway Station","terms":""},"aerialway/t-bar":{"name":"T-bar Lift","terms":""},"aeroway":{"name":"Aeroway","terms":""},"aeroway/aerodrome":{"name":"Airport","terms":"airplane,airport,aerodrome"},"aeroway/apron":{"name":"Apron","terms":"ramp"},"aeroway/gate":{"name":"Airport gate","terms":""},"aeroway/hangar":{"name":"Hangar","terms":""},"aeroway/helipad":{"name":"Helipad","terms":"helicopter,helipad,heliport"},"aeroway/runway":{"name":"Runway","terms":"landing strip"},"aeroway/taxiway":{"name":"Taxiway","terms":""},"aeroway/terminal":{"name":"Airport terminal","terms":"airport,aerodrome"},"amenity":{"name":"Amenity","terms":""},"amenity/arts_centre":{"name":"Arts Center","terms":""},"amenity/atm":{"name":"ATM","terms":"money,cash,machine"},"amenity/bank":{"name":"Bank","terms":"credit union,check,deposit,fund,investment,repository,reserve,safe,savings,stock,treasury,trust,vault"},"amenity/bar":{"name":"Bar","terms":"dive,beer,bier,booze"},"amenity/bbq":{"name":"Barbecue/Grill","terms":"bbq"},"amenity/bench":{"name":"Bench","terms":"seat"},"amenity/bicycle_parking":{"name":"Bicycle Parking","terms":"bike"},"amenity/bicycle_rental":{"name":"Bicycle Rental","terms":"bike"},"amenity/bicycle_repair_station":{"name":"Bicycle Repair Tool Stand","terms":"bike,repair,chain,pump"},"amenity/biergarten":{"name":"Beer Garden","terms":"beer,bier,booze"},"amenity/boat_rental":{"name":"Boat Rental","terms":""},"amenity/bureau_de_change":{"name":"Currency Exchange","terms":"bureau de change,money changer"},"amenity/bus_station":{"name":"Bus Station","terms":""},"amenity/cafe":{"name":"Cafe","terms":"bistro,coffee,tea"},"amenity/car_rental":{"name":"Car Rental","terms":""},"amenity/car_sharing":{"name":"Car Sharing","terms":""},"amenity/car_wash":{"name":"Car Wash","terms":""},"amenity/casino":{"name":"Casino","terms":"gambling,roulette,craps,poker,blackjack"},"amenity/charging_station":{"name":"Charging Station","terms":"EV,Electric Vehicle,Supercharger"},"amenity/childcare":{"name":"Nursery/Childcare","terms":"daycare,orphanage,playgroup"},"amenity/cinema":{"name":"Cinema","terms":"drive-in,film,flick,movie,theater,picture,show,screen"},"amenity/clinic":{"name":"Clinic","terms":"medical,urgentcare"},"amenity/clock":{"name":"Clock","terms":""},"amenity/college":{"name":"College Grounds","terms":"university"},"amenity/community_centre":{"name":"Community Center","terms":"event,hall"},"amenity/compressed_air":{"name":"Compressed Air","terms":""},"amenity/courthouse":{"name":"Courthouse","terms":""},"amenity/dentist":{"name":"Dentist","terms":"tooth,teeth"},"amenity/doctors":{"name":"Doctor","terms":"medic*"},"amenity/dojo":{"name":"Dojo / Martial Arts Academy","terms":"martial arts,dojang"},"amenity/drinking_water":{"name":"Drinking Water","terms":"fountain,potable"},"amenity/embassy":{"name":"Embassy","terms":""},"amenity/fast_food":{"name":"Fast Food","terms":"restaurant"},"amenity/ferry_terminal":{"name":"Ferry Terminal","terms":""},"amenity/fire_station":{"name":"Fire Station","terms":""},"amenity/fountain":{"name":"Fountain","terms":""},"amenity/fuel":{"name":"Gas Station","terms":"petrol,fuel,gasoline,propane,diesel,lng,cng,biodiesel"},"amenity/grave_yard":{"name":"Graveyard","terms":""},"amenity/grit_bin":{"name":"Grit Bin","terms":"salt,sand"},"amenity/hospital":{"name":"Hospital Grounds","terms":"clinic,doctor,emergency room,health service,hospice,infirmary,institution,nursing home,sanatorium,sanitarium,sick,surgery,ward"},"amenity/hunting_stand":{"name":"Hunting Stand","terms":""},"amenity/kindergarten":{"name":"Preschool/Kindergarten Grounds","terms":"kindergarden,pre-school"},"amenity/library":{"name":"Library","terms":"book"},"amenity/marketplace":{"name":"Marketplace","terms":""},"amenity/motorcycle_parking":{"name":"Motorcycle Parking","terms":""},"amenity/nightclub":{"name":"Nightclub","terms":"disco*,night club,dancing,dance club"},"amenity/parking":{"name":"Car Parking","terms":""},"amenity/parking_entrance":{"name":"Parking Garage Entrance/Exit","terms":""},"amenity/parking_space":{"name":"Parking Space","terms":""},"amenity/pharmacy":{"name":"Pharmacy","terms":"drug,medicine"},"amenity/place_of_worship":{"name":"Place of Worship","terms":"abbey,basilica,bethel,cathedral,chancel,chantry,chapel,church,fold,house of God,house of prayer,house of worship,minster,mission,mosque,oratory,parish,sacellum,sanctuary,shrine,synagogue,tabernacle,temple"},"amenity/place_of_worship/buddhist":{"name":"Buddhist Temple","terms":"stupa,vihara,monastery,temple,pagoda,zendo,dojo"},"amenity/place_of_worship/christian":{"name":"Church","terms":"christian,abbey,basilica,bethel,cathedral,chancel,chantry,chapel,fold,house of God,house of prayer,house of worship,minster,mission,oratory,parish,sacellum,sanctuary,shrine,tabernacle,temple"},"amenity/place_of_worship/jewish":{"name":"Synagogue","terms":"jewish"},"amenity/place_of_worship/muslim":{"name":"Mosque","terms":"muslim"},"amenity/police":{"name":"Police","terms":"badge,constable,constabulary,cop,detective,fed,law,enforcement,officer,patrol"},"amenity/post_box":{"name":"Mailbox","terms":"letter,post"},"amenity/post_office":{"name":"Post Office","terms":"letter,mail"},"amenity/prison":{"name":"Prison Grounds","terms":"cell,jail"},"amenity/pub":{"name":"Pub","terms":"dive,beer,bier,booze"},"amenity/public_bookcase":{"name":"Public Bookcase","terms":"library,bookcrossing"},"amenity/ranger_station":{"name":"Ranger Station","terms":"visitor center,visitor centre,permit center,permit centre,backcountry office,warden office,warden center"},"amenity/recycling":{"name":"Recycling","terms":"can,bottle,garbage,scrap,trash"},"amenity/register_office":{"name":"Register Office","terms":""},"amenity/restaurant":{"name":"Restaurant","terms":"bar,breakfast,cafe,café,canteen,coffee,dine,dining,dinner,drive-in,eat,grill,lunch,table"},"amenity/sanitary_dump_station":{"name":"RV Toilet Disposal","terms":"Motor Home,Camper,Sanitary,Dump Station,Elsan,CDP,CTDP,Chemical Toilet"},"amenity/school":{"name":"School Grounds","terms":"academy,elementary school,middle school,high school"},"amenity/shelter":{"name":"Shelter","terms":"lean-to,gazebo,picnic"},"amenity/social_facility":{"name":"Social Facility","terms":""},"amenity/social_facility/food_bank":{"name":"Food Bank","terms":""},"amenity/social_facility/group_home":{"name":"Elderly Group Home","terms":"old,senior,living"},"amenity/social_facility/homeless_shelter":{"name":"Homeless Shelter","terms":"houseless,unhoused,displaced"},"amenity/studio":{"name":"Studio","terms":"recording,radio,television"},"amenity/swimming_pool":{"name":"Swimming Pool","terms":""},"amenity/taxi":{"name":"Taxi Stand","terms":"cab"},"amenity/telephone":{"name":"Telephone","terms":"phone"},"amenity/theatre":{"name":"Theater","terms":"theatre,performance,play,musical"},"amenity/toilets":{"name":"Toilets","terms":"bathroom,restroom,outhouse,privy,head,lavatory,latrine,water closet,WC,W.C."},"amenity/townhall":{"name":"Town Hall","terms":"village,city,government,courthouse,municipal"},"amenity/university":{"name":"University Grounds","terms":"college"},"amenity/vending_machine/cigarettes":{"name":"Cigarette Vending Machine","terms":"cigarette"},"amenity/vending_machine/condoms":{"name":"Condom Vending Machine","terms":"condom"},"amenity/vending_machine/drinks":{"name":"Drink Vending Machine","terms":"drink,soda,beverage,juice,pop"},"amenity/vending_machine/excrement_bags":{"name":"Excrement Bag Vending Machine","terms":"excrement bags,poop,dog,animal"},"amenity/vending_machine/news_papers":{"name":"Newspaper Vending Machine","terms":"newspaper"},"amenity/vending_machine/parcel_pickup_dropoff":{"name":"Parcel Pickup/Dropoff Vending Machine","terms":"parcel,mail,pickup"},"amenity/vending_machine/parking_tickets":{"name":"Parking Ticket Vending Machine","terms":"parking,ticket"},"amenity/vending_machine/public_transport_tickets":{"name":"Transit Ticket Vending Machine","terms":"bus,train,ferry,rail,ticket,transportation"},"amenity/vending_machine/sweets":{"name":"Snack Vending Machine","terms":"candy,gum,chip,pretzel,cookie,cracker"},"amenity/vending_machine/vending_machine":{"name":"Vending Machine","terms":""},"amenity/veterinary":{"name":"Veterinary","terms":"pet clinic,veterinarian,animal hospital,pet doctor"},"amenity/waste_basket":{"name":"Waste Basket","terms":"bin,rubbish,litter,trash,garbage"},"amenity/waste_disposal":{"name":"Garbage Dumpster","terms":"rubbish,litter,trash"},"amenity/water_point":{"name":"RV Drinking Water","terms":""},"area":{"name":"Area","terms":""},"area/highway":{"name":"Road Surface","terms":""},"barrier":{"name":"Barrier","terms":""},"barrier/block":{"name":"Block","terms":""},"barrier/bollard":{"name":"Bollard","terms":""},"barrier/cattle_grid":{"name":"Cattle Grid","terms":""},"barrier/city_wall":{"name":"City Wall","terms":""},"barrier/cycle_barrier":{"name":"Cycle Barrier","terms":""},"barrier/ditch":{"name":"Ditch","terms":""},"barrier/entrance":{"name":"Entrance","terms":""},"barrier/fence":{"name":"Fence","terms":""},"barrier/gate":{"name":"Gate","terms":""},"barrier/hedge":{"name":"Hedge","terms":""},"barrier/kissing_gate":{"name":"Kissing Gate","terms":""},"barrier/lift_gate":{"name":"Lift Gate","terms":""},"barrier/retaining_wall":{"name":"Retaining Wall","terms":""},"barrier/stile":{"name":"Stile","terms":""},"barrier/toll_booth":{"name":"Toll Booth","terms":""},"barrier/wall":{"name":"Wall","terms":""},"boundary/administrative":{"name":"Administrative Boundary","terms":""},"building":{"name":"Building","terms":""},"building/apartments":{"name":"Apartments","terms":""},"building/barn":{"name":"Barn","terms":""},"building/bunker":{"name":"Bunker","terms":""},"building/cabin":{"name":"Cabin","terms":""},"building/cathedral":{"name":"Cathedral Building","terms":""},"building/chapel":{"name":"Chapel Building","terms":""},"building/church":{"name":"Church Building","terms":""},"building/college":{"name":"College Building","terms":"university"},"building/commercial":{"name":"Commercial Building","terms":""},"building/construction":{"name":"Building Under Construction","terms":""},"building/detached":{"name":"Detached House","terms":"home,single,family,residence,dwelling"},"building/dormitory":{"name":"Dormitory","terms":""},"building/entrance":{"name":"Entrance/Exit","terms":""},"building/garage":{"name":"Garage","terms":""},"building/garages":{"name":"Garages","terms":""},"building/greenhouse":{"name":"Greenhouse","terms":""},"building/hospital":{"name":"Hospital Building","terms":""},"building/hotel":{"name":"Hotel Building","terms":""},"building/house":{"name":"House","terms":"home,family,residence,dwelling"},"building/hut":{"name":"Hut","terms":""},"building/industrial":{"name":"Industrial Building","terms":""},"building/kindergarten":{"name":"Preschool/Kindergarten Building","terms":"kindergarden,pre-school"},"building/public":{"name":"Public Building","terms":""},"building/residential":{"name":"Residential Building","terms":""},"building/retail":{"name":"Retail Building","terms":""},"building/roof":{"name":"Roof","terms":""},"building/school":{"name":"School Building","terms":"academy,elementary school,middle school,high school"},"building/semidetached_house":{"name":"Semi-Detached House","terms":"home,double,duplex,twin,family,residence,dwelling"},"building/shed":{"name":"Shed","terms":""},"building/stable":{"name":"Stable","terms":""},"building/static_caravan":{"name":"Static Mobile Home","terms":""},"building/terrace":{"name":"Row Houses","terms":"home,terrace,brownstone,family,residence,dwelling"},"building/train_station":{"name":"Train Station","terms":""},"building/university":{"name":"University Building","terms":"college"},"building/warehouse":{"name":"Warehouse","terms":""},"craft":{"name":"Craft","terms":""},"craft/basket_maker":{"name":"Basket Maker","terms":""},"craft/beekeeper":{"name":"Beekeeper","terms":""},"craft/blacksmith":{"name":"Blacksmith","terms":""},"craft/boatbuilder":{"name":"Boat Builder","terms":""},"craft/bookbinder":{"name":"Bookbinder","terms":"book repair"},"craft/brewery":{"name":"Brewery","terms":"beer,bier"},"craft/carpenter":{"name":"Carpenter","terms":"woodworker"},"craft/carpet_layer":{"name":"Carpet Layer","terms":""},"craft/caterer":{"name":"Caterer","terms":""},"craft/clockmaker":{"name":"Clockmaker","terms":""},"craft/confectionery":{"name":"Confectionery","terms":"sweets,candy"},"craft/dressmaker":{"name":"Dressmaker","terms":"seamstress"},"craft/electrician":{"name":"Electrician","terms":"power,wire"},"craft/gardener":{"name":"Gardener","terms":"landscaper,grounds keeper"},"craft/glaziery":{"name":"Glaziery","terms":"glass,stained-glass,window"},"craft/handicraft":{"name":"Handicraft","terms":""},"craft/hvac":{"name":"HVAC","terms":"heat*,vent*,air conditioning"},"craft/insulator":{"name":"Insulator","terms":""},"craft/jeweler":{"name":"Jeweler","terms":""},"craft/key_cutter":{"name":"Key Cutter","terms":""},"craft/locksmith":{"name":"Locksmith","terms":""},"craft/metal_construction":{"name":"Metal Construction","terms":""},"craft/optician":{"name":"Optician","terms":""},"craft/painter":{"name":"Painter","terms":""},"craft/photographer":{"name":"Photographer","terms":""},"craft/photographic_laboratory":{"name":"Photographic Laboratory","terms":"film"},"craft/plasterer":{"name":"Plasterer","terms":""},"craft/plumber":{"name":"Plumber","terms":"pipe"},"craft/pottery":{"name":"Pottery","terms":"ceramic"},"craft/rigger":{"name":"Rigger","terms":""},"craft/roofer":{"name":"Roofer","terms":""},"craft/saddler":{"name":"Saddler","terms":""},"craft/sailmaker":{"name":"Sailmaker","terms":""},"craft/sawmill":{"name":"Sawmill","terms":"lumber"},"craft/scaffolder":{"name":"Scaffolder","terms":""},"craft/sculpter":{"name":"Sculpter","terms":""},"craft/shoemaker":{"name":"Shoemaker","terms":"cobbler"},"craft/stonemason":{"name":"Stonemason","terms":"masonry"},"craft/sweep":{"name":"Chimney Sweep","terms":""},"craft/tailor":{"name":"Tailor","terms":"clothes,suit"},"craft/tiler":{"name":"Tiler","terms":""},"craft/tinsmith":{"name":"Tinsmith","terms":""},"craft/upholsterer":{"name":"Upholsterer","terms":""},"craft/watchmaker":{"name":"Watchmaker","terms":""},"craft/window_construction":{"name":"Window Construction","terms":"glass"},"craft/winery":{"name":"Winery","terms":""},"embankment":{"name":"Embankment","terms":""},"emergency/ambulance_station":{"name":"Ambulance Station","terms":"EMS,EMT,rescue"},"emergency/fire_hydrant":{"name":"Fire Hydrant","terms":""},"emergency/phone":{"name":"Emergency Phone","terms":""},"entrance":{"name":"Entrance/Exit","terms":""},"footway/crossing":{"name":"Street Crossing","terms":""},"footway/crosswalk":{"name":"Pedestrian Crosswalk","terms":"zebra crossing"},"footway/sidewalk":{"name":"Sidewalk","terms":""},"ford":{"name":"Ford","terms":""},"golf/bunker":{"name":"Sand Trap","terms":"hazard,bunker"},"golf/fairway":{"name":"Fairway","terms":""},"golf/green":{"name":"Putting Green","terms":""},"golf/hole":{"name":"Golf Hole","terms":""},"golf/lateral_water_hazard":{"name":"Lateral Water Hazard","terms":""},"golf/rough":{"name":"Rough","terms":""},"golf/tee":{"name":"Tee Box","terms":"teeing ground"},"golf/water_hazard":{"name":"Water Hazard","terms":""},"highway":{"name":"Highway","terms":""},"highway/bridleway":{"name":"Bridle Path","terms":"bridleway,equestrian,horse"},"highway/bus_stop":{"name":"Bus Stop","terms":""},"highway/corridor":{"name":"Indoor Corridor","terms":"gallery,hall,hallway,indoor,passage,passageway"},"highway/crossing":{"name":"Street Crossing","terms":""},"highway/crosswalk":{"name":"Pedestrian Crosswalk","terms":"zebra crossing"},"highway/cycleway":{"name":"Cycle Path","terms":"bike"},"highway/footway":{"name":"Foot Path","terms":"hike,hiking,trackway,trail,walk"},"highway/living_street":{"name":"Living Street","terms":""},"highway/mini_roundabout":{"name":"Mini-Roundabout","terms":""},"highway/motorway":{"name":"Motorway","terms":""},"highway/motorway_junction":{"name":"Motorway Junction / Exit","terms":""},"highway/motorway_link":{"name":"Motorway Link","terms":"ramp,on ramp,off ramp"},"highway/path":{"name":"Path","terms":"hike,hiking,trackway,trail,walk"},"highway/pedestrian":{"name":"Pedestrian Street","terms":""},"highway/primary":{"name":"Primary Road","terms":""},"highway/primary_link":{"name":"Primary Link","terms":"ramp,on ramp,off ramp"},"highway/raceway":{"name":"Motor Raceway","terms":"auto*,race*,nascar"},"highway/residential":{"name":"Residential Road","terms":""},"highway/rest_area":{"name":"Rest Area","terms":"rest stop"},"highway/road":{"name":"Unknown Road","terms":""},"highway/secondary":{"name":"Secondary Road","terms":""},"highway/secondary_link":{"name":"Secondary Link","terms":"ramp,on ramp,off ramp"},"highway/service":{"name":"Service Road","terms":""},"highway/service/alley":{"name":"Alley","terms":""},"highway/service/drive-through":{"name":"Drive-Through","terms":""},"highway/service/driveway":{"name":"Driveway","terms":""},"highway/service/emergency_access":{"name":"Emergency Access","terms":""},"highway/service/parking_aisle":{"name":"Parking Aisle","terms":""},"highway/services":{"name":"Service Area","terms":"services,travel plaza,service station"},"highway/steps":{"name":"Steps","terms":"stairs,staircase"},"highway/stop":{"name":"Stop Sign","terms":"stop sign"},"highway/street_lamp":{"name":"Street Lamp","terms":"streetlight,street light,lamp,light,gaslight"},"highway/tertiary":{"name":"Tertiary Road","terms":""},"highway/tertiary_link":{"name":"Tertiary Link","terms":"ramp,on ramp,off ramp"},"highway/track":{"name":"Unmaintained Track Road","terms":"woods road,forest road,logging road,fire road,farm road,agricultural road,ranch road,carriage road,primitive,unmaintained,rut,offroad,4wd,4x4,four wheel drive,atv,quad,jeep,double track,two track"},"highway/traffic_signals":{"name":"Traffic Signals","terms":"light,stoplight,traffic light"},"highway/trunk":{"name":"Trunk Road","terms":""},"highway/trunk_link":{"name":"Trunk Link","terms":"ramp,on ramp,off ramp"},"highway/turning_circle":{"name":"Turning Circle","terms":"cul-de-sac"},"highway/unclassified":{"name":"Minor/Unclassified Road","terms":""},"historic":{"name":"Historic Site","terms":""},"historic/archaeological_site":{"name":"Archaeological Site","terms":""},"historic/boundary_stone":{"name":"Boundary Stone","terms":""},"historic/castle":{"name":"Castle","terms":""},"historic/memorial":{"name":"Memorial","terms":""},"historic/monument":{"name":"Monument","terms":""},"historic/ruins":{"name":"Ruins","terms":""},"historic/wayside_cross":{"name":"Wayside Cross","terms":""},"historic/wayside_shrine":{"name":"Wayside Shrine","terms":""},"junction":{"name":"Junction","terms":""},"landuse":{"name":"Land Use","terms":""},"landuse/allotments":{"name":"Community Garden","terms":"allotment,garden"},"landuse/basin":{"name":"Basin","terms":""},"landuse/cemetery":{"name":"Cemetery","terms":""},"landuse/churchyard":{"name":"Churchyard","terms":""},"landuse/commercial":{"name":"Commercial Area","terms":""},"landuse/construction":{"name":"Construction","terms":""},"landuse/farm":{"name":"Farmland","terms":""},"landuse/farmland":{"name":"Farmland","terms":""},"landuse/farmyard":{"name":"Farmyard","terms":""},"landuse/forest":{"name":"Forest","terms":"tree"},"landuse/garages":{"name":"Garages","terms":""},"landuse/grass":{"name":"Grass","terms":""},"landuse/industrial":{"name":"Industrial Area","terms":""},"landuse/landfill":{"name":"Landfill","terms":"dump"},"landuse/meadow":{"name":"Meadow","terms":""},"landuse/military":{"name":"Military Area","terms":""},"landuse/orchard":{"name":"Orchard","terms":""},"landuse/plant_nursery":{"name":"Plant Nursery","terms":"vivero"},"landuse/quarry":{"name":"Quarry","terms":""},"landuse/residential":{"name":"Residential Area","terms":""},"landuse/retail":{"name":"Retail Area","terms":""},"landuse/vineyard":{"name":"Vineyard","terms":""},"leisure":{"name":"Leisure","terms":""},"leisure/adult_gaming_centre":{"name":"Adult Gaming Center","terms":"gambling,slot machine"},"leisure/bird_hide":{"name":"Bird Hide","terms":"machan,ornithology"},"leisure/bowling_alley":{"name":"Bowling Alley","terms":""},"leisure/common":{"name":"Common","terms":"open space"},"leisure/dog_park":{"name":"Dog Park","terms":""},"leisure/firepit":{"name":"Firepit","terms":"fireplace,campfire"},"leisure/garden":{"name":"Garden","terms":""},"leisure/golf_course":{"name":"Golf Course","terms":"links"},"leisure/ice_rink":{"name":"Ice Rink","terms":"hockey,skating,curling"},"leisure/marina":{"name":"Marina","terms":"boat"},"leisure/nature_reserve":{"name":"Nature Reserve","terms":"protected,wildlife"},"leisure/park":{"name":"Park","terms":"esplanade,estate,forest,garden,grass,green,grounds,lawn,lot,meadow,parkland,place,playground,plaza,pleasure garden,recreation area,square,tract,village green,woodland"},"leisure/picnic_table":{"name":"Picnic Table","terms":"bench"},"leisure/pitch":{"name":"Sport Pitch","terms":"field"},"leisure/pitch/american_football":{"name":"American Football Field","terms":""},"leisure/pitch/baseball":{"name":"Baseball Diamond","terms":""},"leisure/pitch/basketball":{"name":"Basketball Court","terms":""},"leisure/pitch/rugby_league":{"name":"Rugby League Field","terms":""},"leisure/pitch/rugby_union":{"name":"Rugby Union Field","terms":""},"leisure/pitch/skateboard":{"name":"Skate Park","terms":""},"leisure/pitch/soccer":{"name":"Soccer Field","terms":""},"leisure/pitch/tennis":{"name":"Tennis Court","terms":""},"leisure/pitch/volleyball":{"name":"Volleyball Court","terms":""},"leisure/playground":{"name":"Playground","terms":"jungle gym,play area"},"leisure/running_track":{"name":"Running Track","terms":""},"leisure/slipway":{"name":"Slipway","terms":"boat launch,boat ramp"},"leisure/sports_centre":{"name":"Sports Center / Gym","terms":"gym"},"leisure/sports_centre/swimming":{"name":"Swimming Pool Facility","terms":"dive,water"},"leisure/stadium":{"name":"Stadium","terms":""},"leisure/swimming_pool":{"name":"Swimming Pool","terms":"dive,water"},"leisure/track":{"name":"Racetrack (non-Motorsport)","terms":""},"leisure/water_park":{"name":"Water Park","terms":"swim,pool,dive"},"line":{"name":"Line","terms":""},"man_made":{"name":"Man Made","terms":""},"man_made/adit":{"name":"Adit","terms":"entrance,underground,mine,cave"},"man_made/breakwater":{"name":"Breakwater","terms":""},"man_made/chimney":{"name":"Chimney","terms":""},"man_made/cutline":{"name":"Cut line","terms":""},"man_made/embankment":{"name":"Embankment","terms":""},"man_made/flagpole":{"name":"Flagpole","terms":""},"man_made/gasometer":{"name":"Gasometer","terms":"gas holder"},"man_made/groyne":{"name":"Groyne","terms":""},"man_made/lighthouse":{"name":"Lighthouse","terms":""},"man_made/mast":{"name":"Radio Mast","terms":"broadcast tower,cell phone tower,cell tower,guyed tower,mobile phone tower,radio tower,television tower,transmission mast,transmission tower,tv tower"},"man_made/observation":{"name":"Observation Tower","terms":"lookout tower,fire tower"},"man_made/petroleum_well":{"name":"Oil Well","terms":"drilling rig,oil derrick,oil drill,oil horse,oil rig,oil pump,petroleum well,pumpjack"},"man_made/pier":{"name":"Pier","terms":"dock"},"man_made/pipeline":{"name":"Pipeline","terms":""},"man_made/silo":{"name":"Silo","terms":"grain,corn,wheat"},"man_made/storage_tank":{"name":"Storage Tank","terms":"water,oil,gas,petrol"},"man_made/surveillance":{"name":"Surveillance","terms":""},"man_made/survey_point":{"name":"Survey Point","terms":""},"man_made/tower":{"name":"Tower","terms":""},"man_made/wastewater_plant":{"name":"Wastewater Plant","terms":"sewage*,water treatment plant,reclamation plant"},"man_made/water_tower":{"name":"Water Tower","terms":""},"man_made/water_well":{"name":"Water Well","terms":""},"man_made/water_works":{"name":"Water Works","terms":""},"man_made/works":{"name":"Works","terms":"car assembly plant,aluminium processing plant,brewery,furniture manufacture factory,oil refinery,plastic recycling"},"military/airfield":{"name":"Airfield","terms":""},"military/barracks":{"name":"Barracks","terms":""},"military/bunker":{"name":"Bunker","terms":""},"military/checkpoint":{"name":"Checkpoint","terms":""},"military/danger_area":{"name":"Danger Area","terms":""},"military/naval_base":{"name":"Naval Base","terms":""},"military/obstacle_course":{"name":"Obstacle Course","terms":""},"military/range":{"name":"Military Range","terms":""},"military/training_area":{"name":"Training area","terms":""},"natural":{"name":"Natural","terms":""},"natural/bay":{"name":"Bay","terms":""},"natural/beach":{"name":"Beach","terms":""},"natural/cave_entrance":{"name":"Cave Entrance","terms":"cavern,hollow,grotto,shelter,cavity"},"natural/cliff":{"name":"Cliff","terms":""},"natural/coastline":{"name":"Coastline","terms":"shore"},"natural/fell":{"name":"Fell","terms":""},"natural/glacier":{"name":"Glacier","terms":""},"natural/grassland":{"name":"Grassland","terms":""},"natural/heath":{"name":"Heath","terms":""},"natural/peak":{"name":"Peak","terms":"acme,aiguille,alp,climax,crest,crown,hill,mount,mountain,pinnacle,summit,tip,top"},"natural/saddle":{"name":"Saddle","terms":"pass,mountain pass,top"},"natural/scree":{"name":"Scree","terms":"loose rocks"},"natural/scrub":{"name":"Scrub","terms":"bush,shrubs"},"natural/spring":{"name":"Spring","terms":""},"natural/tree":{"name":"Tree","terms":""},"natural/tree_row":{"name":"Tree row","terms":""},"natural/volcano":{"name":"Volcano","terms":"mountain,crater"},"natural/water":{"name":"Water","terms":""},"natural/water/lake":{"name":"Lake","terms":"lakelet,loch,mere"},"natural/water/pond":{"name":"Pond","terms":"lakelet,millpond,tarn,pool,mere"},"natural/water/reservoir":{"name":"Reservoir","terms":""},"natural/wetland":{"name":"Wetland","terms":""},"natural/wood":{"name":"Wood","terms":"tree"},"office":{"name":"Office","terms":""},"office/accountant":{"name":"Accountant","terms":""},"office/administrative":{"name":"Administrative Office","terms":""},"office/architect":{"name":"Architect","terms":""},"office/company":{"name":"Company Office","terms":""},"office/educational_institution":{"name":"Educational Institution","terms":""},"office/employment_agency":{"name":"Employment Agency","terms":"job"},"office/estate_agent":{"name":"Real Estate Office","terms":""},"office/financial":{"name":"Financial Office","terms":""},"office/government":{"name":"Government Office","terms":""},"office/insurance":{"name":"Insurance Office","terms":""},"office/it":{"name":"IT Office","terms":""},"office/lawyer":{"name":"Law Office","terms":""},"office/newspaper":{"name":"Newspaper","terms":""},"office/ngo":{"name":"NGO Office","terms":""},"office/physician":{"name":"Physician","terms":""},"office/political_party":{"name":"Political Party","terms":""},"office/research":{"name":"Research Office","terms":""},"office/telecommunication":{"name":"Telecom Office","terms":""},"office/therapist":{"name":"Therapist","terms":""},"office/travel_agent":{"name":"Travel Agency","terms":""},"piste":{"name":"Piste/Ski Trail","terms":"ski,sled,sleigh,snowboard,nordic,downhill,snowmobile"},"place":{"name":"Place","terms":""},"place/city":{"name":"City","terms":""},"place/farm":{"name":"Farm","terms":""},"place/hamlet":{"name":"Hamlet","terms":""},"place/island":{"name":"Island","terms":"archipelago,atoll,bar,cay,isle,islet,key,reef"},"place/isolated_dwelling":{"name":"Isolated Dwelling","terms":""},"place/locality":{"name":"Locality","terms":""},"place/neighbourhood":{"name":"Neighborhood","terms":"neighbourhood"},"place/suburb":{"name":"Borough","terms":"Boro,Quarter"},"place/town":{"name":"Town","terms":""},"place/village":{"name":"Village","terms":""},"point":{"name":"Point","terms":""},"power":{"name":"Power","terms":""},"power/generator":{"name":"Power Generator","terms":""},"power/line":{"name":"Power Line","terms":""},"power/minor_line":{"name":"Minor Power Line","terms":""},"power/pole":{"name":"Power Pole","terms":""},"power/sub_station":{"name":"Substation","terms":""},"power/substation":{"name":"Substation","terms":""},"power/tower":{"name":"High-Voltage Tower","terms":""},"power/transformer":{"name":"Transformer","terms":""},"public_transport/platform":{"name":"Platform","terms":""},"public_transport/stop_position":{"name":"Stop Position","terms":""},"railway":{"name":"Railway","terms":""},"railway/abandoned":{"name":"Abandoned Railway","terms":""},"railway/disused":{"name":"Disused Railway","terms":""},"railway/funicular":{"name":"Funicular","terms":"venicular,cliff railway,cable car,cable railway,funicular railway"},"railway/halt":{"name":"Railway Halt","terms":"break,interrupt,rest,wait,interruption"},"railway/level_crossing":{"name":"Railway Crossing","terms":"crossing,railroad crossing,level crossing,grade crossing,road through railroad,train crossing"},"railway/monorail":{"name":"Monorail","terms":""},"railway/narrow_gauge":{"name":"Narrow Gauge Rail","terms":"narrow gauge railway,narrow gauge railroad"},"railway/platform":{"name":"Railway Platform","terms":""},"railway/rail":{"name":"Rail","terms":""},"railway/station":{"name":"Railway Station","terms":"train station,station"},"railway/subway":{"name":"Subway","terms":""},"railway/subway_entrance":{"name":"Subway Entrance","terms":""},"railway/tram":{"name":"Tram","terms":"streetcar"},"relation":{"name":"Relation","terms":""},"roundabout":{"name":"Roundabout","terms":""},"route/ferry":{"name":"Ferry Route","terms":""},"shop":{"name":"Shop","terms":""},"shop/alcohol":{"name":"Liquor Store","terms":"alcohol,beer,booze,wine"},"shop/anime":{"name":"Anime Shop","terms":""},"shop/antiques":{"name":"Antiques Shop","terms":""},"shop/art":{"name":"Art Store","terms":"art*,exhibit*,gallery"},"shop/baby_goods":{"name":"Baby Goods Store","terms":""},"shop/bag":{"name":"Bag/Luggage Store","terms":"handbag,purse"},"shop/bakery":{"name":"Bakery","terms":""},"shop/bathroom_furnishing":{"name":"Bathroom Furnishing Store","terms":""},"shop/beauty":{"name":"Beauty Shop","terms":"nail spa,spa,salon,tanning"},"shop/bed":{"name":"Bedding/Mattress Store","terms":""},"shop/beverages":{"name":"Beverage Store","terms":""},"shop/bicycle":{"name":"Bicycle Shop","terms":"bike,repair"},"shop/bookmaker":{"name":"Bookmaker","terms":""},"shop/books":{"name":"Book Store","terms":""},"shop/boutique":{"name":"Boutique","terms":""},"shop/butcher":{"name":"Butcher","terms":"meat"},"shop/candles":{"name":"Candle Shop","terms":""},"shop/car":{"name":"Car Dealership","terms":"auto"},"shop/car_parts":{"name":"Car Parts Store","terms":"auto"},"shop/car_repair":{"name":"Car Repair Shop","terms":"auto"},"shop/carpet":{"name":"Carpet Store","terms":"rug"},"shop/cheese":{"name":"Cheese Store","terms":""},"shop/chemist":{"name":"Chemist","terms":""},"shop/chocolate":{"name":"Chocolate Store","terms":""},"shop/clothes":{"name":"Clothing Store","terms":""},"shop/coffee":{"name":"Coffee Store","terms":""},"shop/computer":{"name":"Computer Store","terms":""},"shop/confectionery":{"name":"Candy Store","terms":""},"shop/convenience":{"name":"Convenience Store","terms":""},"shop/copyshop":{"name":"Copy Store","terms":""},"shop/cosmetics":{"name":"Cosmetics Store","terms":""},"shop/craft":{"name":"Arts and Crafts Store","terms":"art*,paint*,frame"},"shop/curtain":{"name":"Curtain Store","terms":"drape*,window"},"shop/dairy":{"name":"Dairy Store","terms":"milk,egg,cheese"},"shop/deli":{"name":"Deli","terms":"lunch,meat,sandwich"},"shop/department_store":{"name":"Department Store","terms":""},"shop/doityourself":{"name":"DIY Store","terms":""},"shop/dry_cleaning":{"name":"Dry Cleaner","terms":""},"shop/electronics":{"name":"Electronics Store","terms":"appliance,audio,computer,tv"},"shop/erotic":{"name":"Erotic Store","terms":"sex,porn"},"shop/fabric":{"name":"Fabric Store","terms":"sew"},"shop/farm":{"name":"Produce Stand","terms":"farm shop,farm stand"},"shop/fashion":{"name":"Fashion Store","terms":""},"shop/fishmonger":{"name":"Fishmonger","terms":""},"shop/florist":{"name":"Florist","terms":"flower"},"shop/frame":{"name":"Framing Shop","terms":"art*,paint*,photo*,frame"},"shop/funeral_directors":{"name":"Funeral Home","terms":"undertaker,memorial home"},"shop/furnace":{"name":"Furnace Store","terms":"oven,stove"},"shop/furniture":{"name":"Furniture Store","terms":"chair,sofa,table"},"shop/garden_centre":{"name":"Garden Center","terms":"landscape,mulch,shrub,tree"},"shop/gift":{"name":"Gift Shop","terms":""},"shop/greengrocer":{"name":"Greengrocer","terms":"fruit,vegetable"},"shop/hairdresser":{"name":"Hairdresser","terms":""},"shop/hardware":{"name":"Hardware Store","terms":""},"shop/hearing_aids":{"name":"Hearing Aids Store","terms":""},"shop/herbalist":{"name":"Herbalist","terms":""},"shop/hifi":{"name":"Hifi Store","terms":"stereo,video"},"shop/houseware":{"name":"Houseware Store","terms":"home,household"},"shop/interior_decoration":{"name":"Interior Decoration Store","terms":""},"shop/jewelry":{"name":"Jeweler","terms":"diamond,gem,ring"},"shop/kiosk":{"name":"News Kiosk","terms":""},"shop/kitchen":{"name":"Kitchen Design Store","terms":""},"shop/laundry":{"name":"Laundry","terms":""},"shop/leather":{"name":"Leather Store","terms":""},"shop/locksmith":{"name":"Locksmith","terms":"key,lockpick"},"shop/lottery":{"name":"Lottery Shop","terms":""},"shop/mall":{"name":"Mall","terms":""},"shop/massage":{"name":"Massage Shop","terms":""},"shop/medical_supply":{"name":"Medical Supply Store","terms":""},"shop/mobile_phone":{"name":"Mobile Phone Store","terms":""},"shop/money_lender":{"name":"Money Lender","terms":""},"shop/motorcycle":{"name":"Motorcycle Dealership","terms":""},"shop/music":{"name":"Music Store","terms":"CD,vinyl"},"shop/musical_instrument":{"name":"Musical Instrument Store","terms":""},"shop/newsagent":{"name":"Newspaper/Magazine Shop","terms":""},"shop/nutrition_supplements":{"name":"Nutrition Supplements Store","terms":""},"shop/optician":{"name":"Optician","terms":"eye,glasses"},"shop/organic":{"name":"Organic Goods Store","terms":""},"shop/outdoor":{"name":"Outdoors Store","terms":"camping,climbing,hiking"},"shop/paint":{"name":"Paint Store","terms":""},"shop/pawnbroker":{"name":"Pawn Shop","terms":""},"shop/pet":{"name":"Pet Store","terms":"cat,dog,fish"},"shop/photo":{"name":"Photography Store","terms":"camera,film"},"shop/pyrotechnics":{"name":"Fireworks Store","terms":""},"shop/radiotechnics":{"name":"Radio/Electronic Component Store","terms":""},"shop/religion":{"name":"Religious Store","terms":""},"shop/scuba_diving":{"name":"Scuba Diving Shop","terms":""},"shop/seafood":{"name":"Seafood Shop","terms":"fishmonger"},"shop/second_hand":{"name":"Consignment/Thrift Store","terms":"secondhand,second hand,resale,thrift,used"},"shop/shoes":{"name":"Shoe Store","terms":""},"shop/sports":{"name":"Sporting Goods Store","terms":""},"shop/stationery":{"name":"Stationery Store","terms":"card,paper"},"shop/storage_rental":{"name":"Storage Rental","terms":""},"shop/supermarket":{"name":"Supermarket","terms":"grocery,store,shop"},"shop/tailor":{"name":"Tailor","terms":"clothes,suit"},"shop/tattoo":{"name":"Tattoo Parlor","terms":""},"shop/tea":{"name":"Tea Store","terms":""},"shop/ticket":{"name":"Ticket Seller","terms":""},"shop/tobacco":{"name":"Tobacco Shop","terms":""},"shop/toys":{"name":"Toy Store","terms":""},"shop/travel_agency":{"name":"Travel Agency","terms":""},"shop/tyres":{"name":"Tire Store","terms":""},"shop/vacant":{"name":"Vacant Shop","terms":""},"shop/vacuum_cleaner":{"name":"Vacuum Cleaner Store","terms":""},"shop/variety_store":{"name":"Variety Store","terms":""},"shop/video":{"name":"Video Store","terms":"DVD"},"shop/video_games":{"name":"Video Game Store","terms":""},"shop/water_sports":{"name":"Watersport/Swim Shop","terms":""},"shop/weapons":{"name":"Weapon Shop","terms":"ammo,gun,knife,knives"},"shop/window_blind":{"name":"Window Blind Store","terms":""},"shop/wine":{"name":"Wine Shop","terms":""},"tourism":{"name":"Tourism","terms":""},"tourism/alpine_hut":{"name":"Alpine Hut","terms":""},"tourism/artwork":{"name":"Artwork","terms":"mural,sculpture,statue"},"tourism/attraction":{"name":"Tourist Attraction","terms":""},"tourism/camp_site":{"name":"Camp Site","terms":"Tent"},"tourism/caravan_site":{"name":"RV Park","terms":"Motor Home,Camper"},"tourism/chalet":{"name":"Chalet","terms":""},"tourism/gallery":{"name":"Art Gallery","terms":"art*,exhibit*,paint*,photo*,sculpt*"},"tourism/guest_house":{"name":"Guest House","terms":"B&B,Bed and Breakfast"},"tourism/hostel":{"name":"Hostel","terms":""},"tourism/hotel":{"name":"Hotel","terms":""},"tourism/information":{"name":"Information","terms":""},"tourism/motel":{"name":"Motel","terms":""},"tourism/museum":{"name":"Museum","terms":"art*,exhibit*,gallery,foundation,hall,institution,paint*,photo*,sculpt*"},"tourism/picnic_site":{"name":"Picnic Site","terms":"camp"},"tourism/theme_park":{"name":"Theme Park","terms":""},"tourism/viewpoint":{"name":"Viewpoint","terms":""},"tourism/zoo":{"name":"Zoo","terms":""},"traffic_calming/bump":{"name":"Speed Bump","terms":"speed hump"},"traffic_calming/hump":{"name":"Speed Hump","terms":"speed bump"},"traffic_calming/rumble_strip":{"name":"Rumble Strip","terms":"sleeper lines,audible lines,growlers"},"traffic_calming/table":{"name":"Raised Pedestrian Crossing","terms":"speed table,flat top hump"},"type/boundary":{"name":"Boundary","terms":""},"type/boundary/administrative":{"name":"Administrative Boundary","terms":""},"type/multipolygon":{"name":"Multipolygon","terms":""},"type/restriction":{"name":"Restriction","terms":""},"type/restriction/no_left_turn":{"name":"No Left Turn","terms":""},"type/restriction/no_right_turn":{"name":"No Right Turn","terms":""},"type/restriction/no_straight_on":{"name":"No Straight On","terms":""},"type/restriction/no_u_turn":{"name":"No U-turn","terms":""},"type/restriction/only_left_turn":{"name":"Left Turn Only","terms":""},"type/restriction/only_right_turn":{"name":"Right Turn Only","terms":""},"type/restriction/only_straight_on":{"name":"No Turns","terms":""},"type/route":{"name":"Route","terms":""},"type/route/bicycle":{"name":"Cycle Route","terms":""},"type/route/bus":{"name":"Bus Route","terms":""},"type/route/detour":{"name":"Detour Route","terms":""},"type/route/ferry":{"name":"Ferry Route","terms":""},"type/route/foot":{"name":"Foot Route","terms":""},"type/route/hiking":{"name":"Hiking Route","terms":""},"type/route/pipeline":{"name":"Pipeline Route","terms":""},"type/route/power":{"name":"Power Route","terms":""},"type/route/road":{"name":"Road Route","terms":""},"type/route/train":{"name":"Train Route","terms":""},"type/route/tram":{"name":"Tram Route","terms":""},"type/route_master":{"name":"Route Master","terms":""},"type/site":{"name":"Site","terms":""},"vertex":{"name":"Other","terms":""},"waterway":{"name":"Waterway","terms":""},"waterway/boatyard":{"name":"Boatyard","terms":""},"waterway/canal":{"name":"Canal","terms":""},"waterway/dam":{"name":"Dam","terms":""},"waterway/ditch":{"name":"Ditch","terms":""},"waterway/dock":{"name":"Wet Dock / Dry Dock","terms":"boat,ship,vessel,marine"},"waterway/drain":{"name":"Drain","terms":""},"waterway/fuel":{"name":"Marine Fuel Station","terms":"petrol,gas,diesel,boat"},"waterway/river":{"name":"River","terms":"beck,branch,brook,course,creek,estuary,rill,rivulet,run,runnel,stream,tributary,watercourse"},"waterway/riverbank":{"name":"Riverbank","terms":""},"waterway/sanitary_dump_station":{"name":"Marine Toilet Disposal","terms":"Boat,Watercraft,Sanitary,Dump Station,Pumpout,Pump out,Elsan,CDP,CTDP,Chemical Toilet"},"waterway/stream":{"name":"Stream","terms":"beck,branch,brook,burn,course,creek,current,drift,flood,flow,freshet,race,rill,rindle,rivulet,run,runnel,rush,spate,spritz,surge,tide,torrent,tributary,watercourse"},"waterway/water_point":{"name":"Marine Drinking Water","terms":""},"waterway/weir":{"name":"Weir","terms":""}}}},"suggestions":{"amenity":{"fuel":{"76":{"count":314},"Neste":{"count":189},"BP":{"count":2511},"Shell":{"count":8380},"Agip":{"count":2651},"Migrol":{"count":65},"Avia":{"count":897},"Texaco":{"count":680},"Total":{"count":2607},"Statoil":{"count":596},"Esso":{"count":3652},"Jet":{"count":441},"Avanti":{"count":90},"Sainsbury's":{"count":58},"OMV":{"count":701},"Aral":{"count":1339},"Tesco":{"count":197},"JET":{"count":180},"Morrisons":{"count":111},"United":{"count":91},"Canadian Tire":{"count":66},"Mobil":{"count":613},"Caltex":{"count":1001},"Sunoco":{"count":355},"Q8":{"count":1161},"ABC":{"count":79},"ARAL":{"count":375},"CEPSA":{"count":1018},"BFT":{"count":89},"Petron":{"count":878},"Intermarché":{"count":434},"Total Access":{"count":51},"Super U":{"count":124},"Auchan":{"count":53},"Elf":{"count":129},"Carrefour":{"count":205},"Station Service E. Leclerc":{"count":530},"Shell Express":{"count":131},"Hess":{"count":127},"Flying V":{"count":129},"bft":{"count":168},"Gulf":{"count":199},"PTT":{"count":191},"St1":{"count":100},"Teboil":{"count":115},"HEM":{"count":212},"GALP":{"count":626},"OK":{"count":163},"ÖMV":{"count":101},"Tinq":{"count":215},"OKQ8":{"count":186},"Repsol":{"count":424},"Westfalen":{"count":96},"Esso Express":{"count":98},"Raiffeisenbank":{"count":117},"Tamoil":{"count":866},"Engen":{"count":241},"Sasol":{"count":59},"Topaz":{"count":78},"LPG":{"count":174},"Coop":{"count":62},"Orlen":{"count":598},"Oilibya":{"count":68},"Tango":{"count":122},"Star":{"count":319},"Петрол":{"count":84},"Cepsa":{"count":96},"OIL!":{"count":63},"Ultramar":{"count":125},"Irving":{"count":87},"Lukoil":{"count":701},"Petro-Canada":{"count":489},"7-Eleven":{"count":488},"Agrola":{"count":69},"Husky":{"count":126},"Slovnaft":{"count":219},"Sheetz":{"count":134},"Mol":{"count":61},"Petronas":{"count":159},"Газпромнефть":{"count":748},"Лукойл":{"count":1477},"Elan":{"count":112},"Роснефть":{"count":638},"Turmöl":{"count":57},"Neste A24":{"count":55},"Marathon":{"count":189},"Valero":{"count":366},"Eni":{"count":236},"Chevron":{"count":954},"ТНК":{"count":520},"REPSOL":{"count":1603},"MOL":{"count":228},"Bliska":{"count":150},"Api":{"count":302},"Arco":{"count":179},"Pemex":{"count":423},"Exxon":{"count":506},"Coles Express":{"count":115},"Petrom":{"count":259},"PETRONOR":{"count":207},"Rompetrol":{"count":174},"Lotos":{"count":178},"ОМВ":{"count":60},"BR":{"count":129},"Copec":{"count":505},"Petrobras":{"count":270},"Liberty":{"count":55},"IP":{"count":871},"Erg":{"count":596},"Eneos":{"count":97},"Citgo":{"count":279},"Metano":{"count":208},"Сургутнефтегаз":{"count":61},"EKO":{"count":59},"Eko":{"count":58},"Indipend.":{"count":172},"IES":{"count":63},"TotalErg":{"count":89},"Cenex":{"count":115},"ПТК":{"count":82},"HP":{"count":79},"Phillips 66":{"count":216},"CARREFOUR":{"count":74},"ERG":{"count":76},"Speedway":{"count":148},"Benzina":{"count":96},"Татнефть":{"count":264},"Terpel":{"count":259},"WOG":{"count":189},"Seaoil":{"count":54},"АЗС":{"count":1077},"Kwik Trip":{"count":108},"Wawa":{"count":89},"Pertamina":{"count":186},"COSMO":{"count":64},"Z":{"count":76},"Indian Oil":{"count":183},"АГЗС":{"count":494},"INA":{"count":121},"JOMO":{"count":62},"Holiday":{"count":97},"YPF":{"count":70},"IDEMITSU":{"count":87},"ENEOS":{"count":736},"Bharat Petroleum":{"count":64},"CAMPSA":{"count":615},"Casey's General Store":{"count":190},"Башнефть":{"count":60},"Kangaroo":{"count":60},"コスモ石油 (COSMO)":{"count":136},"MEROIL":{"count":77},"1-2-3":{"count":71},"出光":{"count":228,"tags":{"name:en":"IDEMITSU"}},"НК Альянс":{"count":88},"Sinclair":{"count":100},"Conoco":{"count":189},"SPBU":{"count":54},"Макпетрол":{"count":109},"Circle K":{"count":166},"Posto Ipiranga":{"count":70},"Posto Shell":{"count":54},"Phoenix":{"count":144},"Ipiranga":{"count":119},"OKKO":{"count":85},"ОККО":{"count":119},"บางจาก":{"count":60},"QuikTrip":{"count":105},"Stewart's":{"count":63},"Posto BR":{"count":68},"ป ต ท":{"count":152},"ปตท":{"count":88},"ANP":{"count":80},"Kum & Go":{"count":80},"Petrolimex":{"count":55},"Sokimex":{"count":66},"Tela":{"count":154},"Posto":{"count":71},"H-E-B":{"count":182},"Укрнафта":{"count":58},"Татнефтепродукт":{"count":54},"Afriquia":{"count":88},"Murphy USA":{"count":67},"昭和シェル (Showa-shell)":{"count":94},"エネオス":{"count":53},"CNG":{"count":94}},"pub":{"Kings Arms":{"count":67},"The Ship":{"count":89},"The White Horse":{"count":204},"The White Hart":{"count":226},"Royal Oak":{"count":150},"The Red Lion":{"count":233},"The Kings Arms":{"count":58},"The Star":{"count":73},"The Anchor":{"count":64},"The Cross Keys":{"count":55},"The Wheatsheaf":{"count":117},"The Crown Inn":{"count":67},"The Kings Head":{"count":53},"The Castle":{"count":62},"The Railway":{"count":102},"The White Lion":{"count":118},"The Bell":{"count":121},"The Bull":{"count":68},"The Plough":{"count":179},"The George":{"count":110},"The Royal Oak":{"count":209},"The Fox":{"count":74},"Prince of Wales":{"count":77},"The Rising Sun":{"count":71},"The Prince of Wales":{"count":51},"The Crown":{"count":244},"The Chequers":{"count":66},"The Swan":{"count":152},"Rose and Crown":{"count":79},"The Victoria":{"count":67},"New Inn":{"count":90},"Royal Hotel":{"count":57},"Red Lion":{"count":207},"Cross Keys":{"count":61},"The Greyhound":{"count":96},"The Black Horse":{"count":94},"The New Inn":{"count":105},"Kings Head":{"count":59},"The Albion":{"count":51},"The Angel":{"count":52},"The Queens Head":{"count":52},"The Ship Inn":{"count":83},"Rose & Crown":{"count":51},"Queens Head":{"count":52},"Irish Pub":{"count":76}},"fast_food":{"Quick":{"count":484},"McDonald's":{"count":12376,"tags":{"cuisine":"burger"}},"Subway":{"count":5576,"tags":{"cuisine":"sandwich"}},"Burger King":{"count":3734,"tags":{"cuisine":"burger"}},"Ali Baba":{"count":61},"Hungry Jacks":{"count":173,"tags":{"cuisine":"burger"}},"Red Rooster":{"count":148},"KFC":{"count":3198,"tags":{"cuisine":"chicken"}},"Domino's Pizza":{"count":985,"tags":{"cuisine":"pizza"}},"Chowking":{"count":142},"Jollibee":{"count":396},"Hesburger":{"count":102},"肯德基":{"count":86},"Wendy's":{"count":1621,"tags":{"cuisine":"burger"}},"Tim Hortons":{"count":323},"Steers":{"count":151},"Hardee's":{"count":268,"tags":{"cuisine":"burger"}},"Arby's":{"count":782},"A&W":{"count":283},"Dairy Queen":{"count":791},"Hallo Pizza":{"count":76},"Fish & Chips":{"count":93},"Harvey's":{"count":90},"麥當勞":{"count":65},"Pizza Pizza":{"count":215},"Kotipizza":{"count":74},"Jack in the Box":{"count":546,"tags":{"cuisine":"burger"}},"Istanbul":{"count":56},"Kochlöffel":{"count":68},"Döner":{"count":228},"Telepizza":{"count":201},"Sibylla":{"count":61},"Carl's Jr.":{"count":298,"tags":{"cuisine":"burger"}},"Quiznos":{"count":266,"tags":{"cuisine":"sandwich"}},"Wimpy":{"count":141},"Sonic":{"count":566,"tags":{"cuisine":"burger"}},"Taco Bell":{"count":1423,"tags":{"cuisine":"mexican"}},"Pizza Nova":{"count":63},"Papa John's":{"count":304,"tags":{"cuisine":"pizza"}},"Nordsee":{"count":159},"Mr. Sub":{"count":103},"Макдоналдс":{"count":324,"tags":{"name:en":"McDonald's"}},"Asia Imbiss":{"count":111},"Chipotle":{"count":290,"tags":{"cuisine":"mexican"}},"マクドナルド":{"count":692,"tags":{"name:en":"McDonald's","cuisine":"burger"}},"In-N-Out Burger":{"count":65},"Jimmy John's":{"count":141},"Jamba Juice":{"count":68},"Робин Сдобин":{"count":82},"Baskin Robbins":{"count":74},"ケンタッキーフライドチキン":{"count":164,"tags":{"name:en":"KFC","cuisine":"chicken"}},"吉野家":{"count":191},"Taco Time":{"count":88},"松屋":{"count":281,"tags":{"name:en":"Matsuya"}},"Little Caesars":{"count":81},"El Pollo Loco":{"count":63},"Del Taco":{"count":141},"White Castle":{"count":80},"Boston Market":{"count":66},"Chick-fil-A":{"count":257,"tags":{"cuisine":"chicken"}},"Panda Express":{"count":238,"tags":{"cuisine":"chinese"}},"Whataburger":{"count":364},"Taco John's":{"count":78},"Теремок":{"count":68},"Culver's":{"count":425},"Five Guys":{"count":141},"Church's Chicken":{"count":95},"Popeye's":{"count":167,"tags":{"cuisine":"chicken"}},"Long John Silver's":{"count":93},"Pollo Campero":{"count":62},"Zaxby's":{"count":51},"すき家":{"count":276,"tags":{"name:en":"SUKIYA"}},"モスバーガー":{"count":257,"tags":{"name:en":"MOS BURGER"}},"Русский Аппетит":{"count":69},"なか卯":{"count":63}},"restaurant":{"Pizza Hut":{"count":1180,"tags":{"cuisine":"pizza"}},"Little Chef":{"count":64},"Adler":{"count":158},"Zur Krone":{"count":90},"Deutsches Haus":{"count":90},"Krone":{"count":171},"Akropolis":{"count":152},"Schützenhaus":{"count":124},"Kreuz":{"count":74},"Waldschänke":{"count":55},"La Piazza":{"count":69},"Lamm":{"count":66},"Zur Sonne":{"count":73},"Zur Linde":{"count":204},"Poseidon":{"count":110},"Shanghai":{"count":82},"Red Lobster":{"count":235},"Zum Löwen":{"count":84},"Swiss Chalet":{"count":107},"Olympia":{"count":74},"Wagamama":{"count":64},"Frankie & Benny's":{"count":66},"Hooters":{"count":103},"Sternen":{"count":78},"Hirschen":{"count":79},"Denny's":{"count":450},"Athen":{"count":68},"Sonne":{"count":126},"Hirsch":{"count":79},"Ratskeller":{"count":150},"La Cantina":{"count":56},"Gasthaus Krone":{"count":56},"El Greco":{"count":86},"Gasthof zur Post":{"count":79},"Nando's":{"count":246},"Löwen":{"count":112},"La Pataterie":{"count":51},"Bella Napoli":{"count":53},"Pizza Express":{"count":262},"Mandarin":{"count":65},"Hong Kong":{"count":83},"Zizzi":{"count":68},"Cracker Barrel":{"count":183},"Rhodos":{"count":81},"Lindenhof":{"count":79},"Milano":{"count":54},"Dolce Vita":{"count":77},"Kirchenwirt":{"count":81},"Kantine":{"count":52},"Ochsen":{"count":95},"Spur":{"count":62},"Mykonos":{"count":59},"Lotus":{"count":66},"Applebee's":{"count":531},"Flunch":{"count":72},"Zur Post":{"count":116},"China Town":{"count":76},"La Dolce Vita":{"count":73},"Waffle House":{"count":207},"Delphi":{"count":88},"Linde":{"count":103},"Outback Steakhouse":{"count":218},"Dionysos":{"count":69},"Kelsey's":{"count":57},"Boston Pizza":{"count":165},"Bella Italia":{"count":132},"Sizzler":{"count":53},"Grüner Baum":{"count":116},"Taj Mahal":{"count":104},"Rössli":{"count":68},"Wimpy":{"count":51},"Traube":{"count":65},"Adria":{"count":52},"Red Robin":{"count":185},"Roma":{"count":61},"San Marco":{"count":67},"Hellas":{"count":55},"La Perla":{"count":67},"Vips":{"count":53},"Panera Bread":{"count":218},"Da Vinci":{"count":54},"Hippopotamus":{"count":96},"Prezzo":{"count":75},"Courtepaille":{"count":106},"Hard Rock Cafe":{"count":70},"Panorama":{"count":61},"デニーズ":{"count":82},"Sportheim":{"count":65},"餃子の王将":{"count":57},"Bären":{"count":60},"Alte Post":{"count":60},"Pizzeria Roma":{"count":51},"China Garden":{"count":66},"Vapiano":{"count":82},"Mamma Mia":{"count":64},"Schwarzer Adler":{"count":57},"IHOP":{"count":317},"Chili's":{"count":328},"Asia":{"count":51},"Olive Garden":{"count":279},"TGI Friday's":{"count":159},"Friendly's":{"count":78},"Buffalo Grill":{"count":202},"Texas Roadhouse":{"count":110},"ガスト":{"count":230,"tags":{"name:en":"Gusto"}},"Sakura":{"count":75},"Mensa":{"count":99},"The Keg":{"count":53},"サイゼリヤ":{"count":93},"La Strada":{"count":52},"Village Inn":{"count":92},"Buffalo Wild Wings":{"count":176},"Peking":{"count":59},"Boston Market":{"count":61},"Round Table Pizza":{"count":53},"Jimmy John's":{"count":69},"California Pizza Kitchen":{"count":61},"Якитория":{"count":77},"Golden Corral":{"count":101},"Perkins":{"count":105},"Ruby Tuesday":{"count":162},"Shari's":{"count":65},"Bob Evans":{"count":129},"바다횟집 (Bada Fish Restaurant)":{"count":55},"Mang Inasal":{"count":84},"Евразия":{"count":102},"ジョナサン":{"count":59},"Longhorn Steakhouse":{"count":66}},"bank":{"Chase":{"count":721},"Commonwealth Bank":{"count":232},"Citibank":{"count":277},"HSBC":{"count":1102},"Barclays":{"count":965},"Westpac":{"count":208},"NAB":{"count":131},"ANZ":{"count":218},"Lloyds Bank":{"count":547},"Landbank":{"count":81},"Sparkasse":{"count":4555},"UCPB":{"count":92},"PNB":{"count":244},"Metrobank":{"count":269},"BDO":{"count":290},"Volksbank":{"count":2591},"BPI":{"count":415},"Postbank":{"count":443},"NatWest":{"count":628},"Raiffeisenbank":{"count":2119},"Yorkshire Bank":{"count":63},"ABSA":{"count":95},"Standard Bank":{"count":109},"FNB":{"count":97},"Deutsche Bank":{"count":855},"SEB":{"count":133},"Commerzbank":{"count":806},"Targobank":{"count":166},"ABN AMRO":{"count":130},"Handelsbanken":{"count":184},"Swedbank":{"count":223},"Kreissparkasse":{"count":600},"UniCredit Bank":{"count":408},"Monte dei Paschi di Siena":{"count":132},"Caja Rural":{"count":99},"Dresdner Bank":{"count":66},"Sparda-Bank":{"count":320},"VÚB":{"count":107},"Slovenská sporiteľňa":{"count":134},"Bank of Montreal":{"count":118},"KBC":{"count":203},"Royal Bank of Scotland":{"count":111},"TSB":{"count":80},"US Bank":{"count":256},"HypoVereinsbank":{"count":561},"Bank Austria":{"count":176},"ING":{"count":496},"Erste Bank":{"count":180},"CIBC":{"count":326},"Scotiabank":{"count":413},"Caisse d'Épargne":{"count":882},"Santander":{"count":1323},"Bank of Scotland":{"count":89},"TD Canada Trust":{"count":450},"BMO":{"count":169},"Danske Bank":{"count":131},"OTP":{"count":192},"Crédit Agricole":{"count":1239},"LCL":{"count":553},"VR-Bank":{"count":430},"ČSOB":{"count":160},"Česká spořitelna":{"count":212},"BNP":{"count":112},"Royal Bank":{"count":65},"Nationwide":{"count":209},"Halifax":{"count":225},"BAWAG PSK":{"count":102},"National Bank":{"count":84},"Nedbank":{"count":80},"First National Bank":{"count":85},"Nordea":{"count":319},"Rabobank":{"count":609},"Sparkasse KölnBonn":{"count":69},"Tatra banka":{"count":67},"Berliner Sparkasse":{"count":62},"Berliner Volksbank":{"count":77},"Wells Fargo":{"count":874},"Credit Suisse":{"count":71},"Société Générale":{"count":634},"Osuuspankki":{"count":75},"Sparkasse Aachen":{"count":56},"Hamburger Sparkasse":{"count":156},"Cassa di Risparmio del Veneto":{"count":68},"BNP Paribas":{"count":617},"Banque Populaire":{"count":433},"BNP Paribas Fortis":{"count":209},"Banco Popular":{"count":291},"Bancaja":{"count":55},"Banesto":{"count":208},"La Caixa":{"count":583},"Santander Consumer Bank":{"count":88},"BRD":{"count":191},"BCR":{"count":143},"Banca Transilvania":{"count":141},"BW-Bank":{"count":97},"Komerční banka":{"count":132},"Banco Pastor":{"count":64},"Stadtsparkasse":{"count":86},"Ulster Bank":{"count":86},"Sberbank":{"count":58},"CIC":{"count":427},"Bancpost":{"count":56},"Caja Madrid":{"count":115},"Maybank":{"count":94},"中国银行":{"count":85},"Unicredit Banca":{"count":243},"Crédit Mutuel":{"count":690},"BBVA":{"count":647},"Intesa San Paolo":{"count":69},"TD Bank":{"count":206},"Belfius":{"count":231},"Bank of America":{"count":924},"RBC":{"count":230},"Alpha Bank":{"count":123},"Сбербанк":{"count":4794},"Россельхозбанк":{"count":201},"Crédit du Nord":{"count":96},"BancoEstado":{"count":80},"Millennium Bank":{"count":414},"State Bank of India":{"count":151},"Беларусбанк":{"count":242},"ING Bank Śląski":{"count":67},"Caixa Geral de Depósitos":{"count":129},"Kreissparkasse Köln":{"count":65},"Banco BCI":{"count":51},"Banco de Chile":{"count":98},"ВТБ24":{"count":326},"UBS":{"count":134},"PKO BP":{"count":265},"Chinabank":{"count":55},"PSBank":{"count":59},"Union Bank":{"count":124},"China Bank":{"count":66},"RCBC":{"count":122},"Unicaja":{"count":83},"BBK":{"count":79},"Ibercaja":{"count":69},"RBS":{"count":143},"Commercial Bank of Ceylon PLC":{"count":79},"Bank of Ireland":{"count":109},"BNL":{"count":87},"Banco Santander":{"count":138},"Banco Itaú":{"count":111},"AIB":{"count":72},"BZ WBK":{"count":77},"Banco do Brasil":{"count":557},"Caixa Econômica Federal":{"count":184},"Fifth Third Bank":{"count":84},"Banca Popolare di Vicenza":{"count":81},"Wachovia":{"count":58},"OLB":{"count":53},"みずほ銀行":{"count":78},"BES":{"count":72},"ICICI Bank":{"count":91},"HDFC Bank":{"count":91},"La Banque Postale":{"count":67},"Pekao SA":{"count":56},"Oberbank":{"count":90},"Bradesco":{"count":295},"Oldenburgische Landesbank":{"count":56},"Bendigo Bank":{"count":93},"Argenta":{"count":86},"AXA":{"count":68},"Axis Bank":{"count":61},"Banco Nación":{"count":67},"GE Money Bank":{"count":72},"Альфа-Банк":{"count":185},"Белагропромбанк":{"count":70},"Caja Círculo":{"count":65},"Banco Galicia":{"count":51},"Eurobank":{"count":97},"Banca Intesa":{"count":62},"Canara Bank":{"count":92},"Cajamar":{"count":77},"Banamex":{"count":149},"Crédit Mutuel de Bretagne":{"count":335},"Davivienda":{"count":83},"Bank Spółdzielczy":{"count":159},"Credit Agricole":{"count":157},"Bankinter":{"count":59},"Banque Nationale":{"count":63},"Bank of the West":{"count":96},"Key Bank":{"count":155},"Western Union":{"count":88},"Citizens Bank":{"count":115},"ПриватБанк":{"count":513},"Security Bank":{"count":78},"Millenium":{"count":60},"Bankia":{"count":149},"三菱東京UFJ銀行":{"count":159},"Caixa":{"count":117},"Banco de Costa Rica":{"count":63},"SunTrust Bank":{"count":73},"Itaú":{"count":338},"PBZ":{"count":52},"中国工商银行":{"count":51},"Bancolombia":{"count":89},"Райффайзен Банк Аваль":{"count":64},"Bancomer":{"count":115},"Banorte":{"count":80},"Alior Bank":{"count":81},"BOC":{"count":51},"Банк Москвы":{"count":118},"ВТБ":{"count":59},"Getin Bank":{"count":55},"Caja Duero":{"count":57},"Regions Bank":{"count":62},"Росбанк":{"count":177},"Banco Estado":{"count":72},"BCI":{"count":68},"SunTrust":{"count":68},"PNC Bank":{"count":254},"신한은행":{"count":217,"tags":{"name:en":"Sinhan Bank"}},"우리은행":{"count":291,"tags":{"name:en":"Uri Bank"}},"국민은행":{"count":165,"tags":{"name:en":"Gungmin Bank"}},"중소기업은행":{"count":52,"tags":{"name:en":"Industrial Bank of Korea"}},"광주은행":{"count":51,"tags":{"name:en":"Gwangju Bank"}},"Газпромбанк":{"count":100},"M&T Bank":{"count":92},"Caja de Burgos":{"count":51},"Santander Totta":{"count":69},"УкрСиббанк":{"count":192},"Ощадбанк":{"count":364},"Уралсиб":{"count":85},"りそな銀行":{"count":225,"tags":{"name:en":"Mizuho Bank"}},"Ecobank":{"count":66},"Cajero Automatico Bancared":{"count":145},"Промсвязьбанк":{"count":93},"三井住友銀行":{"count":129},"Banco Provincia":{"count":67},"BB&T":{"count":147},"Возрождение":{"count":59},"Capital One":{"count":59},"横浜銀行":{"count":51},"Bank Mandiri":{"count":62},"Banco de la Nación":{"count":92},"Banco G&T Continental":{"count":62},"Peoples Bank":{"count":60},"工商银行":{"count":51},"Совкомбанк":{"count":55},"Provincial":{"count":56},"Banco de Desarrollo Banrural":{"count":73},"Banco Bradesco":{"count":65},"Bicentenario":{"count":182},"ლიბერთი ბანკი":{"count":54,"tags":{"name:en":"Liberty Bank"}},"Banesco":{"count":108},"Mercantil":{"count":75},"Bank BRI":{"count":53},"Del Tesoro":{"count":91},"하나은행":{"count":77},"CityCommerce Bank":{"count":71},"De Venezuela":{"count":117}},"car_rental":{"Europcar":{"count":291},"Budget":{"count":92},"Sixt":{"count":161},"Avis":{"count":282},"Hertz":{"count":293},"Enterprise":{"count":199},"stadtmobil CarSharing-Station":{"count":148}},"pharmacy":{"Rowlands Pharmacy":{"count":71},"Boots":{"count":840},"Marien-Apotheke":{"count":314},"Mercury Drug":{"count":426},"Löwen-Apotheke":{"count":356},"Superdrug":{"count":117},"Sonnen-Apotheke":{"count":311},"Rathaus-Apotheke":{"count":132},"Engel-Apotheke":{"count":123},"Hirsch-Apotheke":{"count":83},"Stern-Apotheke":{"count":67},"Lloyds Pharmacy":{"count":295},"Rosen-Apotheke":{"count":208},"Stadt-Apotheke":{"count":302},"Markt-Apotheke":{"count":164},"Аптека":{"count":1989},"Pharmasave":{"count":64},"Brunnen-Apotheke":{"count":53},"Shoppers Drug Mart":{"count":430},"Apotheke am Markt":{"count":60},"Alte Apotheke":{"count":88},"Neue Apotheke":{"count":109},"Gintarinė vaistinė":{"count":101},"Rats-Apotheke":{"count":84},"Adler Apotheke":{"count":313},"Pharmacie Centrale":{"count":64},"Walgreens":{"count":1619},"Rite Aid":{"count":745},"Apotheke":{"count":165},"Linden-Apotheke":{"count":211},"Bahnhof-Apotheke":{"count":66},"Burg-Apotheke":{"count":55},"Jean Coutu":{"count":62},"Pharmaprix":{"count":60},"Farmacias Ahumada":{"count":104},"Farmacia Comunale":{"count":113},"Farmacias Cruz Verde":{"count":86},"Cruz Verde":{"count":99},"Hubertus Apotheke":{"count":52},"CVS":{"count":1560},"Farmacias SalcoBrand":{"count":133},"Фармация":{"count":120},"Bären-Apotheke":{"count":74},"Clicks":{"count":113},"セイジョー":{"count":53},"マツモトキヨシ":{"count":115},"Dr. Max":{"count":51},"Вита":{"count":106},"Радуга":{"count":70},"サンドラッグ":{"count":61},"Apteka":{"count":366},"Первая помощь":{"count":74},"Ригла":{"count":113},"Имплозия":{"count":63},"Kinney Drugs":{"count":68},"Классика":{"count":67},"Ljekarna":{"count":53},"SalcoBrand":{"count":88},"Аптека 36,6":{"count":224},"Фармакор":{"count":75},"スギ薬局":{"count":84},"Аптечный пункт":{"count":148},"Невис":{"count":60},"トモズ (Tomod's)":{"count":83},"Eurovaistinė":{"count":65},"Farmacity":{"count":68},"аптека":{"count":96},"The Generics Pharmacy":{"count":95},"Farmatodo":{"count":123},"Duane Reade":{"count":61},"H-E-B":{"count":262},"Фармленд":{"count":82},"ドラッグてらしま (Drug Terashima)":{"count":96},"Арніка":{"count":125},"ავერსი (Aversi)":{"count":62},"Farmahorro":{"count":58}},"cafe":{"Starbucks":{"count":4238,"tags":{"cuisine":"coffee_shop"}},"Cafeteria":{"count":115},"Costa":{"count":618},"Caffè Nero":{"count":169},"Кафе":{"count":226},"Café Central":{"count":61},"Second Cup":{"count":193},"Dunkin Donuts":{"count":428,"tags":{"cuisine":"donut"}},"Espresso House":{"count":53},"Segafredo":{"count":69},"Coffee Time":{"count":94},"Cafe Coffee Day":{"count":120},"Eiscafe Venezia":{"count":180},"スターバックス":{"count":251,"tags":{"name:en":"Starbucks"}},"Шоколадница":{"count":145},"Pret A Manger":{"count":119},"Столовая":{"count":391},"Jamba Juice":{"count":53},"ドトール":{"count":164,"tags":{"name:en":"DOUTOR"}},"Tchibo":{"count":100},"Кофе Хауз":{"count":104},"Caribou Coffee":{"count":100},"Уют":{"count":51},"Шашлычная":{"count":58},"คาเฟ่ อเมซอน":{"count":62},"Traveler's Coffee":{"count":60},"カフェ・ド・クリエ":{"count":67,"tags":{"name:en":"Cafe de CRIE"}},"Cafe Amazon":{"count":65}}},"shop":{"supermarket":{"Budgens":{"count":88},"Morrisons":{"count":411},"Interspar":{"count":142},"Merkur":{"count":107},"Sainsbury's":{"count":547},"Lidl":{"count":7130},"Edeka":{"count":2293},"Coles":{"count":400},"Iceland":{"count":315},"Coop":{"count":2100},"Tesco":{"count":1297},"Woolworths":{"count":541},"Zielpunkt":{"count":239},"Nahkauf":{"count":170},"Billa":{"count":1432},"Kaufland":{"count":1004},"Plus":{"count":120},"ALDI":{"count":5172},"Checkers":{"count":128},"Tesco Metro":{"count":137},"NP":{"count":153},"Penny":{"count":1759},"Norma":{"count":1068},"Asda":{"count":225},"Netto":{"count":4379},"Rewe":{"count":2645},"Aldi Süd":{"count":594},"Real":{"count":246},"Tesco Express":{"count":406},"King Soopers":{"count":72},"Kiwi":{"count":167},"Pick n Pay":{"count":241},"ICA":{"count":192},"Tengelmann":{"count":188},"Carrefour":{"count":1640},"Waitrose":{"count":258},"Spar":{"count":2386},"Hofer":{"count":442},"M-Preis":{"count":76},"tegut":{"count":210},"Sainsbury's Local":{"count":118},"E-Center":{"count":66},"Aldi Nord":{"count":210},"nahkauf":{"count":84},"Meijer":{"count":76},"Safeway":{"count":410},"Costco":{"count":152},"Albert":{"count":185},"Jumbo":{"count":194},"Shoprite":{"count":244},"MPreis":{"count":54},"Penny Market":{"count":429},"Tesco Extra":{"count":123},"Albert Heijn":{"count":476},"IGA":{"count":363},"Super U":{"count":488},"Metro":{"count":260},"Neukauf":{"count":77},"Migros":{"count":459},"Marktkauf":{"count":121},"Delikatesy Centrum":{"count":59},"C1000":{"count":307},"Hoogvliet":{"count":53},"Food Basics":{"count":75},"Casino":{"count":264},"Penny Markt":{"count":466},"Giant":{"count":191},"COOP Jednota":{"count":73},"Rema 1000":{"count":368},"Kaufpark":{"count":96},"ALDI SÜD":{"count":113},"Simply Market":{"count":330},"Konzum":{"count":230},"Carrefour Express":{"count":353},"Eurospar":{"count":270},"Mercator":{"count":125},"Famila":{"count":130},"Hemköp":{"count":82},"real,-":{"count":81},"Markant":{"count":88},"Volg":{"count":135},"Leader Price":{"count":267},"Treff 3000":{"count":94},"SuperBrugsen":{"count":67},"Kaiser's":{"count":256},"K+K":{"count":106},"Unimarkt":{"count":86},"Carrefour City":{"count":126},"Sobeys":{"count":122},"S-Market":{"count":109},"Combi":{"count":55},"Denner":{"count":276},"Konsum":{"count":133},"Franprix":{"count":312},"Monoprix":{"count":198},"Diska":{"count":69},"PENNY":{"count":79},"Dia":{"count":835},"Giant Eagle":{"count":85},"NORMA":{"count":115},"AD Delhaize":{"count":63},"Auchan":{"count":152},"Mercadona":{"count":769},"Consum":{"count":130},"Carrefour Market":{"count":80},"Whole Foods":{"count":210,"tags":{"shop":"supermarket"}},"Pam":{"count":56},"sky":{"count":105},"Despar":{"count":146},"Eroski":{"count":208},"Costcutter":{"count":63},"Maxi":{"count":108},"Colruyt":{"count":180},"The Co-operative":{"count":64},"Intermarché":{"count":1210},"Delhaize":{"count":207},"CBA":{"count":176},"Shopi":{"count":53},"Walmart":{"count":644},"Kroger":{"count":317},"Albertsons":{"count":242},"Trader Joe's":{"count":235},"Feneberg":{"count":58},"denn's Biomarkt":{"count":52},"dm":{"count":114},"Kvickly":{"count":55},"Makro":{"count":140},"Dico":{"count":53},"Nah & Frisch":{"count":73},"Champion":{"count":59},"ICA Supermarket":{"count":51},"Fakta":{"count":235},"Магнит":{"count":1760},"Caprabo":{"count":103},"Famiglia Cooperativa":{"count":64},"Народная 7Я семьЯ":{"count":154},"Esselunga":{"count":85},"Maxima":{"count":102},"Petit Casino":{"count":111},"Wasgau":{"count":60},"Pingo Doce":{"count":253},"Match":{"count":140},"Profi":{"count":60},"Lider":{"count":65},"Unimarc":{"count":177},"The Co-operative Food":{"count":190},"Santa Isabel":{"count":128},"Седьмой континент":{"count":79},"HIT":{"count":59},"Rimi":{"count":106},"Conad":{"count":304},"Фуршет":{"count":76},"Willys":{"count":56},"Farmfoods":{"count":64},"U Express":{"count":51},"Фора":{"count":52},"Dunnes Stores":{"count":73},"Сільпо":{"count":125},"マルエツ":{"count":59},"Piggly Wiggly":{"count":57},"Crai":{"count":54},"El Árbol":{"count":73},"Centre Commercial E. Leclerc":{"count":549},"Foodland":{"count":100},"Super Brugsen":{"count":67},"Дикси":{"count":683},"Пятёрочка":{"count":1344},"Publix":{"count":339},"Føtex":{"count":66},"coop":{"count":73},"Fressnapf":{"count":69},"Coop Konsum":{"count":79},"Carrefour Contact":{"count":83},"No Frills":{"count":105},"Plodine":{"count":52},"ADEG":{"count":68},"Minipreço":{"count":111},"Biedronka":{"count":1335},"Eurospin":{"count":155},"Семья":{"count":62},"Gadis":{"count":53},"Евроопт":{"count":68},"Centra":{"count":51},"Квартал":{"count":82},"New World":{"count":69},"Countdown":{"count":95},"Reliance Fresh":{"count":61},"Stokrotka":{"count":98},"Coop Jednota":{"count":74},"Fred Meyer":{"count":64},"Irma":{"count":58},"Continente":{"count":75},"Price Chopper":{"count":99},"Game":{"count":52},"Soriana":{"count":93},"Alimerka":{"count":64},"Piotr i Paweł":{"count":53},"Перекресток":{"count":312},"Maxima X":{"count":117},"Карусель":{"count":55},"ALDI Nord":{"count":51},"Condis":{"count":67},"Sam's Club":{"count":138},"Копейка":{"count":87},"Géant Casino":{"count":54},"ASDA":{"count":180},"Intermarche":{"count":115},"Stop & Shop":{"count":66},"Food Lion":{"count":216},"Harris Teeter":{"count":92},"Foodworks":{"count":62},"Polo Market":{"count":86},"Лента":{"count":51},"西友 (SEIYU)":{"count":58},"H-E-B":{"count":293},"Атак":{"count":53},"Полушка":{"count":139},"Extra":{"count":82},"Lewiatan":{"count":94},"Sigma":{"count":51},"АТБ":{"count":322},"Społem":{"count":55},"Bodega Aurrera":{"count":82},"Tesco Lotus":{"count":77},"Мария-Ра":{"count":108},"Магнолия":{"count":72},"Магазин":{"count":120},"Монетка":{"count":174},"Hy-Vee":{"count":75},"Walmart Supercenter":{"count":133},"Hannaford":{"count":57},"Wegmans":{"count":83},"業務スーパー":{"count":61},"Norfa XL":{"count":55},"ヨークマート (YorkMart)":{"count":64},"Leclerc Drive":{"count":76}},"electronics":{"Media Markt":{"count":285},"Maplin":{"count":65},"Best Buy":{"count":345},"Future Shop":{"count":73},"Saturn":{"count":134},"Currys":{"count":80},"Radio Shack":{"count":269},"Euronics":{"count":115},"Expert":{"count":123},"Эльдорадо":{"count":184},"Darty":{"count":74},"М.Видео":{"count":89},"ヤマダ電機":{"count":51}},"convenience":{"Shell":{"count":255},"Spar":{"count":1119},"McColl's":{"count":100},"Tesco Express":{"count":426},"Sainsbury's Local":{"count":104},"Aral":{"count":56},"One Stop":{"count":146},"The Co-operative Food":{"count":115},"Londis":{"count":352},"7-Eleven":{"count":4440},"CBA":{"count":135},"Coop":{"count":678},"Sale":{"count":80},"Statoil":{"count":69},"Sheetz":{"count":54},"Konzum":{"count":173},"Siwa":{"count":216},"Mercator":{"count":57},"Esso":{"count":67},"COOP Jednota":{"count":181},"Mac's":{"count":152},"Alepa":{"count":62},"Hasty Market":{"count":54},"K-Market":{"count":54},"Costcutter":{"count":292},"Valintatalo":{"count":62},"Casino":{"count":90},"Franprix":{"count":61},"Circle K":{"count":289},"セブンイレブン":{"count":3011,"tags":{"name:en":"7-Eleven"}},"ローソン":{"count":1596,"tags":{"name:en":"LAWSON"}},"BP":{"count":163},"Tesco":{"count":55},"Petit Casino":{"count":233},"Volg":{"count":116},"Mace":{"count":115},"Mini Market":{"count":272},"Nisa Local":{"count":77},"Dorfladen":{"count":75},"Продукты":{"count":4285},"Mini Stop":{"count":228},"LAWSON":{"count":419},"デイリーヤマザキ":{"count":141},"Biedronka":{"count":83},"Надежда":{"count":56},"Mobil":{"count":66},"Nisa":{"count":51},"Premier":{"count":129},"ABC":{"count":152},"ミニストップ":{"count":316,"tags":{"name:en":"MINISTOP"}},"サンクス":{"count":560,"tags":{"name:en":"sunkus"}},"スリーエフ":{"count":88},"8 à Huit":{"count":61},"Tchibo":{"count":56},"Żabka":{"count":546},"Almacen":{"count":229},"Vival":{"count":194},"FamilyMart":{"count":529},"ファミリーマート":{"count":1608,"tags":{"name:en":"FamilyMart"}},"Carrefour City":{"count":57},"Sunkus":{"count":62},"Casey's General Store":{"count":95},"セブンイレブン(Seven-Eleven)":{"count":65},"Jednota":{"count":58},"Магазин":{"count":915},"Гастроном":{"count":152},"Centra":{"count":111},"Магнит":{"count":701},"サークルK":{"count":538,"tags":{"name:en":"Circle K"}},"Wawa":{"count":135},"Proxi":{"count":123},"Универсам":{"count":78},"Перекресток":{"count":51},"Groszek":{"count":65},"Select":{"count":62},"Večerka":{"count":51},"Potraviny":{"count":249},"Смак":{"count":78},"Эконом":{"count":55},"Березка":{"count":77},"Społem":{"count":93},"Carrefour Express":{"count":84},"Cumberland Farms":{"count":63},"Chevron":{"count":59},"Coop Jednota":{"count":66},"Tesco Lotus Express":{"count":67},"Kiosk":{"count":55},"Sklep spożywczy":{"count":130},"24 часа":{"count":58},"Минимаркет":{"count":102},"Oxxo":{"count":669},"Пятёрочка":{"count":398},"abc":{"count":74},"7/11":{"count":51},"Stewart's":{"count":255},"Продукти":{"count":171},"ローソンストア100 (LAWSON STORE 100)":{"count":85},"Дикси":{"count":119},"Радуга":{"count":86},"ローソンストア100":{"count":76},"เซเว่นอีเลฟเว่น":{"count":185},"Delikatesy Centrum":{"count":53},"Citgo":{"count":62},"Фортуна":{"count":51},"Kum & Go":{"count":59},"Мария-Ра":{"count":76},"Picard":{"count":57},"Four Square":{"count":52},"Визит":{"count":57},"Авоська":{"count":55},"Dollar General":{"count":127},"Studenac":{"count":76},"Central Convenience Store":{"count":55},"Монетка":{"count":62},"продукты":{"count":114},"Теремок":{"count":56},"Kwik Trip":{"count":69},"Кулинария":{"count":55},"全家":{"count":90},"Мечта":{"count":54},"Epicerie":{"count":102},"Кировский":{"count":67},"Food Mart":{"count":117},"Delikatesy":{"count":81},"ポプラ":{"count":54},"Lewiatan":{"count":135},"Продуктовый магазин":{"count":149},"Продуктовый":{"count":84},"セイコーマート (Seicomart)":{"count":72},"Виктория":{"count":70},"Весна":{"count":57},"Mini Market Non-Stop":{"count":60},"QuikTrip":{"count":75},"Копеечка":{"count":51},"Royal Farms":{"count":51},"Alfamart":{"count":103},"Indomaret":{"count":141},"магазин":{"count":171},"全家便利商店":{"count":156},"Boutique":{"count":59},"მარკეტი (Market)":{"count":144},"Stores":{"count":61}},"chemist":{"dm":{"count":939},"Müller":{"count":212},"Schlecker":{"count":187},"Etos":{"count":467},"Bipa":{"count":289},"Rossmann":{"count":1669},"DM Drogeriemarkt":{"count":55},"Ihr Platz":{"count":73},"Douglas":{"count":62},"Kruidvat":{"count":123}},"car_repair":{"Peugeot":{"count":83},"Kwik Fit":{"count":128},"ATU":{"count":261},"Midas":{"count":202},"Feu Vert":{"count":113},"Norauto":{"count":152},"Speedy":{"count":115},"Автозапчасти":{"count":212},"Renault":{"count":171},"Pit Stop":{"count":58},"Jiffy Lube":{"count":198},"Шиномонтаж":{"count":1157},"СТО":{"count":395},"O'Reilly Auto Parts":{"count":81},"Carglass":{"count":112},"шиномонтаж":{"count":62},"Citroen":{"count":51},"Euromaster":{"count":87},"Firestone":{"count":88},"Автосервис":{"count":361},"Advance Auto Parts":{"count":52},"Roady":{"count":56}},"furniture":{"IKEA":{"count":169},"Jysk":{"count":109},"Roller":{"count":78},"Dänisches Bettenlager":{"count":309},"Conforama":{"count":99},"Matratzen Concord":{"count":52},"Мебель":{"count":210},"But":{"count":63}},"doityourself":{"Hornbach":{"count":123},"B&Q":{"count":225},"Hubo":{"count":77},"Mr Bricolage":{"count":88},"Gamma":{"count":111},"OBI":{"count":422},"Lowes":{"count":1152},"Wickes":{"count":123},"Hagebau":{"count":59},"Max Bahr":{"count":79},"Castorama":{"count":153},"Rona":{"count":61},"Home Depot":{"count":865},"Toom Baumarkt":{"count":71},"Homebase":{"count":225},"Baumax":{"count":95},"Lagerhaus":{"count":79},"Bauhaus":{"count":186},"Canadian Tire":{"count":97},"Leroy Merlin":{"count":209},"Hellweg":{"count":58},"Brico":{"count":98},"Bricomarché":{"count":235},"Toom":{"count":67},"Hagebaumarkt":{"count":107},"Praktiker":{"count":122},"Menards":{"count":70},"Weldom":{"count":73},"Bunnings Warehouse":{"count":91},"Ace Hardware":{"count":147},"Home Hardware":{"count":72},"Хозтовары":{"count":86},"Стройматериалы":{"count":197},"Bricorama":{"count":60},"Point P":{"count":59}},"stationery":{"Staples":{"count":299},"McPaper":{"count":83},"Office Depot":{"count":98},"Канцтовары":{"count":63}},"car":{"Skoda":{"count":97},"BMW":{"count":149},"Citroen":{"count":277},"Renault":{"count":382},"Mercedes-Benz":{"count":235},"Volvo":{"count":96},"Ford":{"count":239},"Volkswagen":{"count":217},"Mazda":{"count":105},"Mitsubishi":{"count":73},"Fiat":{"count":93},"Автозапчасти":{"count":277},"Opel":{"count":165},"Audi":{"count":121},"Toyota":{"count":271},"Nissan":{"count":189},"Suzuki":{"count":75},"Honda":{"count":157},"Peugeot":{"count":308},"Шиномонтаж":{"count":259},"Hyundai":{"count":166},"Subaru":{"count":58},"Chevrolet":{"count":86},"Автомагазин":{"count":72}},"clothes":{"Matalan":{"count":90},"KiK":{"count":1219},"H&M":{"count":658},"Urban Outfitters":{"count":63},"Vögele":{"count":132},"Zeeman":{"count":121},"Takko":{"count":515},"Adler":{"count":55},"C&A":{"count":506},"Zara":{"count":217},"Vero Moda":{"count":95},"NKD":{"count":486},"Ernsting's family":{"count":312},"Winners":{"count":65},"River Island":{"count":59},"Next":{"count":176},"Gap":{"count":81},"Adidas":{"count":92},"Woolworths":{"count":117},"Mr Price":{"count":88},"Jet":{"count":61},"Pep":{"count":134},"Edgars":{"count":110},"Ackermans":{"count":91},"Truworths":{"count":65},"Ross":{"count":93},"Burton":{"count":51},"Dorothy Perkins":{"count":53},"Deichmann":{"count":61},"Lindex":{"count":73},"s.Oliver":{"count":56},"Cecil":{"count":51},"Dress Barn":{"count":52},"Old Navy":{"count":174},"Jack & Jones":{"count":52},"Pimkie":{"count":73},"Esprit":{"count":231},"Primark":{"count":92},"Bonita":{"count":155},"Mexx":{"count":67},"Gerry Weber":{"count":71},"Tally Weijl":{"count":70},"Mango":{"count":133},"TK Maxx":{"count":84},"Benetton":{"count":101},"Ulla Popken":{"count":61},"AWG":{"count":66},"Tommy Hilfiger":{"count":75},"New Yorker":{"count":180},"Orsay":{"count":73},"Jeans Fritz":{"count":51},"Charles Vögele":{"count":69},"New Look":{"count":126},"Lacoste":{"count":78},"Etam":{"count":53},"Kiabi":{"count":148},"Jack Wolfskin":{"count":60},"American Apparel":{"count":57},"Men's Wearhouse":{"count":54},"Intimissimi":{"count":52},"United Colors of Benetton":{"count":96},"Jules":{"count":63},"Second Hand":{"count":53},"AOKI":{"count":57},"Calzedonia":{"count":68},"洋服の青山":{"count":100},"Levi's":{"count":63},"Celio":{"count":74},"TJ Maxx":{"count":57},"Promod":{"count":82},"Street One":{"count":72},"ユニクロ":{"count":59},"Banana Republic":{"count":57},"Одежда":{"count":75},"Marshalls":{"count":56},"La Halle":{"count":62},"Peacocks":{"count":89},"しまむら":{"count":60}},"books":{"Bruna":{"count":58},"Waterstones":{"count":90},"Libro":{"count":57},"Barnes & Noble":{"count":267},"Weltbild":{"count":74},"Thalia":{"count":121},"Книги":{"count":112}},"department_store":{"Debenhams":{"count":67},"Canadian Tire":{"count":75},"Karstadt":{"count":64},"Walmart":{"count":517},"Kmart":{"count":143},"Target":{"count":574},"Galeria Kaufhof":{"count":61},"Marks & Spencer":{"count":66},"Big W":{"count":57},"Woolworth":{"count":78},"Универмаг":{"count":72},"Sears":{"count":235},"Walmart Supercenter":{"count":101},"Kohl's":{"count":153},"Macy's":{"count":147},"Sam's Club":{"count":54},"JCPenney":{"count":66}},"alcohol":{"Alko":{"count":145},"The Beer Store":{"count":150},"Systembolaget":{"count":210},"LCBO":{"count":239},"Ароматный мир":{"count":62},"Bargain Booze":{"count":62},"Nicolas":{"count":119},"BWS":{"count":70},"Botilleria":{"count":77},"SAQ":{"count":72},"Gall & Gall":{"count":512},"Живое пиво":{"count":70}},"bakery":{"Kamps":{"count":252},"Banette":{"count":52},"Bäckerei Schmidt":{"count":57},"Anker":{"count":73},"Hofpfisterei":{"count":111},"Greggs":{"count":276},"Oebel":{"count":57},"Boulangerie":{"count":266},"Stadtbäckerei":{"count":57},"Steinecke":{"count":145},"Ihle":{"count":76},"Goldilocks":{"count":59},"Dat Backhus":{"count":67},"K&U":{"count":61},"Der Beck":{"count":96},"Thürmann":{"count":54},"Backwerk":{"count":95},"Schäfer's":{"count":51},"Panaderia":{"count":168},"Goeken backen":{"count":51},"Stadtbäckerei Junge":{"count":51},"Boulangerie Patisserie":{"count":119},"Paul":{"count":81},"Хлеб":{"count":89},"Пекарня":{"count":52},"Кулиничи":{"count":51}},"sports":{"Sports Direct":{"count":57},"Decathlon":{"count":309},"Intersport":{"count":283},"Sports Authority":{"count":75},"Спортмастер":{"count":87},"Sport 2000":{"count":90},"Dick's Sporting Goods":{"count":77}},"variety_store":{"Tedi":{"count":157},"Dollarama":{"count":103},"Family Dollar":{"count":61},"Dollar Tree":{"count":110},"Dollar General":{"count":80}},"pet":{"Fressnapf":{"count":318},"PetSmart":{"count":177},"Das Futterhaus":{"count":69},"Pets at Home":{"count":62},"Petco":{"count":101},"Зоомагазин":{"count":100}},"shoes":{"Deichmann":{"count":622},"Reno":{"count":183},"Ecco":{"count":55},"Clarks":{"count":109},"La Halle aux Chaussures":{"count":69},"Brantano":{"count":71},"Geox":{"count":51},"Salamander":{"count":51},"Обувь":{"count":100},"Payless Shoe Source":{"count":67},"Famous Footwear":{"count":59},"Quick Schuh":{"count":72},"Shoe Zone":{"count":55},"Foot Locker":{"count":82},"Bata":{"count":101},"ЦентрОбувь":{"count":51}},"toys":{"La Grande Récré":{"count":56},"Toys R Us":{"count":151,"tags":{"shop":"toys"}},"Intertoys":{"count":57},"Детский мир":{"count":86},"Игрушки":{"count":58}},"travel_agency":{"Flight Centre":{"count":92},"Thomas Cook":{"count":119}},"jewelry":{"Bijou Brigitte":{"count":57},"Christ":{"count":57},"Swarovski":{"count":74}},"optician":{"Fielmann":{"count":232},"Apollo Optik":{"count":150},"Vision Express":{"count":58},"Оптика":{"count":182},"Optic 2000":{"count":98},"Alain Afflelou":{"count":73},"Specsavers":{"count":124},"Krys":{"count":77},"Atol":{"count":55}},"video":{"Blockbuster":{"count":184},"World of Video":{"count":64}},"mobile_phone":{"Билайн":{"count":128},"ソフトバンクショップ (SoftBank shop)":{"count":255},"Vodafone":{"count":355},"O2":{"count":208},"Carphone Warehouse":{"count":127},"Orange":{"count":246},"Verizon Wireless":{"count":125},"Sprint":{"count":109},"T-Mobile":{"count":175},"МТС":{"count":352},"Евросеть":{"count":506},"Bell":{"count":190},"The Phone House":{"count":83},"SFR":{"count":71},"Связной":{"count":439},"Мегафон":{"count":251},"AT&T":{"count":124},"ドコモショップ (docomo shop)":{"count":114},"au":{"count":65},"Movistar":{"count":77},"Bitė":{"count":72}},"hifi":{},"computer":{"PC World":{"count":55},"DNS":{"count":128}},"hairdresser":{"Klier":{"count":119},"Supercuts":{"count":106},"Hairkiller":{"count":51},"Great Clips":{"count":182},"Парикмахерская":{"count":510},"Стиль":{"count":51},"Franck Provost":{"count":70},"Салон красоты":{"count":70}},"hardware":{"1000 мелочей":{"count":61},"Хозтовары":{"count":151},"Стройматериалы":{"count":54}},"motorcycle":{"Yamaha":{"count":67},"Honda":{"count":69}}}},"addressFormats":[{"format":[["housenumber","street"],["city","postcode"]]},{"countryCodes":["gb"],"format":[["housename"],["housenumber","street"],["city","postcode"]]},{"countryCodes":["ie"],"format":[["housename"],["housenumber","street"],["city"],["postcode"]]},{"countryCodes":["ad","at","ba","be","ch","cz","de","dk","es","fi","gr","hr","is","it","li","nl","no","pl","pt","se","si","sk","sm","va"],"format":[["street","housenumber"],["postcode","city"]]},{"countryCodes":["fr","lu","mo"],"format":[["housenumber","street"],["postcode","city"]]},{"countryCodes":["br"],"format":[["street"],["housenumber","suburb"],["city","postcode"]]},{"countryCodes":["vn"],"format":[["housenumber","street"],["subdistrict"],["district"],["city"],["province","postcode"]]},{"countryCodes":["us"],"format":[["housenumber","street"],["city","state","postcode"]]},{"countryCodes":["ca"],"format":[["housenumber","street"],["city","province","postcode"]]}]};